ldap - PHP strpos on string returned from ldap_search -


ok, here's problem:

i search userparameters attribute ldap_search. needed couple of values out, being "ctxwfhomedirdrive", "ctxwfhomedir" , "ctxwfprofilepath"

the string got complete jibberish, after entire day of trying out every single character encoding conversion find, 1 did half trick:

$puserparams = iconv('utf-8', 'utf-32', $entry_value) 

for reason individual hex numbers following 3 values needed extract inversed (so 5c, backslash, came out c5. don't ask how figured 1 out :-p )

knowing this, convert hex values display actual citrix homedirdrive, profilepath, etc.

however, still need filter out lot of unneeded characters final result string, following returns false reason:

if (strpos($puserparams,"ctxwfprofilepath") !== false) {} 

when echo $puserparams variable, display whole string above 3 ctx parameters in it.

i suspected must have special characters in result string, tried removing line breaks, eol's, unserializing string (which produces error @ offset 0), etc etc

nothing seems work... have idea?

thanks, vincent

original string run through hex2bin:

20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202050071a080143747843666750726573656e74e394b5e694b1e688b0e381a2180801437478436667466c61677331e380b0e381a6e380b2e380b9120801437478536861646f77e384b0e380b0e380b0e380b02a02014374784d696e456e6372797074696f6e4c6576656ce384b02206014374785746486f6d654469724472697665e3a0b4e684b3e380b018c282014374785746486f6d65446972e68cb5e68cb5e394b7e38cb7e39cb6e388b6e394b6e398b6e3a4b6e68cb6e394b6e380b3e380b3e384b3e694b2e394b7e38cb7e39cb6e380b7e394b6e698b6e380b7e68cb6e394b6e388b6e394b6e694b2e3a4b6e694b6e390b7e68cb5e394b7e38cb7e394b6e388b7e3a0b6e698b6e690b6e394b6e390b6e388b7e3a4b6e398b7e394b6e390b2e68cb5e38cb7e390b7e3a4b6e684b6e694b6e694b2e688b6e698b6e688b6e688b6e394b6e68cb6e394b6e694b6e388b6e394b6e388b7e39cb6e380b020c28001437478574650726f66696c6550617468e68cb5e68cb5e384b6e38cb7e380b7e694b6e394b6e390b7e384b6e380b7e380b7e380b3e380b3e384b3e694b2e384b6e38cb7e380b7e694b2e3a4b6e694b6e390b7e68cb5e380b7e388b7e698b6e398b6e3a4b6e68cb6e394b6e38cb7e698b5e388b6e394b6e68cb6e39cb6e3a4b6e394b7e690b6e390b2e68cb5e38cb5e38cb5e38cb4e68cb5e38cb7e390b7e3a4b6e684b6e694b6e694b2e688b6e698b6e688b6e688b6e394b6e68cb6e394b6e694b6e388b6e394b6e388b7e39cb6e380b0e380b0 ��  

to build on work done tenzian, ended creating few self-contained classes can used safely decode/encode userparameters blob extract , modify/view/create ts properties. make few assumptions:

  • php 5.6+ (feel free edit suit needs/work lower version)
  • assumes of classes defined in same namespace.
  • for multi-byte string support have mbstring extension loaded.
  • any ts property time represented in terms of minutes.

anyway, these classes need:

tspropertyarray

/**  * represents tspropertyarray data contains individual tsproperty structures in userparameters value.  *  * @see https://msdn.microsoft.com/en-us/library/ff635189.aspx  * @author chad sikorra <chad.sikorra@gmail.com>  */ class tspropertyarray {     /**      * represents tspropertyarray data valid.      */     const valid_signature = 'p';      /**      * @var array default values tspropertyarray structure.      */     const defaults = [         'ctxcfgpresent' => 2953518677,         'ctxwfprofilepath' => '',         'ctxwfprofilepathw' => '',         'ctxwfhomedir' => '',         'ctxwfhomedirw' => '',         'ctxwfhomedirdrive' => '',         'ctxwfhomedirdrivew' => '',         'ctxshadow' => 1,         'ctxmaxdisconnectiontime' => 0,         'ctxmaxconnectiontime' => 0,         'ctxmaxidletime' => 0,         'ctxworkdirectory' => '',         'ctxworkdirectoryw' => '',         'ctxcfgflags1' => 2418077696,         'ctxinitialprogram' => '',         'ctxinitialprogramw' => '',     ];      /**      * @var string default data occurs before tspropertyarray (ctxcfgpresent bunch of spaces...?)      */     protected $defaultprebinary = '43747843666750726573656e742020202020202020202020202020202020202020202020202020202020202020202020';      /**      * @var tsproperty[]      */     protected $tsproperty = [];      /**      * @var string      */     protected $signature = self::valid_signature;      /**      * @var string binary data occurs before tspropertyarray data in userparameters.      */     protected $prebinary = '';      /**      * @var string binary data occurs after tspropertyarray data in userparameters.      */     protected $postbinary = '';      /**      * construct in 1 of following ways:      *      *   - pass array of tsproperty key => value pairs (see defaults constant).      *   - pass userparameters binary value. object representation of decoded , constructed.      *   - pass nothing , default set of tsproperty key => value pairs used (see defaults constant).      *      * @param mixed $tspropertyarray      */     public function __construct($tspropertyarray = null)     {         $this->prebinary = hex2bin($this->defaultprebinary);          if (is_null($tspropertyarray) || is_array($tspropertyarray)) {             $tspropertyarray = $tspropertyarray ?: self::defaults;             foreach ($tspropertyarray $key => $value) {                 $tsproperty = new tsproperty();                 $tsproperty->setname($key);                 $tsproperty->setvalue($value);                 $this->tsproperty[$key] = $tsproperty;             }         } else {             $this->decodeuserparameters($tspropertyarray);         }     }      /**      * check if specific tsproperty exists property name.      *       * @param string $propname      * @return bool      */     public function has($propname)     {         return array_key_exists(strtolower($propname), array_change_key_case($this->tsproperty));     }      /**      * tsproperty object property name (ie. ctxwfprofilepath).      *      * @param string $propname      * @return tsproperty      */     public function get($propname)     {         $this->validateprop($propname);          return $this->gettspropobj($propname)->getvalue();     }      /**      * add tsproperty object. if exists, overwritten.      *      * @param tsproperty $tsproperty      * @return $this      */     public function add(tsproperty $tsproperty)     {         $this->tsproperty[$tsproperty->getname()] = $tsproperty;          return $this;     }      /**      * remove tsproperty property name (ie. ctxminencryptionlevel).      *       * @param string $propname      * @return $this      */     public function remove($propname)     {         foreach (array_keys($this->tsproperty) $property) {             if (strtolower($propname) == strtolower($property)) {                 unset($this->tsproperty[$property]);             }         }          return $this;     }      /**      * set value specific tsproperty name.      *       * @param string $propname      * @param mixed $propvalue      * @return $this      */     public function set($propname, $propvalue)     {         $this->validateprop($propname);         $this->gettspropobj($propname)->setvalue($propvalue);          return $this;     }      /**      * full binary representation of userparameters containing tspropertyarray data.      *      * @return string      */     public function tobinary()     {         $binary = $this->prebinary;          $binary .= hex2bin(str_pad(dechex(mbstring::ord($this->signature)), 2, 0, str_pad_left));         $binary .= hex2bin(str_pad(dechex(count($this->tsproperty)), 2, 0, str_pad_left));         foreach ($this->tsproperty $tsproperty) {             $binary .= $tsproperty->tobinary();         }          return $binary.$this->postbinary;     }      /**      * simple associative array containing of tsproperty names , values.      *       * @return array      */     public function toarray()     {         $userparameters = [];          foreach ($this->tsproperty $property => $tspropobj) {             $userparameters[$property] = $tspropobj->getvalue();         }          return $userparameters;     }      /**      * tsproperty objects.      *       * @return tsproperty[]      */     public function gettsproperties()     {         return $this->tsproperty;     }      /**      * @param string $propname      */     protected function validateprop($propname)     {         if (!$this->has($propname)) {             throw new \invalidargumentexception(sprintf('tsproperty "%s" not exist.', $propname));         }         }      /**      * @param string $propname      * @return tsproperty      */     protected function gettspropobj($propname)     {         return array_change_key_case($this->tsproperty)[strtolower($propname)];     }      /**      * associative array of userparameters property names , values.      *      * @param string $userparameters      * @return array      */     protected function decodeuserparameters($userparameters)     {         $userparameters = bin2hex($userparameters);          // save 96-byte array of reserved data, not ruin may stored there.         $this->prebinary = hex2bin(substr($userparameters, 0, 96));         // signature 2-byte unicode character @ front         $this->signature = mbstring::chr(hexdec(substr($userparameters, 96, 2)));         // asserts validity of tspropertyarray data. reason 'p' means valid...         if ($this->signature != self::valid_signature) {             throw new \invalidargumentexception('invalid tspropertyarray data');         }          // property count 2-byte unsigned integer indicating number of elements tspropertyarray         // starts @ position 98. actual variable data begins @ position 100.         $length = $this->addtspropdata(substr($userparameters, 100), hexdec(substr($userparameters, 98, 2)));          // reserved data length + (count , sig length == 4) + added lengths of tspropertyarray         // saves after variable tspropertyarray data, not squash stored there         if (strlen($userparameters) > (96 + 4 + $length)) {             $this->postbinary = hex2bin(substr($userparameters, (96 + 4 + $length)));         }     }      /**      * given start of tspropertyarray hex data, , count number of tsproperty structures in contains,      * parse , split out individual tsproperty structures. return full length of tspropertyarray data.      *      * @param string $tspropertyarray      * @param int $tspropcount      * @return int length of data in tspropertyarray      */     protected function addtspropdata($tspropertyarray, $tspropcount)     {         $length = 0;          ($i = 0; $i < $tspropcount; $i++) {             // prop length = name length + value length + type length + space length data.             $proplength = hexdec(substr($tspropertyarray, $length, 2)) + (hexdec(substr($tspropertyarray, $length + 2, 2)) * 3) + 6;             $tsproperty = new tsproperty(hex2bin(substr($tspropertyarray, $length, $proplength)));             $this->tsproperty[$tsproperty->getname()] = $tsproperty;             $length += $proplength;         }          return $length;     } } 

tsproperty

/**  * represents tsproperty structure in tspropertyarray of userparameters binary value.  *  * @see https://msdn.microsoft.com/en-us/library/ff635169.aspx  * @see http://daduke.org/linux/userparameters.html  * @author chad sikorra <chad.sikorra@gmail.com>  */ class tsproperty {     /**      * nibble control values. first value each if nibble <= 9, otherwise second value used.      */     const nibble_control = [         'x' => ['001011', '011010'],         'y' => ['001110', '011010'],     ];      /**      * nibble header.      */     const nibble_header = '1110';      /**      * conversion factor needed time values in tspropertyarray (stored in microseconds).      */     const time_conversion = 60 * 1000;      /**      * simple map determine how property needs decoded/encoded from/to binary value.      *      * there names simple repeats have 'w' @ end. not sure signifies.      * cannot find information on them in microsoft documentation. however, values appear stay in sync      * non 'w' counterparts. not doing when manipulating data manually not seem affect anything.      * needs more investigation.      *       * @var array      */     protected $proptypes = [         'string' => [             'ctxwfhomedir',             'ctxwfhomedirw',             'ctxwfhomedirdrive',             'ctxwfhomedirdrivew',             'ctxinitialprogram',             'ctxinitialprogramw',             'ctxwfprofilepath',             'ctxwfprofilepathw',             'ctxworkdirectory',             'ctxworkdirectoryw',             'ctxcallbacknumber',         ],         'time' => [             'ctxmaxdisconnectiontime',             'ctxmaxconnectiontime',             'ctxmaxidletime',         ],         'int' => [             'ctxcfgflags1',             'ctxcfgpresent',             'ctxkeyboardlayout',             'ctxminencryptionlevel',             'ctxnwlogonserver',             'ctxshadow',         ],     ];      /**      * @var string property name.      */     protected $name;      /**      * @var string|int property value.      */     protected $value;      /**      * @var int property value type.      */     protected $valuetype = 1;      /**      * @param string|null $value pass binary tsproperty data construct object representation.      */     public function __construct($value = null)     {         if ($value) {             $this->decode(bin2hex($value));         }     }      /**      * set name tsproperty.      *      * @param string $name      */     public function setname($name)     {         $this->name = $name;     }      /**      * name tsproperty.      *      * @return string      */     public function getname()     {         return $this->name;     }      /**      * set value tsproperty.      *      * @param string|int $value      */     public function setvalue($value)     {         $this->value = $value;     }      /**      * value tsproperty.      *      * @return string|int      */     public function getvalue()     {         return $this->value;     }      /**      * convert tsproperty name/value binary representation userparameters blob.      *       * @return string      */     public function tobinary()     {         $name = bin2hex($this->name);         $binvalue = $this->getencodedvalueforprop($this->name, $this->value);         $valuelen = strlen(bin2hex($binvalue)) / 3;          $binary = hex2bin(             $this->dec2hex(strlen($name))             .$this->dec2hex($valuelen)             .$this->dec2hex($this->valuetype)             .$name         );          return $binary.$binvalue;     }      /**      * given tsproperty blob, decode name/value/type/etc.      *      * @param string $tsproperty      */     protected function decode($tsproperty)     {         $namelength = hexdec(substr($tsproperty, 0, 2));         # 1 data byte 3 encoded bytes         $valuelength = hexdec(substr($tsproperty, 2, 2)) * 3;          $this->valuetype = hexdec(substr($tsproperty, 4, 2));         $this->name = pack('h*', substr($tsproperty, 6, $namelength));         $this->value = $this->getdecodedvalueforprop($this->name, substr($tsproperty, 6 + $namelength, $valuelength));     }      /**      * based on property name/value in question, encoded form.      *      * @param string $propname      * @param string|int $propvalue      * @return string      */     protected function getencodedvalueforprop($propname, $propvalue)     {         if (in_array($propname, $this->proptypes['string'])) {             # simple strings null terminated. unsure if needed or product of how aduc stuff?             $value = $this->encodepropvalue($propvalue."\0", true);         } elseif (in_array($propname, $this->proptypes['time'])) {             # needs in microseconds (assuming in minute format)...             $value = $this->encodepropvalue($propvalue * self::time_conversion);         } else {             $value = $this->encodepropvalue($propvalue);         }          return $value;     }      /**      * based on property name in question, actual value binary blob value.      *      * @param string $propname      * @param string $propvalue      * @return string|int      */     protected function getdecodedvalueforprop($propname, $propvalue)     {         if (in_array($propname, $this->proptypes['string'])) {             // strip away null terminators. think should desired, otherwise ends in confusion.             $value = str_replace("\0", '', $this->decodepropvalue($propvalue, true));         } elseif (in_array($propname, $this->proptypes['time'])) {             // convert microseconds minutes (how aduc displays anyway, , seems practical).             $value = hexdec($this->decodepropvalue($propvalue)) / self::time_conversion;         } elseif (in_array($propname, $this->proptypes['int'])) {             $value = hexdec($this->decodepropvalue($propvalue));         } else {             $value = $this->decodepropvalue($propvalue);         }          return $value;     }      /**      * decode property inspecting nibbles of each blob, checking control, , adding results      * final value.      *      * @param string $hex      * @param bool $string whether or not simple string data.      * @return string      */     protected function decodepropvalue($hex, $string = false)     {         $decodepropvalue = '';         $blobs = str_split($hex, 6);          foreach ($blobs $blob) {             $bin = decbin(hexdec($blob));             $controly = substr($bin, 4, 6);             $nibbley = substr($bin, 10, 4);             $controlx = substr($bin, 14, 6);             $nibblex = substr($bin, 20, 4);             $byte = $this->nibblecontrol($nibblex, $controlx).$this->nibblecontrol($nibbley, $controly);             if ($string) {                 $decodepropvalue .= mbstring::chr(bindec($byte));             } else {                 $decodepropvalue = $this->dec2hex(bindec($byte)).$decodepropvalue;             }         }          return $decodepropvalue;     }      /**      * encoded property value binary blob.      *      * @param string $value      * @param bool $string      * @return string      */     protected function encodepropvalue($value, $string = false)     {         // int must padded. (then split , reversed). string, split chars. seems         // easiest way handle utf-8 characters instead of trying work hex values.         $chars = $string ? mbstring::str_split($value) : array_reverse(str_split($this->dec2hex($value, 8), 2));          $encoded = '';         foreach ($chars $char) {             // bits char. using method ensure padded.             $bits = sprintf('%08b', $string ? mbstring::ord($char) : hexdec($char));             $nibblex = substr($bits, 0, 4);             $nibbley = substr($bits, 4, 4);              // construct value header, high nibble, low nibble.             $value = self::nibble_header;             foreach (['y' => $nibbley, 'x' => $nibblex] $nibbletype => $nibble) {                 $value .= $this->getnibblewithcontrol($nibbletype, $nibble);             }              // convert binary bit stream             foreach ([0, 8, 16] $start) {                 $encoded .= $this->packbitstring(substr($value, $start, 8), 8);             }         }          return $encoded;     }      /**      * php's pack() function has no 'b' or 'b' template. workaround turns literal bit-string      * packed byte-string 8 bits per byte.      *      * @param string $bits      * @param bool $len      * @return string      */     protected function packbitstring($bits, $len)     {         $bits = substr($bits, 0, $len);         // pad input zeros next multiple of 4 above $len         $bits = str_pad($bits, 4 * (int) (($len + 3) / 4), '0');          // split input chunks of 4 bits, convert each hex , pack them         $nibbles = str_split($bits, 4);         foreach ($nibbles $i => $nibble) {             $nibbles[$i] = base_convert($nibble, 2, 16);         }          return pack('h*', implode('', $nibbles));     }      /**      * based on control, adjust nibble accordingly.      *      * @param string $nibble      * @param string $control      * @return string      */     protected function nibblecontrol($nibble, $control)     {         // control stays constant low/high nibbles, doesn't matter compare         if ($control == self::nibble_control['x'][1]) {             $dec = bindec($nibble);             $dec += 9;             $nibble = str_pad(decbin($dec), 4, '0', str_pad_left);         }          return $nibble;     }      /**      * nibble value control prefixed.      *      * if nibble dec <= 9, control x equals 001011 , y equals 001110, otherwise if nibble dec > 9      * control x or y equals 011010. additionally, if dec value of nibble > 9, nibble value      * must subtracted 9 before final value constructed.      *      * @param string $nibbletype either x or y      * @param $nibble      * @return string      */     protected function getnibblewithcontrol($nibbletype, $nibble)     {         $dec = bindec($nibble);          if ($dec > 9) {             $dec -= 9;             $control = self::nibble_control[$nibbletype][1];         } else {             $control = self::nibble_control[$nibbletype][0];         }          return $control.sprintf('%04d', decbin($dec));     }      /**      * need make sure hex values length, pad needed.      *      * @param int $int      * @param int $padlength hex string must padded length (with zeros).      * @return string      */     protected function dec2hex($int, $padlength = 2)     {         return str_pad(dechex($int), $padlength, 0, str_pad_left);     } } 

mbstring

/**  * utility functions handle multi-byte strings properly, support lacking/inconsistent php string  * functions. provides wrapper various workarounds , falls normal functions if needed.  *  * @author chad sikorra <chad.sikorra@gmail.com>  */ class mbstring {     /**      * integer value of specific character.      *      * @param $string      * @return int      */     public static function ord($string)     {         if (self::ismbstringloaded()) {             $result = unpack('n', mb_convert_encoding($string, 'ucs-4be', 'utf-8'));             if (is_array($result) === true) {                 return $result[1];             }         }          return ord($string);     }      /**      * character specific integer value.      *        * @param $int      * @return string      */     public static function chr($int)     {         if (self::ismbstringloaded()) {             return mb_convert_encoding(pack('n', $int), 'utf-8', 'utf-16be');         }          return chr($int);     }      /**      * split string individual characters , return array.      *       * @param string $value      * @return string[]      */     public static function str_split($value)     {         return preg_split('/(?<!^)(?!$)/u', $value);     }      /**      * simple check mbstring extension.      *       * @return bool      */     protected static function ismbstringloaded()     {         return extension_loaded('mbstring');     } } 

displaying userparameters values

// assuming $value binary value userparameters. $tsproparray = new tspropertyarray($value);  // prints out each ts property name , value user. foreach($tsproparray->toarray() $prop => $value) {     echo "$prop => $value".php_eol; }  // print single value echo $tsproparray->get('ctxwfprofilepath'); 

modifying/creating userparameters values

// creates new set of values userparameters (default values). $tsproparray = new tspropertyarray(); // set max session idle time of 30 minutes. $tsproparray->set('ctxmaxidletime', 30); // binary value save ldap $binary = $tsproparray->tobinary();  // load binary userparameters data user $tsproparray = new tspropertyarray($binary); // replace user profile location... $tsproparray->set('ctxwfprofilepath', '\\some\path'); // new binary value, save needed ldap... $binary = $tsproparray->tobinary(); 

a few additional notes

the code above handle multi-byte chars, if there utf8 chars in value should fine. respects other binary data within userparameters binary blob. not destructive, data preserved when modifying existing value.

i noticed there ts properties in userparameters end in 'w' , duplicates of other properties (even values duplicated). not find information on in msdn or elsewhere, i'm not sure significance is.


Comments

Popular posts from this blog

php - Why I am getting the Error "Commands out of sync; you can't run this command now" -

linux - Does gcc have any options to add version info in ELF binary file? -

java - Are there any classes that implement javax.persistence.Parameter<T>? -