1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22/** 23 * A parser for AD samAccountName or userPrincipalName parser. 24 */ 25class CADNameAttributeParser extends CParser { 26 27 const ZBX_TYPE_UNKNOWN = 0; 28 const ZBX_TYPE_SAMA = 0x1; 29 const ZBX_TYPE_UPN = 0x2; 30 31 /** 32 * User name attribute type. 33 * 34 * @var int 35 */ 36 private $name_type; 37 38 /** 39 * @var string 40 */ 41 private $user_name; 42 43 /** 44 * @var string 45 */ 46 private $domain_name; 47 48 /** 49 * @var array 50 */ 51 private $options; 52 53 /** 54 * Create instance of parser. 55 * 56 * @param array $options Array of options. 57 * @param int $options['strict'] For sAMAccount name check length of parsed domain and user. Default false. 58 * @param int $options['nametype'] Bit mask what type of user name should be parsed. 59 * Default parse sAMAccountName and UserPrincipalName. 60 */ 61 public function __construct(array $options = []) { 62 $options += ['nametype' => self::ZBX_TYPE_SAMA | self::ZBX_TYPE_UPN]; 63 $this->options = [ 64 'strict' => array_key_exists('strict', $options) && (bool) $options['strict'], 65 'type_sama' => array_key_exists('nametype', $options) && ($options['nametype'] & self::ZBX_TYPE_SAMA), 66 'type_upn' => array_key_exists('nametype', $options) && ($options['nametype'] & self::ZBX_TYPE_UPN) 67 ]; 68 } 69 70 /** 71 * Parse given name attribute value. 72 * 73 * @param string $source Source string that needs to be parsed. 74 * @param int $pos Position offset. 75 * 76 * @return int 77 */ 78 public function parse($source, $pos = 0) { 79 $this->length = 0; 80 $this->user_name = ''; 81 $this->domain_name = ''; 82 $this->match = ''; 83 $this->name_type = self::ZBX_TYPE_UNKNOWN; 84 85 if (($this->options['type_upn']) && $this->parseUserPrincipalName($source, $pos) == self::PARSE_SUCCESS) { 86 $this->name_type = self::ZBX_TYPE_UPN; 87 } 88 elseif ($this->options['type_sama'] && $this->parseSamAccountName($source, $pos) == self::PARSE_SUCCESS) { 89 $this->name_type = self::ZBX_TYPE_SAMA; 90 } 91 else { 92 return self::PARSE_FAIL; 93 } 94 95 return isset($source[$pos]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS; 96 } 97 98 /** 99 * Get parsed name attribute type. 100 * 101 * @return int 102 */ 103 public function getNameType() { 104 return $this->name_type; 105 } 106 107 /** 108 * Get parsed user name value. 109 * 110 * @return string|null 111 */ 112 public function getUserName() { 113 return ($this->name_type == self::ZBX_TYPE_UNKNOWN) ? null : $this->user_name; 114 } 115 116 /** 117 * Get parsed user domain name value. 118 * 119 * @return string|null 120 */ 121 public function getDomainName() { 122 return ($this->name_type == self::ZBX_TYPE_UNKNOWN) ? null : $this->domain_name; 123 } 124 125 /** 126 * Parse string searching for samAccountName. 127 * https://docs.microsoft.com/en-us/windows/desktop/ADSchema/a-samaccountname 128 * https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx 129 * 130 * @param string $source Source string that needs to be parsed. 131 * @param int $pos Position offset. 132 * 133 * @return int 134 */ 135 private function parseSamAccountName($source, &$pos) { 136 $strict = $this->options['strict']; 137 $regex = '/^(?<domain>[^\\\\\/\:\*\?\"\<\>]'. 138 ($strict ? '{1,15}' : '+'). 139 ')\\\(?<user>[^\\\\\/\:\*\?\"\<\>@]'. 140 ($strict ? '{1,20}' : '+'). 141 ')/i'; 142 143 if (preg_match($regex, substr($source, $pos), $matches)) { 144 $this->length = strlen($matches[0]); 145 } 146 else { 147 return self::PARSE_FAIL; 148 } 149 150 $this->match = $matches[0]; 151 $this->domain_name = $matches['domain']; 152 $this->user_name = $matches['user']; 153 $pos += $this->length; 154 155 return self::PARSE_SUCCESS; 156 } 157 158 /** 159 * Parse string searching for UserPrincipalName. 160 * https://docs.microsoft.com/en-us/windows/desktop/ADSchema/a-userprincipalname 161 * 162 * @param string $source Source string that needs to be parsed. 163 * @param int $pos Position offset. 164 * 165 * @return int 166 */ 167 private function parseUserPrincipalName($source, &$pos) { 168 $regex = '/^(?<user>[_a-z0-9-@]+(\.[_a-z0-9-]+)*)@(?<domain>[a-z0-9-]+(\.[a-z0-9-]+)*)/i'; 169 170 if (preg_match($regex, substr($source, $pos), $matches)) { 171 $this->length = strlen($matches[0]); 172 } 173 else { 174 return self::PARSE_FAIL; 175 } 176 177 $this->match = $matches[0]; 178 $this->domain_name = $matches['domain']; 179 $this->user_name = $matches['user']; 180 $pos += $this->length; 181 182 return self::PARSE_SUCCESS; 183 } 184} 185