File: CVE-2011-2483.patch

package info (click to toggle)
php5 5.3.3.1-7%2Bsqueeze29
  • links: PTS, VCS
  • area: main
  • in suites: squeeze-lts
  • size: 123,520 kB
  • ctags: 55,742
  • sloc: ansic: 633,963; php: 19,620; sh: 11,344; xml: 5,816; cpp: 2,400; yacc: 1,745; exp: 1,514; makefile: 1,019; pascal: 623; awk: 537; sql: 22
file content (164 lines) | stat: -rw-r--r-- 4,978 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
--- a/ext/standard/crypt_blowfish.c
+++ b/ext/standard/crypt_blowfish.c
@@ -7,6 +7,7 @@
  * cracking removed.
  *
  * Written by Solar Designer <solar at openwall.com> in 1998-2002 and
+ * placed in the public domain.  Quick self-test added in 2011 and also
  * placed in the public domain.
  *
  * There's absolutely no warranty.
@@ -51,6 +52,13 @@
 #define __CONST __const
 #endif
 
+/*
+ * Please keep this enabled.  We really don't want incompatible hashes to be
+ * produced.  The performance cost of this quick self-test is around 0.6% at
+ * the "$2a$08" setting.
+ */
+#define BF_SELF_TEST
+
 #ifdef __i386__
 #define BF_ASM				0
 #define BF_SCALE			1
@@ -63,6 +71,7 @@
 #endif
 
 typedef unsigned int BF_word;
+typedef signed int BF_word_signed;
 
 /* Number of Blowfish rounds, this is also hardcoded into a few places */
 #define BF_N				16
@@ -555,7 +564,8 @@ static void BF_swap(BF_word *x, int coun
 	} while (ptr < &data.ctx.S[3][0xFF]);
 #endif
 
-static void BF_set_key(__CONST char *key, BF_key expanded, BF_key initial)
+static void BF_set_key(__CONST char *key, BF_key expanded, BF_key initial,
+    int sign_extension_bug)
 {
 	__CONST char *ptr = key;
 	int i, j;
@@ -565,7 +575,10 @@ static void BF_set_key(__CONST char *key
 		tmp = 0;
 		for (j = 0; j < 4; j++) {
 			tmp <<= 8;
-			tmp |= *ptr;
+			if (sign_extension_bug)
+				tmp |= (BF_word_signed)(signed char)*ptr;
+			else
+				tmp |= (unsigned char)*ptr;
 
 			if (!*ptr) ptr = key; else ptr++;
 		}
@@ -575,8 +588,9 @@ static void BF_set_key(__CONST char *key
 	}
 }
 
-char *php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting,
-	char *output, int size)
+static char *BF_crypt(__CONST char *key, __CONST char *setting,
+	char *output, int size,
+	BF_word min)
 {
 #if BF_ASM
 	extern void _BF_body_r(BF_ctx *ctx);
@@ -602,7 +616,7 @@ char *php_crypt_blowfish_rn(__CONST char
 
 	if (setting[0] != '$' ||
 	    setting[1] != '2' ||
-	    setting[2] != 'a' ||
+	    (setting[2] != 'a' && setting[2] != 'x') ||
 	    setting[3] != '$' ||
 	    setting[4] < '0' || setting[4] > '3' ||
 	    setting[5] < '0' || setting[5] > '9' ||
@@ -613,7 +627,7 @@ char *php_crypt_blowfish_rn(__CONST char
 	}
 
 	count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
-	if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16)) {
+	if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
 		clean(data.binary.salt, sizeof(data.binary.salt));
 		__set_errno(EINVAL);
 		return NULL;
@@ -621,7 +635,7 @@ char *php_crypt_blowfish_rn(__CONST char
 
 	BF_swap(data.binary.salt, 4);
 
-	BF_set_key(key, data.expanded_key, data.ctx.P);
+	BF_set_key(key, data.expanded_key, data.ctx.P, setting[2] == 'x');
 
 	memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
 
@@ -721,14 +735,59 @@ char *php_crypt_blowfish_rn(__CONST char
 	BF_encode(&output[7 + 22], data.binary.output, 23);
 	output[7 + 22 + 31] = '\0';
 
+#ifndef BF_SELF_TEST
 /* Overwrite the most obvious sensitive data we have on the stack. Note
  * that this does not guarantee there's no sensitive data left on the
  * stack and/or in registers; I'm not aware of portable code that does. */
 	clean(&data, sizeof(data));
+#endif
 
 	return output;
 }
 
+char *php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting,
+	char *output, int size)
+{
+#ifdef BF_SELF_TEST
+	__CONST char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
+	__CONST char *test_2a =
+	    "$2a$00$abcdefghijklmnopqrstuui1D709vfamulimlGcq0qq3UvuUasvEa"
+	    "\0"
+	    "canary";
+	__CONST char *test_2x =
+	    "$2x$00$abcdefghijklmnopqrstuuVUrPmXD6q/nVSSp7pNDhCR9071IfIRe"
+	    "\0"
+	    "canary";
+	__CONST char *test_hash, *p;
+	int ok;
+	char buf[7 + 22 + 31 + 1 + 6 + 1];
+
+	output = BF_crypt(key, setting, output, size, 16);
+
+/* Do a quick self-test.  This also happens to overwrite BF_crypt()'s data. */
+	test_hash = (setting[2] == 'x') ? test_2x : test_2a;
+	memcpy(buf, test_hash, sizeof(buf));
+	memset(buf, -1, sizeof(buf) - (6 + 1)); /* keep "canary" only */
+	p = BF_crypt(test_key, test_hash, buf, sizeof(buf) - 6, 1);
+
+	ok = (p == buf && !memcmp(p, test_hash, sizeof(buf)));
+
+/* This could reveal what hash type we were using last.  Unfortunately, we
+ * can't reliably clean the test_hash pointer. */
+	clean(&buf, sizeof(buf));
+
+	if (ok)
+		return output;
+
+/* Should not happen */
+	__set_errno(EINVAL); /* pretend we don't support this hash type */
+	return NULL;
+#else
+#warning Self-test is disabled, please enable
+	return BF_crypt(key, setting, output, size, 16);
+#endif
+}
+
 char *php_crypt_gensalt_blowfish_rn(unsigned long count,
 	__CONST char *input, int size, char *output, int output_size)
 {
--- a/ext/standard/crypt.c
+++ b/ext/standard/crypt.c
@@ -241,7 +241,7 @@ PHP_FUNCTION(crypt)
 		} else if (
 				salt[0] == '$' &&
 				salt[1] == '2' &&
-				salt[2] == 'a' &&
+				(salt[2] == 'a' || salt[2] == 'x') &&
 				salt[3] == '$' &&
 				salt[6] == '$') {
 			/* CRYPT_BLOWFISH */