mirror of https://github.com/ipxe/ipxe.git
[iSCSI] Add support for mutual CHAP
Allow initiator to verify target authentication using CHAP.pull/1/head
parent
7ad2f652b4
commit
5d4839b577
|
@ -165,16 +165,19 @@ static int ibft_alloc_string ( struct ibft_string_block *strings,
|
||||||
*
|
*
|
||||||
* @v strings iBFT string block descriptor
|
* @v strings iBFT string block descriptor
|
||||||
* @v string String field
|
* @v string String field
|
||||||
* @v data String to fill in
|
* @v data String to fill in, or NULL
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int ibft_set_string ( struct ibft_string_block *strings,
|
static int ibft_set_string ( struct ibft_string_block *strings,
|
||||||
struct ibft_string *string, const char *data ) {
|
struct ibft_string *string, const char *data ) {
|
||||||
size_t len = strlen ( data );
|
|
||||||
char *dest;
|
char *dest;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 )
|
if ( ! data )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ( ( rc = ibft_alloc_string ( strings, string,
|
||||||
|
strlen ( data ) ) ) != 0 )
|
||||||
return rc;
|
return rc;
|
||||||
dest = ( ( ( char * ) strings->table ) + string->offset );
|
dest = ( ( ( char * ) strings->table ) + string->offset );
|
||||||
strcpy ( dest, data );
|
strcpy ( dest, data );
|
||||||
|
@ -270,6 +273,62 @@ static int ibft_fill_initiator ( struct ibft_initiator *initiator,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill in Target CHAP portion of iBFT
|
||||||
|
*
|
||||||
|
* @v target Target portion of iBFT
|
||||||
|
* @v strings iBFT string block descriptor
|
||||||
|
* @v iscsi iSCSI session
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int ibft_fill_target_chap ( struct ibft_target *target,
|
||||||
|
struct ibft_string_block *strings,
|
||||||
|
struct iscsi_session *iscsi ) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if ( ! iscsi->initiator_username )
|
||||||
|
return 0;
|
||||||
|
assert ( iscsi->initiator_password );
|
||||||
|
|
||||||
|
target->chap_type = IBFT_CHAP_ONE_WAY;
|
||||||
|
if ( ( rc = ibft_set_string ( strings, &target->chap_name,
|
||||||
|
iscsi->initiator_username ) ) != 0 )
|
||||||
|
return rc;
|
||||||
|
if ( ( rc = ibft_set_string ( strings, &target->chap_secret,
|
||||||
|
iscsi->initiator_password ) ) != 0 )
|
||||||
|
return rc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill in Target Reverse CHAP portion of iBFT
|
||||||
|
*
|
||||||
|
* @v target Target portion of iBFT
|
||||||
|
* @v strings iBFT string block descriptor
|
||||||
|
* @v iscsi iSCSI session
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int ibft_fill_target_reverse_chap ( struct ibft_target *target,
|
||||||
|
struct ibft_string_block *strings,
|
||||||
|
struct iscsi_session *iscsi ) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if ( ! iscsi->target_username )
|
||||||
|
return 0;
|
||||||
|
assert ( iscsi->target_password );
|
||||||
|
assert ( iscsi->initiator_username );
|
||||||
|
assert ( iscsi->initiator_password );
|
||||||
|
|
||||||
|
target->chap_type = IBFT_CHAP_MUTUAL;
|
||||||
|
if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_name,
|
||||||
|
iscsi->target_username ) ) != 0 )
|
||||||
|
return rc;
|
||||||
|
if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_secret,
|
||||||
|
iscsi->target_password ) ) != 0 )
|
||||||
|
return rc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill in Target portion of iBFT
|
* Fill in Target portion of iBFT
|
||||||
*
|
*
|
||||||
|
@ -291,17 +350,11 @@ static int ibft_fill_target ( struct ibft_target *target,
|
||||||
if ( ( rc = ibft_set_string ( strings, &target->target_name,
|
if ( ( rc = ibft_set_string ( strings, &target->target_name,
|
||||||
iscsi->target_iqn ) ) != 0 )
|
iscsi->target_iqn ) ) != 0 )
|
||||||
return rc;
|
return rc;
|
||||||
if ( iscsi->username ) {
|
if ( ( rc = ibft_fill_target_chap ( target, strings, iscsi ) ) != 0 )
|
||||||
if ( ( rc = ibft_set_string ( strings, &target->chap_name,
|
return rc;
|
||||||
iscsi->username ) ) != 0 )
|
if ( ( rc = ibft_fill_target_reverse_chap ( target, strings,
|
||||||
return rc;
|
iscsi ) ) != 0 )
|
||||||
}
|
return rc;
|
||||||
if ( iscsi->password ) {
|
|
||||||
if ( ( rc = ibft_set_string ( strings, &target->chap_secret,
|
|
||||||
iscsi->password ) ) != 0 )
|
|
||||||
return rc;
|
|
||||||
target->chap_type = IBFT_CHAP_ONE_WAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
* allocates memory, and so may fail. The allocated memory must
|
* allocates memory, and so may fail. The allocated memory must
|
||||||
* eventually be freed by a call to chap_finish().
|
* eventually be freed by a call to chap_finish().
|
||||||
*/
|
*/
|
||||||
int chap_init ( struct chap_challenge *chap,
|
int chap_init ( struct chap_response *chap,
|
||||||
struct crypto_algorithm *digest ) {
|
struct crypto_algorithm *digest ) {
|
||||||
size_t state_len;
|
size_t state_len;
|
||||||
void *state;
|
void *state;
|
||||||
|
@ -71,11 +71,11 @@ int chap_init ( struct chap_challenge *chap,
|
||||||
/**
|
/**
|
||||||
* Add data to the CHAP challenge
|
* Add data to the CHAP challenge
|
||||||
*
|
*
|
||||||
* @v chap CHAP challenge/response
|
* @v chap CHAP response
|
||||||
* @v data Data to add
|
* @v data Data to add
|
||||||
* @v len Length of data to add
|
* @v len Length of data to add
|
||||||
*/
|
*/
|
||||||
void chap_update ( struct chap_challenge *chap, const void *data,
|
void chap_update ( struct chap_response *chap, const void *data,
|
||||||
size_t len ) {
|
size_t len ) {
|
||||||
assert ( chap->digest != NULL );
|
assert ( chap->digest != NULL );
|
||||||
assert ( chap->digest_context != NULL );
|
assert ( chap->digest_context != NULL );
|
||||||
|
@ -89,12 +89,12 @@ void chap_update ( struct chap_challenge *chap, const void *data,
|
||||||
/**
|
/**
|
||||||
* Respond to the CHAP challenge
|
* Respond to the CHAP challenge
|
||||||
*
|
*
|
||||||
* @v chap CHAP challenge/response
|
* @v chap CHAP response
|
||||||
*
|
*
|
||||||
* Calculates the final CHAP response value, and places it in @c
|
* Calculates the final CHAP response value, and places it in @c
|
||||||
* chap->response, with a length of @c chap->response_len.
|
* chap->response, with a length of @c chap->response_len.
|
||||||
*/
|
*/
|
||||||
void chap_respond ( struct chap_challenge *chap ) {
|
void chap_respond ( struct chap_response *chap ) {
|
||||||
assert ( chap->digest != NULL );
|
assert ( chap->digest != NULL );
|
||||||
assert ( chap->digest_context != NULL );
|
assert ( chap->digest_context != NULL );
|
||||||
assert ( chap->response != NULL );
|
assert ( chap->response != NULL );
|
||||||
|
@ -108,11 +108,11 @@ void chap_respond ( struct chap_challenge *chap ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free resources used by a CHAP challenge/response
|
* Free resources used by a CHAP response
|
||||||
*
|
*
|
||||||
* @v chap CHAP challenge/response
|
* @v chap CHAP response
|
||||||
*/
|
*/
|
||||||
void chap_finish ( struct chap_challenge *chap ) {
|
void chap_finish ( struct chap_response *chap ) {
|
||||||
void *state = chap->digest_context;
|
void *state = chap->digest_context;
|
||||||
|
|
||||||
DBG ( "CHAP %p finished\n", chap );
|
DBG ( "CHAP %p finished\n", chap );
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
struct crypto_algorithm;
|
struct crypto_algorithm;
|
||||||
|
|
||||||
/** A CHAP challenge/response */
|
/** A CHAP response */
|
||||||
struct chap_challenge {
|
struct chap_response {
|
||||||
/** Digest algorithm used for the response */
|
/** Digest algorithm used for the response */
|
||||||
struct crypto_algorithm *digest;
|
struct crypto_algorithm *digest;
|
||||||
/** Context used by the digest algorithm */
|
/** Context used by the digest algorithm */
|
||||||
|
@ -24,24 +24,24 @@ struct chap_challenge {
|
||||||
size_t response_len;
|
size_t response_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int chap_init ( struct chap_challenge *chap,
|
extern int chap_init ( struct chap_response *chap,
|
||||||
struct crypto_algorithm *digest );
|
struct crypto_algorithm *digest );
|
||||||
extern void chap_update ( struct chap_challenge *chap, const void *data,
|
extern void chap_update ( struct chap_response *chap, const void *data,
|
||||||
size_t len );
|
size_t len );
|
||||||
extern void chap_respond ( struct chap_challenge *chap );
|
extern void chap_respond ( struct chap_response *chap );
|
||||||
extern void chap_finish ( struct chap_challenge *chap );
|
extern void chap_finish ( struct chap_response *chap );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add identifier data to the CHAP challenge
|
* Add identifier data to the CHAP challenge
|
||||||
*
|
*
|
||||||
* @v chap CHAP challenge/response
|
* @v chap CHAP response
|
||||||
* @v identifier CHAP identifier
|
* @v identifier CHAP identifier
|
||||||
*
|
*
|
||||||
* The CHAP identifier is the first byte of the CHAP challenge. This
|
* The CHAP identifier is the first byte of the CHAP challenge. This
|
||||||
* function is a notational convenience for calling chap_update() for
|
* function is a notational convenience for calling chap_update() for
|
||||||
* the identifier byte.
|
* the identifier byte.
|
||||||
*/
|
*/
|
||||||
static inline void chap_set_identifier ( struct chap_challenge *chap,
|
static inline void chap_set_identifier ( struct chap_response *chap,
|
||||||
unsigned int identifier ) {
|
unsigned int identifier ) {
|
||||||
uint8_t ident_byte = identifier;
|
uint8_t ident_byte = identifier;
|
||||||
|
|
||||||
|
|
|
@ -241,6 +241,24 @@ struct dhcp_packet;
|
||||||
*/
|
*/
|
||||||
#define DHCP_EB_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbf )
|
#define DHCP_EB_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbf )
|
||||||
|
|
||||||
|
/** Reverse username
|
||||||
|
*
|
||||||
|
* This will be used as the reverse username (i.e. the username
|
||||||
|
* provided by the server) for any required authentication. It is
|
||||||
|
* expected that this option's value will be held in non-volatile
|
||||||
|
* storage, rather than transmitted as part of a DHCP packet.
|
||||||
|
*/
|
||||||
|
#define DHCP_EB_REVERSE_USERNAME DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc0 )
|
||||||
|
|
||||||
|
/** Reverse password
|
||||||
|
*
|
||||||
|
* This will be used as the reverse password (i.e. the password
|
||||||
|
* provided by the server) for any required authentication. It is
|
||||||
|
* expected that this option's value will be held in non-volatile
|
||||||
|
* storage, rather than transmitted as part of a DHCP packet.
|
||||||
|
*/
|
||||||
|
#define DHCP_EB_REVERSE_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc1 )
|
||||||
|
|
||||||
/** iSCSI primary target IQN */
|
/** iSCSI primary target IQN */
|
||||||
#define DHCP_ISCSI_PRIMARY_TARGET_IQN 201
|
#define DHCP_ISCSI_PRIMARY_TARGET_IQN 201
|
||||||
|
|
||||||
|
|
|
@ -522,12 +522,25 @@ struct iscsi_session {
|
||||||
*/
|
*/
|
||||||
int retry_count;
|
int retry_count;
|
||||||
|
|
||||||
/** Username (if any) */
|
/** Initiator username (if any) */
|
||||||
char *username;
|
char *initiator_username;
|
||||||
/** Password (if any) */
|
/** Initiator password (if any) */
|
||||||
char *password;
|
char *initiator_password;
|
||||||
/** CHAP challenge/response */
|
/** Target username (if any) */
|
||||||
struct chap_challenge chap;
|
char *target_username;
|
||||||
|
/** Target password (if any) */
|
||||||
|
char *target_password;
|
||||||
|
/** Target has authenticated acceptably */
|
||||||
|
int target_auth_ok;
|
||||||
|
/** CHAP challenge (for target auth only)
|
||||||
|
*
|
||||||
|
* This is a block of random data; the first byte is used as
|
||||||
|
* the CHAP identifier (CHAP_I) and the remainder as the CHAP
|
||||||
|
* challenge (CHAP_C).
|
||||||
|
*/
|
||||||
|
unsigned char chap_challenge[17];
|
||||||
|
/** CHAP response (used for both initiator and target auth) */
|
||||||
|
struct chap_response chap;
|
||||||
|
|
||||||
/** Target session identifying handle
|
/** Target session identifying handle
|
||||||
*
|
*
|
||||||
|
@ -642,8 +655,11 @@ struct iscsi_session {
|
||||||
/** iSCSI session needs to send the CHAP response */
|
/** iSCSI session needs to send the CHAP response */
|
||||||
#define ISCSI_STATUS_STRINGS_CHAP_RESPONSE 0x0400
|
#define ISCSI_STATUS_STRINGS_CHAP_RESPONSE 0x0400
|
||||||
|
|
||||||
|
/** iSCSI session needs to send the mutual CHAP challenge */
|
||||||
|
#define ISCSI_STATUS_STRINGS_CHAP_CHALLENGE 0x0800
|
||||||
|
|
||||||
/** iSCSI session needs to send the operational negotiation strings */
|
/** iSCSI session needs to send the operational negotiation strings */
|
||||||
#define ISCSI_STATUS_STRINGS_OPERATIONAL 0x0800
|
#define ISCSI_STATUS_STRINGS_OPERATIONAL 0x1000
|
||||||
|
|
||||||
/** Mask for all iSCSI "needs to send" flags */
|
/** Mask for all iSCSI "needs to send" flags */
|
||||||
#define ISCSI_STATUS_STRINGS_MASK 0xff00
|
#define ISCSI_STATUS_STRINGS_MASK 0xff00
|
||||||
|
|
|
@ -49,11 +49,17 @@ static char *iscsi_explicit_initiator_iqn;
|
||||||
/** Default iSCSI initiator name (constructed from hostname) */
|
/** Default iSCSI initiator name (constructed from hostname) */
|
||||||
static char *iscsi_default_initiator_iqn;
|
static char *iscsi_default_initiator_iqn;
|
||||||
|
|
||||||
/** iSCSI username */
|
/** iSCSI initiator username */
|
||||||
static char *iscsi_username;
|
static char *iscsi_initiator_username;
|
||||||
|
|
||||||
/** iSCSI password */
|
/** iSCSI initiator password */
|
||||||
static char *iscsi_password;
|
static char *iscsi_initiator_password;
|
||||||
|
|
||||||
|
/** iSCSI target username */
|
||||||
|
static char *iscsi_target_username;
|
||||||
|
|
||||||
|
/** iSCSI target password */
|
||||||
|
static char *iscsi_target_password;
|
||||||
|
|
||||||
static void iscsi_start_tx ( struct iscsi_session *iscsi );
|
static void iscsi_start_tx ( struct iscsi_session *iscsi );
|
||||||
static void iscsi_start_login ( struct iscsi_session *iscsi );
|
static void iscsi_start_login ( struct iscsi_session *iscsi );
|
||||||
|
@ -81,8 +87,10 @@ static void iscsi_free ( struct refcnt *refcnt ) {
|
||||||
|
|
||||||
free ( iscsi->target_address );
|
free ( iscsi->target_address );
|
||||||
free ( iscsi->target_iqn );
|
free ( iscsi->target_iqn );
|
||||||
free ( iscsi->username );
|
free ( iscsi->initiator_username );
|
||||||
free ( iscsi->password );
|
free ( iscsi->initiator_password );
|
||||||
|
free ( iscsi->target_username );
|
||||||
|
free ( iscsi->target_password );
|
||||||
chap_finish ( &iscsi->chap );
|
chap_finish ( &iscsi->chap );
|
||||||
iscsi_rx_buffered_data_done ( iscsi );
|
iscsi_rx_buffered_data_done ( iscsi );
|
||||||
free ( iscsi );
|
free ( iscsi );
|
||||||
|
@ -144,6 +152,9 @@ static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) {
|
||||||
/* Clear connection status */
|
/* Clear connection status */
|
||||||
iscsi->status = 0;
|
iscsi->status = 0;
|
||||||
|
|
||||||
|
/* Deauthenticate target */
|
||||||
|
iscsi->target_auth_ok = 0;
|
||||||
|
|
||||||
/* Reset TX and RX state machines */
|
/* Reset TX and RX state machines */
|
||||||
iscsi->tx_state = ISCSI_TX_IDLE;
|
iscsi->tx_state = ISCSI_TX_IDLE;
|
||||||
iscsi->rx_state = ISCSI_RX_BHS;
|
iscsi->rx_state = ISCSI_RX_BHS;
|
||||||
|
@ -213,11 +224,12 @@ static void iscsi_start_command ( struct iscsi_session *iscsi ) {
|
||||||
command->cmdsn = htonl ( iscsi->cmdsn );
|
command->cmdsn = htonl ( iscsi->cmdsn );
|
||||||
command->expstatsn = htonl ( iscsi->statsn + 1 );
|
command->expstatsn = htonl ( iscsi->statsn + 1 );
|
||||||
memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
|
memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
|
||||||
DBGC ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
|
DBGC2 ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
|
||||||
iscsi, SCSI_CDB_DATA ( command->cdb ),
|
iscsi, SCSI_CDB_DATA ( command->cdb ),
|
||||||
( iscsi->command->data_in ? "in" : "out" ),
|
( iscsi->command->data_in ? "in" : "out" ),
|
||||||
( iscsi->command->data_in ?
|
( iscsi->command->data_in ?
|
||||||
iscsi->command->data_in_len : iscsi->command->data_out_len ));
|
iscsi->command->data_in_len :
|
||||||
|
iscsi->command->data_out_len ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -450,17 +462,25 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
||||||
void *data, size_t len ) {
|
void *data, size_t len ) {
|
||||||
unsigned int used = 0;
|
unsigned int used = 0;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
const char *auth_method;
|
||||||
|
|
||||||
if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
|
if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
|
||||||
|
/* Default to allowing no authentication */
|
||||||
|
auth_method = "None";
|
||||||
|
/* If we have a credential to supply, permit CHAP */
|
||||||
|
if ( iscsi->initiator_username )
|
||||||
|
auth_method = "CHAP,None";
|
||||||
|
/* If we have a credential to check, force CHAP */
|
||||||
|
if ( iscsi->target_username )
|
||||||
|
auth_method = "CHAP";
|
||||||
used += ssnprintf ( data + used, len - used,
|
used += ssnprintf ( data + used, len - used,
|
||||||
"InitiatorName=%s%c"
|
"InitiatorName=%s%c"
|
||||||
"TargetName=%s%c"
|
"TargetName=%s%c"
|
||||||
"SessionType=Normal%c"
|
"SessionType=Normal%c"
|
||||||
"AuthMethod=%sNone%c",
|
"AuthMethod=%s%c",
|
||||||
iscsi_initiator_iqn(), 0,
|
iscsi_initiator_iqn(), 0,
|
||||||
iscsi->target_iqn, 0, 0,
|
iscsi->target_iqn, 0, 0,
|
||||||
( ( iscsi->username && iscsi->password ) ?
|
auth_method, 0 );
|
||||||
"CHAP," : "" ), 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
|
if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
|
||||||
|
@ -468,9 +488,10 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) {
|
if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) {
|
||||||
|
assert ( iscsi->initiator_username != NULL );
|
||||||
used += ssnprintf ( data + used, len - used,
|
used += ssnprintf ( data + used, len - used,
|
||||||
"CHAP_N=%s%cCHAP_R=0x",
|
"CHAP_N=%s%cCHAP_R=0x",
|
||||||
iscsi->username, 0 );
|
iscsi->initiator_username, 0 );
|
||||||
for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
|
for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
|
||||||
used += ssnprintf ( data + used, len - used, "%02x",
|
used += ssnprintf ( data + used, len - used, "%02x",
|
||||||
iscsi->chap.response[i] );
|
iscsi->chap.response[i] );
|
||||||
|
@ -478,6 +499,17 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
||||||
used += ssnprintf ( data + used, len - used, "%c", 0 );
|
used += ssnprintf ( data + used, len - used, "%c", 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_CHALLENGE ) ) {
|
||||||
|
used += ssnprintf ( data + used, len - used,
|
||||||
|
"CHAP_I=%d%cCHAP_C=0x",
|
||||||
|
iscsi->chap_challenge[0], 0 );
|
||||||
|
for ( i = 1 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) {
|
||||||
|
used += ssnprintf ( data + used, len - used, "%02x",
|
||||||
|
iscsi->chap_challenge[i] );
|
||||||
|
}
|
||||||
|
used += ssnprintf ( data + used, len - used, "%c", 0 );
|
||||||
|
}
|
||||||
|
|
||||||
if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
|
if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
|
||||||
used += ssnprintf ( data + used, len - used,
|
used += ssnprintf ( data + used, len - used,
|
||||||
"HeaderDigest=None%c"
|
"HeaderDigest=None%c"
|
||||||
|
@ -602,12 +634,17 @@ static int iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi,
|
||||||
static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
|
static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
|
||||||
const char *value ) {
|
const char *value ) {
|
||||||
|
|
||||||
|
/* Mark target as authenticated if no authentication required */
|
||||||
|
if ( ! iscsi->target_username )
|
||||||
|
iscsi->target_auth_ok = 1;
|
||||||
|
|
||||||
/* If server requests CHAP, send the CHAP_A string */
|
/* If server requests CHAP, send the CHAP_A string */
|
||||||
if ( strcmp ( value, "CHAP" ) == 0 ) {
|
if ( strcmp ( value, "CHAP" ) == 0 ) {
|
||||||
DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n",
|
DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n",
|
||||||
iscsi );
|
iscsi );
|
||||||
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
|
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,7 +657,6 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
|
||||||
*/
|
*/
|
||||||
static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
|
static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
|
||||||
const char *value ) {
|
const char *value ) {
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* We only ever offer "5" (i.e. MD5) as an algorithm, so if
|
/* We only ever offer "5" (i.e. MD5) as an algorithm, so if
|
||||||
* the server responds with anything else it is a protocol
|
* 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,
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare for CHAP with MD5 */
|
|
||||||
if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
|
|
||||||
DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
|
|
||||||
iscsi, strerror ( rc ) );
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,6 +682,7 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
|
||||||
const char *value ) {
|
const char *value ) {
|
||||||
unsigned int identifier;
|
unsigned int identifier;
|
||||||
char *endp;
|
char *endp;
|
||||||
|
int rc;
|
||||||
|
|
||||||
/* The CHAP identifier is an integer value */
|
/* The CHAP identifier is an integer value */
|
||||||
identifier = strtoul ( value, &endp, 0 );
|
identifier = strtoul ( value, &endp, 0 );
|
||||||
|
@ -662,13 +692,21 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prepare for CHAP with MD5 */
|
||||||
|
chap_finish ( &iscsi->chap );
|
||||||
|
if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
|
||||||
|
DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
|
||||||
|
iscsi, strerror ( rc ) );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/* Identifier and secret are the first two components of the
|
/* Identifier and secret are the first two components of the
|
||||||
* challenge.
|
* challenge.
|
||||||
*/
|
*/
|
||||||
chap_set_identifier ( &iscsi->chap, identifier );
|
chap_set_identifier ( &iscsi->chap, identifier );
|
||||||
if ( iscsi->password ) {
|
if ( iscsi->initiator_password ) {
|
||||||
chap_update ( &iscsi->chap, iscsi->password,
|
chap_update ( &iscsi->chap, iscsi->initiator_password,
|
||||||
strlen ( iscsi->password ) );
|
strlen ( iscsi->initiator_password ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -686,11 +724,13 @@ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
|
||||||
char buf[3];
|
char buf[3];
|
||||||
char *endp;
|
char *endp;
|
||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
/* Check and strip leading "0x" */
|
/* Check and strip leading "0x" */
|
||||||
if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
|
if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
|
||||||
DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n",
|
DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n",
|
||||||
iscsi, value );
|
iscsi, value );
|
||||||
|
return -EPROTO;
|
||||||
}
|
}
|
||||||
value += 2;
|
value += 2;
|
||||||
|
|
||||||
|
@ -712,6 +752,114 @@ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
|
||||||
chap_respond ( &iscsi->chap );
|
chap_respond ( &iscsi->chap );
|
||||||
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
|
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
|
||||||
|
|
||||||
|
/* Send CHAP challenge, if applicable */
|
||||||
|
if ( iscsi->target_username ) {
|
||||||
|
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_CHALLENGE;
|
||||||
|
/* Generate CHAP challenge data */
|
||||||
|
for ( i = 0 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) {
|
||||||
|
iscsi->chap_challenge[i] = random();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle iSCSI CHAP_N text value
|
||||||
|
*
|
||||||
|
* @v iscsi iSCSI session
|
||||||
|
* @v value CHAP_N value
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int iscsi_handle_chap_n_value ( struct iscsi_session *iscsi,
|
||||||
|
const char *value ) {
|
||||||
|
|
||||||
|
/* The target username isn't actually involved at any point in
|
||||||
|
* the authentication process; it merely serves to identify
|
||||||
|
* which password the target is using to generate the CHAP
|
||||||
|
* response. We unnecessarily verify that the username is as
|
||||||
|
* expected, in order to provide mildly helpful diagnostics if
|
||||||
|
* the target is supplying the wrong username/password
|
||||||
|
* combination.
|
||||||
|
*/
|
||||||
|
if ( iscsi->target_username &&
|
||||||
|
( strcmp ( iscsi->target_username, value ) != 0 ) ) {
|
||||||
|
DBGC ( iscsi, "iSCSI %p target username \"%s\" incorrect "
|
||||||
|
"(wanted \"%s\")\n",
|
||||||
|
iscsi, value, iscsi->target_username );
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle iSCSI CHAP_R text value
|
||||||
|
*
|
||||||
|
* @v iscsi iSCSI session
|
||||||
|
* @v value CHAP_R value
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi,
|
||||||
|
const char *value ) {
|
||||||
|
char buf[3];
|
||||||
|
char *endp;
|
||||||
|
uint8_t byte;
|
||||||
|
unsigned int i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Generate CHAP response for verification */
|
||||||
|
chap_finish ( &iscsi->chap );
|
||||||
|
if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
|
||||||
|
DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
|
||||||
|
iscsi, strerror ( rc ) );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
chap_set_identifier ( &iscsi->chap, iscsi->chap_challenge[0] );
|
||||||
|
if ( iscsi->target_password ) {
|
||||||
|
chap_update ( &iscsi->chap, iscsi->target_password,
|
||||||
|
strlen ( iscsi->target_password ) );
|
||||||
|
}
|
||||||
|
chap_update ( &iscsi->chap, &iscsi->chap_challenge[1],
|
||||||
|
( sizeof ( iscsi->chap_challenge ) - 1 ) );
|
||||||
|
chap_respond ( &iscsi->chap );
|
||||||
|
|
||||||
|
/* Check and strip leading "0x" */
|
||||||
|
if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
|
||||||
|
DBGC ( iscsi, "iSCSI %p saw invalid CHAP response \"%s\"\n",
|
||||||
|
iscsi, value );
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
value += 2;
|
||||||
|
|
||||||
|
/* Check CHAP response length */
|
||||||
|
if ( strlen ( value ) != ( 2 * iscsi->chap.response_len ) ) {
|
||||||
|
DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n",
|
||||||
|
iscsi );
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process response an octet at a time */
|
||||||
|
for ( i = 0 ; ( value[0] && value[1] ) ; value += 2, i++ ) {
|
||||||
|
memcpy ( buf, value, 2 );
|
||||||
|
buf[2] = 0;
|
||||||
|
byte = strtoul ( buf, &endp, 16 );
|
||||||
|
if ( *endp != '\0' ) {
|
||||||
|
DBGC ( iscsi, "iSCSI %p saw invalid CHAP response "
|
||||||
|
"byte \"%s\"\n", iscsi, buf );
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
if ( byte != iscsi->chap.response[i] ) {
|
||||||
|
DBGC ( iscsi, "iSCSI %p saw incorrect CHAP "
|
||||||
|
"response\n", iscsi );
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert ( i == iscsi->chap.response_len );
|
||||||
|
|
||||||
|
/* Mark session as authenticated */
|
||||||
|
iscsi->target_auth_ok = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,6 +887,8 @@ static struct iscsi_string_type iscsi_string_types[] = {
|
||||||
{ "CHAP_A=", iscsi_handle_chap_a_value },
|
{ "CHAP_A=", iscsi_handle_chap_a_value },
|
||||||
{ "CHAP_I=", iscsi_handle_chap_i_value },
|
{ "CHAP_I=", iscsi_handle_chap_i_value },
|
||||||
{ "CHAP_C=", iscsi_handle_chap_c_value },
|
{ "CHAP_C=", iscsi_handle_chap_c_value },
|
||||||
|
{ "CHAP_N=", iscsi_handle_chap_n_value },
|
||||||
|
{ "CHAP_R=", iscsi_handle_chap_r_value },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -939,6 +1089,13 @@ static int iscsi_rx_login_response ( struct iscsi_session *iscsi,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check that target authentication was successful (if required) */
|
||||||
|
if ( ! iscsi->target_auth_ok ) {
|
||||||
|
DBGC ( iscsi, "iSCSI %p nefarious target tried to bypass "
|
||||||
|
"authentication\n", iscsi );
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset retry count */
|
/* Reset retry count */
|
||||||
iscsi->retry_count = 0;
|
iscsi->retry_count = 0;
|
||||||
|
|
||||||
|
@ -1148,9 +1305,9 @@ static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data,
|
||||||
size_t len, size_t remaining __unused ) {
|
size_t len, size_t remaining __unused ) {
|
||||||
memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
|
memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
|
||||||
if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
|
if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
|
||||||
DBGC ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
|
DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
|
||||||
iscsi, iscsi->rx_bhs.common.opcode,
|
iscsi, iscsi->rx_bhs.common.opcode,
|
||||||
ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
|
ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1546,26 +1703,61 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
|
||||||
* Set iSCSI authentication details
|
* Set iSCSI authentication details
|
||||||
*
|
*
|
||||||
* @v iscsi iSCSI session
|
* @v iscsi iSCSI session
|
||||||
* @v username Username, if any
|
* @v initiator_username Initiator username, if any
|
||||||
* @v password Password, if any
|
* @v initiator_password Initiator password, if any
|
||||||
|
* @v target_username Target username, if any
|
||||||
|
* @v target_password Target password, if any
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int iscsi_set_auth ( struct iscsi_session *iscsi,
|
static int iscsi_set_auth ( struct iscsi_session *iscsi,
|
||||||
const char *username, const char *password ) {
|
const char *initiator_username,
|
||||||
|
const char *initiator_password,
|
||||||
|
const char *target_username,
|
||||||
|
const char *target_password ) {
|
||||||
|
|
||||||
if ( username ) {
|
/* Check for initiator or target credentials */
|
||||||
iscsi->username = strdup ( username );
|
if ( initiator_username || initiator_password ||
|
||||||
if ( ! iscsi->username )
|
target_username || target_password ) {
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( password ) {
|
/* We must have at least an initiator username+password */
|
||||||
iscsi->password = strdup ( password );
|
if ( ! ( initiator_username && initiator_password ) )
|
||||||
if ( ! iscsi->password )
|
goto invalid_auth;
|
||||||
|
|
||||||
|
/* Store initiator credentials */
|
||||||
|
iscsi->initiator_username = strdup ( initiator_username );
|
||||||
|
if ( ! iscsi->initiator_username )
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
iscsi->initiator_password = strdup ( initiator_password );
|
||||||
|
if ( ! iscsi->initiator_password )
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Check for target credentials */
|
||||||
|
if ( target_username || target_password ) {
|
||||||
|
|
||||||
|
/* We must have target username+password */
|
||||||
|
if ( ! ( target_username && target_password ) )
|
||||||
|
goto invalid_auth;
|
||||||
|
|
||||||
|
/* Store target credentials */
|
||||||
|
iscsi->target_username = strdup ( target_username );
|
||||||
|
if ( ! iscsi->target_username )
|
||||||
|
return -ENOMEM;
|
||||||
|
iscsi->target_password = strdup ( target_password );
|
||||||
|
if ( ! iscsi->target_password )
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
invalid_auth:
|
||||||
|
DBGC ( iscsi, "iSCSI %p invalid credentials: initiator "
|
||||||
|
"%sname,%spw, target %sname,%spw\n", iscsi,
|
||||||
|
( initiator_username ? "" : "no " ),
|
||||||
|
( initiator_password ? "" : "no " ),
|
||||||
|
( target_username ? "" : "no " ),
|
||||||
|
( target_password ? "" : "no " ) );
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1591,8 +1783,11 @@ int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) {
|
||||||
if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
|
if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
|
||||||
goto err;
|
goto err;
|
||||||
/* Set fields not specified by root path */
|
/* Set fields not specified by root path */
|
||||||
if ( ( rc = iscsi_set_auth ( iscsi, iscsi_username,
|
if ( ( rc = iscsi_set_auth ( iscsi,
|
||||||
iscsi_password ) ) != 0 )
|
iscsi_initiator_username,
|
||||||
|
iscsi_initiator_password,
|
||||||
|
iscsi_target_username,
|
||||||
|
iscsi_target_password ) ) != 0 )
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* Sanity checks */
|
/* Sanity checks */
|
||||||
|
@ -1635,6 +1830,22 @@ struct setting initiator_iqn_setting __setting = {
|
||||||
.type = &setting_type_string,
|
.type = &setting_type_string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** iSCSI reverse username setting */
|
||||||
|
struct setting reverse_username_setting __setting = {
|
||||||
|
.name = "reverse-username",
|
||||||
|
.description = "Reverse user name",
|
||||||
|
.tag = DHCP_EB_REVERSE_USERNAME,
|
||||||
|
.type = &setting_type_string,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** iSCSI reverse password setting */
|
||||||
|
struct setting reverse_password_setting __setting = {
|
||||||
|
.name = "reverse-password",
|
||||||
|
.description = "Reverse password",
|
||||||
|
.tag = DHCP_EB_REVERSE_PASSWORD,
|
||||||
|
.type = &setting_type_string,
|
||||||
|
};
|
||||||
|
|
||||||
/** An iSCSI string setting */
|
/** An iSCSI string setting */
|
||||||
struct iscsi_string_setting {
|
struct iscsi_string_setting {
|
||||||
/** Setting */
|
/** Setting */
|
||||||
|
@ -1654,12 +1865,22 @@ static struct iscsi_string_setting iscsi_string_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.setting = &username_setting,
|
.setting = &username_setting,
|
||||||
.string = &iscsi_username,
|
.string = &iscsi_initiator_username,
|
||||||
.prefix = "",
|
.prefix = "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.setting = &password_setting,
|
.setting = &password_setting,
|
||||||
.string = &iscsi_password,
|
.string = &iscsi_initiator_password,
|
||||||
|
.prefix = "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.setting = &reverse_username_setting,
|
||||||
|
.string = &iscsi_target_username,
|
||||||
|
.prefix = "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.setting = &reverse_password_setting,
|
||||||
|
.string = &iscsi_target_password,
|
||||||
.prefix = "",
|
.prefix = "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue