Back to index

php5  5.3.10
crypt.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Stig Bakken <ssb@php.net>                                   |
00016    |          Zeev Suraski <zeev@zend.com>                                |
00017    |          Rasmus Lerdorf <rasmus@php.net>                             |
00018    |          Pierre Joye <pierre@php.net>                                |
00019    +----------------------------------------------------------------------+
00020 */
00021 
00022 /* $Id: crypt.c 321634 2012-01-01 13:15:04Z felipe $ */
00023 
00024 #include <stdlib.h>
00025 
00026 #include "php.h"
00027 #if HAVE_CRYPT
00028 
00029 #if HAVE_UNISTD_H
00030 #include <unistd.h>
00031 #endif
00032 #if PHP_USE_PHP_CRYPT_R
00033 # include "php_crypt_r.h"
00034 # include "crypt_freesec.h"
00035 #else
00036 # if HAVE_CRYPT_H
00037 #  if defined(CRYPT_R_GNU_SOURCE) && !defined(_GNU_SOURCE)
00038 #   define _GNU_SOURCE
00039 #  endif
00040 #  include <crypt.h>
00041 # endif
00042 #endif
00043 #if TM_IN_SYS_TIME
00044 #include <sys/time.h>
00045 #else
00046 #include <time.h>
00047 #endif
00048 #if HAVE_STRING_H
00049 #include <string.h>
00050 #else
00051 #include <strings.h>
00052 #endif
00053 
00054 #ifdef PHP_WIN32
00055 #include <process.h>
00056 #endif
00057 
00058 #include "php_lcg.h"
00059 #include "php_crypt.h"
00060 #include "php_rand.h"
00061 
00062 /* The capabilities of the crypt() function is determined by the test programs
00063  * run by configure from aclocal.m4.  They will set PHP_STD_DES_CRYPT,
00064  * PHP_EXT_DES_CRYPT, PHP_MD5_CRYPT and PHP_BLOWFISH_CRYPT as appropriate
00065  * for the target platform. */
00066 
00067 #if PHP_STD_DES_CRYPT
00068 #define PHP_MAX_SALT_LEN 2
00069 #endif
00070 
00071 #if PHP_EXT_DES_CRYPT
00072 #undef PHP_MAX_SALT_LEN
00073 #define PHP_MAX_SALT_LEN 9
00074 #endif
00075 
00076 #if PHP_MD5_CRYPT
00077 #undef PHP_MAX_SALT_LEN
00078 #define PHP_MAX_SALT_LEN 12
00079 #endif
00080 
00081 #if PHP_BLOWFISH_CRYPT
00082 #undef PHP_MAX_SALT_LEN
00083 #define PHP_MAX_SALT_LEN 60
00084 #endif
00085 
00086 #if PHP_SHA512_CRYPT
00087 #undef PHP_MAX_SALT_LEN
00088 #define PHP_MAX_SALT_LEN 123
00089 #endif
00090 
00091 
00092 /* If the configure-time checks fail, we provide DES.
00093  * XXX: This is a hack. Fix the real problem! */
00094 
00095 #ifndef PHP_MAX_SALT_LEN
00096 #define PHP_MAX_SALT_LEN 2
00097 #undef PHP_STD_DES_CRYPT
00098 #define PHP_STD_DES_CRYPT 1
00099 #endif
00100 
00101 #define PHP_CRYPT_RAND php_rand(TSRMLS_C)
00102 
00103 PHP_MINIT_FUNCTION(crypt) /* {{{ */
00104 {
00105        REGISTER_LONG_CONSTANT("CRYPT_SALT_LENGTH", PHP_MAX_SALT_LEN, CONST_CS | CONST_PERSISTENT);
00106        REGISTER_LONG_CONSTANT("CRYPT_STD_DES", PHP_STD_DES_CRYPT, CONST_CS | CONST_PERSISTENT);
00107        REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", PHP_EXT_DES_CRYPT, CONST_CS | CONST_PERSISTENT);
00108        REGISTER_LONG_CONSTANT("CRYPT_MD5", PHP_MD5_CRYPT, CONST_CS | CONST_PERSISTENT);
00109        REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH", PHP_BLOWFISH_CRYPT, CONST_CS | CONST_PERSISTENT);
00110 
00111 #ifdef PHP_SHA256_CRYPT
00112    REGISTER_LONG_CONSTANT("CRYPT_SHA256", PHP_SHA256_CRYPT, CONST_CS | CONST_PERSISTENT);
00113 #endif
00114 
00115 #ifdef PHP_SHA512_CRYPT
00116    REGISTER_LONG_CONSTANT("CRYPT_SHA512", PHP_SHA512_CRYPT, CONST_CS | CONST_PERSISTENT);
00117 #endif
00118 
00119 #if PHP_USE_PHP_CRYPT_R
00120        php_init_crypt_r();
00121 #endif
00122 
00123        return SUCCESS;
00124 }
00125 /* }}} */
00126 
00127 PHP_MSHUTDOWN_FUNCTION(crypt) /* {{{ */
00128 {
00129 #if PHP_USE_PHP_CRYPT_R
00130        php_shutdown_crypt_r();
00131 #endif
00132 
00133        return SUCCESS;
00134 }
00135 /* }}} */
00136 
00137 static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
00138 
00139 static void php_to64(char *s, long v, int n) /* {{{ */
00140 {
00141        while (--n >= 0) {
00142               *s++ = itoa64[v&0x3f];
00143               v >>= 6;
00144        }
00145 }
00146 /* }}} */
00147 
00148 /* {{{ proto string crypt(string str [, string salt])
00149    Hash a string */
00150 PHP_FUNCTION(crypt)
00151 {
00152        char salt[PHP_MAX_SALT_LEN + 1];
00153        char *str, *salt_in = NULL;
00154        int str_len, salt_in_len = 0;
00155        char *crypt_res;
00156        salt[0] = salt[PHP_MAX_SALT_LEN] = '\0';
00157 
00158        /* This will produce suitable results if people depend on DES-encryption
00159         * available (passing always 2-character salt). At least for glibc6.1 */
00160        memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1);
00161 
00162        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &str, &str_len, &salt_in, &salt_in_len) == FAILURE) {
00163               return;
00164        }
00165 
00166        if (salt_in) {
00167               memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len));
00168        }
00169 
00170        /* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */
00171        if (!*salt) {
00172 #if PHP_MD5_CRYPT
00173               strncpy(salt, "$1$", PHP_MAX_SALT_LEN);
00174               php_to64(&salt[3], PHP_CRYPT_RAND, 4);
00175               php_to64(&salt[7], PHP_CRYPT_RAND, 4);
00176               strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11);
00177 #elif PHP_STD_DES_CRYPT
00178               php_to64(&salt[0], PHP_CRYPT_RAND, 2);
00179               salt[2] = '\0';
00180 #endif
00181               salt_in_len = strlen(salt);
00182        } else {
00183               salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len);
00184        }
00185 
00186 /* Windows (win32/crypt) has a stripped down version of libxcrypt and 
00187        a CryptoApi md5_crypt implementation */
00188 #if PHP_USE_PHP_CRYPT_R
00189        {
00190               struct php_crypt_extended_data buffer;
00191 
00192               if (salt[0]=='$' && salt[1]=='1' && salt[2]=='$') {
00193                      char output[MD5_HASH_MAX_LEN];
00194 
00195                      RETURN_STRING(php_md5_crypt_r(str, salt, output), 1);
00196               } else if (salt[0]=='$' && salt[1]=='6' && salt[2]=='$') {
00197                      const char sha512_salt_prefix[] = "$6$";
00198                      const char sha512_rounds_prefix[] = "rounds=";
00199                      char *output;
00200                      int needed = (sizeof(sha512_salt_prefix) - 1
00201                                           + sizeof(sha512_rounds_prefix) + 9 + 1
00202                                           + strlen(salt) + 1 + 43 + 1);
00203                      output = emalloc(needed * sizeof(char *));
00204                      salt[salt_in_len] = '\0';
00205 
00206                      crypt_res = php_sha512_crypt_r(str, salt, output, needed);
00207                      if (!crypt_res) {
00208                             if (salt[0]=='*' && salt[1]=='0') {
00209                                    RETVAL_STRING("*1", 1);
00210                             } else {
00211                                    RETVAL_STRING("*0", 1);
00212                             }
00213                      } else {
00214                             RETVAL_STRING(output, 1);
00215                      }
00216 
00217                      memset(output, 0, PHP_MAX_SALT_LEN + 1);
00218                      efree(output);
00219               } else if (salt[0]=='$' && salt[1]=='5' && salt[2]=='$') {
00220                      const char sha256_salt_prefix[] = "$5$";
00221                      const char sha256_rounds_prefix[] = "rounds=";
00222                      char *output;
00223                      int needed = (sizeof(sha256_salt_prefix) - 1
00224                                           + sizeof(sha256_rounds_prefix) + 9 + 1
00225                                           + strlen(salt) + 1 + 43 + 1);
00226                      output = emalloc(needed * sizeof(char *));
00227                      salt[salt_in_len] = '\0';
00228 
00229                      crypt_res = php_sha256_crypt_r(str, salt, output, needed);
00230                      if (!crypt_res) {
00231                             if (salt[0]=='*' && salt[1]=='0') {
00232                                    RETVAL_STRING("*1", 1);
00233                             } else {
00234                                    RETVAL_STRING("*0", 1);
00235                             }
00236                      } else {
00237                             RETVAL_STRING(output, 1);
00238                      }
00239 
00240                      memset(output, 0, PHP_MAX_SALT_LEN + 1);
00241                      efree(output);
00242               } else if (
00243                             salt[0] == '$' &&
00244                             salt[1] == '2' &&
00245                             salt[2] >= 'a' && salt[2] <= 'z' &&
00246                             salt[3] == '$' &&
00247                             salt[4] >= '0' && salt[4] <= '3' &&
00248                             salt[5] >= '0' && salt[5] <= '9' &&
00249                             salt[6] == '$') {
00250                      char output[PHP_MAX_SALT_LEN + 1];
00251 
00252                      memset(output, 0, PHP_MAX_SALT_LEN + 1);
00253 
00254                      crypt_res = php_crypt_blowfish_rn(str, salt, output, sizeof(output));
00255                      if (!crypt_res) {
00256                             if (salt[0]=='*' && salt[1]=='0') {
00257                                    RETVAL_STRING("*1", 1);
00258                             } else {
00259                                    RETVAL_STRING("*0", 1);
00260                             }
00261                      } else {
00262                             RETVAL_STRING(output, 1);
00263                      }
00264 
00265                      memset(output, 0, PHP_MAX_SALT_LEN + 1);
00266               } else {
00267                      memset(&buffer, 0, sizeof(buffer));
00268                      _crypt_extended_init_r();
00269 
00270                      crypt_res = _crypt_extended_r(str, salt, &buffer);
00271                      if (!crypt_res) {
00272                             if (salt[0]=='*' && salt[1]=='0') {
00273                                    RETURN_STRING("*1", 1);
00274                             } else {
00275                                    RETURN_STRING("*0", 1);
00276                             }
00277                      } else {
00278                             RETURN_STRING(crypt_res, 1);
00279                      }
00280               }
00281        }
00282 #else
00283 
00284 # if defined(HAVE_CRYPT_R) && (defined(_REENTRANT) || defined(_THREAD_SAFE))
00285        {
00286 #  if defined(CRYPT_R_STRUCT_CRYPT_DATA)
00287               struct crypt_data buffer;
00288               memset(&buffer, 0, sizeof(buffer));
00289 #  elif defined(CRYPT_R_CRYPTD)
00290               CRYPTD buffer;
00291 #  else
00292 #    error Data struct used by crypt_r() is unknown. Please report.
00293 #  endif
00294               crypt_res = crypt_r(str, salt, &buffer);
00295               if (!crypt_res) {
00296                             if (salt[0]=='*' && salt[1]=='0') {
00297                                    RETURN_STRING("*1", 1);
00298                             } else {
00299                                    RETURN_STRING("*0", 1);
00300                             }
00301               } else {
00302                      RETURN_STRING(crypt_res, 1);
00303               }
00304        }
00305 # endif
00306 #endif
00307 }
00308 /* }}} */
00309 #endif
00310 
00311 /*
00312  * Local variables:
00313  * tab-width: 4
00314  * c-basic-offset: 4
00315  * End:
00316  * vim600: sw=4 ts=4 fdm=marker
00317  * vim<600: sw=4 ts=4
00318  */