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
  *
165
  *
166
  * @v strings		iBFT string block descriptor
166
  * @v strings		iBFT string block descriptor
167
  * @v string		String field
167
  * @v string		String field
168
- * @v data		String to fill in
168
+ * @v data		String to fill in, or NULL
169
  * @ret rc		Return status code
169
  * @ret rc		Return status code
170
  */
170
  */
171
 static int ibft_set_string ( struct ibft_string_block *strings,
171
 static int ibft_set_string ( struct ibft_string_block *strings,
172
 			     struct ibft_string *string, const char *data ) {
172
 			     struct ibft_string *string, const char *data ) {
173
-	size_t len = strlen ( data );
174
 	char *dest;
173
 	char *dest;
175
 	int rc;
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
 		return rc;
181
 		return rc;
179
 	dest = ( ( ( char * ) strings->table ) + string->offset );
182
 	dest = ( ( ( char * ) strings->table ) + string->offset );
180
 	strcpy ( dest, data );
183
 	strcpy ( dest, data );
270
 	return 0;
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
  * Fill in Target portion of iBFT
333
  * Fill in Target portion of iBFT
275
  *
334
  *
291
 	if ( ( rc = ibft_set_string ( strings, &target->target_name,
350
 	if ( ( rc = ibft_set_string ( strings, &target->target_name,
292
 				      iscsi->target_iqn ) ) != 0 )
351
 				      iscsi->target_iqn ) ) != 0 )
293
 		return rc;
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
 	return 0;
359
 	return 0;
307
 }
360
 }

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

41
  * allocates memory, and so may fail.  The allocated memory must
41
  * allocates memory, and so may fail.  The allocated memory must
42
  * eventually be freed by a call to chap_finish().
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
 		struct crypto_algorithm *digest ) {
45
 		struct crypto_algorithm *digest ) {
46
 	size_t state_len;
46
 	size_t state_len;
47
 	void *state;
47
 	void *state;
71
 /**
71
 /**
72
  * Add data to the CHAP challenge
72
  * Add data to the CHAP challenge
73
  *
73
  *
74
- * @v chap		CHAP challenge/response
74
+ * @v chap		CHAP response
75
  * @v data		Data to add
75
  * @v data		Data to add
76
  * @v len		Length of data to add
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
 		   size_t len ) {
79
 		   size_t len ) {
80
 	assert ( chap->digest != NULL );
80
 	assert ( chap->digest != NULL );
81
 	assert ( chap->digest_context != NULL );
81
 	assert ( chap->digest_context != NULL );
89
 /**
89
 /**
90
  * Respond to the CHAP challenge
90
  * Respond to the CHAP challenge
91
  *
91
  *
92
- * @v chap		CHAP challenge/response
92
+ * @v chap		CHAP response
93
  *
93
  *
94
  * Calculates the final CHAP response value, and places it in @c
94
  * Calculates the final CHAP response value, and places it in @c
95
  * chap->response, with a length of @c chap->response_len.
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
 	assert ( chap->digest != NULL );
98
 	assert ( chap->digest != NULL );
99
 	assert ( chap->digest_context != NULL );
99
 	assert ( chap->digest_context != NULL );
100
 	assert ( chap->response != NULL );
100
 	assert ( chap->response != NULL );
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
 	void *state = chap->digest_context;
116
 	void *state = chap->digest_context;
117
 
117
 
118
 	DBG ( "CHAP %p finished\n", chap );
118
 	DBG ( "CHAP %p finished\n", chap );

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

12
 
12
 
13
 struct crypto_algorithm;
13
 struct crypto_algorithm;
14
 
14
 
15
-/** A CHAP challenge/response */
16
-struct chap_challenge {
15
+/** A CHAP response */
16
+struct chap_response {
17
 	/** Digest algorithm used for the response */
17
 	/** Digest algorithm used for the response */
18
 	struct crypto_algorithm *digest;
18
 	struct crypto_algorithm *digest;
19
 	/** Context used by the digest algorithm */
19
 	/** Context used by the digest algorithm */
24
 	size_t response_len;
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
 		       struct crypto_algorithm *digest );
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
 			  size_t len );
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
  * Add identifier data to the CHAP challenge
35
  * Add identifier data to the CHAP challenge
36
  *
36
  *
37
- * @v chap		CHAP challenge/response
37
+ * @v chap		CHAP response
38
  * @v identifier	CHAP identifier
38
  * @v identifier	CHAP identifier
39
  *
39
  *
40
  * The CHAP identifier is the first byte of the CHAP challenge.  This
40
  * The CHAP identifier is the first byte of the CHAP challenge.  This
41
  * function is a notational convenience for calling chap_update() for
41
  * function is a notational convenience for calling chap_update() for
42
  * the identifier byte.
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
 					 unsigned int identifier ) {
45
 					 unsigned int identifier ) {
46
 	uint8_t ident_byte = identifier;
46
 	uint8_t ident_byte = identifier;
47
 
47
 

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

241
  */
241
  */
242
 #define DHCP_EB_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbf )
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
 /** iSCSI primary target IQN */
262
 /** iSCSI primary target IQN */
245
 #define DHCP_ISCSI_PRIMARY_TARGET_IQN 201
263
 #define DHCP_ISCSI_PRIMARY_TARGET_IQN 201
246
 
264
 

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

522
 	 */
522
 	 */
523
 	int retry_count;
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
 	/** Target session identifying handle
545
 	/** Target session identifying handle
533
 	 *
546
 	 *
642
 /** iSCSI session needs to send the CHAP response */
655
 /** iSCSI session needs to send the CHAP response */
643
 #define ISCSI_STATUS_STRINGS_CHAP_RESPONSE 0x0400
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
 /** iSCSI session needs to send the operational negotiation strings */
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
 /** Mask for all iSCSI "needs to send" flags */
664
 /** Mask for all iSCSI "needs to send" flags */
649
 #define ISCSI_STATUS_STRINGS_MASK 0xff00
665
 #define ISCSI_STATUS_STRINGS_MASK 0xff00

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

49
 /** Default iSCSI initiator name (constructed from hostname) */
49
 /** Default iSCSI initiator name (constructed from hostname) */
50
 static char *iscsi_default_initiator_iqn;
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
 static void iscsi_start_tx ( struct iscsi_session *iscsi );
64
 static void iscsi_start_tx ( struct iscsi_session *iscsi );
59
 static void iscsi_start_login ( struct iscsi_session *iscsi );
65
 static void iscsi_start_login ( struct iscsi_session *iscsi );
81
 
87
 
82
 	free ( iscsi->target_address );
88
 	free ( iscsi->target_address );
83
 	free ( iscsi->target_iqn );
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
 	chap_finish ( &iscsi->chap );
94
 	chap_finish ( &iscsi->chap );
87
 	iscsi_rx_buffered_data_done ( iscsi );
95
 	iscsi_rx_buffered_data_done ( iscsi );
88
 	free ( iscsi );
96
 	free ( iscsi );
144
 	/* Clear connection status */
152
 	/* Clear connection status */
145
 	iscsi->status = 0;
153
 	iscsi->status = 0;
146
 
154
 
155
+	/* Deauthenticate target */
156
+	iscsi->target_auth_ok = 0;
157
+
147
 	/* Reset TX and RX state machines */
158
 	/* Reset TX and RX state machines */
148
 	iscsi->tx_state = ISCSI_TX_IDLE;
159
 	iscsi->tx_state = ISCSI_TX_IDLE;
149
 	iscsi->rx_state = ISCSI_RX_BHS;
160
 	iscsi->rx_state = ISCSI_RX_BHS;
213
 	command->cmdsn = htonl ( iscsi->cmdsn );
224
 	command->cmdsn = htonl ( iscsi->cmdsn );
214
 	command->expstatsn = htonl ( iscsi->statsn + 1 );
225
 	command->expstatsn = htonl ( iscsi->statsn + 1 );
215
 	memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
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
 					       void *data, size_t len ) {
462
 					       void *data, size_t len ) {
451
 	unsigned int used = 0;
463
 	unsigned int used = 0;
452
 	unsigned int i;
464
 	unsigned int i;
465
+	const char *auth_method;
453
 
466
 
454
 	if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
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
 		used += ssnprintf ( data + used, len - used,
476
 		used += ssnprintf ( data + used, len - used,
456
 				    "InitiatorName=%s%c"
477
 				    "InitiatorName=%s%c"
457
 				    "TargetName=%s%c"
478
 				    "TargetName=%s%c"
458
 				    "SessionType=Normal%c"
479
 				    "SessionType=Normal%c"
459
-				    "AuthMethod=%sNone%c",
480
+				    "AuthMethod=%s%c",
460
 				    iscsi_initiator_iqn(), 0,
481
 				    iscsi_initiator_iqn(), 0,
461
 				    iscsi->target_iqn, 0, 0,
482
 				    iscsi->target_iqn, 0, 0,
462
-				    ( ( iscsi->username && iscsi->password ) ?
463
-				      "CHAP," : "" ), 0 );
483
+				    auth_method, 0 );
464
 	}
484
 	}
465
 
485
 
466
 	if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
486
 	if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
468
 	}
488
 	}
469
 	
489
 	
470
 	if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) {
490
 	if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) {
491
+		assert ( iscsi->initiator_username != NULL );
471
 		used += ssnprintf ( data + used, len - used,
492
 		used += ssnprintf ( data + used, len - used,
472
 				    "CHAP_N=%s%cCHAP_R=0x",
493
 				    "CHAP_N=%s%cCHAP_R=0x",
473
-				    iscsi->username, 0 );
494
+				    iscsi->initiator_username, 0 );
474
 		for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
495
 		for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
475
 			used += ssnprintf ( data + used, len - used, "%02x",
496
 			used += ssnprintf ( data + used, len - used, "%02x",
476
 					    iscsi->chap.response[i] );
497
 					    iscsi->chap.response[i] );
478
 		used += ssnprintf ( data + used, len - used, "%c", 0 );
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
 	if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
513
 	if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
482
 		used += ssnprintf ( data + used, len - used,
514
 		used += ssnprintf ( data + used, len - used,
483
 				    "HeaderDigest=None%c"
515
 				    "HeaderDigest=None%c"
602
 static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
634
 static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
603
 					   const char *value ) {
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
 	/* If server requests CHAP, send the CHAP_A string */
641
 	/* If server requests CHAP, send the CHAP_A string */
606
 	if ( strcmp ( value, "CHAP" ) == 0 ) {
642
 	if ( strcmp ( value, "CHAP" ) == 0 ) {
607
 		DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n",
643
 		DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n",
608
 		       iscsi );
644
 		       iscsi );
609
 		iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
645
 		iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
610
 	}
646
 	}
647
+
611
 	return 0;
648
 	return 0;
612
 }
649
 }
613
 
650
 
620
  */
657
  */
621
 static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
658
 static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
622
 				       const char *value ) {
659
 				       const char *value ) {
623
-	int rc;
624
 
660
 
625
 	/* We only ever offer "5" (i.e. MD5) as an algorithm, so if
661
 	/* We only ever offer "5" (i.e. MD5) as an algorithm, so if
626
 	 * the server responds with anything else it is a protocol
662
 	 * the server responds with anything else it is a protocol
632
 		return -EPROTO;
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
 	return 0;
671
 	return 0;
643
 }
672
 }
644
 
673
 
653
 				       const char *value ) {
682
 				       const char *value ) {
654
 	unsigned int identifier;
683
 	unsigned int identifier;
655
 	char *endp;
684
 	char *endp;
685
+	int rc;
656
 
686
 
657
 	/* The CHAP identifier is an integer value */
687
 	/* The CHAP identifier is an integer value */
658
 	identifier = strtoul ( value, &endp, 0 );
688
 	identifier = strtoul ( value, &endp, 0 );
662
 		return -EPROTO;
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
 	/* Identifier and secret are the first two components of the
703
 	/* Identifier and secret are the first two components of the
666
 	 * challenge.
704
 	 * challenge.
667
 	 */
705
 	 */
668
 	chap_set_identifier ( &iscsi->chap, identifier );
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
 	return 0;
712
 	return 0;
686
 	char buf[3];
724
 	char buf[3];
687
 	char *endp;
725
 	char *endp;
688
 	uint8_t byte;
726
 	uint8_t byte;
727
+	unsigned int i;
689
 
728
 
690
 	/* Check and strip leading "0x" */
729
 	/* Check and strip leading "0x" */
691
 	if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
730
 	if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
692
 		DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n",
731
 		DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n",
693
 		       iscsi, value );
732
 		       iscsi, value );
733
+		return -EPROTO;
694
 	}
734
 	}
695
 	value += 2;
735
 	value += 2;
696
 
736
 
712
 	chap_respond ( &iscsi->chap );
752
 	chap_respond ( &iscsi->chap );
713
 	iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
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
 	return 0;
863
 	return 0;
716
 }
864
 }
717
 
865
 
739
 	{ "CHAP_A=", iscsi_handle_chap_a_value },
887
 	{ "CHAP_A=", iscsi_handle_chap_a_value },
740
 	{ "CHAP_I=", iscsi_handle_chap_i_value },
888
 	{ "CHAP_I=", iscsi_handle_chap_i_value },
741
 	{ "CHAP_C=", iscsi_handle_chap_c_value },
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
 	{ NULL, NULL }
892
 	{ NULL, NULL }
743
 };
893
 };
744
 
894
 
939
 		return 0;
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
 	/* Reset retry count */
1099
 	/* Reset retry count */
943
 	iscsi->retry_count = 0;
1100
 	iscsi->retry_count = 0;
944
 
1101
 
1148
 			  size_t len, size_t remaining __unused ) {
1305
 			  size_t len, size_t remaining __unused ) {
1149
 	memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
1306
 	memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
1150
 	if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
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
 	return 0;
1312
 	return 0;
1156
 }
1313
 }
1546
  * Set iSCSI authentication details
1703
  * Set iSCSI authentication details
1547
  *
1704
  *
1548
  * @v iscsi		iSCSI session
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
  * @ret rc		Return status code
1710
  * @ret rc		Return status code
1552
  */
1711
  */
1553
 static int iscsi_set_auth ( struct iscsi_session *iscsi,
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
 			return -ENOMEM;
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
 			return -ENOMEM;
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
 	return 0;
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
 	if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
1783
 	if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
1592
 		goto err;
1784
 		goto err;
1593
 	/* Set fields not specified by root path */
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
 		goto err;
1791
 		goto err;
1597
 
1792
 
1598
 	/* Sanity checks */
1793
 	/* Sanity checks */
1635
 	.type = &setting_type_string,
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
 /** An iSCSI string setting */
1849
 /** An iSCSI string setting */
1639
 struct iscsi_string_setting {
1850
 struct iscsi_string_setting {
1640
 	/** Setting */
1851
 	/** Setting */
1654
 	},
1865
 	},
1655
 	{
1866
 	{
1656
 		.setting = &username_setting,
1867
 		.setting = &username_setting,
1657
-		.string = &iscsi_username,
1868
+		.string = &iscsi_initiator_username,
1658
 		.prefix = "",
1869
 		.prefix = "",
1659
 	},
1870
 	},
1660
 	{
1871
 	{
1661
 		.setting = &password_setting,
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
 		.prefix = "",
1884
 		.prefix = "",
1664
 	},
1885
 	},
1665
 	{
1886
 	{

Loading…
Cancel
Save