Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.2 464 lines 12 kB view raw
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/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 31static 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 43static 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 52static 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 78static 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 = (struct iscsi_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, 91 CHAP_CHALLENGE_LENGTH); 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 103static 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 = (struct iscsi_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; 137 chap->digest_type = CHAP_DIGEST_MD5; 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 154static void chap_close(struct iscsi_conn *conn) 155{ 156 kfree(conn->auth_protocol); 157 conn->auth_protocol = NULL; 158} 159 160static 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 char id, digest[MD5_SIGNATURE_SIZE]; 169 unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2]; 170 unsigned char identifier[10], *challenge = NULL; 171 unsigned char *challenge_binhex = NULL; 172 unsigned char client_digest[MD5_SIGNATURE_SIZE]; 173 unsigned char server_digest[MD5_SIGNATURE_SIZE]; 174 unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH]; 175 struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; 176 struct crypto_hash *tfm; 177 struct hash_desc desc; 178 struct scatterlist sg; 179 int auth_ret = -1, ret, challenge_len; 180 181 memset(identifier, 0, 10); 182 memset(chap_n, 0, MAX_CHAP_N_SIZE); 183 memset(chap_r, 0, MAX_RESPONSE_LENGTH); 184 memset(digest, 0, MD5_SIGNATURE_SIZE); 185 memset(response, 0, MD5_SIGNATURE_SIZE * 2 + 2); 186 memset(client_digest, 0, MD5_SIGNATURE_SIZE); 187 memset(server_digest, 0, MD5_SIGNATURE_SIZE); 188 189 challenge = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL); 190 if (!challenge) { 191 pr_err("Unable to allocate challenge buffer\n"); 192 goto out; 193 } 194 195 challenge_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL); 196 if (!challenge_binhex) { 197 pr_err("Unable to allocate challenge_binhex buffer\n"); 198 goto out; 199 } 200 /* 201 * Extract CHAP_N. 202 */ 203 if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n, 204 &type) < 0) { 205 pr_err("Could not find CHAP_N.\n"); 206 goto out; 207 } 208 if (type == HEX) { 209 pr_err("Could not find CHAP_N.\n"); 210 goto out; 211 } 212 213 if (memcmp(chap_n, auth->userid, strlen(auth->userid)) != 0) { 214 pr_err("CHAP_N values do not match!\n"); 215 goto out; 216 } 217 pr_debug("[server] Got CHAP_N=%s\n", chap_n); 218 /* 219 * Extract CHAP_R. 220 */ 221 if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r, 222 &type) < 0) { 223 pr_err("Could not find CHAP_R.\n"); 224 goto out; 225 } 226 if (type != HEX) { 227 pr_err("Could not find CHAP_R.\n"); 228 goto out; 229 } 230 231 pr_debug("[server] Got CHAP_R=%s\n", chap_r); 232 chap_string_to_hex(client_digest, chap_r, strlen(chap_r)); 233 234 tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); 235 if (IS_ERR(tfm)) { 236 pr_err("Unable to allocate struct crypto_hash\n"); 237 goto out; 238 } 239 desc.tfm = tfm; 240 desc.flags = 0; 241 242 ret = crypto_hash_init(&desc); 243 if (ret < 0) { 244 pr_err("crypto_hash_init() failed\n"); 245 crypto_free_hash(tfm); 246 goto out; 247 } 248 249 sg_init_one(&sg, (void *)&chap->id, 1); 250 ret = crypto_hash_update(&desc, &sg, 1); 251 if (ret < 0) { 252 pr_err("crypto_hash_update() failed for id\n"); 253 crypto_free_hash(tfm); 254 goto out; 255 } 256 257 sg_init_one(&sg, (void *)&auth->password, strlen(auth->password)); 258 ret = crypto_hash_update(&desc, &sg, strlen(auth->password)); 259 if (ret < 0) { 260 pr_err("crypto_hash_update() failed for password\n"); 261 crypto_free_hash(tfm); 262 goto out; 263 } 264 265 sg_init_one(&sg, (void *)chap->challenge, CHAP_CHALLENGE_LENGTH); 266 ret = crypto_hash_update(&desc, &sg, CHAP_CHALLENGE_LENGTH); 267 if (ret < 0) { 268 pr_err("crypto_hash_update() failed for challenge\n"); 269 crypto_free_hash(tfm); 270 goto out; 271 } 272 273 ret = crypto_hash_final(&desc, server_digest); 274 if (ret < 0) { 275 pr_err("crypto_hash_final() failed for server digest\n"); 276 crypto_free_hash(tfm); 277 goto out; 278 } 279 crypto_free_hash(tfm); 280 281 chap_binaryhex_to_asciihex(response, server_digest, MD5_SIGNATURE_SIZE); 282 pr_debug("[server] MD5 Server Digest: %s\n", response); 283 284 if (memcmp(server_digest, client_digest, MD5_SIGNATURE_SIZE) != 0) { 285 pr_debug("[server] MD5 Digests do not match!\n\n"); 286 goto out; 287 } else 288 pr_debug("[server] MD5 Digests match, CHAP connetication" 289 " successful.\n\n"); 290 /* 291 * One way authentication has succeeded, return now if mutual 292 * authentication is not enabled. 293 */ 294 if (!auth->authenticate_target) { 295 kfree(challenge); 296 kfree(challenge_binhex); 297 return 0; 298 } 299 /* 300 * Get CHAP_I. 301 */ 302 if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) { 303 pr_err("Could not find CHAP_I.\n"); 304 goto out; 305 } 306 307 if (type == HEX) 308 id = (unsigned char)simple_strtoul((char *)&identifier[2], 309 &endptr, 0); 310 else 311 id = (unsigned char)simple_strtoul(identifier, &endptr, 0); 312 /* 313 * RFC 1994 says Identifier is no more than octet (8 bits). 314 */ 315 pr_debug("[server] Got CHAP_I=%d\n", id); 316 /* 317 * Get CHAP_C. 318 */ 319 if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN, 320 challenge, &type) < 0) { 321 pr_err("Could not find CHAP_C.\n"); 322 goto out; 323 } 324 325 if (type != HEX) { 326 pr_err("Could not find CHAP_C.\n"); 327 goto out; 328 } 329 pr_debug("[server] Got CHAP_C=%s\n", challenge); 330 challenge_len = chap_string_to_hex(challenge_binhex, challenge, 331 strlen(challenge)); 332 if (!challenge_len) { 333 pr_err("Unable to convert incoming challenge\n"); 334 goto out; 335 } 336 /* 337 * Generate CHAP_N and CHAP_R for mutual authentication. 338 */ 339 tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); 340 if (IS_ERR(tfm)) { 341 pr_err("Unable to allocate struct crypto_hash\n"); 342 goto out; 343 } 344 desc.tfm = tfm; 345 desc.flags = 0; 346 347 ret = crypto_hash_init(&desc); 348 if (ret < 0) { 349 pr_err("crypto_hash_init() failed\n"); 350 crypto_free_hash(tfm); 351 goto out; 352 } 353 354 sg_init_one(&sg, (void *)&id, 1); 355 ret = crypto_hash_update(&desc, &sg, 1); 356 if (ret < 0) { 357 pr_err("crypto_hash_update() failed for id\n"); 358 crypto_free_hash(tfm); 359 goto out; 360 } 361 362 sg_init_one(&sg, (void *)auth->password_mutual, 363 strlen(auth->password_mutual)); 364 ret = crypto_hash_update(&desc, &sg, strlen(auth->password_mutual)); 365 if (ret < 0) { 366 pr_err("crypto_hash_update() failed for" 367 " password_mutual\n"); 368 crypto_free_hash(tfm); 369 goto out; 370 } 371 /* 372 * Convert received challenge to binary hex. 373 */ 374 sg_init_one(&sg, (void *)challenge_binhex, challenge_len); 375 ret = crypto_hash_update(&desc, &sg, challenge_len); 376 if (ret < 0) { 377 pr_err("crypto_hash_update() failed for ma challenge\n"); 378 crypto_free_hash(tfm); 379 goto out; 380 } 381 382 ret = crypto_hash_final(&desc, digest); 383 if (ret < 0) { 384 pr_err("crypto_hash_final() failed for ma digest\n"); 385 crypto_free_hash(tfm); 386 goto out; 387 } 388 crypto_free_hash(tfm); 389 /* 390 * Generate CHAP_N and CHAP_R. 391 */ 392 *nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual); 393 *nr_out_len += 1; 394 pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual); 395 /* 396 * Convert response from binary hex to ascii hext. 397 */ 398 chap_binaryhex_to_asciihex(response, digest, MD5_SIGNATURE_SIZE); 399 *nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s", 400 response); 401 *nr_out_len += 1; 402 pr_debug("[server] Sending CHAP_R=0x%s\n", response); 403 auth_ret = 0; 404out: 405 kfree(challenge); 406 kfree(challenge_binhex); 407 return auth_ret; 408} 409 410static int chap_got_response( 411 struct iscsi_conn *conn, 412 struct iscsi_node_auth *auth, 413 char *nr_in_ptr, 414 char *nr_out_ptr, 415 unsigned int *nr_out_len) 416{ 417 struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; 418 419 switch (chap->digest_type) { 420 case CHAP_DIGEST_MD5: 421 if (chap_server_compute_md5(conn, auth, nr_in_ptr, 422 nr_out_ptr, nr_out_len) < 0) 423 return -1; 424 return 0; 425 default: 426 pr_err("Unknown CHAP digest type %d!\n", 427 chap->digest_type); 428 return -1; 429 } 430} 431 432u32 chap_main_loop( 433 struct iscsi_conn *conn, 434 struct iscsi_node_auth *auth, 435 char *in_text, 436 char *out_text, 437 int *in_len, 438 int *out_len) 439{ 440 struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; 441 442 if (!chap) { 443 chap = chap_server_open(conn, auth, in_text, out_text, out_len); 444 if (!chap) 445 return 2; 446 chap->chap_state = CHAP_STAGE_SERVER_AIC; 447 return 0; 448 } else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) { 449 convert_null_to_semi(in_text, *in_len); 450 if (chap_got_response(conn, auth, in_text, out_text, 451 out_len) < 0) { 452 chap_close(conn); 453 return 2; 454 } 455 if (auth->authenticate_target) 456 chap->chap_state = CHAP_STAGE_SERVER_NR; 457 else 458 *out_len = 0; 459 chap_close(conn); 460 return 1; 461 } 462 463 return 2; 464}