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