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