diff options
author | ULF WENDEL <uw@php.net> | 2012-09-29 17:40:12 +0200 |
---|---|---|
committer | ULF WENDEL <uw@php.net> | 2012-09-29 17:40:12 +0200 |
commit | 0e1df4dfe735eb038964aaf917d1e14cc7ad7de3 (patch) | |
tree | c33f8047fad4776961cec32b7fedbaa8aef3e267 | |
parent | 9da4db523f9825d65fa49fd0da87dc3d28d2591d (diff) | |
parent | 7eba512b5170fc57dc3d4a6b93f98a0e0acc7721 (diff) | |
download | php-git-0e1df4dfe735eb038964aaf917d1e14cc7ad7de3.tar.gz |
Merge branch 'master' of git.php.net:php-src
* 'master' of git.php.net:php-src: (46 commits)
updated NEWS
Fixed bug #63248 Load multiple magic files on win
Refactor to using a stack based zval instead of dynamic allocation
Clean up unreported memory leak by switching to zval_ptr_dtor
fix allocation and copy issue
Really fix leaks, add test cases to prove it...
Fix issue with possible memory leak
Fix some double free issues, and more cleanup work
Refactor slightly to enable cleaner readability
Fix arg info for required params passed to needs_rehash
Fix ucwords error casing
A bunch of naming convention fixes. No functionality changes
Switch to using an ENUM for algorithms instead of a constant
Fix incorrect arg info required param count for password_hash
Add news entry for password API
Expose PASSWORD_BCRYPT_DEFAULT_COST constant and update test to use it
Remove bcrypt_cost ini entry from declaration
Switch test to using strict comparison for crypt fallback
Add tests for password_get_info and password_needs_rehash
Refactoring to use size_t instead of int most places
...
21 files changed, 1074 insertions, 204 deletions
@@ -3,6 +3,8 @@ PHP NEWS ?? ??? 201?, PHP 5.5.0 - General improvements: + . Add simplified password hashing API + (https://wiki.php.net/rfc/password_hash). (Anthony Ferrara) . Add generators and coroutines (https://wiki.php.net/rfc/generators). (Nikita Popov) . Support list in foreach (https://wiki.php.net/rfc/foreachlist). (Laruence) @@ -84,4 +86,8 @@ PHP NEWS - Zip: . Upgraded libzip to 0.10.1 (Anatoliy) +- Fileinfo: + . Fixed bug #63248 (Load multiple magic files from a directory under Windows). + (Anatoliy) + <<< NOTE: Insert NEWS from last stable release here prior to actual release! >>> diff --git a/ext/fileinfo/config.w32 b/ext/fileinfo/config.w32 index 46b87b56dc..873a12c2f4 100644 --- a/ext/fileinfo/config.w32 +++ b/ext/fileinfo/config.w32 @@ -4,22 +4,16 @@ ARG_ENABLE("fileinfo", "fileinfo support", "no"); if (PHP_FILEINFO != 'no') { - if (CHECK_HEADER_ADD_INCLUDE("dirent.h", "CFLAGS_FILEINFO") && - CHECK_LIB("dirent_a.lib", "fileinfo", PHP_FILEINFO)) { - LIBMAGIC_SOURCES=" apprentice.c apptype.c ascmagic.c \ - cdf.c cdf_time.c compress.c \ - encoding.c fsmagic.c funcs.c \ - is_tar.c magic.c print.c \ - readcdf.c readelf.c softmagic.c"; + LIBMAGIC_SOURCES=" apprentice.c apptype.c ascmagic.c \ + cdf.c cdf_time.c compress.c \ + encoding.c fsmagic.c funcs.c \ + is_tar.c magic.c print.c \ + readcdf.c readelf.c softmagic.c"; - if (VCVERS < 1500) { - ADD_FLAG('CFLAGS', '/Zm1000'); - } + if (VCVERS < 1500) { + ADD_FLAG('CFLAGS', '/Zm1000'); + } - EXTENSION('fileinfo', 'fileinfo.c', true, "/I" + configure_module_dirname + "/libmagic /I" + configure_module_dirname); - ADD_SOURCES(configure_module_dirname + '\\libmagic', LIBMAGIC_SOURCES, "fileinfo"); - } else { - WARNING("fileinfo not enabled; libraries and headers not found"); - PHP_FILEINFO = "no"; - } + EXTENSION('fileinfo', 'fileinfo.c', true, "/I" + configure_module_dirname + "/libmagic /I" + configure_module_dirname); + ADD_SOURCES(configure_module_dirname + '\\libmagic', LIBMAGIC_SOURCES, "fileinfo"); } diff --git a/ext/fileinfo/libmagic.patch b/ext/fileinfo/libmagic.patch index 15f6a6dadd..ecb178ffa9 100644 --- a/ext/fileinfo/libmagic.patch +++ b/ext/fileinfo/libmagic.patch @@ -1,6 +1,6 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c ---- libmagic.origin/apprentice.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/apprentice.c 2012-09-11 11:36:51.000000000 +0800 +--- libmagic.origin/apprentice.c Sat Dec 17 18:17:18 2011 ++++ libmagic/apprentice.c Tue Oct 16 10:21:49 2012 @@ -29,6 +29,8 @@ * apprentice - make one pass through /etc/magic, learning its secrets. */ @@ -10,7 +10,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c #include "file.h" #ifndef lint -@@ -36,18 +38,34 @@ +@@ -36,18 +38,31 @@ #endif /* lint */ #include "magic.h" @@ -43,13 +43,11 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c -#ifdef QUICK -#include <sys/mman.h> -#endif -+#ifndef PHP_WIN32 - #include <dirent.h> -+#endif +-#include <dirent.h> #define EATAB {while (isascii((unsigned char) *l) && \ isspace((unsigned char) *l)) ++l;} -@@ -112,12 +130,10 @@ +@@ -112,12 +127,10 @@ private int parse_strength(struct magic_set *, struct magic_entry *, const char *); private int parse_apple(struct magic_set *, struct magic_entry *, const char *); @@ -62,7 +60,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c private struct { const char *name; size_t len; -@@ -131,38 +147,7 @@ +@@ -131,38 +144,7 @@ { NULL, 0, NULL } }; @@ -102,7 +100,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c static const struct type_tbl_s { const char name[16]; -@@ -218,6 +203,10 @@ +@@ -218,6 +200,10 @@ # undef XX_NULL }; @@ -113,7 +111,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c private int get_type(const char *l, const char **t) { -@@ -275,15 +264,17 @@ +@@ -275,15 +261,17 @@ if (rv != 0) return -1; rv = apprentice_compile(ms, &magic, &nmagic, fn); @@ -136,7 +134,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c if (rv != 0) return -1; } -@@ -295,11 +286,7 @@ +@@ -295,11 +283,7 @@ return -1; } @@ -149,7 +147,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c ml->magic = magic; ml->nmagic = nmagic; -@@ -318,7 +305,6 @@ +@@ -318,7 +302,6 @@ } return 0; @@ -157,7 +155,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c } protected void -@@ -327,22 +313,18 @@ +@@ -327,22 +310,18 @@ if (p == NULL) return; switch (type) { @@ -186,7 +184,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c default: abort(); } -@@ -355,23 +337,27 @@ +@@ -355,23 +334,27 @@ char *p, *mfn; int file_err, errs = -1; struct mlist *mlist; @@ -223,7 +221,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c mlist->next = mlist->prev = mlist; while (fn) { -@@ -385,13 +371,13 @@ +@@ -385,13 +368,13 @@ fn = p; } if (errs == -1) { @@ -240,7 +238,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c return mlist; } -@@ -524,6 +510,7 @@ +@@ -524,6 +507,7 @@ abort(); } @@ -248,7 +246,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c /* * Magic entries with no description get a bonus because they depend * on subsequent magic entries to print something. -@@ -539,8 +526,8 @@ +@@ -539,8 +523,8 @@ private int apprentice_sort(const void *a, const void *b) { @@ -259,7 +257,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c size_t sa = apprentice_magic_strength(ma->mp); size_t sb = apprentice_magic_strength(mb->mp); if (sa == sb) -@@ -671,12 +658,22 @@ +@@ -671,12 +655,22 @@ load_1(struct magic_set *ms, int action, const char *fn, int *errs, struct magic_entry **marray, uint32_t *marraycount) { @@ -286,7 +284,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c if (errno != ENOENT) file_error(ms, errno, "cannot read magic file `%s'", fn); -@@ -684,9 +681,12 @@ +@@ -684,9 +678,12 @@ return; } @@ -302,7 +300,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c if (len == 0) /* null line, garbage, etc */ continue; if (line[len - 1] == '\n') { -@@ -736,8 +736,7 @@ +@@ -736,8 +733,7 @@ break; } } @@ -312,7 +310,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c } /* -@@ -754,23 +753,19 @@ +@@ -754,23 +750,21 @@ apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, const char *fn, int action) { @@ -325,8 +323,12 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c + size_t files = 0, maxfiles = 0; + char **filearr = NULL; struct stat st; - DIR *dir; - struct dirent *d; +- DIR *dir; +- struct dirent *d; ++ php_stream *dir; ++ php_stream_dirent d; ++ ++ TSRMLS_FETCH(); ms->flags |= MAGIC_CHECK; /* Enable checks for parsed files */ @@ -341,28 +343,33 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c marraycount = 0; /* print silly verbose header for USG compat. */ -@@ -778,14 +773,18 @@ +@@ -778,22 +772,26 @@ (void)fprintf(stderr, "%s\n", usg_hdr); /* load directory or file */ - if (stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) { +- dir = opendir(fn); + /* FIXME: Read file names and sort them to prevent + non-determinism. See Debian bug #488562. */ + if (php_sys_stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) { -+ int mflen; -+ char mfn[MAXPATHLEN]; - dir = opendir(fn); ++ int mflen; ++ char mfn[MAXPATHLEN]; ++ ++ dir = php_stream_opendir(fn, REPORT_ERRORS, NULL); if (!dir) { errs++; goto out; } - while ((d = readdir(dir)) != NULL) { +- while ((d = readdir(dir)) != NULL) { - if (asprintf(&mfn, "%s/%s", fn, d->d_name) < 0) { -+ if ((mflen = snprintf(mfn, sizeof(mfn), "%s/%s", fn, d->d_name)) < 0) { ++ while (php_stream_readdir(dir, &d)) { ++ if ((mflen = snprintf(mfn, sizeof(mfn), "%s/%s", fn, d.d_name)) < 0) { file_oomem(ms, - strlen(fn) + strlen(d->d_name) + 2); +- strlen(fn) + strlen(d->d_name) + 2); ++ strlen(fn) + strlen(d.d_name) + 2); errs++; -@@ -793,7 +792,6 @@ +- closedir(dir); ++ php_stream_closedir(dir); goto out; } if (stat(mfn, &st) == -1 || !S_ISREG(st.st_mode)) { @@ -375,7 +382,8 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c realloc(filearr, mlen))) == NULL) { file_oomem(ms, mlen); - free(mfn); - closedir(dir); +- closedir(dir); ++ php_stream_closedir(dir); errs++; goto out; } @@ -383,7 +391,8 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c - filearr[files++] = mfn; + filearr[files++] = estrndup(mfn, (mflen > sizeof(mfn) - 1)? sizeof(mfn) - 1: mflen); } - closedir(dir); +- closedir(dir); ++ php_stream_closedir(dir); qsort(filearr, files, sizeof(*filearr), cmpstrp); for (i = 0; i < files; i++) { load_1(ms, action, filearr[i], &errs, &marray, @@ -512,7 +521,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c m->mimetype[0] = '\0'; /* initialise MIME type to none */ if (m->cont_level == 0) ++(*nmentryp); /* make room for next */ -@@ -2195,56 +2180,69 @@ +@@ -2195,56 +2180,79 @@ /* * handle a compiled file. @@ -543,6 +552,16 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c + ret = 3; + goto internal_loaded; + } ++ ++#ifdef PHP_WIN32 ++ /* Don't bother on windows with php_stream_open_wrapper, ++ return to give apprentice_load() a chance. */ ++ if (php_stream_stat_path_ex(fn, 0, &st, NULL) == SUCCESS) { ++ if (st.sb.st_mode & S_IFDIR) { ++ goto error2; ++ } ++ } ++#endif dbname = mkdbname(ms, fn, 0); if (dbname == NULL) @@ -605,7 +624,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c ptr = (uint32_t *)(void *)*magicp; if (*ptr != MAGICNO) { if (swap4(*ptr) != MAGICNO) { -@@ -2259,35 +2257,55 @@ +@@ -2259,35 +2267,55 @@ else version = ptr[1]; if (version != VERSIONNO) { @@ -677,7 +696,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c return -1; } -@@ -2301,42 +2319,49 @@ +@@ -2301,42 +2329,49 @@ apprentice_compile(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, const char *fn) { @@ -738,7 +757,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c return rv; } -@@ -2349,6 +2374,7 @@ +@@ -2349,6 +2384,7 @@ { const char *p, *q; char *buf; @@ -746,7 +765,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c if (strip) { if ((p = strrchr(fn, '/')) != NULL) -@@ -2370,14 +2396,14 @@ +@@ -2370,14 +2406,14 @@ q++; /* Compatibility with old code that looked in .mime */ if (ms->flags & MAGIC_MIME) { @@ -765,7 +784,7 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c /* Compatibility with old code that looked in .mime */ if (strstr(p, ".mime") != NULL) -@@ -2467,7 +2493,7 @@ +@@ -2467,7 +2503,7 @@ m->offset = swap4((uint32_t)m->offset); m->in_offset = swap4((uint32_t)m->in_offset); m->lineno = swap4((uint32_t)m->lineno); @@ -775,8 +794,8 @@ diff -u libmagic.origin/apprentice.c libmagic/apprentice.c m->str_flags = swap4(m->str_flags); } diff -u libmagic.origin/ascmagic.c libmagic/ascmagic.c ---- libmagic.origin/ascmagic.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/ascmagic.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/ascmagic.c Sat Dec 17 18:17:18 2011 ++++ libmagic/ascmagic.c Tue Apr 10 09:46:33 2012 @@ -139,10 +139,8 @@ /* malloc size is a conservative overestimate; could be improved, or at least realloced after conversion. */ @@ -801,8 +820,8 @@ diff -u libmagic.origin/ascmagic.c libmagic/ascmagic.c return rv; } diff -u libmagic.origin/cdf.c libmagic/cdf.c ---- libmagic.origin/cdf.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/cdf.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/cdf.c Mon Feb 20 23:35:29 2012 ++++ libmagic/cdf.c Tue Apr 10 09:46:33 2012 @@ -43,7 +43,17 @@ #include <err.h> #endif @@ -865,8 +884,8 @@ diff -u libmagic.origin/cdf.c libmagic/cdf.c (void)fprintf(stderr, "timestamp %s\n", buf); } else { diff -u libmagic.origin/cdf.h libmagic/cdf.h ---- libmagic.origin/cdf.h 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/cdf.h 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/cdf.h Fri Feb 17 06:28:31 2012 ++++ libmagic/cdf.h Tue Apr 10 09:46:34 2012 @@ -35,7 +35,7 @@ #ifndef _H_CDF_ #define _H_CDF_ @@ -903,8 +922,8 @@ diff -u libmagic.origin/cdf.h libmagic/cdf.h void cdf_swap_header(cdf_header_t *); void cdf_unpack_header(cdf_header_t *, char *); diff -u libmagic.origin/cdf_time.c libmagic/cdf_time.c ---- libmagic.origin/cdf_time.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/cdf_time.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/cdf_time.c Tue Dec 13 14:48:41 2011 ++++ libmagic/cdf_time.c Tue Apr 10 09:46:34 2012 @@ -96,7 +96,7 @@ } @@ -962,8 +981,8 @@ diff -u libmagic.origin/cdf_time.c libmagic/cdf_time.c static const char *ref = "Sat Apr 23 01:30:00 1977"; char *p, *q; diff -u libmagic.origin/compress.c libmagic/compress.c ---- libmagic.origin/compress.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/compress.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/compress.c Sat Dec 17 18:17:18 2011 ++++ libmagic/compress.c Tue Apr 10 09:46:34 2012 @@ -32,6 +32,7 @@ * uncompress(method, old, n, newch) - uncompress old into new, * using method, return sizeof new @@ -1124,10 +1143,9 @@ diff -u libmagic.origin/compress.c libmagic/compress.c } -#endif +#endif /* if PHP_FILEINFO_UNCOMPRESS */ -Only in libmagic: diff diff -u libmagic.origin/file.h libmagic/file.h ---- libmagic.origin/file.h 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/file.h 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/file.h Tue Sep 20 17:30:14 2011 ++++ libmagic/file.h Mon Apr 23 17:58:54 2012 @@ -33,11 +33,9 @@ #ifndef __file_h__ #define __file_h__ @@ -1285,22 +1303,24 @@ diff -u libmagic.origin/file.h libmagic/file.h size_t strlcat(char *dst, const char *src, size_t siz); #endif #ifndef HAVE_GETLINE -@@ -500,4 +487,12 @@ - #define FILE_RCSID(id) +@@ -498,6 +485,14 @@ #endif - + #else + #define FILE_RCSID(id) ++#endif ++ +#ifdef PHP_WIN32 +#define FINFO_LSEEK_FUNC _lseek +#define FINFO_READ_FUNC _read +#else +#define FINFO_LSEEK_FUNC lseek +#define FINFO_READ_FUNC read -+#endif -+ + #endif + #endif /* __file_h__ */ diff -u libmagic.origin/fsmagic.c libmagic/fsmagic.c ---- libmagic.origin/fsmagic.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/fsmagic.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/fsmagic.c Tue Aug 23 10:57:10 2011 ++++ libmagic/fsmagic.c Tue Apr 10 09:46:34 2012 @@ -59,27 +59,21 @@ # define minor(dev) ((dev) & 0xff) #endif @@ -1511,10 +1531,10 @@ diff -u libmagic.origin/fsmagic.c libmagic/fsmagic.c -#else - if (file_printf(ms, "block special") == -1) - return -1; --#endif + #endif - } - return 1; - #endif +-#endif - /* TODO add code to handle V7 MUX and Blit MUX files */ + #ifdef S_IFIFO @@ -1624,8 +1644,8 @@ diff -u libmagic.origin/fsmagic.c libmagic/fsmagic.c /* diff -u libmagic.origin/funcs.c libmagic/funcs.c ---- libmagic.origin/funcs.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/funcs.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/funcs.c Sat Dec 17 18:17:18 2011 ++++ libmagic/funcs.c Mon Apr 23 17:58:54 2012 @@ -41,52 +41,42 @@ #if defined(HAVE_WCTYPE_H) #include <wctype.h> @@ -1920,8 +1940,8 @@ diff -u libmagic.origin/funcs.c libmagic/funcs.c } + diff -u libmagic.origin/magic.c libmagic/magic.c ---- libmagic.origin/magic.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/magic.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/magic.c Thu May 26 03:27:59 2011 ++++ libmagic/magic.c Tue Apr 10 09:46:34 2012 @@ -25,11 +25,6 @@ * SUCH DAMAGE. */ @@ -2298,8 +2318,8 @@ diff -u libmagic.origin/magic.c libmagic/magic.c public const char * magic_error(struct magic_set *ms) diff -u libmagic.origin/magic.h libmagic/magic.h ---- libmagic.origin/magic.h 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/magic.h 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/magic.h Sun Dec 18 15:54:43 2011 ++++ libmagic/magic.h Tue Apr 10 09:46:34 2012 @@ -85,6 +85,7 @@ const char *magic_getpath(const char *, int); @@ -2317,9 +2337,9 @@ diff -u libmagic.origin/magic.h libmagic/magic.h int magic_errno(magic_t); diff -u libmagic.origin/print.c libmagic/print.c ---- libmagic.origin/print.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/print.c 2012-09-11 11:33:55.000000000 +0800 -@@ -29,6 +29,9 @@ +--- libmagic.origin/print.c Tue Sep 20 17:28:09 2011 ++++ libmagic/print.c Tue Oct 16 10:13:39 2012 +@@ -29,12 +29,16 @@ * print.c - debugging printout routines */ @@ -2329,7 +2349,14 @@ diff -u libmagic.origin/print.c libmagic/print.c #include "file.h" #ifndef lint -@@ -46,174 +49,21 @@ + FILE_RCSID("@(#)$File: print.c,v 1.71 2011/09/20 15:28:09 christos Exp $") + #endif /* lint */ + ++#include <stdio.h> + #include <string.h> + #include <stdarg.h> + #include <stdlib.h> +@@ -45,174 +49,21 @@ #define SZOF(a) (sizeof(a) / sizeof(a[0])) @@ -2512,8 +2539,8 @@ diff -u libmagic.origin/print.c libmagic/print.c protected const char * diff -u libmagic.origin/readcdf.c libmagic/readcdf.c ---- libmagic.origin/readcdf.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/readcdf.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/readcdf.c Mon Feb 20 21:04:58 2012 ++++ libmagic/readcdf.c Tue Apr 10 09:46:34 2012 @@ -30,7 +30,11 @@ #endif @@ -2560,8 +2587,8 @@ diff -u libmagic.origin/readcdf.c libmagic/readcdf.c if ((ec = strchr(c, '\n')) != NULL) *ec = '\0'; diff -u libmagic.origin/readelf.c libmagic/readelf.c ---- libmagic.origin/readelf.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/readelf.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/readelf.c Tue Aug 23 10:57:10 2011 ++++ libmagic/readelf.c Tue Apr 10 09:46:34 2012 @@ -49,7 +49,7 @@ off_t, int *, int); private int doshn(struct magic_set *, int, int, int, off_t, int, size_t, @@ -2717,8 +2744,8 @@ diff -u libmagic.origin/readelf.c libmagic/readelf.c if (fstat(fd, &st) == -1) { diff -u libmagic.origin/softmagic.c libmagic/softmagic.c ---- libmagic.origin/softmagic.c 2012-09-11 11:09:26.000000000 +0800 -+++ libmagic/softmagic.c 2012-09-11 11:33:55.000000000 +0800 +--- libmagic.origin/softmagic.c Sat Dec 17 18:17:18 2011 ++++ libmagic/softmagic.c Fri May 25 09:59:25 2012 @@ -41,6 +41,11 @@ #include <stdlib.h> #include <time.h> diff --git a/ext/fileinfo/libmagic/apprentice.c b/ext/fileinfo/libmagic/apprentice.c index d11bd159a8..787eb79367 100644 --- a/ext/fileinfo/libmagic/apprentice.c +++ b/ext/fileinfo/libmagic/apprentice.c @@ -63,9 +63,6 @@ FILE_RCSID("@(#)$File: apprentice.c,v 1.173 2011/12/08 12:38:24 rrt Exp $") #include <assert.h> #include <ctype.h> #include <fcntl.h> -#ifndef PHP_WIN32 -#include <dirent.h> -#endif #define EATAB {while (isascii((unsigned char) *l) && \ isspace((unsigned char) *l)) ++l;} @@ -759,8 +756,10 @@ apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, size_t files = 0, maxfiles = 0; char **filearr = NULL; struct stat st; - DIR *dir; - struct dirent *d; + php_stream *dir; + php_stream_dirent d; + + TSRMLS_FETCH(); ms->flags |= MAGIC_CHECK; /* Enable checks for parsed files */ @@ -776,19 +775,20 @@ apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, /* FIXME: Read file names and sort them to prevent non-determinism. See Debian bug #488562. */ if (php_sys_stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) { - int mflen; - char mfn[MAXPATHLEN]; - dir = opendir(fn); + int mflen; + char mfn[MAXPATHLEN]; + + dir = php_stream_opendir(fn, REPORT_ERRORS, NULL); if (!dir) { errs++; goto out; } - while ((d = readdir(dir)) != NULL) { - if ((mflen = snprintf(mfn, sizeof(mfn), "%s/%s", fn, d->d_name)) < 0) { + while (php_stream_readdir(dir, &d)) { + if ((mflen = snprintf(mfn, sizeof(mfn), "%s/%s", fn, d.d_name)) < 0) { file_oomem(ms, - strlen(fn) + strlen(d->d_name) + 2); + strlen(fn) + strlen(d.d_name) + 2); errs++; - closedir(dir); + php_stream_closedir(dir); goto out; } if (stat(mfn, &st) == -1 || !S_ISREG(st.st_mode)) { @@ -801,14 +801,14 @@ apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, if ((filearr = CAST(char **, realloc(filearr, mlen))) == NULL) { file_oomem(ms, mlen); - closedir(dir); + php_stream_closedir(dir); errs++; goto out; } } filearr[files++] = estrndup(mfn, (mflen > sizeof(mfn) - 1)? sizeof(mfn) - 1: mflen); } - closedir(dir); + php_stream_closedir(dir); qsort(filearr, files, sizeof(*filearr), cmpstrp); for (i = 0; i < files; i++) { load_1(ms, action, filearr[i], &errs, &marray, @@ -2206,6 +2206,16 @@ apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, goto internal_loaded; } +#ifdef PHP_WIN32 + /* Don't bother on windows with php_stream_open_wrapper, + return to give apprentice_load() a chance. */ + if (php_stream_stat_path_ex(fn, 0, &st, NULL) == SUCCESS) { + if (st.sb.st_mode & S_IFDIR) { + goto error2; + } + } +#endif + dbname = mkdbname(ms, fn, 0); if (dbname == NULL) goto error2; diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index a2d236c9df..a30579e143 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1854,6 +1854,25 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getlastmod, 0) ZEND_END_ARG_INFO() /* }}} */ +/* {{{ password.c */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_password_hash, 0, 0, 2) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, algo) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_password_get_info, 0, 0, 1) + ZEND_ARG_INFO(0, hash) +ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_password_needs_rehash, 0, 0, 2) + ZEND_ARG_INFO(0, hash) + ZEND_ARG_INFO(0, algo) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_password_verify, 0, 0, 2) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, hash) +ZEND_END_ARG_INFO() +/* }}} */ /* {{{ proc_open.c */ #ifdef PHP_CAN_SUPPORT_PROC_OPEN ZEND_BEGIN_ARG_INFO_EX(arginfo_proc_terminate, 0, 0, 1) @@ -2864,6 +2883,10 @@ const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(base64_decode, arginfo_base64_decode) PHP_FE(base64_encode, arginfo_base64_encode) + PHP_FE(password_hash, arginfo_password_hash) + PHP_FE(password_get_info, arginfo_password_get_info) + PHP_FE(password_needs_rehash, arginfo_password_needs_rehash) + PHP_FE(password_verify, arginfo_password_verify) PHP_FE(convert_uuencode, arginfo_convert_uuencode) PHP_FE(convert_uudecode, arginfo_convert_uudecode) @@ -3614,6 +3637,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ BASIC_MINIT_SUBMODULE(browscap) BASIC_MINIT_SUBMODULE(standard_filters) BASIC_MINIT_SUBMODULE(user_filters) + BASIC_MINIT_SUBMODULE(password) #if defined(HAVE_LOCALECONV) && defined(ZTS) BASIC_MINIT_SUBMODULE(localeconv) diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index c33ae1e05c..fba423b191 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -580,7 +580,7 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32. incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \ http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \ var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \ - filters.c proc_open.c streamsfuncs.c http.c) + filters.c proc_open.c streamsfuncs.c http.c password.c) PHP_ADD_MAKEFILE_FRAGMENT PHP_INSTALL_HEADERS([ext/standard/]) diff --git a/ext/standard/config.w32 b/ext/standard/config.w32 index d14b859e9d..5f24641b4d 100644 --- a/ext/standard/config.w32 +++ b/ext/standard/config.w32 @@ -19,7 +19,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \ versioning.c assert.c strnatcmp.c levenshtein.c incomplete_class.c \ url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \ php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \ - user_filters.c uuencode.c filters.c proc_open.c \ + user_filters.c uuencode.c filters.c proc_open.c password.c \ streamsfuncs.c http.c flock_compat.c", false /* never shared */); PHP_INSTALL_HEADERS("", "ext/standard"); if (PHP_MBREGEX != "no") { diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c index 27a8d82d0e..3b443fc4d5 100644 --- a/ext/standard/crypt.c +++ b/ext/standard/crypt.c @@ -145,44 +145,9 @@ static void php_to64(char *s, long v, int n) /* {{{ */ } /* }}} */ -/* {{{ proto string crypt(string str [, string salt]) - Hash a string */ -PHP_FUNCTION(crypt) +PHPAPI int php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, char **result) { - char salt[PHP_MAX_SALT_LEN + 1]; - char *str, *salt_in = NULL; - int str_len, salt_in_len = 0; char *crypt_res; - salt[0] = salt[PHP_MAX_SALT_LEN] = '\0'; - - /* This will produce suitable results if people depend on DES-encryption - * available (passing always 2-character salt). At least for glibc6.1 */ - memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &str, &str_len, &salt_in, &salt_in_len) == FAILURE) { - return; - } - - if (salt_in) { - memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len)); - } - - /* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */ - if (!*salt) { -#if PHP_MD5_CRYPT - strncpy(salt, "$1$", PHP_MAX_SALT_LEN); - php_to64(&salt[3], PHP_CRYPT_RAND, 4); - php_to64(&salt[7], PHP_CRYPT_RAND, 4); - strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11); -#elif PHP_STD_DES_CRYPT - php_to64(&salt[0], PHP_CRYPT_RAND, 2); - salt[2] = '\0'; -#endif - salt_in_len = strlen(salt); - } else { - salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len); - } - /* Windows (win32/crypt) has a stripped down version of libxcrypt and a CryptoApi md5_crypt implementation */ #if PHP_USE_PHP_CRYPT_R @@ -190,55 +155,44 @@ PHP_FUNCTION(crypt) struct php_crypt_extended_data buffer; if (salt[0]=='$' && salt[1]=='1' && salt[2]=='$') { - char output[MD5_HASH_MAX_LEN]; + char output[MD5_HASH_MAX_LEN], *out; - RETURN_STRING(php_md5_crypt_r(str, salt, output), 1); + out = php_md5_crypt_r(password, salt, output); + if (out) { + *result = estrdup(out); + return SUCCESS; + } + return FAILURE; } else if (salt[0]=='$' && salt[1]=='6' && salt[2]=='$') { - const char sha512_salt_prefix[] = "$6$"; - const char sha512_rounds_prefix[] = "rounds="; char *output; - int needed = (sizeof(sha512_salt_prefix) - 1 - + sizeof(sha512_rounds_prefix) + 9 + 1 - + salt_in_len + 1 + 86 + 1); - output = emalloc(needed); - salt[salt_in_len] = '\0'; + output = emalloc(PHP_MAX_SALT_LEN); - crypt_res = php_sha512_crypt_r(str, salt, output, needed); + crypt_res = php_sha512_crypt_r(password, salt, output, PHP_MAX_SALT_LEN); if (!crypt_res) { - if (salt[0]=='*' && salt[1]=='0') { - RETVAL_STRING("*1", 1); - } else { - RETVAL_STRING("*0", 1); - } + memset(output, 0, PHP_MAX_SALT_LEN); + efree(output); + return FAILURE; } else { - RETVAL_STRING(output, 1); + *result = estrdup(output); + memset(output, 0, PHP_MAX_SALT_LEN); + efree(output); + return SUCCESS; } - - memset(output, 0, needed); - efree(output); } else if (salt[0]=='$' && salt[1]=='5' && salt[2]=='$') { - const char sha256_salt_prefix[] = "$5$"; - const char sha256_rounds_prefix[] = "rounds="; char *output; - int needed = (sizeof(sha256_salt_prefix) - 1 - + sizeof(sha256_rounds_prefix) + 9 + 1 - + salt_in_len + 1 + 43 + 1); - output = emalloc(needed); - salt[salt_in_len] = '\0'; + output = emalloc(PHP_MAX_SALT_LEN); - crypt_res = php_sha256_crypt_r(str, salt, output, needed); + crypt_res = php_sha256_crypt_r(password, salt, output, PHP_MAX_SALT_LEN); if (!crypt_res) { - if (salt[0]=='*' && salt[1]=='0') { - RETVAL_STRING("*1", 1); - } else { - RETVAL_STRING("*0", 1); - } + memset(output, 0, PHP_MAX_SALT_LEN); + efree(output); + return FAILURE; } else { - RETVAL_STRING(output, 1); + *result = estrdup(output); + memset(output, 0, PHP_MAX_SALT_LEN); + efree(output); + return SUCCESS; } - - memset(output, 0, needed); - efree(output); } else if ( salt[0] == '$' && salt[1] == '2' && @@ -251,31 +205,25 @@ PHP_FUNCTION(crypt) memset(output, 0, PHP_MAX_SALT_LEN + 1); - crypt_res = php_crypt_blowfish_rn(str, salt, output, sizeof(output)); + crypt_res = php_crypt_blowfish_rn(password, salt, output, sizeof(output)); if (!crypt_res) { - if (salt[0]=='*' && salt[1]=='0') { - RETVAL_STRING("*1", 1); - } else { - RETVAL_STRING("*0", 1); - } + memset(output, 0, PHP_MAX_SALT_LEN + 1); + return FAILURE; } else { - RETVAL_STRING(output, 1); + *result = estrdup(output); + memset(output, 0, PHP_MAX_SALT_LEN + 1); + return SUCCESS; } - - memset(output, 0, PHP_MAX_SALT_LEN + 1); } else { memset(&buffer, 0, sizeof(buffer)); _crypt_extended_init_r(); - crypt_res = _crypt_extended_r(str, salt, &buffer); + crypt_res = _crypt_extended_r(password, salt, &buffer); if (!crypt_res) { - if (salt[0]=='*' && salt[1]=='0') { - RETURN_STRING("*1", 1); - } else { - RETURN_STRING("*0", 1); - } + return FAILURE; } else { - RETURN_STRING(crypt_res, 1); + *result = estrdup(crypt_res); + return SUCCESS; } } } @@ -291,21 +239,68 @@ PHP_FUNCTION(crypt) # else # error Data struct used by crypt_r() is unknown. Please report. # endif - crypt_res = crypt_r(str, salt, &buffer); + crypt_res = crypt_r(password, salt, &buffer); if (!crypt_res) { - if (salt[0]=='*' && salt[1]=='0') { - RETURN_STRING("*1", 1); - } else { - RETURN_STRING("*0", 1); - } + return FAILURE; } else { - RETURN_STRING(crypt_res, 1); + *result = estrdup(crypt_res); + return SUCCESS; } } # endif #endif } /* }}} */ + + +/* {{{ proto string crypt(string str [, string salt]) + Hash a string */ +PHP_FUNCTION(crypt) +{ + char salt[PHP_MAX_SALT_LEN + 1]; + char *str, *salt_in = NULL, *result = NULL; + int str_len, salt_in_len = 0; + salt[0] = salt[PHP_MAX_SALT_LEN] = '\0'; + + /* This will produce suitable results if people depend on DES-encryption + * available (passing always 2-character salt). At least for glibc6.1 */ + memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &str, &str_len, &salt_in, &salt_in_len) == FAILURE) { + return; + } + + if (salt_in) { + memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len)); + } + + /* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */ + if (!*salt) { +#if PHP_MD5_CRYPT + strncpy(salt, "$1$", PHP_MAX_SALT_LEN); + php_to64(&salt[3], PHP_CRYPT_RAND, 4); + php_to64(&salt[7], PHP_CRYPT_RAND, 4); + strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11); +#elif PHP_STD_DES_CRYPT + php_to64(&salt[0], PHP_CRYPT_RAND, 2); + salt[2] = '\0'; +#endif + salt_in_len = strlen(salt); + } else { + salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len); + } + salt[salt_in_len] = '\0'; + + if (php_crypt(str, str_len, salt, salt_in_len, &result) == FAILURE) { + if (salt[0] == '*' && salt[1] == '0') { + RETURN_STRING("*1", 1); + } else { + RETURN_STRING("*0", 1); + } + } + RETURN_STRING(result, 0); +} +/* }}} */ #endif /* diff --git a/ext/standard/password.c b/ext/standard/password.c new file mode 100644 index 0000000000..266ad0a421 --- /dev/null +++ b/ext/standard/password.c @@ -0,0 +1,460 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2012 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Anthony Ferrara <ircmaxell@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include <stdlib.h> + +#include "php.h" +#if HAVE_CRYPT + +#include "fcntl.h" +#include "php_password.h" +#include "php_rand.h" +#include "php_crypt.h" +#include "base64.h" +#include "zend_interfaces.h" +#include "info.h" + +#if PHP_WIN32 +#include "win32/winutil.h" +#endif + +PHP_MINIT_FUNCTION(password) /* {{{ */ +{ + REGISTER_LONG_CONSTANT("PASSWORD_DEFAULT", PHP_PASSWORD_DEFAULT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PASSWORD_BCRYPT", PHP_PASSWORD_BCRYPT, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("PASSWORD_BCRYPT_DEFAULT_COST", PHP_PASSWORD_BCRYPT_COST, CONST_CS | CONST_PERSISTENT); + + return SUCCESS; +} +/* }}} */ + +static char* php_password_get_algo_name(const php_password_algo algo) +{ + switch (algo) { + case PHP_PASSWORD_BCRYPT: + return "bcrypt"; + case PHP_PASSWORD_UNKNOWN: + default: + return "unknown"; + } +} + +static php_password_algo php_password_determine_algo(const char *hash, const size_t len) +{ + if (len > 3 && hash[0] == '$' && hash[1] == '2' && hash[2] == 'y' && len == 60) { + return PHP_PASSWORD_BCRYPT; + } + + return PHP_PASSWORD_UNKNOWN; +} + +static zend_bool php_password_salt_is_alphabet(const char *str, const size_t len) /* {{{ */ +{ + size_t i = 0; + + for (i = 0; i < len; i++) { + if (!((str[i] >= 'A' && str[i] <= 'Z') || (str[i] >= 'a' && str[i] <= 'z') || (str[i] >= '0' && str[i] <= '9') || str[i] == '.' || str[i] == '/')) { + return 0; + } + } + return 1; +} +/* }}} */ + +static zend_bool php_password_salt_to64(const char *str, const size_t str_len, const size_t out_len, char *ret) /* {{{ */ +{ + size_t pos = 0; + size_t ret_len = 0; + unsigned char *buffer; + if ((int) str_len < 0) { + return FAILURE; + } + buffer = php_base64_encode((unsigned char*) str, (int) str_len, (int*) &ret_len); + if (ret_len < out_len) { + /* Too short of an encoded string generated */ + efree(buffer); + return FAILURE; + } + for (pos = 0; pos < out_len; pos++) { + if (buffer[pos] == '+') { + ret[pos] = '.'; + } else if (buffer[pos] == '=') { + efree(buffer); + return FAILURE; + } else { + ret[pos] = buffer[pos]; + } + } + efree(buffer); + return SUCCESS; +} +/* }}} */ + +static zend_bool php_password_make_salt(size_t length, char *ret TSRMLS_DC) /* {{{ */ +{ + int buffer_valid = 0; + size_t i, raw_length; + char *buffer; + char *result; + + if (length > (INT_MAX / 3)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length is too large to safely generate"); + return FAILURE; + } + + raw_length = length * 3 / 4 + 1; + + buffer = (char *) safe_emalloc(raw_length, 1, 1); + +#if PHP_WIN32 + { + BYTE *iv_b = (BYTE *) buffer; + if (php_win32_get_random_bytes(iv_b, raw_length) == SUCCESS) { + buffer_valid = 1; + } + } +#else + { + int fd, n; + size_t read_bytes = 0; + fd = open("/dev/urandom", O_RDONLY); + if (fd >= 0) { + while (read_bytes < raw_length) { + n = read(fd, buffer + read_bytes, raw_length - read_bytes); + if (n < 0) { + break; + } + read_bytes += (size_t) n; + } + close(fd); + } + if (read_bytes >= raw_length) { + buffer_valid = 1; + } + } +#endif + if (!buffer_valid) { + for (i = 0; i < raw_length; i++) { + buffer[i] ^= (char) (255.0 * php_rand(TSRMLS_C) / RAND_MAX); + } + } + + result = safe_emalloc(length, 1, 1); + if (php_password_salt_to64(buffer, raw_length, length, result) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Generated salt too short"); + efree(buffer); + efree(result); + return FAILURE; + } + memcpy(ret, result, (int) length); + efree(result); + efree(buffer); + ret[length] = 0; + return SUCCESS; +} +/* }}} */ + +PHP_FUNCTION(password_get_info) +{ + php_password_algo algo; + int hash_len; + char *hash, *algo_name; + zval *options; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hash, &hash_len) == FAILURE) { + return; + } + + if (hash_len < 0 || (size_t) hash_len < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Supplied password hash too long to safely identify"); + RETURN_FALSE; + } + + ALLOC_INIT_ZVAL(options); + array_init(options); + + algo = php_password_determine_algo(hash, (size_t) hash_len); + algo_name = php_password_get_algo_name(algo); + + switch (algo) { + case PHP_PASSWORD_BCRYPT: + { + long cost = PHP_PASSWORD_BCRYPT_COST; + sscanf(hash, "$2y$%ld$", &cost); + add_assoc_long(options, "cost", cost); + } + break; + case PHP_PASSWORD_UNKNOWN: + default: + break; + } + + array_init(return_value); + + add_assoc_long(return_value, "algo", algo); + add_assoc_string(return_value, "algoName", algo_name, 1); + add_assoc_zval(return_value, "options", options); +} + +PHP_FUNCTION(password_needs_rehash) +{ + long new_algo = 0; + php_password_algo algo; + int hash_len; + char *hash; + HashTable *options = 0; + zval **option_buffer; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|H", &hash, &hash_len, &new_algo, &options) == FAILURE) { + return; + } + + if (hash_len < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Supplied password hash too long to safely identify"); + RETURN_FALSE; + } + + algo = php_password_determine_algo(hash, (size_t) hash_len); + + if (algo != new_algo) { + RETURN_TRUE; + } + + switch (algo) { + case PHP_PASSWORD_BCRYPT: + { + long new_cost = PHP_PASSWORD_BCRYPT_COST, cost = 0; + + if (options && zend_symtable_find(options, "cost", sizeof("cost"), (void **) &option_buffer) == SUCCESS) { + if (Z_TYPE_PP(option_buffer) != IS_LONG) { + zval cast_option_buffer; + MAKE_COPY_ZVAL(option_buffer, &cast_option_buffer); + convert_to_long(&cast_option_buffer); + new_cost = Z_LVAL(cast_option_buffer); + zval_dtor(&cast_option_buffer); + } else { + new_cost = Z_LVAL_PP(option_buffer); + } + } + + sscanf(hash, "$2y$%ld$", &cost); + if (cost != new_cost) { + RETURN_TRUE; + } + } + break; + case PHP_PASSWORD_UNKNOWN: + default: + break; + } + RETURN_FALSE; +} + +/* {{{ proto boolean password_make_salt(string password, string hash) +Verify a hash created using crypt() or password_hash() */ +PHP_FUNCTION(password_verify) +{ + int status = 0, i; + int password_len, hash_len; + char *ret, *password, *hash; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &password, &password_len, &hash, &hash_len) == FAILURE) { + RETURN_FALSE; + } + if (php_crypt(password, password_len, hash, hash_len, &ret) == FAILURE) { + RETURN_FALSE; + } + + if (strlen(ret) != hash_len || hash_len < 13) { + efree(ret); + RETURN_FALSE; + } + + /* We're using this method instead of == in order to provide + * resistence towards timing attacks. This is a constant time + * equality check that will always check every byte of both + * values. */ + for (i = 0; i < hash_len; i++) { + status |= (ret[i] ^ hash[i]); + } + + efree(ret); + + RETURN_BOOL(status == 0); + +} +/* }}} */ + +/* {{{ proto string password_hash(string password, int algo, array options = array()) +Hash a password */ +PHP_FUNCTION(password_hash) +{ + char *hash_format, *hash, *salt, *password, *result; + long algo = 0; + int password_len = 0, hash_len; + size_t salt_len = 0, required_salt_len = 0, hash_format_len; + HashTable *options = 0; + zval **option_buffer; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|H", &password, &password_len, &algo, &options) == FAILURE) { + return; + } + + switch (algo) { + case PHP_PASSWORD_BCRYPT: + { + long cost = PHP_PASSWORD_BCRYPT_COST; + + if (options && zend_symtable_find(options, "cost", 5, (void **) &option_buffer) == SUCCESS) { + if (Z_TYPE_PP(option_buffer) != IS_LONG) { + zval cast_option_buffer; + MAKE_COPY_ZVAL(option_buffer, &cast_option_buffer); + convert_to_long(&cast_option_buffer); + cost = Z_LVAL(cast_option_buffer); + zval_dtor(&cast_option_buffer); + } else { + cost = Z_LVAL_PP(option_buffer); + } + } + + if (cost < 4 || cost > 31) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid bcrypt cost parameter specified: %ld", cost); + RETURN_NULL(); + } + + required_salt_len = 22; + hash_format = emalloc(8); + sprintf(hash_format, "$2y$%02ld$", cost); + hash_format_len = 7; + } + break; + case PHP_PASSWORD_UNKNOWN: + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown password hashing algorithm: %ld", algo); + RETURN_NULL(); + } + + if (options && zend_symtable_find(options, "salt", 5, (void**) &option_buffer) == SUCCESS) { + char *buffer; + int buffer_len_int = 0; + size_t buffer_len; + switch (Z_TYPE_PP(option_buffer)) { + case IS_STRING: + buffer = estrndup(Z_STRVAL_PP(option_buffer), Z_STRLEN_PP(option_buffer)); + buffer_len_int = Z_STRLEN_PP(option_buffer); + break; + case IS_LONG: + case IS_DOUBLE: + case IS_OBJECT: { + zval cast_option_buffer; + MAKE_COPY_ZVAL(option_buffer, &cast_option_buffer); + convert_to_string(&cast_option_buffer); + if (Z_TYPE(cast_option_buffer) == IS_STRING) { + buffer = estrndup(Z_STRVAL(cast_option_buffer), Z_STRLEN(cast_option_buffer)); + buffer_len_int = Z_STRLEN(cast_option_buffer); + zval_dtor(&cast_option_buffer); + break; + } + zval_dtor(&cast_option_buffer); + } + case IS_BOOL: + case IS_NULL: + case IS_RESOURCE: + case IS_ARRAY: + default: + efree(hash_format); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Non-string salt parameter supplied"); + RETURN_NULL(); + } + if (buffer_len_int < 0) { + efree(hash_format); + efree(buffer); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Supplied salt is too long"); + } + buffer_len = (size_t) buffer_len_int; + if (buffer_len < required_salt_len) { + efree(hash_format); + efree(buffer); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Provided salt is too short: %lu expecting %lu", (unsigned long) buffer_len, (unsigned long) required_salt_len); + RETURN_NULL(); + } else if (0 == php_password_salt_is_alphabet(buffer, buffer_len)) { + salt = safe_emalloc(required_salt_len, 1, 1); + if (php_password_salt_to64(buffer, buffer_len, required_salt_len, salt) == FAILURE) { + efree(hash_format); + efree(buffer); + efree(salt); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Provided salt is too short: %lu", (unsigned long) buffer_len); + RETURN_NULL(); + } + salt_len = required_salt_len; + } else { + salt = safe_emalloc(required_salt_len, 1, 1); + memcpy(salt, buffer, (int) required_salt_len); + salt_len = required_salt_len; + } + efree(buffer); + } else { + salt = safe_emalloc(required_salt_len, 1, 1); + if (php_password_make_salt(required_salt_len, salt TSRMLS_CC) == FAILURE) { + efree(hash_format); + efree(salt); + RETURN_FALSE; + } + salt_len = required_salt_len; + } + + salt[salt_len] = 0; + + hash = safe_emalloc(salt_len + hash_format_len, 1, 1); + sprintf(hash, "%s%s", hash_format, salt); + hash[hash_format_len + salt_len] = 0; + + efree(hash_format); + efree(salt); + + /* This cast is safe, since both values are defined here in code and cannot overflow */ + hash_len = (int) (hash_format_len + salt_len); + + if (php_crypt(password, password_len, hash, hash_len, &result) == FAILURE) { + efree(hash); + RETURN_FALSE; + } + + efree(hash); + + if (strlen(result) < 13) { + efree(result); + RETURN_FALSE; + } + + RETURN_STRING(result, 0); +} +/* }}} */ + +#endif /* HAVE_CRYPT */ +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/standard/php_crypt.h b/ext/standard/php_crypt.h index 93b232896a..7410a8c328 100644 --- a/ext/standard/php_crypt.h +++ b/ext/standard/php_crypt.h @@ -23,6 +23,7 @@ #ifndef PHP_CRYPT_H #define PHP_CRYPT_H +PHPAPI int php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, char **result); PHP_FUNCTION(crypt); #if HAVE_CRYPT PHP_MINIT_FUNCTION(crypt); diff --git a/ext/standard/php_password.h b/ext/standard/php_password.h new file mode 100644 index 0000000000..079f187703 --- /dev/null +++ b/ext/standard/php_password.h @@ -0,0 +1,48 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2012 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Anthony Ferrara <ircmaxell@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_PASSWORD_H +#define PHP_PASSWORD_H + +PHP_FUNCTION(password_hash); +PHP_FUNCTION(password_verify); +PHP_FUNCTION(password_needs_rehash); +PHP_FUNCTION(password_get_info); + +PHP_MINIT_FUNCTION(password); + +#define PHP_PASSWORD_DEFAULT PHP_PASSWORD_BCRYPT + +#define PHP_PASSWORD_BCRYPT_COST 10 + +typedef enum { + PHP_PASSWORD_UNKNOWN, + PHP_PASSWORD_BCRYPT +} php_password_algo; + +#endif + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/standard/php_standard.h b/ext/standard/php_standard.h index 483dbc33bc..bccfebe543 100644 --- a/ext/standard/php_standard.h +++ b/ext/standard/php_standard.h @@ -58,6 +58,7 @@ #include "php_versioning.h" #include "php_ftok.h" #include "php_type.h" +#include "php_password.h" #define phpext_standard_ptr basic_functions_module_ptr PHP_MINIT_FUNCTION(standard_filters); diff --git a/ext/standard/tests/password/password_bcrypt_errors.phpt b/ext/standard/tests/password/password_bcrypt_errors.phpt new file mode 100644 index 0000000000..2548c9accb --- /dev/null +++ b/ext/standard/tests/password/password_bcrypt_errors.phpt @@ -0,0 +1,39 @@ +--TEST-- +Test error operation of password_hash() with bcrypt hashing +--FILE-- +<?php +//-=-=-=- + +var_dump(password_hash("foo", PASSWORD_BCRYPT, array("cost" => 3))); + +var_dump(password_hash("foo", PASSWORD_BCRYPT, array("cost" => 32))); + +var_dump(password_hash("foo", PASSWORD_BCRYPT, array("salt" => "foo"))); + +var_dump(password_hash("foo", PASSWORD_BCRYPT, array("salt" => "123456789012345678901"))); + +var_dump(password_hash("foo", PASSWORD_BCRYPT, array("salt" => 123))); + +var_dump(password_hash("foo", PASSWORD_BCRYPT, array("cost" => "foo"))); + +?> +--EXPECTF-- +Warning: password_hash(): Invalid bcrypt cost parameter specified: 3 in %s on line %d +NULL + +Warning: password_hash(): Invalid bcrypt cost parameter specified: 32 in %s on line %d +NULL + +Warning: password_hash(): Provided salt is too short: 3 expecting 22 in %s on line %d +NULL + +Warning: password_hash(): Provided salt is too short: 21 expecting 22 in %s on line %d +NULL + +Warning: password_hash(): Provided salt is too short: 3 expecting 22 in %s on line %d +NULL + +Warning: password_hash(): Invalid bcrypt cost parameter specified: 0 in %s on line %d +NULL + + diff --git a/ext/standard/tests/password/password_get_info.phpt b/ext/standard/tests/password/password_get_info.phpt new file mode 100644 index 0000000000..4c8dc04ff8 --- /dev/null +++ b/ext/standard/tests/password/password_get_info.phpt @@ -0,0 +1,58 @@ +--TEST-- +Test normal operation of password_get_info() +--FILE-- +<?php +//-=-=-=- +// Test Bcrypt +var_dump(password_get_info('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y')); +// Test Bcrypt Cost +var_dump(password_get_info('$2y$11$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y')); +// Test Bcrypt Invalid Length +var_dump(password_get_info('$2y$11$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100')); +// Test Non-Bcrypt +var_dump(password_get_info('$1$rasmusle$rISCgZzpwk3UhDidwXvin0')); + +echo "OK!"; +?> +--EXPECT-- +array(3) { + ["algo"]=> + int(1) + ["algoName"]=> + string(6) "bcrypt" + ["options"]=> + array(1) { + ["cost"]=> + int(10) + } +} +array(3) { + ["algo"]=> + int(1) + ["algoName"]=> + string(6) "bcrypt" + ["options"]=> + array(1) { + ["cost"]=> + int(11) + } +} +array(3) { + ["algo"]=> + int(0) + ["algoName"]=> + string(7) "unknown" + ["options"]=> + array(0) { + } +} +array(3) { + ["algo"]=> + int(0) + ["algoName"]=> + string(7) "unknown" + ["options"]=> + array(0) { + } +} +OK! diff --git a/ext/standard/tests/password/password_get_info_error.phpt b/ext/standard/tests/password/password_get_info_error.phpt new file mode 100644 index 0000000000..af676744c8 --- /dev/null +++ b/ext/standard/tests/password/password_get_info_error.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test error operation of password_get_info() +--FILE-- +<?php +//-=-=-=- +var_dump(password_get_info()); +var_dump(password_get_info(array())); + +echo "OK!"; +?> +--EXPECTF-- +Warning: password_get_info() expects exactly 1 parameter, 0 given in %s on line %d +NULL + +Warning: password_get_info() expects parameter 1 to be string, array given in %s on line %d +NULL +OK! diff --git a/ext/standard/tests/password/password_hash.phpt b/ext/standard/tests/password/password_hash.phpt new file mode 100644 index 0000000000..f59d3d5e48 --- /dev/null +++ b/ext/standard/tests/password/password_hash.phpt @@ -0,0 +1,25 @@ +--TEST-- +Test normal operation of password_hash() +--FILE-- +<?php +//-=-=-=- + +var_dump(strlen(password_hash("foo", PASSWORD_BCRYPT))); + +$hash = password_hash("foo", PASSWORD_BCRYPT); + +var_dump($hash === crypt("foo", $hash)); + +var_dump(password_hash("rasmuslerdorf", PASSWORD_BCRYPT, array("cost" => 7, "salt" => "usesomesillystringforsalt"))); + +var_dump(password_hash("test", PASSWORD_BCRYPT, array("salt" => "123456789012345678901" . chr(0)))); + +echo "OK!"; +?> +--EXPECT-- +int(60) +bool(true) +string(60) "$2y$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi" +string(60) "$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y" +OK! + diff --git a/ext/standard/tests/password/password_hash_error.phpt b/ext/standard/tests/password/password_hash_error.phpt new file mode 100644 index 0000000000..952250cb30 --- /dev/null +++ b/ext/standard/tests/password/password_hash_error.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test error operation of password_hash() +--FILE-- +<?php +//-=-=-=- + +var_dump(password_hash()); + +var_dump(password_hash("foo")); + +var_dump(password_hash("foo", array())); + +var_dump(password_hash("foo", 19, new StdClass)); + +var_dump(password_hash("foo", PASSWORD_BCRYPT, "baz")); + +var_dump(password_hash(array(), PASSWORD_BCRYPT)); + +var_dump(password_hash("123", PASSWORD_BCRYPT, array("salt" => array()))); + +/* Non-string salt, checking for memory leaks */ +var_dump(password_hash('123', PASSWORD_BCRYPT, array('salt' => 1234))); + +?> +--EXPECTF-- +Warning: password_hash() expects at least 2 parameters, 0 given in %s on line %d +NULL + +Warning: password_hash() expects at least 2 parameters, 1 given in %s on line %d +NULL + +Warning: password_hash() expects parameter 2 to be long, array given in %s on line %d +NULL + +Warning: password_hash(): Unknown password hashing algorithm: 19 in %s on line %d +NULL + +Warning: password_hash() expects parameter 3 to be array, string given in %s on line %d +NULL + +Warning: password_hash() expects parameter 1 to be string, array given in %s on line %d +NULL + +Warning: password_hash(): Non-string salt parameter supplied in %s on line %d +NULL + +Warning: password_hash(): Provided salt is too short: 4 expecting 22 in %s on line %d +NULL diff --git a/ext/standard/tests/password/password_needs_rehash.phpt b/ext/standard/tests/password/password_needs_rehash.phpt new file mode 100644 index 0000000000..734729e63d --- /dev/null +++ b/ext/standard/tests/password/password_needs_rehash.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test normal operation of password_needs_rehash() +--FILE-- +<?php +//-=-=-=- + +// Invalid Hash, always rehash +var_dump(password_needs_rehash('', PASSWORD_BCRYPT)); + +// Valid, as it's an unknown algorithm +var_dump(password_needs_rehash('', 0)); + +// Valid with cost the same +var_dump(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 10))); + +// Valid with cost the same, additional params +var_dump(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 10, 'foo' => 3))); + +// Invalid, different (lower) cost +var_dump(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 09))); + +// Invalid, different (higher) cost +var_dump(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 11))); + +// Valid with cost the default +$cost = str_pad(PASSWORD_BCRYPT_DEFAULT_COST, 2, '0', STR_PAD_LEFT); +var_dump(password_needs_rehash('$2y$'.$cost.'$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT)); + +// Should Issue Needs Rehash, Since Foo is cast to 0... +var_dump(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 'foo'))); + + + +echo "OK!"; +?> +--EXPECT-- +bool(true) +bool(false) +bool(false) +bool(false) +bool(true) +bool(true) +bool(false) +bool(true) +OK! diff --git a/ext/standard/tests/password/password_needs_rehash_error.phpt b/ext/standard/tests/password/password_needs_rehash_error.phpt new file mode 100644 index 0000000000..e25ef8db3f --- /dev/null +++ b/ext/standard/tests/password/password_needs_rehash_error.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test error operation of password_needs_rehash() +--FILE-- +<?php +//-=-=-=- +var_dump(password_needs_rehash()); + +var_dump(password_needs_rehash('')); + +var_dump(password_needs_rehash('', "foo")); + +var_dump(password_needs_rehash(array(), 1)); + +var_dump(password_needs_rehash("", 1, "foo")); + +echo "OK!"; +?> +--EXPECTF-- +Warning: password_needs_rehash() expects at least 2 parameters, 0 given in %s on line %d +NULL + +Warning: password_needs_rehash() expects at least 2 parameters, 1 given in %s on line %d +NULL + +Warning: password_needs_rehash() expects parameter 2 to be long, string given in %s on line %d +NULL + +Warning: password_needs_rehash() expects parameter 1 to be string, array given in %s on line %d +NULL + +Warning: password_needs_rehash() expects parameter 3 to be array, string given in %s on line %d +NULL +OK! diff --git a/ext/standard/tests/password/password_verify.phpt b/ext/standard/tests/password/password_verify.phpt new file mode 100644 index 0000000000..e7ecc7edd3 --- /dev/null +++ b/ext/standard/tests/password/password_verify.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test normal operation of password_verify) +--FILE-- +<?php +//-=-=-=- + +var_dump(password_verify(123, 123)); + +var_dump(password_verify("foo", '$2a$07$usesomesillystringforsalt$')); + +var_dump(password_verify('rasmusler', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi')); + +var_dump(password_verify('rasmuslerdorf', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi')); +echo "OK!"; +?> +--EXPECT-- +bool(false) +bool(false) +bool(false) +bool(true) +OK! diff --git a/ext/standard/tests/password/password_verify_error.phpt b/ext/standard/tests/password/password_verify_error.phpt new file mode 100644 index 0000000000..3e653fa04e --- /dev/null +++ b/ext/standard/tests/password/password_verify_error.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test error operation of password_verify() +--FILE-- +<?php +//-=-=-=- + +var_dump(password_verify()); + +var_dump(password_verify("foo")); + +?> +--EXPECTF-- +Warning: password_verify() expects exactly 2 parameters, 0 given in %s on line %d +bool(false) + +Warning: password_verify() expects exactly 2 parameters, 1 given in %s on line %d +bool(false) + |