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

Eclipse formatter for java ending braces -

ruby - Nesting modules inside of a Rails eninge gem -

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