Browse Source

[iSCSI] Add support for mutual CHAP

Allow initiator to verify target authentication using CHAP.
tags/v0.9.4
Michael Brown 16 years ago
parent
commit
5d4839b577
6 changed files with 390 additions and 82 deletions
  1. 67
    14
      src/core/ibft.c
  2. 8
    8
      src/crypto/chap.c
  3. 8
    8
      src/include/gpxe/chap.h
  4. 18
    0
      src/include/gpxe/dhcp.h
  5. 23
    7
      src/include/gpxe/iscsi.h
  6. 266
    45
      src/net/tcp/iscsi.c

+ 67
- 14
src/core/ibft.c View File

@@ -165,16 +165,19 @@ static int ibft_alloc_string ( struct ibft_string_block *strings,
165 165
  *
166 166
  * @v strings		iBFT string block descriptor
167 167
  * @v string		String field
168
- * @v data		String to fill in
168
+ * @v data		String to fill in, or NULL
169 169
  * @ret rc		Return status code
170 170
  */
171 171
 static int ibft_set_string ( struct ibft_string_block *strings,
172 172
 			     struct ibft_string *string, const char *data ) {
173
-	size_t len = strlen ( data );
174 173
 	char *dest;
175 174
 	int rc;
176 175
 
177
-	if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 )
176
+	if ( ! data )
177
+		return 0;
178
+
179
+	if ( ( rc = ibft_alloc_string ( strings, string,
180
+					strlen ( data ) ) ) != 0 )
178 181
 		return rc;
179 182
 	dest = ( ( ( char * ) strings->table ) + string->offset );
180 183
 	strcpy ( dest, data );
@@ -270,6 +273,62 @@ static int ibft_fill_initiator ( struct ibft_initiator *initiator,
270 273
 	return 0;
271 274
 }
272 275
 
276
+/**
277
+ * Fill in Target CHAP portion of iBFT
278
+ *
279
+ * @v target		Target portion of iBFT
280
+ * @v strings		iBFT string block descriptor
281
+ * @v iscsi		iSCSI session
282
+ * @ret rc		Return status code
283
+ */
284
+static int ibft_fill_target_chap ( struct ibft_target *target,
285
+				   struct ibft_string_block *strings,
286
+				   struct iscsi_session *iscsi ) {
287
+	int rc;
288
+
289
+	if ( ! iscsi->initiator_username )
290
+		return 0;
291
+	assert ( iscsi->initiator_password );
292
+
293
+	target->chap_type = IBFT_CHAP_ONE_WAY;
294
+	if ( ( rc = ibft_set_string ( strings, &target->chap_name,
295
+				      iscsi->initiator_username ) ) != 0 )
296
+		return rc;
297
+	if ( ( rc = ibft_set_string ( strings, &target->chap_secret,
298
+				      iscsi->initiator_password ) ) != 0 )
299
+		return rc;
300
+	return 0;
301
+}
302
+
303
+/**
304
+ * Fill in Target Reverse CHAP portion of iBFT
305
+ *
306
+ * @v target		Target portion of iBFT
307
+ * @v strings		iBFT string block descriptor
308
+ * @v iscsi		iSCSI session
309
+ * @ret rc		Return status code
310
+ */
311
+static int ibft_fill_target_reverse_chap ( struct ibft_target *target,
312
+					   struct ibft_string_block *strings,
313
+					   struct iscsi_session *iscsi ) {
314
+	int rc;
315
+
316
+	if ( ! iscsi->target_username )
317
+		return 0;
318
+	assert ( iscsi->target_password );
319
+	assert ( iscsi->initiator_username );
320
+	assert ( iscsi->initiator_password );
321
+
322
+	target->chap_type = IBFT_CHAP_MUTUAL;
323
+	if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_name,
324
+				      iscsi->target_username ) ) != 0 )
325
+		return rc;
326
+	if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_secret,
327
+				      iscsi->target_password ) ) != 0 )
328
+		return rc;
329
+	return 0;
330
+}
331
+
273 332
 /**
274 333
  * Fill in Target portion of iBFT
275 334
  *
@@ -291,17 +350,11 @@ static int ibft_fill_target ( struct ibft_target *target,
291 350
 	if ( ( rc = ibft_set_string ( strings, &target->target_name,
292 351
 				      iscsi->target_iqn ) ) != 0 )
293 352
 		return rc;
294
-	if ( iscsi->username ) {
295
-		if ( ( rc = ibft_set_string ( strings, &target->chap_name,
296
-					      iscsi->username ) ) != 0 )
297
-			return rc;
298
-	}
299
-	if ( iscsi->password ) {
300
-		if ( ( rc = ibft_set_string ( strings, &target->chap_secret,
301
-					      iscsi->password ) ) != 0 )
302
-			return rc;
303
-		target->chap_type = IBFT_CHAP_ONE_WAY;
304
-	}
353
+	if ( ( rc = ibft_fill_target_chap ( target, strings, iscsi ) ) != 0 )
354
+		return rc;
355
+	if ( ( rc = ibft_fill_target_reverse_chap ( target, strings,
356
+						    iscsi ) ) != 0 )
357
+		return rc;
305 358
 
306 359
 	return 0;
307 360
 }

+ 8
- 8
src/crypto/chap.c View File

@@ -41,7 +41,7 @@
41 41
  * allocates memory, and so may fail.  The allocated memory must
42 42
  * eventually be freed by a call to chap_finish().
43 43
  */
44
-int chap_init ( struct chap_challenge *chap,
44
+int chap_init ( struct chap_response *chap,
45 45
 		struct crypto_algorithm *digest ) {
46 46
 	size_t state_len;
47 47
 	void *state;
@@ -71,11 +71,11 @@ int chap_init ( struct chap_challenge *chap,
71 71
 /**
72 72
  * Add data to the CHAP challenge
73 73
  *
74
- * @v chap		CHAP challenge/response
74
+ * @v chap		CHAP response
75 75
  * @v data		Data to add
76 76
  * @v len		Length of data to add
77 77
  */
78
-void chap_update ( struct chap_challenge *chap, const void *data,
78
+void chap_update ( struct chap_response *chap, const void *data,
79 79
 		   size_t len ) {
80 80
 	assert ( chap->digest != NULL );
81 81
 	assert ( chap->digest_context != NULL );
@@ -89,12 +89,12 @@ void chap_update ( struct chap_challenge *chap, const void *data,
89 89
 /**
90 90
  * Respond to the CHAP challenge
91 91
  *
92
- * @v chap		CHAP challenge/response
92
+ * @v chap		CHAP response
93 93
  *
94 94
  * Calculates the final CHAP response value, and places it in @c
95 95
  * chap->response, with a length of @c chap->response_len.
96 96
  */
97
-void chap_respond ( struct chap_challenge *chap ) {
97
+void chap_respond ( struct chap_response *chap ) {
98 98
 	assert ( chap->digest != NULL );
99 99
 	assert ( chap->digest_context != NULL );
100 100
 	assert ( chap->response != NULL );
@@ -108,11 +108,11 @@ void chap_respond ( struct chap_challenge *chap ) {
108 108
 }
109 109
 
110 110
 /**
111
- * Free resources used by a CHAP challenge/response
111
+ * Free resources used by a CHAP response
112 112
  *
113
- * @v chap		CHAP challenge/response
113
+ * @v chap		CHAP response
114 114
  */
115
-void chap_finish ( struct chap_challenge *chap ) {
115
+void chap_finish ( struct chap_response *chap ) {
116 116
 	void *state = chap->digest_context;
117 117
 
118 118
 	DBG ( "CHAP %p finished\n", chap );

+ 8
- 8
src/include/gpxe/chap.h View File

@@ -12,8 +12,8 @@
12 12
 
13 13
 struct crypto_algorithm;
14 14
 
15
-/** A CHAP challenge/response */
16
-struct chap_challenge {
15
+/** A CHAP response */
16
+struct chap_response {
17 17
 	/** Digest algorithm used for the response */
18 18
 	struct crypto_algorithm *digest;
19 19
 	/** Context used by the digest algorithm */
@@ -24,24 +24,24 @@ struct chap_challenge {
24 24
 	size_t response_len;
25 25
 };
26 26
 
27
-extern int chap_init ( struct chap_challenge *chap,
27
+extern int chap_init ( struct chap_response *chap,
28 28
 		       struct crypto_algorithm *digest );
29
-extern void chap_update ( struct chap_challenge *chap, const void *data,
29
+extern void chap_update ( struct chap_response *chap, const void *data,
30 30
 			  size_t len );
31
-extern void chap_respond ( struct chap_challenge *chap );
32
-extern void chap_finish ( struct chap_challenge *chap );
31
+extern void chap_respond ( struct chap_response *chap );
32
+extern void chap_finish ( struct chap_response *chap );
33 33
 
34 34
 /**
35 35
  * Add identifier data to the CHAP challenge
36 36
  *
37
- * @v chap		CHAP challenge/response
37
+ * @v chap		CHAP response
38 38
  * @v identifier	CHAP identifier
39 39
  *
40 40
  * The CHAP identifier is the first byte of the CHAP challenge.  This
41 41
  * function is a notational convenience for calling chap_update() for
42 42
  * the identifier byte.
43 43
  */
44
-static inline void chap_set_identifier ( struct chap_challenge *chap,
44
+static inline void chap_set_identifier ( struct chap_response *chap,
45 45
 					 unsigned int identifier ) {
46 46
 	uint8_t ident_byte = identifier;
47 47
 

+ 18
- 0
src/include/gpxe/dhcp.h View File

@@ -241,6 +241,24 @@ struct dhcp_packet;
241 241
  */
242 242
 #define DHCP_EB_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbf )
243 243
 
244
+/** Reverse username
245
+ *
246
+ * This will be used as the reverse username (i.e. the username
247
+ * provided by the server) for any required authentication.  It is
248
+ * expected that this option's value will be held in non-volatile
249
+ * storage, rather than transmitted as part of a DHCP packet.
250
+ */
251
+#define DHCP_EB_REVERSE_USERNAME DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc0 )
252
+
253
+/** Reverse password
254
+ *
255
+ * This will be used as the reverse password (i.e. the password
256
+ * provided by the server) for any required authentication.  It is
257
+ * expected that this option's value will be held in non-volatile
258
+ * storage, rather than transmitted as part of a DHCP packet.
259
+ */
260
+#define DHCP_EB_REVERSE_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc1 )
261
+
244 262
 /** iSCSI primary target IQN */
245 263
 #define DHCP_ISCSI_PRIMARY_TARGET_IQN 201
246 264
 

+ 23
- 7
src/include/gpxe/iscsi.h View File

@@ -522,12 +522,25 @@ struct iscsi_session {
522 522
 	 */
523 523
 	int retry_count;
524 524
 
525
-	/** Username (if any) */
526
-	char *username;
527
-	/** Password (if any) */
528
-	char *password;
529
-	/** CHAP challenge/response */
530
-	struct chap_challenge chap;
525
+	/** Initiator username (if any) */
526
+	char *initiator_username;
527
+	/** Initiator password (if any) */
528
+	char *initiator_password;
529
+	/** Target username (if any) */
530
+	char *target_username;
531
+	/** Target password (if any) */
532
+	char *target_password;
533
+	/** Target has authenticated acceptably */
534
+	int target_auth_ok;
535
+	/** CHAP challenge (for target auth only)
536
+	 *
537
+	 * This is a block of random data; the first byte is used as
538
+	 * the CHAP identifier (CHAP_I) and the remainder as the CHAP
539
+	 * challenge (CHAP_C).
540
+	 */
541
+	unsigned char chap_challenge[17];
542
+	/** CHAP response (used for both initiator and target auth) */
543
+	struct chap_response chap;
531 544
 
532 545
 	/** Target session identifying handle
533 546
 	 *
@@ -642,8 +655,11 @@ struct iscsi_session {
642 655
 /** iSCSI session needs to send the CHAP response */
643 656
 #define ISCSI_STATUS_STRINGS_CHAP_RESPONSE 0x0400
644 657
 
658
+/** iSCSI session needs to send the mutual CHAP challenge */
659
+#define ISCSI_STATUS_STRINGS_CHAP_CHALLENGE 0x0800
660
+
645 661
 /** iSCSI session needs to send the operational negotiation strings */
646
-#define ISCSI_STATUS_STRINGS_OPERATIONAL 0x0800
662
+#define ISCSI_STATUS_STRINGS_OPERATIONAL 0x1000
647 663
 
648 664
 /** Mask for all iSCSI "needs to send" flags */
649 665
 #define ISCSI_STATUS_STRINGS_MASK 0xff00

+ 266
- 45
src/net/tcp/iscsi.c View File

@@ -49,11 +49,17 @@ static char *iscsi_explicit_initiator_iqn;
49 49
 /** Default iSCSI initiator name (constructed from hostname) */
50 50
 static char *iscsi_default_initiator_iqn;
51 51
 
52
-/** iSCSI username */
53
-static char *iscsi_username;
52
+/** iSCSI initiator username */
53
+static char *iscsi_initiator_username;
54 54
 
55
-/** iSCSI password */
56
-static char *iscsi_password;
55
+/** iSCSI initiator password */
56
+static char *iscsi_initiator_password;
57
+
58
+/** iSCSI target username */
59
+static char *iscsi_target_username;
60
+
61
+/** iSCSI target password */
62
+static char *iscsi_target_password;
57 63
 
58 64
 static void iscsi_start_tx ( struct iscsi_session *iscsi );
59 65
 static void iscsi_start_login ( struct iscsi_session *iscsi );
@@ -81,8 +87,10 @@ static void iscsi_free ( struct refcnt *refcnt ) {
81 87
 
82 88
 	free ( iscsi->target_address );
83 89
 	free ( iscsi->target_iqn );
84
-	free ( iscsi->username );
85
-	free ( iscsi->password );
90
+	free ( iscsi->initiator_username );
91
+	free ( iscsi->initiator_password );
92
+	free ( iscsi->target_username );
93
+	free ( iscsi->target_password );
86 94
 	chap_finish ( &iscsi->chap );
87 95
 	iscsi_rx_buffered_data_done ( iscsi );
88 96
 	free ( iscsi );
@@ -144,6 +152,9 @@ static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) {
144 152
 	/* Clear connection status */
145 153
 	iscsi->status = 0;
146 154
 
155
+	/* Deauthenticate target */
156
+	iscsi->target_auth_ok = 0;
157
+
147 158
 	/* Reset TX and RX state machines */
148 159
 	iscsi->tx_state = ISCSI_TX_IDLE;
149 160
 	iscsi->rx_state = ISCSI_RX_BHS;
@@ -213,11 +224,12 @@ static void iscsi_start_command ( struct iscsi_session *iscsi ) {
213 224
 	command->cmdsn = htonl ( iscsi->cmdsn );
214 225
 	command->expstatsn = htonl ( iscsi->statsn + 1 );
215 226
 	memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
216
-	DBGC ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
217
-	       iscsi, SCSI_CDB_DATA ( command->cdb ),
218
-	       ( iscsi->command->data_in ? "in" : "out" ),
219
-	       ( iscsi->command->data_in ?
220
-		 iscsi->command->data_in_len : iscsi->command->data_out_len ));
227
+	DBGC2 ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
228
+		iscsi, SCSI_CDB_DATA ( command->cdb ),
229
+		( iscsi->command->data_in ? "in" : "out" ),
230
+		( iscsi->command->data_in ?
231
+		  iscsi->command->data_in_len :
232
+		  iscsi->command->data_out_len ) );
221 233
 }
222 234
 
223 235
 /**
@@ -450,17 +462,25 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
450 462
 					       void *data, size_t len ) {
451 463
 	unsigned int used = 0;
452 464
 	unsigned int i;
465
+	const char *auth_method;
453 466
 
454 467
 	if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
468
+		/* Default to allowing no authentication */
469
+		auth_method = "None";
470
+		/* If we have a credential to supply, permit CHAP */
471
+		if ( iscsi->initiator_username )
472
+			auth_method = "CHAP,None";
473
+		/* If we have a credential to check, force CHAP */
474
+		if ( iscsi->target_username )
475
+			auth_method = "CHAP";
455 476
 		used += ssnprintf ( data + used, len - used,
456 477
 				    "InitiatorName=%s%c"
457 478
 				    "TargetName=%s%c"
458 479
 				    "SessionType=Normal%c"
459
-				    "AuthMethod=%sNone%c",
480
+				    "AuthMethod=%s%c",
460 481
 				    iscsi_initiator_iqn(), 0,
461 482
 				    iscsi->target_iqn, 0, 0,
462
-				    ( ( iscsi->username && iscsi->password ) ?
463
-				      "CHAP," : "" ), 0 );
483
+				    auth_method, 0 );
464 484
 	}
465 485
 
466 486
 	if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
@@ -468,9 +488,10 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
468 488
 	}
469 489
 	
470 490
 	if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) {
491
+		assert ( iscsi->initiator_username != NULL );
471 492
 		used += ssnprintf ( data + used, len - used,
472 493
 				    "CHAP_N=%s%cCHAP_R=0x",
473
-				    iscsi->username, 0 );
494
+				    iscsi->initiator_username, 0 );
474 495
 		for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
475 496
 			used += ssnprintf ( data + used, len - used, "%02x",
476 497
 					    iscsi->chap.response[i] );
@@ -478,6 +499,17 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
478 499
 		used += ssnprintf ( data + used, len - used, "%c", 0 );
479 500
 	}
480 501
 
502
+	if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_CHALLENGE ) ) {
503
+		used += ssnprintf ( data + used, len - used,
504
+				    "CHAP_I=%d%cCHAP_C=0x",
505
+				    iscsi->chap_challenge[0], 0 );
506
+		for ( i = 1 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) {
507
+			used += ssnprintf ( data + used, len - used, "%02x",
508
+					    iscsi->chap_challenge[i] );
509
+		}
510
+		used += ssnprintf ( data + used, len - used, "%c", 0 );
511
+	}
512
+
481 513
 	if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
482 514
 		used += ssnprintf ( data + used, len - used,
483 515
 				    "HeaderDigest=None%c"
@@ -602,12 +634,17 @@ static int iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi,
602 634
 static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
603 635
 					   const char *value ) {
604 636
 
637
+	/* Mark target as authenticated if no authentication required */
638
+	if ( ! iscsi->target_username )
639
+		iscsi->target_auth_ok = 1;
640
+
605 641
 	/* If server requests CHAP, send the CHAP_A string */
606 642
 	if ( strcmp ( value, "CHAP" ) == 0 ) {
607 643
 		DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n",
608 644
 		       iscsi );
609 645
 		iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
610 646
 	}
647
+
611 648
 	return 0;
612 649
 }
613 650
 
@@ -620,7 +657,6 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
620 657
  */
621 658
 static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
622 659
 				       const char *value ) {
623
-	int rc;
624 660
 
625 661
 	/* We only ever offer "5" (i.e. MD5) as an algorithm, so if
626 662
 	 * the server responds with anything else it is a protocol
@@ -632,13 +668,6 @@ static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
632 668
 		return -EPROTO;
633 669
 	}
634 670
 
635
-	/* Prepare for CHAP with MD5 */
636
-	if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
637
-		DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
638
-		       iscsi, strerror ( rc ) );
639
-		return rc;
640
-	}
641
-
642 671
 	return 0;
643 672
 }
644 673
 
@@ -653,6 +682,7 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
653 682
 				       const char *value ) {
654 683
 	unsigned int identifier;
655 684
 	char *endp;
685
+	int rc;
656 686
 
657 687
 	/* The CHAP identifier is an integer value */
658 688
 	identifier = strtoul ( value, &endp, 0 );
@@ -662,13 +692,21 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
662 692
 		return -EPROTO;
663 693
 	}
664 694
 
695
+	/* Prepare for CHAP with MD5 */
696
+	chap_finish ( &iscsi->chap );
697
+	if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
698
+		DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
699
+		       iscsi, strerror ( rc ) );
700
+		return rc;
701
+	}
702
+
665 703
 	/* Identifier and secret are the first two components of the
666 704
 	 * challenge.
667 705
 	 */
668 706
 	chap_set_identifier ( &iscsi->chap, identifier );
669
-	if ( iscsi->password ) {
670
-		chap_update ( &iscsi->chap, iscsi->password,
671
-			      strlen ( iscsi->password ) );
707
+	if ( iscsi->initiator_password ) {
708
+		chap_update ( &iscsi->chap, iscsi->initiator_password,
709
+			      strlen ( iscsi->initiator_password ) );
672 710
 	}
673 711
 
674 712
 	return 0;
@@ -686,11 +724,13 @@ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
686 724
 	char buf[3];
687 725
 	char *endp;
688 726
 	uint8_t byte;
727
+	unsigned int i;
689 728
 
690 729
 	/* Check and strip leading "0x" */
691 730
 	if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
692 731
 		DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n",
693 732
 		       iscsi, value );
733
+		return -EPROTO;
694 734
 	}
695 735
 	value += 2;
696 736
 
@@ -712,6 +752,114 @@ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
712 752
 	chap_respond ( &iscsi->chap );
713 753
 	iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
714 754
 
755
+	/* Send CHAP challenge, if applicable */
756
+	if ( iscsi->target_username ) {
757
+		iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_CHALLENGE;
758
+		/* Generate CHAP challenge data */
759
+		for ( i = 0 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) {
760
+			iscsi->chap_challenge[i] = random();
761
+		}
762
+	}
763
+
764
+	return 0;
765
+}
766
+
767
+/**
768
+ * Handle iSCSI CHAP_N text value
769
+ *
770
+ * @v iscsi		iSCSI session
771
+ * @v value		CHAP_N value
772
+ * @ret rc		Return status code
773
+ */
774
+static int iscsi_handle_chap_n_value ( struct iscsi_session *iscsi,
775
+				       const char *value ) {
776
+
777
+	/* The target username isn't actually involved at any point in
778
+	 * the authentication process; it merely serves to identify
779
+	 * which password the target is using to generate the CHAP
780
+	 * response.  We unnecessarily verify that the username is as
781
+	 * expected, in order to provide mildly helpful diagnostics if
782
+	 * the target is supplying the wrong username/password
783
+	 * combination.
784
+	 */
785
+	if ( iscsi->target_username &&
786
+	     ( strcmp ( iscsi->target_username, value ) != 0 ) ) {
787
+		DBGC ( iscsi, "iSCSI %p target username \"%s\" incorrect "
788
+		       "(wanted \"%s\")\n",
789
+		       iscsi, value, iscsi->target_username );
790
+		return -EACCES;
791
+	}
792
+
793
+	return 0;
794
+}
795
+
796
+/**
797
+ * Handle iSCSI CHAP_R text value
798
+ *
799
+ * @v iscsi		iSCSI session
800
+ * @v value		CHAP_R value
801
+ * @ret rc		Return status code
802
+ */
803
+static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi,
804
+				       const char *value ) {
805
+	char buf[3];
806
+	char *endp;
807
+	uint8_t byte;
808
+	unsigned int i;
809
+	int rc;
810
+
811
+	/* Generate CHAP response for verification */
812
+	chap_finish ( &iscsi->chap );
813
+	if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
814
+		DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
815
+		       iscsi, strerror ( rc ) );
816
+		return rc;
817
+	}
818
+	chap_set_identifier ( &iscsi->chap, iscsi->chap_challenge[0] );
819
+	if ( iscsi->target_password ) {
820
+		chap_update ( &iscsi->chap, iscsi->target_password,
821
+			      strlen ( iscsi->target_password ) );
822
+	}
823
+	chap_update ( &iscsi->chap, &iscsi->chap_challenge[1],
824
+		      ( sizeof ( iscsi->chap_challenge ) - 1 ) );
825
+	chap_respond ( &iscsi->chap );
826
+
827
+	/* Check and strip leading "0x" */
828
+	if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
829
+		DBGC ( iscsi, "iSCSI %p saw invalid CHAP response \"%s\"\n",
830
+		       iscsi, value );
831
+		return -EPROTO;
832
+	}
833
+	value += 2;
834
+
835
+	/* Check CHAP response length */
836
+	if ( strlen ( value ) != ( 2 * iscsi->chap.response_len ) ) {
837
+		DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n",
838
+		       iscsi );
839
+		return -EPROTO;
840
+	}
841
+
842
+	/* Process response an octet at a time */
843
+	for ( i = 0 ; ( value[0] && value[1] ) ; value += 2, i++ ) {
844
+		memcpy ( buf, value, 2 );
845
+		buf[2] = 0;
846
+		byte = strtoul ( buf, &endp, 16 );
847
+		if ( *endp != '\0' ) {
848
+			DBGC ( iscsi, "iSCSI %p saw invalid CHAP response "
849
+			       "byte \"%s\"\n", iscsi, buf );
850
+			return -EPROTO;
851
+		}
852
+		if ( byte != iscsi->chap.response[i] ) {
853
+			DBGC ( iscsi, "iSCSI %p saw incorrect CHAP "
854
+			       "response\n", iscsi );
855
+			return -EACCES;
856
+		}
857
+	}
858
+	assert ( i == iscsi->chap.response_len );
859
+
860
+	/* Mark session as authenticated */
861
+	iscsi->target_auth_ok = 1;
862
+
715 863
 	return 0;
716 864
 }
717 865
 
@@ -739,6 +887,8 @@ static struct iscsi_string_type iscsi_string_types[] = {
739 887
 	{ "CHAP_A=", iscsi_handle_chap_a_value },
740 888
 	{ "CHAP_I=", iscsi_handle_chap_i_value },
741 889
 	{ "CHAP_C=", iscsi_handle_chap_c_value },
890
+	{ "CHAP_N=", iscsi_handle_chap_n_value },
891
+	{ "CHAP_R=", iscsi_handle_chap_r_value },
742 892
 	{ NULL, NULL }
743 893
 };
744 894
 
@@ -939,6 +1089,13 @@ static int iscsi_rx_login_response ( struct iscsi_session *iscsi,
939 1089
 		return 0;
940 1090
 	}
941 1091
 
1092
+	/* Check that target authentication was successful (if required) */
1093
+	if ( ! iscsi->target_auth_ok ) {
1094
+		DBGC ( iscsi, "iSCSI %p nefarious target tried to bypass "
1095
+		       "authentication\n", iscsi );
1096
+		return -EPROTO;
1097
+	}
1098
+
942 1099
 	/* Reset retry count */
943 1100
 	iscsi->retry_count = 0;
944 1101
 
@@ -1148,9 +1305,9 @@ static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data,
1148 1305
 			  size_t len, size_t remaining __unused ) {
1149 1306
 	memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
1150 1307
 	if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
1151
-		DBGC ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
1152
-		       iscsi, iscsi->rx_bhs.common.opcode,
1153
-		       ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
1308
+		DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
1309
+			iscsi, iscsi->rx_bhs.common.opcode,
1310
+			ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
1154 1311
 	}
1155 1312
 	return 0;
1156 1313
 }
@@ -1546,26 +1703,61 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
1546 1703
  * Set iSCSI authentication details
1547 1704
  *
1548 1705
  * @v iscsi		iSCSI session
1549
- * @v username		Username, if any
1550
- * @v password		Password, if any
1706
+ * @v initiator_username Initiator username, if any
1707
+ * @v initiator_password Initiator password, if any
1708
+ * @v target_username	Target username, if any
1709
+ * @v target_password	Target password, if any
1551 1710
  * @ret rc		Return status code
1552 1711
  */
1553 1712
 static int iscsi_set_auth ( struct iscsi_session *iscsi,
1554
-			    const char *username, const char *password ) {
1555
-
1556
-	if ( username ) {
1557
-		iscsi->username = strdup ( username );
1558
-		if ( ! iscsi->username )
1713
+			    const char *initiator_username,
1714
+			    const char *initiator_password,
1715
+			    const char *target_username,
1716
+			    const char *target_password ) {
1717
+
1718
+	/* Check for initiator or target credentials */
1719
+	if ( initiator_username || initiator_password ||
1720
+	     target_username || target_password ) {
1721
+
1722
+		/* We must have at least an initiator username+password */
1723
+		if ( ! ( initiator_username && initiator_password ) )
1724
+			goto invalid_auth;
1725
+
1726
+		/* Store initiator credentials */
1727
+		iscsi->initiator_username = strdup ( initiator_username );
1728
+		if ( ! iscsi->initiator_username )
1559 1729
 			return -ENOMEM;
1560
-	}
1561
-
1562
-	if ( password ) {
1563
-		iscsi->password = strdup ( password );
1564
-		if ( ! iscsi->password )
1730
+		iscsi->initiator_password = strdup ( initiator_password );
1731
+		if ( ! iscsi->initiator_password )
1565 1732
 			return -ENOMEM;
1733
+
1734
+		/* Check for target credentials */
1735
+		if ( target_username || target_password ) {
1736
+
1737
+			/* We must have target username+password */
1738
+			if ( ! ( target_username && target_password ) )
1739
+				goto invalid_auth;
1740
+
1741
+			/* Store target credentials */
1742
+			iscsi->target_username = strdup ( target_username );
1743
+			if ( ! iscsi->target_username )
1744
+				return -ENOMEM;
1745
+			iscsi->target_password = strdup ( target_password );
1746
+			if ( ! iscsi->target_password )
1747
+				return -ENOMEM;
1748
+		}
1566 1749
 	}
1567 1750
 
1568 1751
 	return 0;
1752
+
1753
+ invalid_auth:
1754
+	DBGC ( iscsi, "iSCSI %p invalid credentials: initiator "
1755
+	       "%sname,%spw, target %sname,%spw\n", iscsi,
1756
+	       ( initiator_username ? "" : "no " ),
1757
+	       ( initiator_password ? "" : "no " ),
1758
+	       ( target_username ? "" : "no " ),
1759
+	       ( target_password ? "" : "no " ) );
1760
+	return -EINVAL;
1569 1761
 }
1570 1762
 
1571 1763
 /**
@@ -1591,8 +1783,11 @@ int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) {
1591 1783
 	if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
1592 1784
 		goto err;
1593 1785
 	/* Set fields not specified by root path */
1594
-	if ( ( rc = iscsi_set_auth ( iscsi, iscsi_username,
1595
-				     iscsi_password ) ) != 0 )
1786
+	if ( ( rc = iscsi_set_auth ( iscsi,
1787
+				     iscsi_initiator_username,
1788
+				     iscsi_initiator_password,
1789
+				     iscsi_target_username,
1790
+				     iscsi_target_password ) ) != 0 )
1596 1791
 		goto err;
1597 1792
 
1598 1793
 	/* Sanity checks */
@@ -1635,6 +1830,22 @@ struct setting initiator_iqn_setting __setting = {
1635 1830
 	.type = &setting_type_string,
1636 1831
 };
1637 1832
 
1833
+/** iSCSI reverse username setting */
1834
+struct setting reverse_username_setting __setting = {
1835
+	.name = "reverse-username",
1836
+	.description = "Reverse user name",
1837
+	.tag = DHCP_EB_REVERSE_USERNAME,
1838
+	.type = &setting_type_string,
1839
+};
1840
+
1841
+/** iSCSI reverse password setting */
1842
+struct setting reverse_password_setting __setting = {
1843
+	.name = "reverse-password",
1844
+	.description = "Reverse password",
1845
+	.tag = DHCP_EB_REVERSE_PASSWORD,
1846
+	.type = &setting_type_string,
1847
+};
1848
+
1638 1849
 /** An iSCSI string setting */
1639 1850
 struct iscsi_string_setting {
1640 1851
 	/** Setting */
@@ -1654,12 +1865,22 @@ static struct iscsi_string_setting iscsi_string_settings[] = {
1654 1865
 	},
1655 1866
 	{
1656 1867
 		.setting = &username_setting,
1657
-		.string = &iscsi_username,
1868
+		.string = &iscsi_initiator_username,
1658 1869
 		.prefix = "",
1659 1870
 	},
1660 1871
 	{
1661 1872
 		.setting = &password_setting,
1662
-		.string = &iscsi_password,
1873
+		.string = &iscsi_initiator_password,
1874
+		.prefix = "",
1875
+	},
1876
+	{
1877
+		.setting = &reverse_username_setting,
1878
+		.string = &iscsi_target_username,
1879
+		.prefix = "",
1880
+	},
1881
+	{
1882
+		.setting = &reverse_password_setting,
1883
+		.string = &iscsi_target_password,
1663 1884
 		.prefix = "",
1664 1885
 	},
1665 1886
 	{

Loading…
Cancel
Save