| 1 | 2136 | darkviper | <?php
 | 
      
        | 2 |  |  | 
 | 
      
        | 3 |  |  | /*
 | 
      
        | 4 |  |  |  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 | 
      
        | 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 3 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, see <http://www.gnu.org/licenses/>.
 | 
      
        | 18 |  |  |  */
 | 
      
        | 19 |  |  | 
 | 
      
        | 20 |  |  | /**
 | 
      
        | 21 |  |  |  * SecureTokens.php
 | 
      
        | 22 |  |  |  *
 | 
      
        | 23 |  |  |  * @category      Core
 | 
      
        | 24 |  |  |  * @package       Core_Security
 | 
      
        | 25 |  |  |  * @subpackage    WB-2.8.4 and up
 | 
      
        | 26 |  |  |  * @copyright     Manuela v.d.Decken <manuela@isteam.de>
 | 
      
        | 27 |  |  |  * @author        Manuela v.d.Decken <manuela@isteam.de>
 | 
      
        | 28 |  |  |  * @license       http://www.gnu.org/licenses/gpl.html   GPL License
 | 
      
        | 29 |  |  |  * @
 | 
      
        | 30 |  |  |  * @version       0.1.2
 | 
      
        | 31 |  |  |  * @revision      $Revision: $
 | 
      
        | 32 |  |  |  * @link          $HeadURL: $
 | 
      
        | 33 |  |  |  * @lastmodified $Date: $
 | 
      
        | 34 |  |  |  * @since         File available since 12.09.2015
 | 
      
        | 35 |  |  |  * @description
 | 
      
        | 36 |  |  |  * This class is a replacement for the former class SecureForm using the SecureTokensInterface
 | 
      
        | 37 |  |  |  *
 | 
      
        | 38 |  |  |  * Settings for this class
 | 
      
        | 39 | 2138 | darkviper |  * TYPE    KONSTANTE                    REGISTY-VAR                       DEFAULTWERT
 | 
      
        | 40 |  |  |  * boolean SEC_TOKEN_FINGERPRINT        ($oReg->SecTokenFingerprint)      [default=true]
 | 
      
        | 41 |  |  |  * integer SEC_TOKEN_IPV4_NETMASK       ($oReg->SecTokenIpv4Netmask)      0-255 [default=24]
 | 
      
        | 42 |  |  |  * integer SEC_TOKEN_IPV6_PREFIX_LENGTH ($oReg->SecTokenIpv6PrefixLength) 0-128 [default=64]
 | 
      
        | 43 |  |  |  * integer SEC_TOKEN_LIFE_TIME          ($oReg->SecTokenLifeTime)         1800 | 2700 | 3600[default] | 7200
 | 
      
        | 44 | 2136 | darkviper | */
 | 
      
        | 45 |  |  | 
 | 
      
        | 46 |  |  | class SecureTokens
 | 
      
        | 47 |  |  | {
 | 
      
        | 48 | 2137 | darkviper | /**
 | 
      
        | 49 |  |  |  * possible settings for TokenLifeTime in seconds
 | 
      
        | 50 |  |  |  * @description seconds for 30min / 45min / 1h / 75min / 90min / 105min / 2h
 | 
      
        | 51 |  |  |  */
 | 
      
        | 52 |  |  | /** minimum lifetime in seconds */
 | 
      
        | 53 |  |  |     const LIFETIME_MIN  = 1800; // 30min
 | 
      
        | 54 |  |  | /** maximum lifetime in seconds */
 | 
      
        | 55 |  |  |     const LIFETIME_MAX  = 7200; // 120min (2h)
 | 
      
        | 56 |  |  | /** stepwidth between min and max */
 | 
      
        | 57 |  |  |     const LIFETIME_STEP =  900; // 15min
 | 
      
        | 58 | 2136 | darkviper | /** lifetime in seconds to use in DEBUG mode if negative value is given (-1) */
 | 
      
        | 59 | 2137 | darkviper |     const DEBUG_LIFETIME = 300; // 5
 | 
      
        | 60 | 2136 | darkviper | /** array to hold all tokens from the session */
 | 
      
        | 61 |  |  |     private $aTokens = array(
 | 
      
        | 62 | 2137 | darkviper |         'default' => array('value' => 0, 'expire' => 0, 'instance' => 0)
 | 
      
        | 63 | 2136 | darkviper |     );
 | 
      
        | 64 | 2137 | darkviper | /** the salt for this instance */
 | 
      
        | 65 | 2139 | darkviper |     private $sSalt             = '';
 | 
      
        | 66 | 2136 | darkviper | /** fingerprint of the current connection */
 | 
      
        | 67 | 2139 | darkviper |     private $sFingerprint      = '';
 | 
      
        | 68 | 2137 | darkviper | /** the FTAN token which is valid for this instance */
 | 
      
        | 69 | 2139 | darkviper |     private $aLastCreatedFtan  = null;
 | 
      
        | 70 | 2137 | darkviper | /** the time when tokens expired if they created in this instance */
 | 
      
        | 71 | 2139 | darkviper |     private $iExpireTime       = 0;
 | 
      
        | 72 | 2137 | darkviper | /** remove selected tokens only and update all others */
 | 
      
        | 73 |  |  |     private $bPreserveAllOtherTokens = false;
 | 
      
        | 74 |  |  | /** id of the current instance */
 | 
      
        | 75 |  |  |     private $sCurrentInstance  = null;
 | 
      
        | 76 |  |  | /** id of the instance to remove */
 | 
      
        | 77 |  |  |     private $sInstanceToDelete = null;
 | 
      
        | 78 |  |  | /** id of the instance to update expire time */
 | 
      
        | 79 |  |  |     private $sInstanceToUpdate = null;
 | 
      
        | 80 | 2136 | darkviper | /* --- settings for SecureTokens ------------------------------------------------------ */
 | 
      
        | 81 |  |  | /** use fingerprinting to encode */
 | 
      
        | 82 | 2139 | darkviper |     private $bUseFingerprint   = true;
 | 
      
        | 83 | 2136 | darkviper | /** maximum lifetime of a token in seconds */
 | 
      
        | 84 | 2139 | darkviper |     private $iTokenLifeTime    = 1800; // between LIFETIME_MIN and LIFETIME_MAX (default = 30min)
 | 
      
        | 85 | 2136 | darkviper | /** bit length of the IPv4 Netmask (0-32 // 0 = off  default = 24) */
 | 
      
        | 86 | 2139 | darkviper |     private $iNetmaskLengthV4  = 0;
 | 
      
        | 87 | 2136 | darkviper | /** bit length of the IPv6 Netmask (0-128 // 0 = off  default = 64) */
 | 
      
        | 88 | 2139 | darkviper |     private $iNetmaskLengthV6  = 0;
 | 
      
        | 89 | 2136 | darkviper | 
 | 
      
        | 90 |  |  | /**
 | 
      
        | 91 |  |  |  * constructor
 | 
      
        | 92 |  |  |  * @param (void)
 | 
      
        | 93 |  |  |  */
 | 
      
        | 94 | 2137 | darkviper |     protected function __construct()
 | 
      
        | 95 |  |  |     {
 | 
      
        | 96 | 2136 | darkviper |     // load settings if available
 | 
      
        | 97 |  |  |         $this->getSettings();
 | 
      
        | 98 | 2137 | darkviper |     // generate salt for calculations in this instance
 | 
      
        | 99 |  |  |         $this->sSalt        = $this->generateSalt();
 | 
      
        | 100 |  |  |     // generate fingerprint for the current connection
 | 
      
        | 101 |  |  |         $this->sFingerprint = $this->buildFingerprint();
 | 
      
        | 102 |  |  |     // define the expiretime for this instance
 | 
      
        | 103 |  |  |         $this->iExpireTime  = time() + $this->iTokenLifeTime;
 | 
      
        | 104 |  |  |     // calculate the instance id for this instance
 | 
      
        | 105 | 2138 | darkviper |         $this->sCurrentInstance = $this->encodeHash(md5($this->iExpireTime.$this->sSalt));
 | 
      
        | 106 | 2136 | darkviper |     // load array of tokens from session
 | 
      
        | 107 |  |  |         $this->loadTokens();
 | 
      
        | 108 |  |  |     // at first of all remove expired tokens
 | 
      
        | 109 |  |  |         $this->removeExpiredTokens();
 | 
      
        | 110 |  |  |     }
 | 
      
        | 111 |  |  | 
 | 
      
        | 112 |  |  | /**
 | 
      
        | 113 | 2137 | darkviper |  * destructor
 | 
      
        | 114 | 2136 | darkviper |  */
 | 
      
        | 115 | 2137 | darkviper |     final public function __destruct()
 | 
      
        | 116 | 2136 | darkviper |     {
 | 
      
        | 117 | 2137 | darkviper |         foreach ($this->aTokens as $sKey => $aToken) {
 | 
      
        | 118 |  |  |             if ($aToken['instance'] == $this->sInstanceToUpdate) {
 | 
      
        | 119 |  |  |                 $this->aTokens[$sKey]['instance'] = $this->sCurrentInstance;
 | 
      
        | 120 |  |  |                 $this->aTokens[$sKey]['expire']   = $this->iExpireTime;
 | 
      
        | 121 |  |  |             } elseif ($aToken['instance'] == $this->sInstanceToDelete) {
 | 
      
        | 122 |  |  |                 unset($this->aTokens[$sKey]);
 | 
      
        | 123 |  |  |             }
 | 
      
        | 124 |  |  |         }
 | 
      
        | 125 |  |  |         $this->saveTokens();
 | 
      
        | 126 | 2136 | darkviper |     }
 | 
      
        | 127 | 2137 | darkviper | 
 | 
      
        | 128 | 2136 | darkviper | /**
 | 
      
        | 129 | 2137 | darkviper |  * returns all TokenLifeTime values
 | 
      
        | 130 |  |  |  * @return array
 | 
      
        | 131 |  |  |  */
 | 
      
        | 132 |  |  |     final public function getTokenLifeTime()
 | 
      
        | 133 |  |  |     {
 | 
      
        | 134 |  |  |         return array(
 | 
      
        | 135 |  |  |             'min'   => self::LIFETIME_MIN,
 | 
      
        | 136 |  |  |             'max'   => self::LIFETIME_MAX,
 | 
      
        | 137 |  |  |             'step'  => self::LIFETIME_STEP,
 | 
      
        | 138 |  |  |             'value' => $this->iTokenLifeTime
 | 
      
        | 139 |  |  |         );
 | 
      
        | 140 |  |  |     }
 | 
      
        | 141 |  |  | 
 | 
      
        | 142 |  |  | /**
 | 
      
        | 143 | 2136 | darkviper |  * Dummy method for backward compatibility
 | 
      
        | 144 |  |  |  * @return void
 | 
      
        | 145 |  |  |  * @deprecated from WB-2.8.3-SP5
 | 
      
        | 146 |  |  |  */
 | 
      
        | 147 | 2138 | darkviper |     final public function createFTAN()
 | 
      
        | 148 |  |  |     {
 | 
      
        | 149 |  |  |         trigger_error('Deprecated function call: '.__CLASS__.'::'.__METHOD__, E_USER_DEPRECATED);
 | 
      
        | 150 |  |  |     } // do nothing
 | 
      
        | 151 | 2136 | darkviper | 
 | 
      
        | 152 |  |  | /**
 | 
      
        | 153 |  |  |  * Dummy method for backward compatibility
 | 
      
        | 154 |  |  |  * @return void
 | 
      
        | 155 |  |  |  * @deprecated from WB-2.8.3-SP5
 | 
      
        | 156 |  |  |  */
 | 
      
        | 157 | 2138 | darkviper |     final public function clearIDKEY()
 | 
      
        | 158 |  |  |     {
 | 
      
        | 159 |  |  |         trigger_error('Deprecated function call: '.__CLASS__.'::'.__METHOD__, E_USER_DEPRECATED);
 | 
      
        | 160 |  |  |     } // do nothing
 | 
      
        | 161 | 2136 | darkviper | 
 | 
      
        | 162 |  |  | /**
 | 
      
        | 163 |  |  |  * returns the current FTAN
 | 
      
        | 164 |  |  |  * @param bool $mode: true or POST returns a complete prepared, hidden HTML-Input-Tag (default)
 | 
      
        | 165 |  |  |  *                     false or GET returns an GET argument 'key=value'
 | 
      
        | 166 |  |  |  * @return mixed:     array or string
 | 
      
        | 167 |  |  |  * @deprecated the param $mMode is set deprecated
 | 
      
        | 168 |  |  |  *              string retvals are set deprecated. From versions after 2.8.4 retval will be array only
 | 
      
        | 169 |  |  |  */
 | 
      
        | 170 |  |  |     final public function getFTAN($mMode = 'POST')
 | 
      
        | 171 |  |  |     {
 | 
      
        | 172 |  |  |         if (is_null($this->aLastCreatedFtan)) {
 | 
      
        | 173 |  |  |             $sFtan = md5($this->sSalt);
 | 
      
        | 174 |  |  |             $this->aLastCreatedFtan = $this->addToken(
 | 
      
        | 175 |  |  |                 substr($sFtan, rand(0,15), 16),
 | 
      
        | 176 |  |  |                 substr($sFtan, rand(0,15), 16)
 | 
      
        | 177 |  |  |             );
 | 
      
        | 178 |  |  |         }
 | 
      
        | 179 |  |  |         $aFtan = $this->aTokens[$this->aLastCreatedFtan];
 | 
      
        | 180 |  |  |         $aFtan['name']  = $this->aLastCreatedFtan;
 | 
      
        | 181 | 2138 | darkviper |         $aFtan['value'] = $this->encodeHash(md5($aFtan['value'].$this->sFingerprint));
 | 
      
        | 182 | 2136 | darkviper |         if (is_string($mMode)) {
 | 
      
        | 183 |  |  |             $mMode = strtoupper($mMode);
 | 
      
        | 184 |  |  |         } else {
 | 
      
        | 185 |  |  |             $mMode = $mMode === true ? 'POST' : 'GET';
 | 
      
        | 186 |  |  |         }
 | 
      
        | 187 |  |  |         switch ($mMode):
 | 
      
        | 188 |  |  |             case 'POST':
 | 
      
        | 189 |  |  |                 return '<input type="hidden" name="'.$aFtan['name'].'" value="'
 | 
      
        | 190 |  |  |                       .$aFtan['value'].'" title="">';
 | 
      
        | 191 |  |  |                 break;
 | 
      
        | 192 |  |  |             case 'GET':
 | 
      
        | 193 |  |  |                 return $aFtan['name'].'='.$aFtan['value'];
 | 
      
        | 194 |  |  |                 break;
 | 
      
        | 195 |  |  |             default:
 | 
      
        | 196 |  |  |                 return array('name' => $aFtan['name'], 'value' => $aFtan['value']);
 | 
      
        | 197 |  |  |         endswitch;
 | 
      
        | 198 |  |  |     }
 | 
      
        | 199 |  |  | 
 | 
      
        | 200 |  |  | /**
 | 
      
        | 201 |  |  |  * checks received form-transactionnumbers against session-stored one
 | 
      
        | 202 |  |  |  * @param string $mode: requestmethode POST(default) or GET
 | 
      
        | 203 | 2137 | darkviper |  * @param bool $bPreserve (default=false)
 | 
      
        | 204 | 2136 | darkviper |  * @return bool:    true if numbers matches against stored ones
 | 
      
        | 205 |  |  |  *
 | 
      
        | 206 |  |  |  * requirements: an active session must be available
 | 
      
        | 207 |  |  |  * this check will prevent from multiple sending a form. history.back() also will never work
 | 
      
        | 208 |  |  |  */
 | 
      
        | 209 | 2137 | darkviper |     final public function checkFTAN($mMode = 'POST', $bPreserve = false)
 | 
      
        | 210 | 2136 | darkviper |     {
 | 
      
        | 211 |  |  |         $bRetval = false;
 | 
      
        | 212 | 2137 | darkviper |         $this->bPreserveAllOtherTokens = $bPreserve ?: $this->bPreserveAllOtherTokens;
 | 
      
        | 213 | 2136 | darkviper |         // get the POST/GET arguments
 | 
      
        | 214 |  |  |         $aArguments = (strtoupper($mMode) == 'POST' ? $_POST : $_GET);
 | 
      
        | 215 |  |  |         // encode the value of all matching tokens
 | 
      
        | 216 |  |  |         $aMatchingTokens = array_map(
 | 
      
        | 217 |  |  |             function ($aToken) {
 | 
      
        | 218 | 2138 | darkviper |                 return $this->encodeHash(md5($aToken['value'].$this->sFingerprint));
 | 
      
        | 219 | 2136 | darkviper |             },
 | 
      
        | 220 |  |  |             // extract all matching tokens from $this->aTokens
 | 
      
        | 221 |  |  |             array_intersect_key($this->aTokens, $aArguments)
 | 
      
        | 222 |  |  |         );
 | 
      
        | 223 |  |  |         // extract all matching arguments from $aArguments
 | 
      
        | 224 |  |  |         $aMatchingArguments = array_intersect_key($aArguments, $this->aTokens);
 | 
      
        | 225 |  |  |         // get all tokens with matching values from match lists
 | 
      
        | 226 |  |  |         $aHits = array_intersect($aMatchingTokens, $aMatchingArguments);
 | 
      
        | 227 |  |  |         foreach ($aHits as $sTokenName => $sValue) {
 | 
      
        | 228 |  |  |             $bRetval = true;
 | 
      
        | 229 |  |  |             $this->removeToken($sTokenName);
 | 
      
        | 230 |  |  |         }
 | 
      
        | 231 |  |  |         return $bRetval;
 | 
      
        | 232 |  |  |     }
 | 
      
        | 233 |  |  | /**
 | 
      
        | 234 |  |  |  * store value in session and returns an accesskey to it
 | 
      
        | 235 |  |  |  * @param mixed $mValue can be numeric, string or array
 | 
      
        | 236 |  |  |  * @return string
 | 
      
        | 237 |  |  |  */
 | 
      
        | 238 |  |  |     final public function getIDKEY($mValue)
 | 
      
        | 239 |  |  |     {
 | 
      
        | 240 |  |  |         if (is_array($mValue) == true) {
 | 
      
        | 241 |  |  |             // serialize value, if it's an array
 | 
      
        | 242 |  |  |             $mValue = serialize($mValue);
 | 
      
        | 243 |  |  |         }
 | 
      
        | 244 |  |  |         // crypt value with salt into md5-hash and return a 16-digit block from random start position
 | 
      
        | 245 |  |  |         $sTokenName = $this->addToken(
 | 
      
        | 246 |  |  |             substr(md5($this->sSalt.(string)$mValue), rand(0,15), 16),
 | 
      
        | 247 |  |  |             $mValue
 | 
      
        | 248 |  |  |         );
 | 
      
        | 249 |  |  |         return $sTokenName;
 | 
      
        | 250 |  |  |     }
 | 
      
        | 251 |  |  | 
 | 
      
        | 252 |  |  | /*
 | 
      
        | 253 |  |  |  * search for key in session and returns the original value
 | 
      
        | 254 |  |  |  * @param string $sFieldname: name of the POST/GET-Field containing the key or hex-key itself
 | 
      
        | 255 |  |  |  * @param mixed $mDefault: returnvalue if key not exist (default 0)
 | 
      
        | 256 |  |  |  * @param string $sRequest: requestmethode can be POST or GET or '' (default POST)
 | 
      
        | 257 | 2137 | darkviper |  * @param bool $bPreserve (default=false)
 | 
      
        | 258 | 2136 | darkviper |  * @return mixed: the original value (string, numeric, array) or DEFAULT if request fails
 | 
      
        | 259 |  |  |  * @description: each IDKEY can be checked only once. Unused Keys stay in list until they expire
 | 
      
        | 260 |  |  |  */
 | 
      
        | 261 | 2137 | darkviper |     final public function checkIDKEY($sFieldname, $mDefault = 0, $sRequest = 'POST', $bPreserve = false)
 | 
      
        | 262 | 2136 | darkviper |     {
 | 
      
        | 263 |  |  |         $mReturnValue = $mDefault; // set returnvalue to default
 | 
      
        | 264 | 2137 | darkviper |         $this->bPreserveAllOtherTokens = $bPreserve ?: $this->bPreserveAllOtherTokens;
 | 
      
        | 265 | 2136 | darkviper |         $sRequest = strtoupper($sRequest);
 | 
      
        | 266 |  |  |         switch ($sRequest) {
 | 
      
        | 267 |  |  |             case 'POST':
 | 
      
        | 268 |  |  |             case 'GET':
 | 
      
        | 269 |  |  |                 $sTokenName = @$GLOBALS['_'.$sRequest][$sFieldname] ?: $sFieldname;
 | 
      
        | 270 |  |  |                 break;
 | 
      
        | 271 |  |  |             default:
 | 
      
        | 272 |  |  |                 $sTokenName = $sFieldname;
 | 
      
        | 273 |  |  |         }
 | 
      
        | 274 | 2139 | darkviper |         if (preg_match('/^[0-9a-f]{16}$/i', $sTokenName)) {
 | 
      
        | 275 | 2136 | darkviper |         // key must be a 16-digit hexvalue
 | 
      
        | 276 |  |  |             if (array_key_exists($sTokenName, $this->aTokens)) {
 | 
      
        | 277 |  |  |             // check if key is stored in IDKEYs-list
 | 
      
        | 278 |  |  |                 $mReturnValue = $this->aTokens[$sTokenName]['value']; // get stored value
 | 
      
        | 279 |  |  |                 $this->removeToken($sTokenName);   // remove from list to prevent multiuse
 | 
      
        | 280 |  |  |                 if (preg_match('/.*(?<!\{).*(\d:\{.*;\}).*(?!\}).*/', $mReturnValue)) {
 | 
      
        | 281 |  |  |                 // if value is a serialized array, then deserialize it
 | 
      
        | 282 |  |  |                     $mReturnValue = unserialize($mReturnValue);
 | 
      
        | 283 |  |  |                 }
 | 
      
        | 284 |  |  |             }
 | 
      
        | 285 |  |  |         }
 | 
      
        | 286 |  |  |         return $mReturnValue;
 | 
      
        | 287 |  |  |     }
 | 
      
        | 288 |  |  | 
 | 
      
        | 289 |  |  | /**
 | 
      
        | 290 | 2137 | darkviper |  * make a valid LifeTime value from given integer on the rules of class SecureTokens
 | 
      
        | 291 |  |  |  * @param integer  $iLifeTime
 | 
      
        | 292 |  |  |  * @return integer
 | 
      
        | 293 |  |  |  */
 | 
      
        | 294 |  |  |     final public function sanitizeLifeTime($iLifeTime)
 | 
      
        | 295 |  |  |     {
 | 
      
        | 296 |  |  |         $iLifeTime = intval($iLifeTime);
 | 
      
        | 297 |  |  |         for ($i = self::LIFETIME_MIN; $i <= self::LIFETIME_MAX; $i += self::LIFETIME_STEP) {
 | 
      
        | 298 |  |  |             $aLifeTimes[] = $i;
 | 
      
        | 299 |  |  |         }
 | 
      
        | 300 |  |  |         $iRetval = array_pop($aLifeTimes);
 | 
      
        | 301 |  |  |         foreach ($aLifeTimes as $iValue) {
 | 
      
        | 302 |  |  |             if ($iLifeTime <= $iValue) {
 | 
      
        | 303 |  |  |                 $iRetval = $iValue;
 | 
      
        | 304 |  |  |                 break;
 | 
      
        | 305 |  |  |             }
 | 
      
        | 306 |  |  |         }
 | 
      
        | 307 |  |  |         return $iRetval;
 | 
      
        | 308 |  |  |     }
 | 
      
        | 309 |  |  | /* ************************************************************************************ */
 | 
      
        | 310 |  |  | /* *** from here private methods only                                               *** */
 | 
      
        | 311 |  |  | /* ************************************************************************************ */
 | 
      
        | 312 |  |  | /**
 | 
      
        | 313 | 2136 | darkviper |  * load all tokens from session
 | 
      
        | 314 |  |  |  */
 | 
      
        | 315 |  |  |     private function loadTokens()
 | 
      
        | 316 |  |  |     {
 | 
      
        | 317 |  |  |         if (isset($_SESSION['TOKENS'])) {
 | 
      
        | 318 |  |  |             $this->aTokens = unserialize($_SESSION['TOKENS']);
 | 
      
        | 319 |  |  |         } else {
 | 
      
        | 320 |  |  |             $this->saveTokens();
 | 
      
        | 321 |  |  |         }
 | 
      
        | 322 |  |  |     }
 | 
      
        | 323 |  |  | 
 | 
      
        | 324 |  |  | /**
 | 
      
        | 325 |  |  |  * save all tokens into session
 | 
      
        | 326 |  |  |  */
 | 
      
        | 327 |  |  |     private function saveTokens()
 | 
      
        | 328 |  |  |     {
 | 
      
        | 329 |  |  |         $_SESSION['TOKENS'] = serialize($this->aTokens);
 | 
      
        | 330 |  |  |     }
 | 
      
        | 331 |  |  | 
 | 
      
        | 332 |  |  | /**
 | 
      
        | 333 |  |  |  * add new token to the list
 | 
      
        | 334 |  |  |  * @param string $sTokenName
 | 
      
        | 335 |  |  |  * @param string $sValue
 | 
      
        | 336 |  |  |  * @return string  name(index) of the token
 | 
      
        | 337 |  |  |  */
 | 
      
        | 338 |  |  |     private function addToken($sTokenName, $sValue)
 | 
      
        | 339 |  |  |     {
 | 
      
        | 340 | 2140 | darkviper |         // limit TokenName to 16 digits
 | 
      
        | 341 |  |  |         $sTokenName = substr(str_pad($sTokenName, 16, '0', STR_PAD_LEFT), -16);
 | 
      
        | 342 |  |  |         // make sure, first digit is a alpha char [a-f]
 | 
      
        | 343 | 2136 | darkviper |         $sTokenName[0] = dechex(10 + (hexdec($sTokenName[0]) % 5));
 | 
      
        | 344 | 2140 | darkviper |         // loop as long the generated TokenName already exists in list
 | 
      
        | 345 | 2136 | darkviper |         while (isset($this->aTokens[$sTokenName])) {
 | 
      
        | 346 | 2140 | darkviper |             // split TokenName into 4 words
 | 
      
        | 347 |  |  |             $aWords = str_split($sTokenName, 4);
 | 
      
        | 348 |  |  |             // get lowest word and increment it
 | 
      
        | 349 |  |  |             $iWord = hexdec($aWords[3]) + 1;
 | 
      
        | 350 |  |  |             // reformat integer into a 4 digit hex string
 | 
      
        | 351 |  |  |             $aWords[3] = sprintf('%04x', ($iWord > 0xffff ? 1 : $iWord));
 | 
      
        | 352 |  |  |             // rebuild the TokenName
 | 
      
        | 353 |  |  |             $sTokenName = implode('', $aWords);
 | 
      
        | 354 | 2136 | darkviper |         }
 | 
      
        | 355 | 2140 | darkviper |         // store Token in list
 | 
      
        | 356 | 2136 | darkviper |         $this->aTokens[$sTokenName] = array(
 | 
      
        | 357 | 2137 | darkviper |             'value'    => $sValue,
 | 
      
        | 358 |  |  |             'expire'   => $this->iExpireTime,
 | 
      
        | 359 |  |  |             'instance' => $this->sCurrentInstance
 | 
      
        | 360 | 2136 | darkviper |         );
 | 
      
        | 361 |  |  |         return $sTokenName;
 | 
      
        | 362 |  |  |     }
 | 
      
        | 363 |  |  | 
 | 
      
        | 364 |  |  | /**
 | 
      
        | 365 |  |  |  * remove the token, called sTokenName from list
 | 
      
        | 366 |  |  |  * @param type $sTokenName
 | 
      
        | 367 |  |  |  */
 | 
      
        | 368 |  |  |     private function removeToken($sTokenName)
 | 
      
        | 369 |  |  |     {
 | 
      
        | 370 |  |  |         if (isset($this->aTokens[$sTokenName])) {
 | 
      
        | 371 | 2137 | darkviper |             if ($this->bPreserveAllOtherTokens) {
 | 
      
        | 372 | 2138 | darkviper |                 if ($this->sInstanceToDelete) {
 | 
      
        | 373 |  |  |                     $this->sInstanceToUpdate = $this->sInstanceToDelete;
 | 
      
        | 374 |  |  |                     $this->sInstanceToDelete = null;
 | 
      
        | 375 |  |  |                 } else {
 | 
      
        | 376 |  |  |                     $this->sInstanceToUpdate = $this->aTokens[$sTokenName]['instance'];
 | 
      
        | 377 |  |  |                 }
 | 
      
        | 378 | 2137 | darkviper |             } else {
 | 
      
        | 379 |  |  |                 $this->sInstanceToDelete = $this->aTokens[$sTokenName]['instance'];
 | 
      
        | 380 |  |  |             }
 | 
      
        | 381 | 2136 | darkviper |             unset($this->aTokens[$sTokenName]);
 | 
      
        | 382 |  |  |         }
 | 
      
        | 383 |  |  |     }
 | 
      
        | 384 |  |  | 
 | 
      
        | 385 |  |  | /**
 | 
      
        | 386 |  |  |  * remove all expired tokens from list
 | 
      
        | 387 |  |  |  */
 | 
      
        | 388 |  |  |     private function removeExpiredTokens()
 | 
      
        | 389 |  |  |     {
 | 
      
        | 390 |  |  |         $iTimestamp = time();
 | 
      
        | 391 |  |  |         foreach ($this->aTokens as $sTokenName => $aToken) {
 | 
      
        | 392 |  |  |             if ($aToken['expire'] <= $iTimestamp && $aToken['expire'] != 0){
 | 
      
        | 393 |  |  |                 unset($this->aTokens[$sTokenName]);
 | 
      
        | 394 |  |  |             }
 | 
      
        | 395 |  |  |         }
 | 
      
        | 396 |  |  |     }
 | 
      
        | 397 |  |  | 
 | 
      
        | 398 |  |  | /**
 | 
      
        | 399 |  |  |  * generate a runtime depended hash
 | 
      
        | 400 |  |  |  * @return string  md5 hash
 | 
      
        | 401 |  |  |  */
 | 
      
        | 402 |  |  |     private function generateSalt()
 | 
      
        | 403 |  |  |     {
 | 
      
        | 404 |  |  |         list($fUsec, $fSec) = explode(" ", microtime());
 | 
      
        | 405 |  |  |         $sSalt = (string)rand(10000, 99999)
 | 
      
        | 406 |  |  |                . (string)((float)$fUsec + (float)$fSec)
 | 
      
        | 407 |  |  |                . (string)rand(10000, 99999);
 | 
      
        | 408 |  |  |         return md5($sSalt);
 | 
      
        | 409 |  |  |     }
 | 
      
        | 410 |  |  | 
 | 
      
        | 411 |  |  | /**
 | 
      
        | 412 |  |  |  * build a simple fingerprint
 | 
      
        | 413 |  |  |  * @return string
 | 
      
        | 414 |  |  |  */
 | 
      
        | 415 |  |  |     private function buildFingerprint()
 | 
      
        | 416 |  |  |     {
 | 
      
        | 417 | 2138 | darkviper |         if (!$this->bUseFingerprint) { return md5('this_is_a_dummy_only'); }
 | 
      
        | 418 | 2137 | darkviper |         $sClientIp = '127.0.0.1';
 | 
      
        | 419 |  |  |         if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)){
 | 
      
        | 420 |  |  |             $sClientIp = array_pop(preg_split('/\s*?,\s*?/', $_SERVER['HTTP_X_FORWARDED_FOR'], null, PREG_SPLIT_NO_EMPTY));
 | 
      
        | 421 |  |  |         }else if (array_key_exists('REMOTE_ADDR', $_SERVER)) {
 | 
      
        | 422 |  |  |             $sClientIp = $_SERVER['REMOTE_ADDR'];
 | 
      
        | 423 |  |  |         }else if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) {
 | 
      
        | 424 |  |  |             $sClientIp = $_SERVER['HTTP_CLIENT_IP'];
 | 
      
        | 425 |  |  |         }
 | 
      
        | 426 | 2139 | darkviper |         $aTmp = array_chunk(stat(__FILE__), 11);
 | 
      
        | 427 |  |  |         unset($aTmp[0][8]);
 | 
      
        | 428 |  |  |         return md5(
 | 
      
        | 429 |  |  |             __FILE__ . PHP_VERSION . implode('', $aTmp[0])
 | 
      
        | 430 |  |  |             . (array_key_exists('HTTP_USER_AGENT', $_SERVER) ? $_SERVER['HTTP_USER_AGENT'] : 'AGENT')
 | 
      
        | 431 |  |  |             . $this->calcClientIpHash($sClientIp)
 | 
      
        | 432 |  |  |         );
 | 
      
        | 433 | 2136 | darkviper |     }
 | 
      
        | 434 |  |  | 
 | 
      
        | 435 |  |  | /**
 | 
      
        | 436 |  |  |  * mask IPv4 as well IPv6 addresses with netmask and make a md5 hash from
 | 
      
        | 437 |  |  |  * @param string $sClientIp IP as string from $_SERVER['REMOTE_ADDR']
 | 
      
        | 438 |  |  |  * @return md5 value of masked ip
 | 
      
        | 439 |  |  |  * @description this method does not accept the IPv6/IPv4 mixed format
 | 
      
        | 440 |  |  |  *               like "2222:3333:4444:5555:6666:7777:192.168.1.200"
 | 
      
        | 441 |  |  |  */
 | 
      
        | 442 |  |  |     private function calcClientIpHash($sRawIp)
 | 
      
        | 443 |  |  |     {
 | 
      
        | 444 | 2138 | darkviper |         // clean address from netmask/prefix and port
 | 
      
        | 445 |  |  |         $sPattern = '/^\[?([.:a-f0-9]*)(?:\/[0-1]*)?(?:\]?.*)$/im';
 | 
      
        | 446 |  |  |         $sRawIp = preg_replace($sPattern, '$1', $sRawIp);
 | 
      
        | 447 | 2136 | darkviper |         if (strpos($sRawIp, ':') === false) {
 | 
      
        | 448 | 2138 | darkviper | // sanitize IPv4 ---------------------------------------------------------------------- //
 | 
      
        | 449 | 2136 | darkviper |             $iIpV4 = ip2long($sRawIp);
 | 
      
        | 450 | 2138 | darkviper |             // calculate netmask
 | 
      
        | 451 | 2136 | darkviper |             $iMask = ($this->iNetmaskLengthV4 < 1)
 | 
      
        | 452 |  |  |                 ? 0
 | 
      
        | 453 |  |  |                 : bindec(
 | 
      
        | 454 |  |  |                     str_repeat('1', $this->iNetmaskLengthV4).
 | 
      
        | 455 |  |  |                     str_repeat('0', 32 - $this->iNetmaskLengthV4)
 | 
      
        | 456 |  |  |                 );
 | 
      
        | 457 | 2138 | darkviper |             // apply mask and reformat to IPv4 string notation.
 | 
      
        | 458 | 2136 | darkviper |             $sIp = long2ip($iIpV4 & $iMask);
 | 
      
        | 459 |  |  |         } else {
 | 
      
        | 460 | 2138 | darkviper | // sanitize IPv6 ---------------------------------------------------------------------- //
 | 
      
        | 461 |  |  |             // check if IP includes a IPv4 part and convert this into IPv6 format
 | 
      
        | 462 |  |  |             $sPattern = '/^([:a-f0-9]*?)\:([0-9]{1,3}(?:\.[0-9]{1,3}){3})$/is';
 | 
      
        | 463 |  |  |             if (preg_match($sPattern, $sRawIp, $aMatches)) {
 | 
      
        | 464 | 2139 | darkviper |                 // convert IPv4 into full size 32bit binary string
 | 
      
        | 465 | 2138 | darkviper |                 $sIpV4Bin = str_pad((string)decbin(ip2long($aMatches[2])), 32, '0', STR_PAD_LEFT) ;
 | 
      
        | 466 | 2139 | darkviper |                 // split into 2 parts of 16bit
 | 
      
        | 467 | 2138 | darkviper |                 $aIpV6Hex = str_split($sIpV4Bin, 16);
 | 
      
        | 468 | 2139 | darkviper |                 // concate the IPv6/96 part and hex of both IPv4 parts
 | 
      
        | 469 | 2138 | darkviper |                 $sRawIp = $aMatches[1].':'.dechex(bindec($aIpV6Hex[0])).':'.dechex(bindec($aIpV6Hex[1]));
 | 
      
        | 470 |  |  |             }
 | 
      
        | 471 | 2139 | darkviper |             // calculate number of missing IPv6 words
 | 
      
        | 472 | 2136 | darkviper |             $iWords = 8 - count(preg_split('/:/', $sRawIp, null, PREG_SPLIT_NO_EMPTY));
 | 
      
        | 473 | 2139 | darkviper |             // build multiple ':0000:' replacements for '::'
 | 
      
        | 474 | 2136 | darkviper |             $sReplacement = $iWords ? implode(':', array_fill(0, $iWords, '0000')) : '';
 | 
      
        | 475 | 2138 | darkviper |             // insert replacements and remove trailing/leading ':'
 | 
      
        | 476 | 2136 | darkviper |             $sClientIp = trim(preg_replace('/\:\:/', ':'.$sReplacement.':', $sRawIp), ':');
 | 
      
        | 477 | 2138 | darkviper |             // split all 8 parts from IP into an array
 | 
      
        | 478 | 2136 | darkviper |             $aIpV6 = array_map(
 | 
      
        | 479 |  |  |                 function($sPart) {
 | 
      
        | 480 | 2138 | darkviper |                     // expand all parts to 4 hex digits using leading '0'
 | 
      
        | 481 | 2136 | darkviper |                     return str_pad($sPart, 4, '0', STR_PAD_LEFT);
 | 
      
        | 482 |  |  |                 },
 | 
      
        | 483 |  |  |                 preg_split('/:/', $sClientIp)
 | 
      
        | 484 |  |  |             );
 | 
      
        | 485 | 2138 | darkviper |             // build binary netmask from iNetmaskLengthV6
 | 
      
        | 486 |  |  |             // and split all 8 parts into an array
 | 
      
        | 487 | 2136 | darkviper |             if ($this->iNetmaskLengthV6 < 1) {
 | 
      
        | 488 |  |  |                 $aMask = array_fill(0, 8, str_repeat('0', 16));
 | 
      
        | 489 |  |  |             } else {
 | 
      
        | 490 |  |  |                 $aMask = str_split(
 | 
      
        | 491 |  |  |                     str_repeat('1', $this->iNetmaskLengthV6).
 | 
      
        | 492 |  |  |                     str_repeat('0', 128 - $this->iNetmaskLengthV6),
 | 
      
        | 493 |  |  |                     16
 | 
      
        | 494 |  |  |                 );
 | 
      
        | 495 |  |  |             }
 | 
      
        | 496 | 2138 | darkviper |             // iterate all IP parts, apply its mask and reformat to IPv6 string notation.
 | 
      
        | 497 | 2136 | darkviper |             array_walk(
 | 
      
        | 498 |  |  |                 $aIpV6,
 | 
      
        | 499 |  |  |                 function(&$sWord, $iIndex) use ($aMask) {
 | 
      
        | 500 |  |  |                     $sWord = sprintf('%04x', hexdec($sWord) & bindec($aMask[$iIndex]));
 | 
      
        | 501 |  |  |                 }
 | 
      
        | 502 |  |  |             );
 | 
      
        | 503 | 2138 | darkviper |             // reformat to IPv6 string notation.
 | 
      
        | 504 | 2136 | darkviper |             $sIp = implode(':', $aIpV6);
 | 
      
        | 505 | 2138 | darkviper | // ------------------------------------------------------------------------------------ //
 | 
      
        | 506 | 2136 | darkviper |         }
 | 
      
        | 507 | 2138 | darkviper |         return md5($sIp); // return the hashed IP string
 | 
      
        | 508 | 2136 | darkviper |     }
 | 
      
        | 509 |  |  | 
 | 
      
        | 510 |  |  | /**
 | 
      
        | 511 |  |  |  * encode a hex string into a 64char based string
 | 
      
        | 512 |  |  |  * @param string $sMd5Hash
 | 
      
        | 513 |  |  |  * @return string
 | 
      
        | 514 | 2137 | darkviper |  * @description reduce the 32char length of a MD5 to 22 chars
 | 
      
        | 515 | 2136 | darkviper |  */
 | 
      
        | 516 | 2138 | darkviper |     private function encodeHash($sMd5Hash)
 | 
      
        | 517 | 2136 | darkviper |     {
 | 
      
        | 518 | 2137 | darkviper |         return rtrim(base64_encode(pack('h*',$sMd5Hash)), '+-= ');
 | 
      
        | 519 | 2136 | darkviper |     }
 | 
      
        | 520 |  |  | 
 | 
      
        | 521 |  |  | /**
 | 
      
        | 522 |  |  |  * read settings if available
 | 
      
        | 523 |  |  |  */
 | 
      
        | 524 |  |  |     private function getSettings()
 | 
      
        | 525 |  |  |     {
 | 
      
        | 526 | 2138 | darkviper |         $this->bUseFingerprint  = isset($this->oReg->SecTokenFingerprint)
 | 
      
        | 527 |  |  |                                   ? $this->oReg->SecTokenFingerprint
 | 
      
        | 528 |  |  |                                   : $this->bUseFingerprint;
 | 
      
        | 529 | 2139 | darkviper |         $this->iNetmaskLengthV4 = isset($this->oReg->SecTokenIpv4Netmask)
 | 
      
        | 530 |  |  |                                   ? $this->oReg->SecTokenIpv4Netmask
 | 
      
        | 531 | 2138 | darkviper |                                   : $this->iNetmaskLengthV4;
 | 
      
        | 532 | 2139 | darkviper |         $this->iNetmaskLengthV6 = isset($this->oReg->SecTokenIpv6PrefixLength)
 | 
      
        | 533 |  |  |                                   ? $this->oReg->SecTokenIpv6PrefixLength
 | 
      
        | 534 | 2138 | darkviper |                                   : $this->iNetmaskLengthV6;
 | 
      
        | 535 |  |  |         $this->iTokenLifeTime   = isset($this->oReg->SecTokenLifeTime)
 | 
      
        | 536 |  |  |                                   ? $this->oReg->SecTokenLifeTime
 | 
      
        | 537 |  |  |                                   : $this->iTokenLifeTime;
 | 
      
        | 538 | 2136 | darkviper |         $this->iNetmaskLengthV4 = ($this->iNetmaskLengthV4 < 1 || $this->iNetmaskLengthV4 > 32)
 | 
      
        | 539 |  |  |                                   ? 0 :$this->iNetmaskLengthV4;
 | 
      
        | 540 |  |  |         $this->iNetmaskLengthV6 = ($this->iNetmaskLengthV6 < 1 || $this->iNetmaskLengthV6 > 128)
 | 
      
        | 541 |  |  |                                   ? 0 :$this->iNetmaskLengthV6;
 | 
      
        | 542 | 2137 | darkviper |         $this->iTokenLifeTime   = $this->sanitizeLifeTime($this->iTokenLifeTime);
 | 
      
        | 543 |  |  |         if ($this->iTokenLifeTime <= self::LIFETIME_MIN && DEBUG) {
 | 
      
        | 544 | 2136 | darkviper |             $this->iTokenLifeTime = self::DEBUG_LIFETIME;
 | 
      
        | 545 |  |  |         }
 | 
      
        | 546 |  |  |     }
 | 
      
        | 547 |  |  | 
 | 
      
        | 548 | 2137 | darkviper | 
 | 
      
        | 549 | 2136 | darkviper | } // end of class SecureTokens
 |