Commit e4fae2318b5ddd7aec0e65871f1b455b796cf33d
1 parent
b06eef6eab
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
iscsi-target; Enforce 1024 byte maximum for CHAP_C key value
This patch adds a check in chap_server_compute_md5() to enforce a 1024 byte maximum for the CHAP_C key value following the requirement in RFC-3720 Section 11.1.4: "..., C and R are large-binary-values and their binary length (not the length of the character string that represents them in encoded form) MUST not exceed 1024 bytes." Reported-by: rahul.rane <rahul.rane@calsoftinc.com> Tested-by: rahul.rane <rahul.rane@calsoftinc.com> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Showing 1 changed file with 4 additions and 0 deletions Inline Diff
drivers/target/iscsi/iscsi_target_auth.c
1 | /******************************************************************************* | 1 | /******************************************************************************* |
2 | * This file houses the main functions for the iSCSI CHAP support | 2 | * This file houses the main functions for the iSCSI CHAP support |
3 | * | 3 | * |
4 | * (c) Copyright 2007-2013 Datera, Inc. | 4 | * (c) Copyright 2007-2013 Datera, Inc. |
5 | * | 5 | * |
6 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | 6 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | ******************************************************************************/ | 17 | ******************************************************************************/ |
18 | 18 | ||
19 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
20 | #include <linux/string.h> | 20 | #include <linux/string.h> |
21 | #include <linux/crypto.h> | 21 | #include <linux/crypto.h> |
22 | #include <linux/err.h> | 22 | #include <linux/err.h> |
23 | #include <linux/scatterlist.h> | 23 | #include <linux/scatterlist.h> |
24 | 24 | ||
25 | #include "iscsi_target_core.h" | 25 | #include "iscsi_target_core.h" |
26 | #include "iscsi_target_nego.h" | 26 | #include "iscsi_target_nego.h" |
27 | #include "iscsi_target_auth.h" | 27 | #include "iscsi_target_auth.h" |
28 | 28 | ||
29 | static int chap_string_to_hex(unsigned char *dst, unsigned char *src, int len) | 29 | static int chap_string_to_hex(unsigned char *dst, unsigned char *src, int len) |
30 | { | 30 | { |
31 | int j = DIV_ROUND_UP(len, 2), rc; | 31 | int j = DIV_ROUND_UP(len, 2), rc; |
32 | 32 | ||
33 | rc = hex2bin(dst, src, j); | 33 | rc = hex2bin(dst, src, j); |
34 | if (rc < 0) | 34 | if (rc < 0) |
35 | pr_debug("CHAP string contains non hex digit symbols\n"); | 35 | pr_debug("CHAP string contains non hex digit symbols\n"); |
36 | 36 | ||
37 | dst[j] = '\0'; | 37 | dst[j] = '\0'; |
38 | return j; | 38 | return j; |
39 | } | 39 | } |
40 | 40 | ||
41 | static void chap_binaryhex_to_asciihex(char *dst, char *src, int src_len) | 41 | static void chap_binaryhex_to_asciihex(char *dst, char *src, int src_len) |
42 | { | 42 | { |
43 | int i; | 43 | int i; |
44 | 44 | ||
45 | for (i = 0; i < src_len; i++) { | 45 | for (i = 0; i < src_len; i++) { |
46 | sprintf(&dst[i*2], "%02x", (int) src[i] & 0xff); | 46 | sprintf(&dst[i*2], "%02x", (int) src[i] & 0xff); |
47 | } | 47 | } |
48 | } | 48 | } |
49 | 49 | ||
50 | static void chap_gen_challenge( | 50 | static void chap_gen_challenge( |
51 | struct iscsi_conn *conn, | 51 | struct iscsi_conn *conn, |
52 | int caller, | 52 | int caller, |
53 | char *c_str, | 53 | char *c_str, |
54 | unsigned int *c_len) | 54 | unsigned int *c_len) |
55 | { | 55 | { |
56 | unsigned char challenge_asciihex[CHAP_CHALLENGE_LENGTH * 2 + 1]; | 56 | unsigned char challenge_asciihex[CHAP_CHALLENGE_LENGTH * 2 + 1]; |
57 | struct iscsi_chap *chap = conn->auth_protocol; | 57 | struct iscsi_chap *chap = conn->auth_protocol; |
58 | 58 | ||
59 | memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1); | 59 | memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1); |
60 | 60 | ||
61 | get_random_bytes(chap->challenge, CHAP_CHALLENGE_LENGTH); | 61 | get_random_bytes(chap->challenge, CHAP_CHALLENGE_LENGTH); |
62 | chap_binaryhex_to_asciihex(challenge_asciihex, chap->challenge, | 62 | chap_binaryhex_to_asciihex(challenge_asciihex, chap->challenge, |
63 | CHAP_CHALLENGE_LENGTH); | 63 | CHAP_CHALLENGE_LENGTH); |
64 | /* | 64 | /* |
65 | * Set CHAP_C, and copy the generated challenge into c_str. | 65 | * Set CHAP_C, and copy the generated challenge into c_str. |
66 | */ | 66 | */ |
67 | *c_len += sprintf(c_str + *c_len, "CHAP_C=0x%s", challenge_asciihex); | 67 | *c_len += sprintf(c_str + *c_len, "CHAP_C=0x%s", challenge_asciihex); |
68 | *c_len += 1; | 68 | *c_len += 1; |
69 | 69 | ||
70 | pr_debug("[%s] Sending CHAP_C=0x%s\n\n", (caller) ? "server" : "client", | 70 | pr_debug("[%s] Sending CHAP_C=0x%s\n\n", (caller) ? "server" : "client", |
71 | challenge_asciihex); | 71 | challenge_asciihex); |
72 | } | 72 | } |
73 | 73 | ||
74 | static int chap_check_algorithm(const char *a_str) | 74 | static int chap_check_algorithm(const char *a_str) |
75 | { | 75 | { |
76 | char *tmp, *orig, *token; | 76 | char *tmp, *orig, *token; |
77 | 77 | ||
78 | tmp = kstrdup(a_str, GFP_KERNEL); | 78 | tmp = kstrdup(a_str, GFP_KERNEL); |
79 | if (!tmp) { | 79 | if (!tmp) { |
80 | pr_err("Memory allocation failed for CHAP_A temporary buffer\n"); | 80 | pr_err("Memory allocation failed for CHAP_A temporary buffer\n"); |
81 | return CHAP_DIGEST_UNKNOWN; | 81 | return CHAP_DIGEST_UNKNOWN; |
82 | } | 82 | } |
83 | orig = tmp; | 83 | orig = tmp; |
84 | 84 | ||
85 | token = strsep(&tmp, "="); | 85 | token = strsep(&tmp, "="); |
86 | if (!token) | 86 | if (!token) |
87 | goto out; | 87 | goto out; |
88 | 88 | ||
89 | if (strcmp(token, "CHAP_A")) { | 89 | if (strcmp(token, "CHAP_A")) { |
90 | pr_err("Unable to locate CHAP_A key\n"); | 90 | pr_err("Unable to locate CHAP_A key\n"); |
91 | goto out; | 91 | goto out; |
92 | } | 92 | } |
93 | while (token) { | 93 | while (token) { |
94 | token = strsep(&tmp, ","); | 94 | token = strsep(&tmp, ","); |
95 | if (!token) | 95 | if (!token) |
96 | goto out; | 96 | goto out; |
97 | 97 | ||
98 | if (!strncmp(token, "5", 1)) { | 98 | if (!strncmp(token, "5", 1)) { |
99 | pr_debug("Selected MD5 Algorithm\n"); | 99 | pr_debug("Selected MD5 Algorithm\n"); |
100 | kfree(orig); | 100 | kfree(orig); |
101 | return CHAP_DIGEST_MD5; | 101 | return CHAP_DIGEST_MD5; |
102 | } | 102 | } |
103 | } | 103 | } |
104 | out: | 104 | out: |
105 | kfree(orig); | 105 | kfree(orig); |
106 | return CHAP_DIGEST_UNKNOWN; | 106 | return CHAP_DIGEST_UNKNOWN; |
107 | } | 107 | } |
108 | 108 | ||
109 | static struct iscsi_chap *chap_server_open( | 109 | static struct iscsi_chap *chap_server_open( |
110 | struct iscsi_conn *conn, | 110 | struct iscsi_conn *conn, |
111 | struct iscsi_node_auth *auth, | 111 | struct iscsi_node_auth *auth, |
112 | const char *a_str, | 112 | const char *a_str, |
113 | char *aic_str, | 113 | char *aic_str, |
114 | unsigned int *aic_len) | 114 | unsigned int *aic_len) |
115 | { | 115 | { |
116 | int ret; | 116 | int ret; |
117 | struct iscsi_chap *chap; | 117 | struct iscsi_chap *chap; |
118 | 118 | ||
119 | if (!(auth->naf_flags & NAF_USERID_SET) || | 119 | if (!(auth->naf_flags & NAF_USERID_SET) || |
120 | !(auth->naf_flags & NAF_PASSWORD_SET)) { | 120 | !(auth->naf_flags & NAF_PASSWORD_SET)) { |
121 | pr_err("CHAP user or password not set for" | 121 | pr_err("CHAP user or password not set for" |
122 | " Initiator ACL\n"); | 122 | " Initiator ACL\n"); |
123 | return NULL; | 123 | return NULL; |
124 | } | 124 | } |
125 | 125 | ||
126 | conn->auth_protocol = kzalloc(sizeof(struct iscsi_chap), GFP_KERNEL); | 126 | conn->auth_protocol = kzalloc(sizeof(struct iscsi_chap), GFP_KERNEL); |
127 | if (!conn->auth_protocol) | 127 | if (!conn->auth_protocol) |
128 | return NULL; | 128 | return NULL; |
129 | 129 | ||
130 | chap = conn->auth_protocol; | 130 | chap = conn->auth_protocol; |
131 | ret = chap_check_algorithm(a_str); | 131 | ret = chap_check_algorithm(a_str); |
132 | switch (ret) { | 132 | switch (ret) { |
133 | case CHAP_DIGEST_MD5: | 133 | case CHAP_DIGEST_MD5: |
134 | pr_debug("[server] Got CHAP_A=5\n"); | 134 | pr_debug("[server] Got CHAP_A=5\n"); |
135 | /* | 135 | /* |
136 | * Send back CHAP_A set to MD5. | 136 | * Send back CHAP_A set to MD5. |
137 | */ | 137 | */ |
138 | *aic_len = sprintf(aic_str, "CHAP_A=5"); | 138 | *aic_len = sprintf(aic_str, "CHAP_A=5"); |
139 | *aic_len += 1; | 139 | *aic_len += 1; |
140 | chap->digest_type = CHAP_DIGEST_MD5; | 140 | chap->digest_type = CHAP_DIGEST_MD5; |
141 | pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type); | 141 | pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type); |
142 | break; | 142 | break; |
143 | case CHAP_DIGEST_UNKNOWN: | 143 | case CHAP_DIGEST_UNKNOWN: |
144 | default: | 144 | default: |
145 | pr_err("Unsupported CHAP_A value\n"); | 145 | pr_err("Unsupported CHAP_A value\n"); |
146 | return NULL; | 146 | return NULL; |
147 | } | 147 | } |
148 | 148 | ||
149 | /* | 149 | /* |
150 | * Set Identifier. | 150 | * Set Identifier. |
151 | */ | 151 | */ |
152 | chap->id = conn->tpg->tpg_chap_id++; | 152 | chap->id = conn->tpg->tpg_chap_id++; |
153 | *aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id); | 153 | *aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id); |
154 | *aic_len += 1; | 154 | *aic_len += 1; |
155 | pr_debug("[server] Sending CHAP_I=%d\n", chap->id); | 155 | pr_debug("[server] Sending CHAP_I=%d\n", chap->id); |
156 | /* | 156 | /* |
157 | * Generate Challenge. | 157 | * Generate Challenge. |
158 | */ | 158 | */ |
159 | chap_gen_challenge(conn, 1, aic_str, aic_len); | 159 | chap_gen_challenge(conn, 1, aic_str, aic_len); |
160 | 160 | ||
161 | return chap; | 161 | return chap; |
162 | } | 162 | } |
163 | 163 | ||
164 | static void chap_close(struct iscsi_conn *conn) | 164 | static void chap_close(struct iscsi_conn *conn) |
165 | { | 165 | { |
166 | kfree(conn->auth_protocol); | 166 | kfree(conn->auth_protocol); |
167 | conn->auth_protocol = NULL; | 167 | conn->auth_protocol = NULL; |
168 | } | 168 | } |
169 | 169 | ||
170 | static int chap_server_compute_md5( | 170 | static int chap_server_compute_md5( |
171 | struct iscsi_conn *conn, | 171 | struct iscsi_conn *conn, |
172 | struct iscsi_node_auth *auth, | 172 | struct iscsi_node_auth *auth, |
173 | char *nr_in_ptr, | 173 | char *nr_in_ptr, |
174 | char *nr_out_ptr, | 174 | char *nr_out_ptr, |
175 | unsigned int *nr_out_len) | 175 | unsigned int *nr_out_len) |
176 | { | 176 | { |
177 | unsigned long id; | 177 | unsigned long id; |
178 | unsigned char id_as_uchar; | 178 | unsigned char id_as_uchar; |
179 | unsigned char digest[MD5_SIGNATURE_SIZE]; | 179 | unsigned char digest[MD5_SIGNATURE_SIZE]; |
180 | unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2]; | 180 | unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2]; |
181 | unsigned char identifier[10], *challenge = NULL; | 181 | unsigned char identifier[10], *challenge = NULL; |
182 | unsigned char *challenge_binhex = NULL; | 182 | unsigned char *challenge_binhex = NULL; |
183 | unsigned char client_digest[MD5_SIGNATURE_SIZE]; | 183 | unsigned char client_digest[MD5_SIGNATURE_SIZE]; |
184 | unsigned char server_digest[MD5_SIGNATURE_SIZE]; | 184 | unsigned char server_digest[MD5_SIGNATURE_SIZE]; |
185 | unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH]; | 185 | unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH]; |
186 | size_t compare_len; | 186 | size_t compare_len; |
187 | struct iscsi_chap *chap = conn->auth_protocol; | 187 | struct iscsi_chap *chap = conn->auth_protocol; |
188 | struct crypto_hash *tfm; | 188 | struct crypto_hash *tfm; |
189 | struct hash_desc desc; | 189 | struct hash_desc desc; |
190 | struct scatterlist sg; | 190 | struct scatterlist sg; |
191 | int auth_ret = -1, ret, challenge_len; | 191 | int auth_ret = -1, ret, challenge_len; |
192 | 192 | ||
193 | memset(identifier, 0, 10); | 193 | memset(identifier, 0, 10); |
194 | memset(chap_n, 0, MAX_CHAP_N_SIZE); | 194 | memset(chap_n, 0, MAX_CHAP_N_SIZE); |
195 | memset(chap_r, 0, MAX_RESPONSE_LENGTH); | 195 | memset(chap_r, 0, MAX_RESPONSE_LENGTH); |
196 | memset(digest, 0, MD5_SIGNATURE_SIZE); | 196 | memset(digest, 0, MD5_SIGNATURE_SIZE); |
197 | memset(response, 0, MD5_SIGNATURE_SIZE * 2 + 2); | 197 | memset(response, 0, MD5_SIGNATURE_SIZE * 2 + 2); |
198 | memset(client_digest, 0, MD5_SIGNATURE_SIZE); | 198 | memset(client_digest, 0, MD5_SIGNATURE_SIZE); |
199 | memset(server_digest, 0, MD5_SIGNATURE_SIZE); | 199 | memset(server_digest, 0, MD5_SIGNATURE_SIZE); |
200 | 200 | ||
201 | challenge = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL); | 201 | challenge = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL); |
202 | if (!challenge) { | 202 | if (!challenge) { |
203 | pr_err("Unable to allocate challenge buffer\n"); | 203 | pr_err("Unable to allocate challenge buffer\n"); |
204 | goto out; | 204 | goto out; |
205 | } | 205 | } |
206 | 206 | ||
207 | challenge_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL); | 207 | challenge_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL); |
208 | if (!challenge_binhex) { | 208 | if (!challenge_binhex) { |
209 | pr_err("Unable to allocate challenge_binhex buffer\n"); | 209 | pr_err("Unable to allocate challenge_binhex buffer\n"); |
210 | goto out; | 210 | goto out; |
211 | } | 211 | } |
212 | /* | 212 | /* |
213 | * Extract CHAP_N. | 213 | * Extract CHAP_N. |
214 | */ | 214 | */ |
215 | if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n, | 215 | if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n, |
216 | &type) < 0) { | 216 | &type) < 0) { |
217 | pr_err("Could not find CHAP_N.\n"); | 217 | pr_err("Could not find CHAP_N.\n"); |
218 | goto out; | 218 | goto out; |
219 | } | 219 | } |
220 | if (type == HEX) { | 220 | if (type == HEX) { |
221 | pr_err("Could not find CHAP_N.\n"); | 221 | pr_err("Could not find CHAP_N.\n"); |
222 | goto out; | 222 | goto out; |
223 | } | 223 | } |
224 | 224 | ||
225 | /* Include the terminating NULL in the compare */ | 225 | /* Include the terminating NULL in the compare */ |
226 | compare_len = strlen(auth->userid) + 1; | 226 | compare_len = strlen(auth->userid) + 1; |
227 | if (strncmp(chap_n, auth->userid, compare_len) != 0) { | 227 | if (strncmp(chap_n, auth->userid, compare_len) != 0) { |
228 | pr_err("CHAP_N values do not match!\n"); | 228 | pr_err("CHAP_N values do not match!\n"); |
229 | goto out; | 229 | goto out; |
230 | } | 230 | } |
231 | pr_debug("[server] Got CHAP_N=%s\n", chap_n); | 231 | pr_debug("[server] Got CHAP_N=%s\n", chap_n); |
232 | /* | 232 | /* |
233 | * Extract CHAP_R. | 233 | * Extract CHAP_R. |
234 | */ | 234 | */ |
235 | if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r, | 235 | if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r, |
236 | &type) < 0) { | 236 | &type) < 0) { |
237 | pr_err("Could not find CHAP_R.\n"); | 237 | pr_err("Could not find CHAP_R.\n"); |
238 | goto out; | 238 | goto out; |
239 | } | 239 | } |
240 | if (type != HEX) { | 240 | if (type != HEX) { |
241 | pr_err("Could not find CHAP_R.\n"); | 241 | pr_err("Could not find CHAP_R.\n"); |
242 | goto out; | 242 | goto out; |
243 | } | 243 | } |
244 | 244 | ||
245 | pr_debug("[server] Got CHAP_R=%s\n", chap_r); | 245 | pr_debug("[server] Got CHAP_R=%s\n", chap_r); |
246 | chap_string_to_hex(client_digest, chap_r, strlen(chap_r)); | 246 | chap_string_to_hex(client_digest, chap_r, strlen(chap_r)); |
247 | 247 | ||
248 | tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); | 248 | tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); |
249 | if (IS_ERR(tfm)) { | 249 | if (IS_ERR(tfm)) { |
250 | pr_err("Unable to allocate struct crypto_hash\n"); | 250 | pr_err("Unable to allocate struct crypto_hash\n"); |
251 | goto out; | 251 | goto out; |
252 | } | 252 | } |
253 | desc.tfm = tfm; | 253 | desc.tfm = tfm; |
254 | desc.flags = 0; | 254 | desc.flags = 0; |
255 | 255 | ||
256 | ret = crypto_hash_init(&desc); | 256 | ret = crypto_hash_init(&desc); |
257 | if (ret < 0) { | 257 | if (ret < 0) { |
258 | pr_err("crypto_hash_init() failed\n"); | 258 | pr_err("crypto_hash_init() failed\n"); |
259 | crypto_free_hash(tfm); | 259 | crypto_free_hash(tfm); |
260 | goto out; | 260 | goto out; |
261 | } | 261 | } |
262 | 262 | ||
263 | sg_init_one(&sg, &chap->id, 1); | 263 | sg_init_one(&sg, &chap->id, 1); |
264 | ret = crypto_hash_update(&desc, &sg, 1); | 264 | ret = crypto_hash_update(&desc, &sg, 1); |
265 | if (ret < 0) { | 265 | if (ret < 0) { |
266 | pr_err("crypto_hash_update() failed for id\n"); | 266 | pr_err("crypto_hash_update() failed for id\n"); |
267 | crypto_free_hash(tfm); | 267 | crypto_free_hash(tfm); |
268 | goto out; | 268 | goto out; |
269 | } | 269 | } |
270 | 270 | ||
271 | sg_init_one(&sg, &auth->password, strlen(auth->password)); | 271 | sg_init_one(&sg, &auth->password, strlen(auth->password)); |
272 | ret = crypto_hash_update(&desc, &sg, strlen(auth->password)); | 272 | ret = crypto_hash_update(&desc, &sg, strlen(auth->password)); |
273 | if (ret < 0) { | 273 | if (ret < 0) { |
274 | pr_err("crypto_hash_update() failed for password\n"); | 274 | pr_err("crypto_hash_update() failed for password\n"); |
275 | crypto_free_hash(tfm); | 275 | crypto_free_hash(tfm); |
276 | goto out; | 276 | goto out; |
277 | } | 277 | } |
278 | 278 | ||
279 | sg_init_one(&sg, chap->challenge, CHAP_CHALLENGE_LENGTH); | 279 | sg_init_one(&sg, chap->challenge, CHAP_CHALLENGE_LENGTH); |
280 | ret = crypto_hash_update(&desc, &sg, CHAP_CHALLENGE_LENGTH); | 280 | ret = crypto_hash_update(&desc, &sg, CHAP_CHALLENGE_LENGTH); |
281 | if (ret < 0) { | 281 | if (ret < 0) { |
282 | pr_err("crypto_hash_update() failed for challenge\n"); | 282 | pr_err("crypto_hash_update() failed for challenge\n"); |
283 | crypto_free_hash(tfm); | 283 | crypto_free_hash(tfm); |
284 | goto out; | 284 | goto out; |
285 | } | 285 | } |
286 | 286 | ||
287 | ret = crypto_hash_final(&desc, server_digest); | 287 | ret = crypto_hash_final(&desc, server_digest); |
288 | if (ret < 0) { | 288 | if (ret < 0) { |
289 | pr_err("crypto_hash_final() failed for server digest\n"); | 289 | pr_err("crypto_hash_final() failed for server digest\n"); |
290 | crypto_free_hash(tfm); | 290 | crypto_free_hash(tfm); |
291 | goto out; | 291 | goto out; |
292 | } | 292 | } |
293 | crypto_free_hash(tfm); | 293 | crypto_free_hash(tfm); |
294 | 294 | ||
295 | chap_binaryhex_to_asciihex(response, server_digest, MD5_SIGNATURE_SIZE); | 295 | chap_binaryhex_to_asciihex(response, server_digest, MD5_SIGNATURE_SIZE); |
296 | pr_debug("[server] MD5 Server Digest: %s\n", response); | 296 | pr_debug("[server] MD5 Server Digest: %s\n", response); |
297 | 297 | ||
298 | if (memcmp(server_digest, client_digest, MD5_SIGNATURE_SIZE) != 0) { | 298 | if (memcmp(server_digest, client_digest, MD5_SIGNATURE_SIZE) != 0) { |
299 | pr_debug("[server] MD5 Digests do not match!\n\n"); | 299 | pr_debug("[server] MD5 Digests do not match!\n\n"); |
300 | goto out; | 300 | goto out; |
301 | } else | 301 | } else |
302 | pr_debug("[server] MD5 Digests match, CHAP connetication" | 302 | pr_debug("[server] MD5 Digests match, CHAP connetication" |
303 | " successful.\n\n"); | 303 | " successful.\n\n"); |
304 | /* | 304 | /* |
305 | * One way authentication has succeeded, return now if mutual | 305 | * One way authentication has succeeded, return now if mutual |
306 | * authentication is not enabled. | 306 | * authentication is not enabled. |
307 | */ | 307 | */ |
308 | if (!auth->authenticate_target) { | 308 | if (!auth->authenticate_target) { |
309 | kfree(challenge); | 309 | kfree(challenge); |
310 | kfree(challenge_binhex); | 310 | kfree(challenge_binhex); |
311 | return 0; | 311 | return 0; |
312 | } | 312 | } |
313 | /* | 313 | /* |
314 | * Get CHAP_I. | 314 | * Get CHAP_I. |
315 | */ | 315 | */ |
316 | if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) { | 316 | if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) { |
317 | pr_err("Could not find CHAP_I.\n"); | 317 | pr_err("Could not find CHAP_I.\n"); |
318 | goto out; | 318 | goto out; |
319 | } | 319 | } |
320 | 320 | ||
321 | if (type == HEX) | 321 | if (type == HEX) |
322 | ret = kstrtoul(&identifier[2], 0, &id); | 322 | ret = kstrtoul(&identifier[2], 0, &id); |
323 | else | 323 | else |
324 | ret = kstrtoul(identifier, 0, &id); | 324 | ret = kstrtoul(identifier, 0, &id); |
325 | 325 | ||
326 | if (ret < 0) { | 326 | if (ret < 0) { |
327 | pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret); | 327 | pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret); |
328 | goto out; | 328 | goto out; |
329 | } | 329 | } |
330 | if (id > 255) { | 330 | if (id > 255) { |
331 | pr_err("chap identifier: %lu greater than 255\n", id); | 331 | pr_err("chap identifier: %lu greater than 255\n", id); |
332 | goto out; | 332 | goto out; |
333 | } | 333 | } |
334 | /* | 334 | /* |
335 | * RFC 1994 says Identifier is no more than octet (8 bits). | 335 | * RFC 1994 says Identifier is no more than octet (8 bits). |
336 | */ | 336 | */ |
337 | pr_debug("[server] Got CHAP_I=%lu\n", id); | 337 | pr_debug("[server] Got CHAP_I=%lu\n", id); |
338 | /* | 338 | /* |
339 | * Get CHAP_C. | 339 | * Get CHAP_C. |
340 | */ | 340 | */ |
341 | if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN, | 341 | if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN, |
342 | challenge, &type) < 0) { | 342 | challenge, &type) < 0) { |
343 | pr_err("Could not find CHAP_C.\n"); | 343 | pr_err("Could not find CHAP_C.\n"); |
344 | goto out; | 344 | goto out; |
345 | } | 345 | } |
346 | 346 | ||
347 | if (type != HEX) { | 347 | if (type != HEX) { |
348 | pr_err("Could not find CHAP_C.\n"); | 348 | pr_err("Could not find CHAP_C.\n"); |
349 | goto out; | 349 | goto out; |
350 | } | 350 | } |
351 | pr_debug("[server] Got CHAP_C=%s\n", challenge); | 351 | pr_debug("[server] Got CHAP_C=%s\n", challenge); |
352 | challenge_len = chap_string_to_hex(challenge_binhex, challenge, | 352 | challenge_len = chap_string_to_hex(challenge_binhex, challenge, |
353 | strlen(challenge)); | 353 | strlen(challenge)); |
354 | if (!challenge_len) { | 354 | if (!challenge_len) { |
355 | pr_err("Unable to convert incoming challenge\n"); | 355 | pr_err("Unable to convert incoming challenge\n"); |
356 | goto out; | 356 | goto out; |
357 | } | 357 | } |
358 | if (challenge_len > 1024) { | ||
359 | pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n"); | ||
360 | goto out; | ||
361 | } | ||
358 | /* | 362 | /* |
359 | * During mutual authentication, the CHAP_C generated by the | 363 | * During mutual authentication, the CHAP_C generated by the |
360 | * initiator must not match the original CHAP_C generated by | 364 | * initiator must not match the original CHAP_C generated by |
361 | * the target. | 365 | * the target. |
362 | */ | 366 | */ |
363 | if (!memcmp(challenge_binhex, chap->challenge, CHAP_CHALLENGE_LENGTH)) { | 367 | if (!memcmp(challenge_binhex, chap->challenge, CHAP_CHALLENGE_LENGTH)) { |
364 | pr_err("initiator CHAP_C matches target CHAP_C, failing" | 368 | pr_err("initiator CHAP_C matches target CHAP_C, failing" |
365 | " login attempt\n"); | 369 | " login attempt\n"); |
366 | goto out; | 370 | goto out; |
367 | } | 371 | } |
368 | /* | 372 | /* |
369 | * Generate CHAP_N and CHAP_R for mutual authentication. | 373 | * Generate CHAP_N and CHAP_R for mutual authentication. |
370 | */ | 374 | */ |
371 | tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); | 375 | tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); |
372 | if (IS_ERR(tfm)) { | 376 | if (IS_ERR(tfm)) { |
373 | pr_err("Unable to allocate struct crypto_hash\n"); | 377 | pr_err("Unable to allocate struct crypto_hash\n"); |
374 | goto out; | 378 | goto out; |
375 | } | 379 | } |
376 | desc.tfm = tfm; | 380 | desc.tfm = tfm; |
377 | desc.flags = 0; | 381 | desc.flags = 0; |
378 | 382 | ||
379 | ret = crypto_hash_init(&desc); | 383 | ret = crypto_hash_init(&desc); |
380 | if (ret < 0) { | 384 | if (ret < 0) { |
381 | pr_err("crypto_hash_init() failed\n"); | 385 | pr_err("crypto_hash_init() failed\n"); |
382 | crypto_free_hash(tfm); | 386 | crypto_free_hash(tfm); |
383 | goto out; | 387 | goto out; |
384 | } | 388 | } |
385 | 389 | ||
386 | /* To handle both endiannesses */ | 390 | /* To handle both endiannesses */ |
387 | id_as_uchar = id; | 391 | id_as_uchar = id; |
388 | sg_init_one(&sg, &id_as_uchar, 1); | 392 | sg_init_one(&sg, &id_as_uchar, 1); |
389 | ret = crypto_hash_update(&desc, &sg, 1); | 393 | ret = crypto_hash_update(&desc, &sg, 1); |
390 | if (ret < 0) { | 394 | if (ret < 0) { |
391 | pr_err("crypto_hash_update() failed for id\n"); | 395 | pr_err("crypto_hash_update() failed for id\n"); |
392 | crypto_free_hash(tfm); | 396 | crypto_free_hash(tfm); |
393 | goto out; | 397 | goto out; |
394 | } | 398 | } |
395 | 399 | ||
396 | sg_init_one(&sg, auth->password_mutual, | 400 | sg_init_one(&sg, auth->password_mutual, |
397 | strlen(auth->password_mutual)); | 401 | strlen(auth->password_mutual)); |
398 | ret = crypto_hash_update(&desc, &sg, strlen(auth->password_mutual)); | 402 | ret = crypto_hash_update(&desc, &sg, strlen(auth->password_mutual)); |
399 | if (ret < 0) { | 403 | if (ret < 0) { |
400 | pr_err("crypto_hash_update() failed for" | 404 | pr_err("crypto_hash_update() failed for" |
401 | " password_mutual\n"); | 405 | " password_mutual\n"); |
402 | crypto_free_hash(tfm); | 406 | crypto_free_hash(tfm); |
403 | goto out; | 407 | goto out; |
404 | } | 408 | } |
405 | /* | 409 | /* |
406 | * Convert received challenge to binary hex. | 410 | * Convert received challenge to binary hex. |
407 | */ | 411 | */ |
408 | sg_init_one(&sg, challenge_binhex, challenge_len); | 412 | sg_init_one(&sg, challenge_binhex, challenge_len); |
409 | ret = crypto_hash_update(&desc, &sg, challenge_len); | 413 | ret = crypto_hash_update(&desc, &sg, challenge_len); |
410 | if (ret < 0) { | 414 | if (ret < 0) { |
411 | pr_err("crypto_hash_update() failed for ma challenge\n"); | 415 | pr_err("crypto_hash_update() failed for ma challenge\n"); |
412 | crypto_free_hash(tfm); | 416 | crypto_free_hash(tfm); |
413 | goto out; | 417 | goto out; |
414 | } | 418 | } |
415 | 419 | ||
416 | ret = crypto_hash_final(&desc, digest); | 420 | ret = crypto_hash_final(&desc, digest); |
417 | if (ret < 0) { | 421 | if (ret < 0) { |
418 | pr_err("crypto_hash_final() failed for ma digest\n"); | 422 | pr_err("crypto_hash_final() failed for ma digest\n"); |
419 | crypto_free_hash(tfm); | 423 | crypto_free_hash(tfm); |
420 | goto out; | 424 | goto out; |
421 | } | 425 | } |
422 | crypto_free_hash(tfm); | 426 | crypto_free_hash(tfm); |
423 | /* | 427 | /* |
424 | * Generate CHAP_N and CHAP_R. | 428 | * Generate CHAP_N and CHAP_R. |
425 | */ | 429 | */ |
426 | *nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual); | 430 | *nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual); |
427 | *nr_out_len += 1; | 431 | *nr_out_len += 1; |
428 | pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual); | 432 | pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual); |
429 | /* | 433 | /* |
430 | * Convert response from binary hex to ascii hext. | 434 | * Convert response from binary hex to ascii hext. |
431 | */ | 435 | */ |
432 | chap_binaryhex_to_asciihex(response, digest, MD5_SIGNATURE_SIZE); | 436 | chap_binaryhex_to_asciihex(response, digest, MD5_SIGNATURE_SIZE); |
433 | *nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s", | 437 | *nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s", |
434 | response); | 438 | response); |
435 | *nr_out_len += 1; | 439 | *nr_out_len += 1; |
436 | pr_debug("[server] Sending CHAP_R=0x%s\n", response); | 440 | pr_debug("[server] Sending CHAP_R=0x%s\n", response); |
437 | auth_ret = 0; | 441 | auth_ret = 0; |
438 | out: | 442 | out: |
439 | kfree(challenge); | 443 | kfree(challenge); |
440 | kfree(challenge_binhex); | 444 | kfree(challenge_binhex); |
441 | return auth_ret; | 445 | return auth_ret; |
442 | } | 446 | } |
443 | 447 | ||
444 | static int chap_got_response( | 448 | static int chap_got_response( |
445 | struct iscsi_conn *conn, | 449 | struct iscsi_conn *conn, |
446 | struct iscsi_node_auth *auth, | 450 | struct iscsi_node_auth *auth, |
447 | char *nr_in_ptr, | 451 | char *nr_in_ptr, |
448 | char *nr_out_ptr, | 452 | char *nr_out_ptr, |
449 | unsigned int *nr_out_len) | 453 | unsigned int *nr_out_len) |
450 | { | 454 | { |
451 | struct iscsi_chap *chap = conn->auth_protocol; | 455 | struct iscsi_chap *chap = conn->auth_protocol; |
452 | 456 | ||
453 | switch (chap->digest_type) { | 457 | switch (chap->digest_type) { |
454 | case CHAP_DIGEST_MD5: | 458 | case CHAP_DIGEST_MD5: |
455 | if (chap_server_compute_md5(conn, auth, nr_in_ptr, | 459 | if (chap_server_compute_md5(conn, auth, nr_in_ptr, |
456 | nr_out_ptr, nr_out_len) < 0) | 460 | nr_out_ptr, nr_out_len) < 0) |
457 | return -1; | 461 | return -1; |
458 | return 0; | 462 | return 0; |
459 | default: | 463 | default: |
460 | pr_err("Unknown CHAP digest type %d!\n", | 464 | pr_err("Unknown CHAP digest type %d!\n", |
461 | chap->digest_type); | 465 | chap->digest_type); |
462 | return -1; | 466 | return -1; |
463 | } | 467 | } |
464 | } | 468 | } |
465 | 469 | ||
466 | u32 chap_main_loop( | 470 | u32 chap_main_loop( |
467 | struct iscsi_conn *conn, | 471 | struct iscsi_conn *conn, |
468 | struct iscsi_node_auth *auth, | 472 | struct iscsi_node_auth *auth, |
469 | char *in_text, | 473 | char *in_text, |
470 | char *out_text, | 474 | char *out_text, |
471 | int *in_len, | 475 | int *in_len, |
472 | int *out_len) | 476 | int *out_len) |
473 | { | 477 | { |
474 | struct iscsi_chap *chap = conn->auth_protocol; | 478 | struct iscsi_chap *chap = conn->auth_protocol; |
475 | 479 | ||
476 | if (!chap) { | 480 | if (!chap) { |
477 | chap = chap_server_open(conn, auth, in_text, out_text, out_len); | 481 | chap = chap_server_open(conn, auth, in_text, out_text, out_len); |
478 | if (!chap) | 482 | if (!chap) |
479 | return 2; | 483 | return 2; |
480 | chap->chap_state = CHAP_STAGE_SERVER_AIC; | 484 | chap->chap_state = CHAP_STAGE_SERVER_AIC; |
481 | return 0; | 485 | return 0; |
482 | } else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) { | 486 | } else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) { |
483 | convert_null_to_semi(in_text, *in_len); | 487 | convert_null_to_semi(in_text, *in_len); |
484 | if (chap_got_response(conn, auth, in_text, out_text, | 488 | if (chap_got_response(conn, auth, in_text, out_text, |
485 | out_len) < 0) { | 489 | out_len) < 0) { |
486 | chap_close(conn); | 490 | chap_close(conn); |
487 | return 2; | 491 | return 2; |
488 | } | 492 | } |
489 | if (auth->authenticate_target) | 493 | if (auth->authenticate_target) |
490 | chap->chap_state = CHAP_STAGE_SERVER_NR; | 494 | chap->chap_state = CHAP_STAGE_SERVER_NR; |
491 | else | 495 | else |
492 | *out_len = 0; | 496 | *out_len = 0; |
493 | chap_close(conn); | 497 | chap_close(conn); |
494 | return 1; | 498 | return 1; |
495 | } | 499 | } |
496 | 500 | ||
497 | return 2; | 501 | return 2; |
498 | } | 502 | } |
499 | 503 |