root/ext/standard/php_crypt_r.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_init_crypt_r
  2. php_shutdown_crypt_r
  3. _crypt_extended_init_r
  4. to64
  5. php_md5_crypt_r
  6. php_md5_crypt_r

   1 /* $Id$ */
   2 /*
   3    +----------------------------------------------------------------------+
   4    | PHP Version 7                                                        |
   5    +----------------------------------------------------------------------+
   6    | Copyright (c) 1997-2016 The PHP Group                                |
   7    +----------------------------------------------------------------------+
   8    | This source file is subject to version 3.01 of the PHP license,      |
   9    | that is bundled with this package in the file LICENSE, and is        |
  10    | available through the world-wide-web at the following url:           |
  11    | http://www.php.net/license/3_01.txt                                  |
  12    | If you did not receive a copy of the PHP license and are unable to   |
  13    | obtain it through the world-wide-web, please send a note to          |
  14    | license@php.net so we can mail you a copy immediately.               |
  15    +----------------------------------------------------------------------+
  16    | Authors: Pierre Alain Joye  <pajoye@php.net                          |
  17    +----------------------------------------------------------------------+
  18  */
  19 
  20 /*
  21  * License for the Unix md5crypt implementation (md5_crypt):
  22  *
  23  * ----------------------------------------------------------------------------
  24  * "THE BEER-WARE LICENSE" (Revision 42):
  25  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
  26  * can do whatever you want with this stuff. If we meet some day, and you think
  27  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
  28  * ----------------------------------------------------------------------------
  29  *
  30  * from FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp
  31  * via OpenBSD: md5crypt.c,v 1.9 1997/07/23 20:58:27 kstailey Exp
  32  * via NetBSD: md5crypt.c,v 1.4.2.1 2002/01/22 19:31:59 he Exp
  33  *
  34  */
  35 
  36 #include "php.h"
  37 
  38 #include <string.h>
  39 
  40 #if PHP_WIN32
  41 # include <windows.h>
  42 # include <Wincrypt.h>
  43 #endif
  44 
  45 #ifdef HAVE_ATOMIC_H /* Solaris 10 defines atomic API within */
  46 # include <atomic.h>
  47 #else
  48 # include <signal.h>
  49 #endif
  50 #include "php_crypt_r.h"
  51 #include "crypt_freesec.h"
  52 
  53 #if !PHP_WIN32
  54 #include "ext/standard/md5.h"
  55 #endif
  56 
  57 #ifdef ZTS
  58 MUTEX_T php_crypt_extended_init_lock;
  59 #endif
  60 
  61 /* TODO: enable it when enabling vista/2k8 mode in tsrm */
  62 #if 0
  63 CONDITION_VARIABLE initialized;
  64 #endif
  65 
  66 void php_init_crypt_r()
  67 {
  68 #ifdef ZTS
  69         php_crypt_extended_init_lock = tsrm_mutex_alloc();
  70 #endif
  71 }
  72 
  73 void php_shutdown_crypt_r()
  74 {
  75 #ifdef ZTS
  76         tsrm_mutex_free(php_crypt_extended_init_lock);
  77 #endif
  78 }
  79 
  80 void _crypt_extended_init_r(void)
  81 {
  82 #ifdef PHP_WIN32
  83         LONG volatile initialized = 0;
  84 #elif defined(HAVE_ATOMIC_H) /* Solaris 10 defines atomic API within */
  85         volatile unsigned int initialized = 0;
  86 #else
  87         static volatile sig_atomic_t initialized = 0;
  88 #endif
  89 
  90 #ifdef ZTS
  91         tsrm_mutex_lock(php_crypt_extended_init_lock);
  92 #endif
  93 
  94         if (!initialized) {
  95 #ifdef PHP_WIN32
  96                 InterlockedIncrement(&initialized);
  97 #elif defined(HAVE_SYNC_FETCH_AND_ADD)
  98                 __sync_fetch_and_add(&initialized, 1);
  99 #elif defined(HAVE_ATOMIC_H) /* Solaris 10 defines atomic API within */
 100                 membar_producer();
 101                 atomic_add_int(&initialized, 1);
 102 #endif
 103                 _crypt_extended_init();
 104         }
 105 #ifdef ZTS
 106         tsrm_mutex_unlock(php_crypt_extended_init_lock);
 107 #endif
 108 }
 109 
 110 /* MD% crypt implementation using the windows CryptoApi */
 111 #define MD5_MAGIC "$1$"
 112 #define MD5_MAGIC_LEN 3
 113 
 114 static unsigned char itoa64[] =         /* 0 ... 63 => ascii - 64 */
 115         "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 116 
 117 static void
 118 to64(char *s, int32_t v, int n)
 119 {
 120         while (--n >= 0) {
 121                 *s++ = itoa64[v & 0x3f];
 122                 v >>= 6;
 123         }
 124 }
 125 
 126 #if PHP_WIN32
 127 char * php_md5_crypt_r(const char *pw, const char *salt, char *out) {
 128         HCRYPTPROV hCryptProv;
 129         HCRYPTHASH ctx, ctx1;
 130         DWORD i, pwl, sl;
 131         const BYTE magic_md5[4] = "$1$";
 132         const DWORD magic_md5_len = 3;
 133         DWORD        dwHashLen;
 134         int pl;
 135         __int32 l;
 136         const char *sp = salt;
 137         const char *ep = salt;
 138         char *p = NULL;
 139         char *passwd = out;
 140         unsigned char final[16];
 141 
 142         /* Acquire a cryptographic provider context handle. */
 143         if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
 144                 return NULL;
 145         }
 146 
 147         pwl = (DWORD) strlen(pw);
 148 
 149         /* Refine the salt first */
 150         sp = salt;
 151 
 152         /* If it starts with the magic string, then skip that */
 153         if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0) {
 154                 sp += MD5_MAGIC_LEN;
 155         }
 156 
 157         /* It stops at the first '$', max 8 chars */
 158         for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++) {
 159                 continue;
 160         }
 161 
 162         /* get the length of the true salt */
 163         sl = (DWORD)(ep - sp);
 164 
 165         /* Create an empty hash object. */
 166         if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx)) {
 167                 goto _destroyProv;
 168         }
 169 
 170         /* The password first, since that is what is most unknown */
 171         if(!CryptHashData(ctx, (BYTE *)pw, pwl, 0)) {
 172                 goto _destroyCtx0;
 173         }
 174 
 175         /* Then our magic string */
 176         if(!CryptHashData(ctx, magic_md5, magic_md5_len, 0)) {
 177                 goto _destroyCtx0;
 178         }
 179 
 180         /* Then the raw salt */
 181         if(!CryptHashData( ctx, (BYTE *)sp, sl, 0)) {
 182                 goto _destroyCtx0;
 183         }
 184 
 185         /* MD5(pw,salt,pw), valid. */
 186         /* Then just as many characters of the MD5(pw,salt,pw) */
 187         if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx1)) {
 188                 goto _destroyCtx0;
 189         }
 190         if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
 191                 goto _destroyCtx1;
 192         }
 193         if(!CryptHashData(ctx1, (BYTE *)sp, sl, 0)) {
 194                 goto _destroyCtx1;
 195         }
 196         if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
 197                 goto _destroyCtx1;
 198         }
 199 
 200         dwHashLen = 16;
 201         CryptGetHashParam(ctx1, HP_HASHVAL, final, &dwHashLen, 0);
 202         /*  MD5(pw,salt,pw). Valid. */
 203 
 204         for (pl = pwl; pl > 0; pl -= 16) {
 205                 CryptHashData(ctx, final, (DWORD)(pl > 16 ? 16 : pl), 0);
 206         }
 207 
 208         /* Don't leave anything around in vm they could use. */
 209         ZEND_SECURE_ZERO(final, sizeof(final));
 210 
 211         /* Then something really weird... */
 212         for (i = pwl; i != 0; i >>= 1) {
 213                 if ((i & 1) != 0) {
 214                         CryptHashData(ctx, (const BYTE *)final, 1, 0);
 215                 } else {
 216                         CryptHashData(ctx, (const BYTE *)pw, 1, 0);
 217                 }
 218         }
 219 
 220         memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN);
 221 
 222         if (strncpy_s(passwd + MD5_MAGIC_LEN, MD5_HASH_MAX_LEN - MD5_MAGIC_LEN, sp, sl + 1) != 0) {
 223                 goto _destroyCtx1;
 224         }
 225         passwd[MD5_MAGIC_LEN + sl] = '\0';
 226         strcat_s(passwd, MD5_HASH_MAX_LEN, "$");
 227 
 228         dwHashLen = 16;
 229 
 230         /* Fetch the ctx hash value */
 231         CryptGetHashParam(ctx, HP_HASHVAL, final, &dwHashLen, 0);
 232 
 233         for (i = 0; i < 1000; i++) {
 234                 if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx1)) {
 235                         goto _destroyCtx1;
 236                 }
 237 
 238                 if ((i & 1) != 0) {
 239                         if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
 240                                 goto _destroyCtx1;
 241                         }
 242                 } else {
 243                         if(!CryptHashData(ctx1, (BYTE *)final, 16, 0)) {
 244                                 goto _destroyCtx1;
 245                         }
 246                 }
 247 
 248                 if ((i % 3) != 0) {
 249                         if(!CryptHashData(ctx1, (BYTE *)sp, sl, 0)) {
 250                                 goto _destroyCtx1;
 251                         }
 252                 }
 253 
 254                 if ((i % 7) != 0) {
 255                         if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
 256                                 goto _destroyCtx1;
 257                         }
 258                 }
 259 
 260                 if ((i & 1) != 0) {
 261                         if(!CryptHashData(ctx1, (BYTE *)final, 16, 0)) {
 262                                 goto _destroyCtx1;
 263                         }
 264                 } else {
 265                         if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
 266                                 goto _destroyCtx1;
 267                         }
 268                 }
 269 
 270                 /* Fetch the ctx hash value */
 271                 dwHashLen = 16;
 272                 CryptGetHashParam(ctx1, HP_HASHVAL, final, &dwHashLen, 0);
 273                 if(!(CryptDestroyHash(ctx1))) {
 274                         goto _destroyCtx0;
 275                 }
 276         }
 277 
 278         ctx1 = (HCRYPTHASH) NULL;
 279 
 280         p = passwd + sl + MD5_MAGIC_LEN + 1;
 281 
 282         l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
 283         l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
 284         l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
 285         l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
 286         l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
 287         l = final[11]; to64(p,l,2); p += 2;
 288 
 289         *p = '\0';
 290 
 291         ZEND_SECURE_ZERO(final, sizeof(final));
 292 
 293 
 294 _destroyCtx1:
 295         if (ctx1) {
 296                 if (!CryptDestroyHash(ctx1)) {
 297 
 298                 }
 299         }
 300 
 301 _destroyCtx0:
 302         CryptDestroyHash(ctx);
 303 
 304 _destroyProv:
 305         /* Release the provider handle.*/
 306         if(hCryptProv) {
 307                 if(!(CryptReleaseContext(hCryptProv, 0))) {
 308                         return NULL;
 309                 }
 310         }
 311 
 312         return out;
 313 }
 314 #else
 315 
 316 /*
 317  * MD5 password encryption.
 318  */
 319 char * php_md5_crypt_r(const char *pw, const char *salt, char *out)
 320 {
 321         ZEND_TLS char passwd[MD5_HASH_MAX_LEN], *p;
 322         const char *sp, *ep;
 323         unsigned char final[16];
 324         unsigned int i, sl, pwl;
 325         PHP_MD5_CTX     ctx, ctx1;
 326         php_uint32 l;
 327         int pl;
 328 
 329         pwl = strlen(pw);
 330 
 331         /* Refine the salt first */
 332         sp = salt;
 333 
 334         /* If it starts with the magic string, then skip that */
 335         if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0)
 336                 sp += MD5_MAGIC_LEN;
 337 
 338         /* It stops at the first '$', max 8 chars */
 339         for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++)
 340                 continue;
 341 
 342         /* get the length of the true salt */
 343         sl = ep - sp;
 344 
 345         PHP_MD5Init(&ctx);
 346 
 347         /* The password first, since that is what is most unknown */
 348         PHP_MD5Update(&ctx, (const unsigned char *)pw, pwl);
 349 
 350         /* Then our magic string */
 351         PHP_MD5Update(&ctx, (const unsigned char *)MD5_MAGIC, MD5_MAGIC_LEN);
 352 
 353         /* Then the raw salt */
 354         PHP_MD5Update(&ctx, (const unsigned char *)sp, sl);
 355 
 356         /* Then just as many characters of the MD5(pw,salt,pw) */
 357         PHP_MD5Init(&ctx1);
 358         PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
 359         PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl);
 360         PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
 361         PHP_MD5Final(final, &ctx1);
 362 
 363         for (pl = pwl; pl > 0; pl -= 16)
 364                 PHP_MD5Update(&ctx, final, (unsigned int)(pl > 16 ? 16 : pl));
 365 
 366         /* Don't leave anything around in vm they could use. */
 367         memset(final, 0, sizeof(final));
 368 
 369         /* Then something really weird... */
 370         for (i = pwl; i != 0; i >>= 1)
 371                 if ((i & 1) != 0)
 372                     PHP_MD5Update(&ctx, final, 1);
 373                 else
 374                     PHP_MD5Update(&ctx, (const unsigned char *)pw, 1);
 375 
 376         /* Now make the output string */
 377         memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN);
 378         strlcpy(passwd + MD5_MAGIC_LEN, sp, sl + 1);
 379         strcat(passwd, "$");
 380 
 381         PHP_MD5Final(final, &ctx);
 382 
 383         /*
 384          * And now, just to make sure things don't run too fast. On a 60 MHz
 385          * Pentium this takes 34 msec, so you would need 30 seconds to build
 386          * a 1000 entry dictionary...
 387          */
 388         for (i = 0; i < 1000; i++) {
 389                 PHP_MD5Init(&ctx1);
 390 
 391                 if ((i & 1) != 0)
 392                         PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
 393                 else
 394                         PHP_MD5Update(&ctx1, final, 16);
 395 
 396                 if ((i % 3) != 0)
 397                         PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl);
 398 
 399                 if ((i % 7) != 0)
 400                         PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
 401 
 402                 if ((i & 1) != 0)
 403                         PHP_MD5Update(&ctx1, final, 16);
 404                 else
 405                         PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
 406 
 407                 PHP_MD5Final(final, &ctx1);
 408         }
 409 
 410         p = passwd + sl + MD5_MAGIC_LEN + 1;
 411 
 412         l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
 413         l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
 414         l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
 415         l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
 416         l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
 417         l =                    final[11]                ; to64(p,l,2); p += 2;
 418         *p = '\0';
 419 
 420         /* Don't leave anything around in vm they could use. */
 421         ZEND_SECURE_ZERO(final, sizeof(final));
 422         return (passwd);
 423 }
 424 
 425 #undef MD5_MAGIC
 426 #undef MD5_MAGIC_LEN
 427 #endif
 428 

/* [<][>][^][v][top][bottom][index][help] */