<?php /* ** Copyright (C) 2001-2025 Zabbix SIA ** ** This program is free software: you can redistribute it and/or modify it under the terms of ** the GNU Affero General Public License as published by the Free Software Foundation, version 3. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU Affero General Public License for more details. ** ** You should have received a copy of the GNU Affero General Public License along with this program. ** If not, see <https://www.gnu.org/licenses/>. **/ /** * A parser for AD samAccountName or userPrincipalName parser. */ class CADNameAttributeParser extends CParser { const ZBX_TYPE_UNKNOWN = 0; const ZBX_TYPE_SAMA = 0x1; const ZBX_TYPE_UPN = 0x2; /** * User name attribute type. * * @var int */ private $name_type; /** * @var string */ private $user_name; /** * @var string */ private $domain_name; /** * @var array */ private $options; /** * Create instance of parser. * * @param array $options Array of options. * @param int $options['strict'] For sAMAccount name check length of parsed domain and user. Default false. * @param int $options['nametype'] Bit mask what type of user name should be parsed. * Default parse sAMAccountName and UserPrincipalName. */ public function __construct(array $options = []) { $options += ['nametype' => self::ZBX_TYPE_SAMA | self::ZBX_TYPE_UPN]; $this->options = [ 'strict' => array_key_exists('strict', $options) && (bool) $options['strict'], 'type_sama' => array_key_exists('nametype', $options) && ($options['nametype'] & self::ZBX_TYPE_SAMA), 'type_upn' => array_key_exists('nametype', $options) && ($options['nametype'] & self::ZBX_TYPE_UPN) ]; } /** * Parse given name attribute value. * * @param string $source Source string that needs to be parsed. * @param int $pos Position offset. * * @return int */ public function parse($source, $pos = 0) { $this->length = 0; $this->user_name = ''; $this->domain_name = ''; $this->match = ''; $this->name_type = self::ZBX_TYPE_UNKNOWN; if (($this->options['type_upn']) && $this->parseUserPrincipalName($source, $pos) == self::PARSE_SUCCESS) { $this->name_type = self::ZBX_TYPE_UPN; } elseif ($this->options['type_sama'] && $this->parseSamAccountName($source, $pos) == self::PARSE_SUCCESS) { $this->name_type = self::ZBX_TYPE_SAMA; } else { return self::PARSE_FAIL; } return isset($source[$pos]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS; } /** * Get parsed name attribute type. * * @return int */ public function getNameType() { return $this->name_type; } /** * Get parsed user name value. * * @return string|null */ public function getUserName() { return ($this->name_type == self::ZBX_TYPE_UNKNOWN) ? null : $this->user_name; } /** * Get parsed user domain name value. * * @return string|null */ public function getDomainName() { return ($this->name_type == self::ZBX_TYPE_UNKNOWN) ? null : $this->domain_name; } /** * Parse string searching for samAccountName. * https://docs.microsoft.com/en-us/windows/desktop/ADSchema/a-samaccountname * https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx * * @param string $source Source string that needs to be parsed. * @param int $pos Position offset. * * @return int */ private function parseSamAccountName($source, &$pos) { $strict = $this->options['strict']; $regex = '/^(?<domain>[^\\\\\/\:\*\?\"\<\>]'. ($strict ? '{1,15}' : '+'). ')\\\(?<user>[^\\\\\/\:\*\?\"\<\>@]'. ($strict ? '{1,20}' : '+'). ')/i'; if (preg_match($regex, substr($source, $pos), $matches)) { $this->length = strlen($matches[0]); } else { return self::PARSE_FAIL; } $this->match = $matches[0]; $this->domain_name = $matches['domain']; $this->user_name = $matches['user']; $pos += $this->length; return self::PARSE_SUCCESS; } /** * Parse string searching for UserPrincipalName. * https://docs.microsoft.com/en-us/windows/desktop/ADSchema/a-userprincipalname * * @param string $source Source string that needs to be parsed. * @param int $pos Position offset. * * @return int */ private function parseUserPrincipalName($source, &$pos) { $regex = '/^(?<user>[_a-z0-9-@]+(\.[_a-z0-9-]+)*)@(?<domain>[a-z0-9-]+(\.[a-z0-9-]+)*)/i'; if (preg_match($regex, substr($source, $pos), $matches)) { $this->length = strlen($matches[0]); } else { return self::PARSE_FAIL; } $this->match = $matches[0]; $this->domain_name = $matches['domain']; $this->user_name = $matches['user']; $pos += $this->length; return self::PARSE_SUCCESS; } }