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