Back to index

php5  5.3.10
php_crypt_r.c
Go to the documentation of this file.
00001 /* $Id: php_crypt_r.c 321634 2012-01-01 13:15:04Z felipe $ */
00002 /*
00003    +----------------------------------------------------------------------+
00004    | PHP Version 5                                                        |
00005    +----------------------------------------------------------------------+
00006    | Copyright (c) 1997-2012 The PHP Group                                |
00007    +----------------------------------------------------------------------+
00008    | This source file is subject to version 3.01 of the PHP license,      |
00009    | that is bundled with this package in the file LICENSE, and is        |
00010    | available through the world-wide-web at the following url:           |
00011    | http://www.php.net/license/3_01.txt                                  |
00012    | If you did not receive a copy of the PHP license and are unable to   |
00013    | obtain it through the world-wide-web, please send a note to          |
00014    | license@php.net so we can mail you a copy immediately.               |
00015    +----------------------------------------------------------------------+
00016    | Authors: Pierre Alain Joye  <pajoye@php.net                          |
00017    +----------------------------------------------------------------------+
00018  */
00019 
00020 /*
00021  * License for the Unix md5crypt implementation (md5_crypt):
00022  *
00023  * ----------------------------------------------------------------------------
00024  * "THE BEER-WARE LICENSE" (Revision 42):
00025  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
00026  * can do whatever you want with this stuff. If we meet some day, and you think
00027  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
00028  * ----------------------------------------------------------------------------
00029  *
00030  * from FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp
00031  * via OpenBSD: md5crypt.c,v 1.9 1997/07/23 20:58:27 kstailey Exp
00032  * via NetBSD: md5crypt.c,v 1.4.2.1 2002/01/22 19:31:59 he Exp
00033  *
00034  */
00035 
00036 #include "php.h"
00037 
00038 #include <string.h>
00039 
00040 #if PHP_WIN32
00041 # include <windows.h>
00042 # include <Wincrypt.h>
00043 #endif
00044 
00045 #ifdef HAVE_ATOMIC_H /* Solaris 10 defines atomic API within */
00046 # include <atomic.h>
00047 #else
00048 # include <signal.h>
00049 #endif
00050 #include "php_crypt_r.h"
00051 #include "crypt_freesec.h"
00052 
00053 #if !PHP_WIN32
00054 #include "ext/standard/md5.h"
00055 #endif
00056 
00057 #ifdef ZTS
00058 MUTEX_T php_crypt_extended_init_lock;
00059 #endif
00060 
00061 /* TODO: enable it when enabling vista/2k8 mode in tsrm */
00062 #if 0
00063 CONDITION_VARIABLE initialized;
00064 #endif
00065        
00066 void php_init_crypt_r()
00067 {
00068 #ifdef ZTS
00069        php_crypt_extended_init_lock = tsrm_mutex_alloc();
00070 #endif
00071 }
00072 
00073 void php_shutdown_crypt_r()
00074 {
00075 #ifdef ZTS
00076        tsrm_mutex_free(php_crypt_extended_init_lock);
00077 #endif
00078 }
00079 
00080 void _crypt_extended_init_r(void)
00081 {
00082 #ifdef PHP_WIN32
00083        LONG volatile initialized = 0;
00084 #elif defined(HAVE_ATOMIC_H) /* Solaris 10 defines atomic API within */
00085        volatile unsigned int initialized = 0;
00086 #else
00087        static volatile sig_atomic_t initialized = 0;
00088 #endif
00089 
00090 #ifdef ZTS
00091        tsrm_mutex_lock(php_crypt_extended_init_lock);
00092 #endif
00093 
00094        if (!initialized) {
00095 #ifdef PHP_WIN32
00096               InterlockedIncrement(&initialized);
00097 #elif defined(HAVE_SYNC_FETCH_AND_ADD)
00098               __sync_fetch_and_add(&initialized, 1);
00099 #elif defined(HAVE_ATOMIC_H) /* Solaris 10 defines atomic API within */
00100               membar_producer();
00101               atomic_add_int(&initialized, 1);
00102 #endif
00103               _crypt_extended_init();
00104        }
00105 #ifdef ZTS
00106        tsrm_mutex_unlock(php_crypt_extended_init_lock);
00107 #endif
00108 }
00109 
00110 /* MD% crypt implementation using the windows CryptoApi */
00111 #define MD5_MAGIC "$1$"
00112 #define MD5_MAGIC_LEN 3
00113 
00114 static unsigned char itoa64[] =           /* 0 ... 63 => ascii - 64 */
00115        "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
00116 
00117 static void
00118 to64(char *s, int32_t v, int n)
00119 {
00120        while (--n >= 0) {
00121               *s++ = itoa64[v & 0x3f];
00122               v >>= 6;
00123        }
00124 }
00125 
00126 #if PHP_WIN32
00127 char * php_md5_crypt_r(const char *pw, const char *salt, char *out) {
00128        HCRYPTPROV hCryptProv;
00129        HCRYPTHASH ctx, ctx1;
00130        unsigned int i, pwl, sl;
00131        const BYTE magic_md5[4] = "$1$";
00132        const DWORD magic_md5_len = 3;
00133        DWORD        dwHashLen;
00134        int pl;
00135        __int32 l;
00136        const char *sp = salt;
00137        const char *ep = salt;
00138        char *p = NULL;
00139        char *passwd = out;
00140        unsigned char final[16];
00141 
00142        /* Acquire a cryptographic provider context handle. */
00143        if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
00144               return NULL;
00145        }
00146 
00147        pwl = (unsigned int) strlen(pw);
00148 
00149        /* Refine the salt first */
00150        sp = salt;
00151 
00152        /* If it starts with the magic string, then skip that */
00153        if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0) {
00154               sp += MD5_MAGIC_LEN;
00155        }
00156 
00157        /* It stops at the first '$', max 8 chars */
00158        for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++) {
00159               continue;
00160        }
00161 
00162        /* get the length of the true salt */
00163        sl = ep - sp;
00164 
00165        /* Create an empty hash object. */
00166        if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx)) {
00167               goto _destroyProv;
00168        }
00169 
00170        /* The password first, since that is what is most unknown */
00171        if(!CryptHashData(ctx, (BYTE *)pw, pwl, 0)) {
00172               goto _destroyCtx0;
00173        }
00174 
00175        /* Then our magic string */
00176        if(!CryptHashData(ctx, magic_md5, magic_md5_len, 0)) {
00177               goto _destroyCtx0;
00178        }
00179 
00180        /* Then the raw salt */
00181        if(!CryptHashData( ctx, (BYTE *)sp, sl, 0)) {
00182               goto _destroyCtx0;
00183        }
00184 
00185        /* MD5(pw,salt,pw), valid. */
00186        /* Then just as many characters of the MD5(pw,salt,pw) */
00187        if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx1)) {
00188               goto _destroyCtx0;
00189        }
00190        if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
00191               goto _destroyCtx1;
00192        }
00193        if(!CryptHashData(ctx1, (BYTE *)sp, sl, 0)) {
00194               goto _destroyCtx1;
00195        }
00196        if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
00197               goto _destroyCtx1;
00198        }
00199 
00200        dwHashLen = 16;
00201        CryptGetHashParam(ctx1, HP_HASHVAL, final, &dwHashLen, 0);
00202        /*  MD5(pw,salt,pw). Valid. */
00203 
00204        for (pl = pwl; pl > 0; pl -= 16) {
00205               CryptHashData(ctx, final, (DWORD)(pl > 16 ? 16 : pl), 0);
00206        }
00207 
00208        /* Don't leave anything around in vm they could use. */
00209        memset(final, 0, sizeof(final));
00210 
00211        /* Then something really weird... */
00212        for (i = pwl; i != 0; i >>= 1) {
00213               if ((i & 1) != 0) {
00214                      CryptHashData(ctx, (const BYTE *)final, 1, 0);
00215               } else {
00216                      CryptHashData(ctx, (const BYTE *)pw, 1, 0);
00217               }
00218        }
00219 
00220        memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN);
00221 
00222 #if _MSC_VER >= 1500
00223        if (strncpy_s(passwd + MD5_MAGIC_LEN, MD5_HASH_MAX_LEN - MD5_MAGIC_LEN, sp, sl + 1) != 0) {
00224               goto _destroyCtx1;
00225        }
00226        passwd[MD5_MAGIC_LEN + sl] = '\0';
00227        strcat_s(passwd, MD5_HASH_MAX_LEN, "$");
00228 #else
00229        /* VC6 version doesn't have strcat_s or strncpy_s */
00230        strncpy(passwd + MD5_MAGIC_LEN, sp, sl + 1);
00231        strcat(passwd, "$");
00232 #endif
00233        dwHashLen = 16;
00234 
00235        /* Fetch the ctx hash value */
00236        CryptGetHashParam(ctx, HP_HASHVAL, final, &dwHashLen, 0);
00237 
00238        for (i = 0; i < 1000; i++) {
00239               if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx1)) {
00240                      goto _destroyCtx1;
00241               }
00242 
00243               if ((i & 1) != 0) {
00244                      if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
00245                             goto _destroyCtx1;
00246                      }
00247               } else {
00248                      if(!CryptHashData(ctx1, (BYTE *)final, 16, 0)) {
00249                             goto _destroyCtx1;
00250                      }
00251               }
00252 
00253               if ((i % 3) != 0) {
00254                      if(!CryptHashData(ctx1, (BYTE *)sp, sl, 0)) {
00255                             goto _destroyCtx1;
00256                      }
00257               }
00258 
00259               if ((i % 7) != 0) {
00260                      if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
00261                             goto _destroyCtx1;
00262                      }
00263               }
00264 
00265               if ((i & 1) != 0) {
00266                      if(!CryptHashData(ctx1, (BYTE *)final, 16, 0)) {
00267                             goto _destroyCtx1;
00268                      }
00269               } else {
00270                      if(!CryptHashData(ctx1, (BYTE *)pw, pwl, 0)) {
00271                             goto _destroyCtx1;
00272                      }
00273               }
00274 
00275               /* Fetch the ctx hash value */
00276               dwHashLen = 16;
00277               CryptGetHashParam(ctx1, HP_HASHVAL, final, &dwHashLen, 0);
00278               if(!(CryptDestroyHash(ctx1))) {
00279                      goto _destroyCtx0;
00280               }
00281        }
00282 
00283        ctx1 = (HCRYPTHASH) NULL;
00284 
00285        p = passwd + sl + MD5_MAGIC_LEN + 1;
00286 
00287        l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
00288        l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
00289        l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
00290        l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
00291        l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
00292        l = final[11]; to64(p,l,2); p += 2;
00293 
00294        *p = '\0';
00295 
00296        memset(final, 0, sizeof(final));
00297 
00298 
00299 _destroyCtx1:
00300        if (ctx1) {
00301               if (!CryptDestroyHash(ctx1)) {
00302                      
00303               }
00304        }
00305 
00306 _destroyCtx0:
00307        CryptDestroyHash(ctx);
00308 
00309 _destroyProv:
00310        /* Release the provider handle.*/
00311        if(hCryptProv) {
00312               if(!(CryptReleaseContext(hCryptProv, 0))) {
00313                      return NULL;
00314               }
00315        }
00316 
00317        return out;
00318 }
00319 #else
00320 
00321 /*
00322  * MD5 password encryption.
00323  */
00324 char * php_md5_crypt_r(const char *pw, const char *salt, char *out)
00325 {
00326        static char passwd[MD5_HASH_MAX_LEN], *p;
00327        const char *sp, *ep;
00328        unsigned char final[16];
00329        unsigned int i, sl, pwl;
00330        PHP_MD5_CTX   ctx, ctx1;
00331        php_uint32 l;
00332        int pl;
00333        
00334        pwl = strlen(pw);
00335        
00336        /* Refine the salt first */
00337        sp = salt;
00338 
00339        /* If it starts with the magic string, then skip that */
00340        if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0)
00341               sp += MD5_MAGIC_LEN;
00342 
00343        /* It stops at the first '$', max 8 chars */
00344        for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++)
00345               continue;
00346 
00347        /* get the length of the true salt */
00348        sl = ep - sp;
00349 
00350        PHP_MD5Init(&ctx);
00351 
00352        /* The password first, since that is what is most unknown */
00353        PHP_MD5Update(&ctx, (const unsigned char *)pw, pwl);
00354 
00355        /* Then our magic string */
00356        PHP_MD5Update(&ctx, (const unsigned char *)MD5_MAGIC, MD5_MAGIC_LEN);
00357 
00358        /* Then the raw salt */
00359        PHP_MD5Update(&ctx, (const unsigned char *)sp, sl);
00360 
00361        /* Then just as many characters of the MD5(pw,salt,pw) */
00362        PHP_MD5Init(&ctx1);
00363        PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
00364        PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl);
00365        PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
00366        PHP_MD5Final(final, &ctx1);
00367 
00368        for (pl = pwl; pl > 0; pl -= 16)
00369               PHP_MD5Update(&ctx, final, (unsigned int)(pl > 16 ? 16 : pl));
00370 
00371        /* Don't leave anything around in vm they could use. */
00372        memset(final, 0, sizeof(final));
00373 
00374        /* Then something really weird... */
00375        for (i = pwl; i != 0; i >>= 1)
00376               if ((i & 1) != 0)
00377                   PHP_MD5Update(&ctx, final, 1);
00378               else
00379                   PHP_MD5Update(&ctx, (const unsigned char *)pw, 1);
00380 
00381        /* Now make the output string */
00382        memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN);
00383        strlcpy(passwd + MD5_MAGIC_LEN, sp, sl + 1);
00384        strcat(passwd, "$");
00385 
00386        PHP_MD5Final(final, &ctx);
00387 
00388        /*
00389         * And now, just to make sure things don't run too fast. On a 60 MHz
00390         * Pentium this takes 34 msec, so you would need 30 seconds to build
00391         * a 1000 entry dictionary...
00392         */
00393        for (i = 0; i < 1000; i++) {
00394               PHP_MD5Init(&ctx1);
00395 
00396               if ((i & 1) != 0)
00397                      PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
00398               else
00399                      PHP_MD5Update(&ctx1, final, 16);
00400 
00401               if ((i % 3) != 0)
00402                      PHP_MD5Update(&ctx1, (const unsigned char *)sp, sl);
00403 
00404               if ((i % 7) != 0)
00405                      PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
00406 
00407               if ((i & 1) != 0)
00408                      PHP_MD5Update(&ctx1, final, 16);
00409               else
00410                      PHP_MD5Update(&ctx1, (const unsigned char *)pw, pwl);
00411 
00412               PHP_MD5Final(final, &ctx1);
00413        }
00414 
00415        p = passwd + sl + MD5_MAGIC_LEN + 1;
00416 
00417        l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
00418        l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
00419        l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
00420        l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
00421        l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
00422        l =                  final[11]            ; to64(p,l,2); p += 2;
00423        *p = '\0';
00424 
00425        /* Don't leave anything around in vm they could use. */
00426        memset(final, 0, sizeof(final));
00427        return (passwd);
00428 }
00429 
00430 #undef MD5_MAGIC
00431 #undef MD5_MAGIC_LEN
00432 #endif
00433