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