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);