From 40abe849b01eb552d18dc01b4a4b26e4d84c5864 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 22 Dec 2010 22:23:55 +1100 Subject: [PATCH] CHAP Add unidirectional chap support so we can authenticate to the target. Make the login phase more "intelligent" so we can iterate over login pdus until we have reached full feature phase Add a new helper functions to parse a iscsi url and break it down into elements in a structure Update iscsi-inq to allow CHAP authentication --- include/iscsi-private.h | 16 + include/iscsi.h | 8 + lib/init.c | 94 +++++- lib/login.c | 701 +++++++++++++++++++++++++++++++++------- src/iscsi-inq.c | 7 + 5 files changed, 703 insertions(+), 123 deletions(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 406c7ea..496d1e3 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -44,6 +44,10 @@ struct iscsi_context { const char *initiator_name; const char *target_name; const char *alias; + + const char *user; + const char *passwd; + enum iscsi_session_type session_type; unsigned char isid[6]; uint32_t itt; @@ -56,8 +60,20 @@ struct iscsi_context { int fd; int is_connected; + + int current_phase; + int next_phase; +#define ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP 0 +#define ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM 1 +#define ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE 2 + int secneg_phase; + int login_attempts; int is_loggedin; + int chap_a; + int chap_i; + char *chap_c; + iscsi_command_cb socket_status_cb; void *connect_data; diff --git a/include/iscsi.h b/include/iscsi.h index 4257a05..af36637 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -21,6 +21,8 @@ struct sockaddr; struct iscsi_url { const char *portal; const char *target; + const char *user; + const char *passwd; int lun; }; @@ -134,6 +136,12 @@ enum iscsi_header_digest { int iscsi_set_header_digest(struct iscsi_context *iscsi, enum iscsi_header_digest header_digest); +/* + * Specify the username and password to use for chap authentication + */ +int iscsi_set_initiator_username_pwd(struct iscsi_context *iscsi, + const char *user, + const char *passwd); /* * check if the context is logged in or not diff --git a/lib/init.c b/lib/init.c index ac4e53d..3cfde74 100644 --- a/lib/init.c +++ b/lib/init.c @@ -53,6 +53,11 @@ iscsi_create_context(const char *initiator_name) /* initialize to a "random" isid */ iscsi_set_isid_random(iscsi, getpid() ^ time(NULL)); + /* assume we start in security negotiation phase */ + iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_SECNEG; + iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_OPNEG; + iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP; + return iscsi; } @@ -153,6 +158,16 @@ iscsi_destroy_context(struct iscsi_context *iscsi) free(iscsi->error_string); iscsi->error_string = NULL; + free(discard_const(iscsi->user)); + iscsi->user = NULL; + + free(discard_const(iscsi->passwd)); + iscsi->passwd = NULL; + + + free(discard_const(iscsi->chap_c)); + iscsi->chap_c = NULL; + free(iscsi); return 0; @@ -210,25 +225,43 @@ struct iscsi_url * iscsi_parse_full_url(struct iscsi_context *iscsi, const char *url) { struct iscsi_url *iscsi_url; + char *str; char *portal; + char *user = NULL; + char *passwd = NULL; char *target; char *lun; + char *tmp; if (strncmp(url, "iscsi://", 8)) { iscsi_set_error(iscsi, "Invalid URL %s\niSCSI URL must be of the form \"iscsi://[:]//\"\n", url); return NULL; } - portal = strdup(url + 8); - if (portal == NULL) { + str = strdup(url + 8); + if (str == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup url %s\n", url); return NULL; } + portal = str; + + tmp = index(portal, '@'); + if (tmp != NULL) { + user = portal; + *tmp++ = 0; + portal = tmp; + + tmp = index(user, '%'); + if (tmp != NULL) { + *tmp++ = 0; + passwd = tmp; + } + } target = index(portal, '/'); if (target == NULL) { iscsi_set_error(iscsi, "Invalid URL %s\niSCSI URL must be of the form \"iscsi://[:]//\"\n", url); - free(portal); + free(str); return NULL; } *target++ = 0; @@ -236,7 +269,7 @@ iscsi_parse_full_url(struct iscsi_context *iscsi, const char *url) lun = index(target, '/'); if (lun == NULL) { iscsi_set_error(iscsi, "Invalid URL %s\niSCSI URL must be of the form \"iscsi://[:]//\"\n", url); - free(portal); + free(str); return NULL; } *lun++ = 0; @@ -245,7 +278,7 @@ iscsi_parse_full_url(struct iscsi_context *iscsi, const char *url) iscsi_url = malloc(sizeof(struct iscsi_url)); if (iscsi_url == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate iscsi_url structure\n"); - free(portal); + free(str); return NULL; } memset(iscsi_url, 0, sizeof(struct iscsi_url)); @@ -254,7 +287,7 @@ iscsi_parse_full_url(struct iscsi_context *iscsi, const char *url) if (iscsi_url->portal == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup portal string\n"); iscsi_destroy_url(iscsi_url); - free(portal); + free(str); return NULL; } @@ -262,12 +295,32 @@ iscsi_parse_full_url(struct iscsi_context *iscsi, const char *url) if (iscsi_url->target == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup target string\n"); iscsi_destroy_url(iscsi_url); - free(portal); + free(str); return NULL; } + + if (user != NULL) { + iscsi_url->user = strdup(user); + if (iscsi_url->user == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup username string\n"); + iscsi_destroy_url(iscsi_url); + free(str); + return NULL; + } + } + + if (passwd != NULL) { + iscsi_url->passwd = strdup(passwd); + if (iscsi_url->passwd == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup password string\n"); + iscsi_destroy_url(iscsi_url); + free(str); + return NULL; + } + } iscsi_url->lun = atoi(lun); - free(portal); + free(str); return iscsi_url; } @@ -280,5 +333,30 @@ iscsi_destroy_url(struct iscsi_url *iscsi_url) free(discard_const(iscsi_url->portal)); free(discard_const(iscsi_url->target)); + free(discard_const(iscsi_url->user)); + free(discard_const(iscsi_url->passwd)); free(iscsi_url); } + + +int +iscsi_set_initiator_username_pwd(struct iscsi_context *iscsi, + const char *user, + const char *passwd) +{ + free(discard_const(iscsi->user)); + iscsi->user = strdup(user); + if (iscsi->user == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: failed to strdup username\n"); + return -1; + } + + free(discard_const(iscsi->passwd)); + iscsi->passwd = strdup(passwd); + if (iscsi->passwd == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: failed to strdup password\n"); + return -1; + } + + return 0; +} diff --git a/lib/login.c b/lib/login.c index b7075ee..ed350bb 100644 --- a/lib/login.c +++ b/lib/login.c @@ -25,14 +25,478 @@ #include #include "iscsi.h" #include "iscsi-private.h" +#include "md5.h" + +static int +iscsi_login_add_initiatorname(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + /* We only send InitiatorName during opneg or the first leg of secneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG + && iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { + return 0; + } + + if (iscsi_pdu_add_data(iscsi, pdu, + (unsigned char *)"InitiatorName=", + 14) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data " + "failed for InitiatorName."); + return -1; + } + if (iscsi_pdu_add_data(iscsi, pdu, + (unsigned char *)iscsi->initiator_name, + strlen(iscsi->initiator_name) +1) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data " + "failed for InitiatorName."); + return -1; + } + return 0; +} + +static int +iscsi_login_add_alias(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + /* We only send InitiatorAlias during opneg or the first leg of secneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG + && iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { + return 0; + } + + if (iscsi_pdu_add_data(iscsi, pdu, + (unsigned char *)"InitiatorAlias=", + 15) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data " + "failed."); + return -1; + } + if (iscsi_pdu_add_data(iscsi, pdu, + (unsigned char *)iscsi->alias, + strlen(iscsi->alias) +1) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data " + "failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_targetname(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + /* We only send TargetName during opneg or the first leg of secneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG + && iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { + return 0; + } + + if (iscsi->target_name == NULL) { + iscsi_set_error(iscsi, "Trying normal connect but " + "target name not set."); + return -1; + } + + if (iscsi_pdu_add_data(iscsi, pdu, + (unsigned char *)"TargetName=", + 11) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data " + "failed."); + return -1; + } + if (iscsi_pdu_add_data(iscsi, pdu, + (unsigned char *)iscsi->target_name, + strlen(iscsi->target_name) +1) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data " + "failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_sessiontype(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send TargetName during opneg or the first leg of secneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG + && iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { + return 0; + } + + switch (iscsi->session_type) { + case ISCSI_SESSION_DISCOVERY: + str = (char *)"SessionType=Discovery"; + break; + case ISCSI_SESSION_NORMAL: + str = (char *)"SessionType=Normal"; + break; + default: + iscsi_set_error(iscsi, "Can not handle sessions %d yet.", + iscsi->session_type); + return -1; + } + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_headerdigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send HeaderDigest during opneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { + return 0; + } + + switch (iscsi->want_header_digest) { + case ISCSI_HEADER_DIGEST_NONE: + str = (char *)"HeaderDigest=None"; + break; + case ISCSI_HEADER_DIGEST_NONE_CRC32C: + str = (char *)"HeaderDigest=None,CRC32C"; + break; + case ISCSI_HEADER_DIGEST_CRC32C_NONE: + str = (char *)"HeaderDigest=CRC32C,None"; + break; + case ISCSI_HEADER_DIGEST_CRC32C: + str = (char *)"HeaderDigest=CRC32C"; + break; + } + + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_datadigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send DataDigest during opneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { + return 0; + } + + str = (char *)"DataDigest=None"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_initialr2t(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send InitialR2T during opneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { + return 0; + } + + str = (char *)"InitialR2T=Yes"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_immediatedata(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send ImmediateData during opneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { + return 0; + } + + str = (char *)"ImmediateData=Yes"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_maxburstlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send MaxBurstLength during opneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { + return 0; + } + + str = (char *)"MaxBurstLength=262144"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_firstburstlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send FirstBurstLength during opneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { + return 0; + } + + str = (char *)"FirstBurstLength=262144"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_maxrecvdatasegmentlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send MaxRecvDataSegmentLength during opneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { + return 0; + } + + str = (char *)"MaxRecvDataSegmentLength=262144"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_datapduinorder(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send DataPduInOrder during opneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { + return 0; + } + + str = (char *)"DataPDUInOrder=Yes"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_datasequenceinorder(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + /* We only send DataSequenceInOrder during opneg */ + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { + return 0; + } + + str = (char *)"DataSequenceInOrder=Yes"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_authmethod(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG + || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { + return 0; + } + + str = (char *)"AuthMethod=CHAP,None"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_authalgorithm(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG + || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM) { + return 0; + } + + str = (char *)"CHAP_A=5"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + return 0; +} + +static int +iscsi_login_add_chap_username(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG + || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) { + return 0; + } + + str = (char *)"CHAP_N="; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + if (iscsi_pdu_add_data(iscsi, pdu, + (unsigned char *)iscsi->user, + strlen(iscsi->user) +1) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data " + "failed."); + return -1; + } + + return 0; +} + +static int +h2i(int h) +{ + if (h >= 'a' && h <= 'f') { + return h - 'a' + 10; + } + if (h >= 'A' && h <= 'F') { + return h - 'A' + 10; + } + return h - '0'; +} +static int +i2h(int i) +{ + if (i >= 10) { + return i - 10 + 'A'; + } + + return i + '0'; +} + +static int +iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + char *str; + unsigned char c, cc[2]; + unsigned char digest[16]; + struct MD5Context ctx; + int i; + + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG + || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) { + return 0; + } + + if (iscsi->chap_c == NULL) { + iscsi_set_error(iscsi, "No CHAP challenge found"); + return -1; + } + MD5Init(&ctx); + c = iscsi->chap_i; + MD5Update(&ctx, &c, 1); + MD5Update(&ctx, (unsigned char *)iscsi->passwd, strlen(iscsi->passwd)); + str = iscsi->chap_c; + while (*str != 0) { + c = (h2i(str[0]) << 4) | h2i(str[1]); + str += 2; + MD5Update(&ctx, &c, 1); + } + MD5Final(digest, &ctx); + + str = (char *)"CHAP_R=0x"; + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + return -1; + } + + for (i=0; i<16; i++) { + c = digest[i]; + cc[0] = i2h((c >> 4)&0x0f); + cc[1] = i2h((c )&0x0f); + if (iscsi_pdu_add_data(iscsi, pdu, &cc, 2) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data " + "failed."); + return -1; + } + } + c = 0; + if (iscsi_pdu_add_data(iscsi, pdu, &c, 1) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: pdu add data " + "failed."); + return -1; + } + + return 0; +} int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data) { struct iscsi_pdu *pdu; - char *str; + int transit; + if (iscsi->login_attempts++ > 10) { + iscsi_set_error(iscsi, "login took too many tries." + " giving up."); + return -1; + } + if (iscsi->is_loggedin != 0) { iscsi_set_error(iscsi, "Trying to login while already logged " "in."); @@ -60,45 +524,45 @@ iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, /* login request */ iscsi_pdu_set_immediate(pdu); + if (iscsi->user == NULL) { + iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_OPNEG; + } + + if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) { + iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_OPNEG; + } + if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) { + iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_FF; + } + + transit = 0; + if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) { + transit = ISCSI_PDU_LOGIN_TRANSIT; + } + if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) { + if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { + transit = ISCSI_PDU_LOGIN_TRANSIT; + } + if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) { + transit = ISCSI_PDU_LOGIN_TRANSIT; + } + } + /* flags */ - iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_LOGIN_TRANSIT - | ISCSI_PDU_LOGIN_CSG_OPNEG - | ISCSI_PDU_LOGIN_NSG_FF); + iscsi_pdu_set_pduflags(pdu, transit + | iscsi->current_phase + | iscsi->next_phase); /* initiator name */ - if (iscsi_pdu_add_data(iscsi, pdu, - (unsigned char *)"InitiatorName=", - 14) != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data " - "failed."); - iscsi_free_pdu(iscsi, pdu); - return -1; - } - if (iscsi_pdu_add_data(iscsi, pdu, - (unsigned char *)iscsi->initiator_name, - strlen(iscsi->initiator_name) +1) != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data " - "failed."); + if (iscsi_login_add_initiatorname(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* optional alias */ if (iscsi->alias) { - if (iscsi_pdu_add_data(iscsi, pdu, - (unsigned char *)"InitiatorAlias=", - 15) != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data " - "failed."); - iscsi_free_pdu(iscsi, pdu); - return -1; - } - if (iscsi_pdu_add_data(iscsi, pdu, - (unsigned char *)iscsi->alias, - strlen(iscsi->alias) +1) != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data " - "failed."); + if (iscsi_login_add_alias(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } @@ -106,125 +570,92 @@ iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, /* target name */ if (iscsi->session_type == ISCSI_SESSION_NORMAL) { - if (iscsi->target_name == NULL) { - iscsi_set_error(iscsi, "Trying normal connect but " - "target name not set."); - iscsi_free_pdu(iscsi, pdu); - return -1; - } - - if (iscsi_pdu_add_data(iscsi, pdu, - (unsigned char *)"TargetName=", - 11) != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data " - "failed."); - iscsi_free_pdu(iscsi, pdu); - return -1; - } - if (iscsi_pdu_add_data(iscsi, pdu, - (unsigned char *)iscsi->target_name, - strlen(iscsi->target_name) +1) != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data " - "failed."); + if (iscsi_login_add_targetname(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } } /* session type */ - switch (iscsi->session_type) { - case ISCSI_SESSION_DISCOVERY: - str = (char *)"SessionType=Discovery"; - break; - case ISCSI_SESSION_NORMAL: - str = (char *)"SessionType=Normal"; - break; - default: - iscsi_set_error(iscsi, "Can not handle sessions %d yet.", - iscsi->session_type); - return -1; - } - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + if (iscsi_login_add_sessiontype(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } - switch (iscsi->want_header_digest) { - case ISCSI_HEADER_DIGEST_NONE: - str = (char *)"HeaderDigest=None"; - break; - case ISCSI_HEADER_DIGEST_NONE_CRC32C: - str = (char *)"HeaderDigest=None,CRC32C"; - break; - case ISCSI_HEADER_DIGEST_CRC32C_NONE: - str = (char *)"HeaderDigest=CRC32C,None"; - break; - case ISCSI_HEADER_DIGEST_CRC32C: - str = (char *)"HeaderDigest=CRC32C"; - break; + /* header digest */ + if (iscsi_login_add_headerdigest(iscsi, pdu) != 0) { + iscsi_free_pdu(iscsi, pdu); + return -1; } - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + /* auth method */ + if (iscsi_login_add_authmethod(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } - str = (char *)"DataDigest=None"; - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + + /* auth algorithm */ + if (iscsi_login_add_authalgorithm(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } - str = (char *)"InitialR2T=Yes"; - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + + /* chap username */ + if (iscsi_login_add_chap_username(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } - str = (char *)"ImmediateData=Yes"; - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + + /* chap response */ + if (iscsi_login_add_chap_response(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } - str = (char *)"MaxBurstLength=262144"; - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + + /* data digest */ + if (iscsi_login_add_datadigest(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } - str = (char *)"FirstBurstLength=262144"; - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + + /* initial r2t */ + if (iscsi_login_add_initialr2t(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } - str = (char *)"MaxRecvDataSegmentLength=262144"; - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + + /* immediate data */ + if (iscsi_login_add_immediatedata(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } - str = (char *)"DataPDUInOrder=Yes"; - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + + /* max burst length */ + if (iscsi_login_add_maxburstlength(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } - str = (char *)"DataSequenceInOrder=Yes"; - if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); + + /* first burst length */ + if (iscsi_login_add_firstburstlength(iscsi, pdu) != 0) { + iscsi_free_pdu(iscsi, pdu); + return -1; + } + + /* max recv data segment length */ + if (iscsi_login_add_maxrecvdatasegmentlength(iscsi, pdu) != 0) { + iscsi_free_pdu(iscsi, pdu); + return -1; + } + + /* data pdu in order */ + if (iscsi_login_add_datapduinorder(iscsi, pdu) != 0) { + iscsi_free_pdu(iscsi, pdu); + return -1; + } + + /* data sequence in order */ + if (iscsi_login_add_datasequenceinorder(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } @@ -321,13 +752,51 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, } } + if (!strncmp((char *)ptr, "AuthMethod=", 11)) { + if (!strcmp((char *)ptr + 11, "CHAP")) { + iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM; + } + } + + if (!strncmp((char *)ptr, "CHAP_A=", 7)) { + iscsi->chap_a = atoi((char *)ptr+7); + iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; + } + + if (!strncmp((char *)ptr, "CHAP_I=", 7)) { + iscsi->chap_i = atoi((char *)ptr+7); + iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; + } + + if (!strncmp((char *)ptr, "CHAP_C=0x", 9)) { + free(iscsi->chap_c); + iscsi->chap_c = strdup((char *)ptr+9); + if (iscsi->chap_c == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to strdup CHAP challenge\n"); + pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); + return -1; + } + iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; + } + ptr += len + 1; size -= len + 1; } + if (in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) { + iscsi->current_phase = (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) << 2; + } - iscsi->is_loggedin = 1; - pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data); + if ((in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) + && (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) == ISCSI_PDU_LOGIN_NSG_FF) { + iscsi->is_loggedin = 1; + pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data); + } else { + if (iscsi_login_async(iscsi, pdu->callback, pdu->private_data) != 0) { + iscsi_set_error(iscsi, "Failed to send continuation login pdu"); + pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); + } + } return 0; } @@ -339,6 +808,8 @@ iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, { struct iscsi_pdu *pdu; + iscsi->login_attempts = 0; + if (iscsi->is_loggedin == 0) { iscsi_set_error(iscsi, "Trying to logout while not logged in."); return -1; diff --git a/src/iscsi-inq.c b/src/iscsi-inq.c index e866268..e142458 100644 --- a/src/iscsi-inq.c +++ b/src/iscsi-inq.c @@ -202,6 +202,13 @@ int main(int argc, const char *argv[]) iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); + if (iscsi_url->user != NULL) { + if (iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd) != 0) { + fprintf(stderr, "Failed to set initiator username and password\n"); + exit(10); + } + } + if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "Failed to log in to target %s\n", iscsi_get_error(iscsi)); iscsi_destroy_url(iscsi_url);