📄 iscsi-login.c
字号:
return 0; /* * Note: 12.22 forbids vendor-specific keys on discovery * sessions, so the caller is violating the spec if it asks for * these on a discovery session. */ if (session->vendor_specific_keys) if (!add_vendor_specific_text(session, pdu, data, max_data_length)) return 0; } else if (!check_irrelevant_keys(session, pdu, data, max_data_length)) return 0; return 1;}static voidenum_auth_keys(struct iscsi_acl *auth_client, struct iscsi_hdr *pdu, char *data, int max_data_length, int keytype){ int present = 0, rc; char *key = (char *)acl_get_key_name(keytype); int key_length = key ? strlen(key) : 0; int pdu_length = ntoh24(pdu->dlength); char *auth_value = data + pdu_length + key_length + 1; unsigned int max_length = max_data_length - (pdu_length + key_length + 1); /* * add the key/value pairs the auth code wants to send * directly to the PDU, since they could in theory be large. */ rc = acl_send_key_val(auth_client, keytype, &present, auth_value, max_length); if ((rc == AUTH_STATUS_NO_ERROR) && present) { /* actually fill in the key */ strncpy(&data[pdu_length], key, key_length); pdu_length += key_length; data[pdu_length] = '='; pdu_length++; /* * adjust the PDU's data segment length * to include the value and trailing NUL */ pdu_length += strlen(auth_value) + 1; hton24(pdu->dlength, pdu_length); }}static intfill_security_params_text(struct iscsi_session *session, struct iscsi_hdr *pdu, struct iscsi_acl *auth_client, char *data, int max_data_length, int *transit){ int keytype = AUTH_KEY_TYPE_NONE; int rc = acl_send_transit_bit(auth_client, transit); /* see if we're ready for a stage change */ if (rc != AUTH_STATUS_NO_ERROR) return 0; if (*transit) { /* * discovery sessions can go right to full-feature phase, * unless they want to non-standard values for the few relevant * keys, or want to offer vendor-specific keys */ if (session->type == ISCSI_SESSION_TYPE_DISCOVERY) if ((session->header_digest != ISCSI_DIGEST_NONE) || (session->data_digest != ISCSI_DIGEST_NONE) || (session-> max_recv_data_segment_len != DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH) || session->vendor_specific_keys) session->next_stage = ISCSI_OP_PARMS_NEGOTIATION_STAGE; else session->next_stage = ISCSI_FULL_FEATURE_PHASE; else session->next_stage = ISCSI_OP_PARMS_NEGOTIATION_STAGE; } else session->next_stage = ISCSI_SECURITY_NEGOTIATION_STAGE; /* enumerate all the keys the auth code might want to send */ while (acl_get_next_key_type(&keytype) == AUTH_STATUS_NO_ERROR) enum_auth_keys(auth_client, pdu, data, max_data_length, keytype); return 1;}/** * iscsi_make_login_pdu - Prepare the login pdu to be sent to iSCSI target. * @session: session for which login is initiated. * @pdu: login header * @data: contains text keys to be negotiated during login * @max_data_length: data size * * Description: * Based on whether authentication is enabled or not, corresponding text * keys are filled up in login pdu. * **/static intiscsi_make_login_pdu(struct iscsi_session *session, struct iscsi_hdr *pdu, char *data, int max_data_length){ int transit = 0; int ret; struct iscsi_login_hdr *login_pdu = (struct iscsi_login_hdr *)pdu; struct iscsi_acl *auth_client = session->auth_client_block ? session->auth_client_block : NULL; /* initialize the PDU header */ memset(login_pdu, 0, sizeof(*login_pdu)); login_pdu->opcode = ISCSI_OP_LOGIN_CMD | ISCSI_OP_IMMEDIATE; login_pdu->cid = 0; memcpy(login_pdu->isid, session->isid, sizeof(session->isid)); login_pdu->tsih = 0; login_pdu->cmdsn = htonl(session->cmd_sn); /* don't increment on immediate */ login_pdu->min_version = ISCSI_DRAFT20_VERSION; login_pdu->max_version = ISCSI_DRAFT20_VERSION; /* we have to send 0 until full-feature stage */ login_pdu->expstatsn = htonl(session->exp_stat_sn); /* * the very first Login PDU has some additional requirements, * and we need to decide what stage to start in. */ if (session->current_stage == ISCSI_INITIAL_LOGIN_STAGE) { if (session->initiator_name && session->initiator_name[0]) { if (!iscsi_add_text(session, pdu, data, max_data_length, "InitiatorName", session->initiator_name)) return 0; } else { iscsi_host_err(session, "InitiatorName is required " "on the first Login PDU\n"); return 0; } if (session->initiator_alias && session->initiator_alias[0]) { if (!iscsi_add_text(session, pdu, data, max_data_length, "InitiatorAlias", session->initiator_alias)) return 0; } if ((session->target_name && session->target_name[0]) && (session->type == ISCSI_SESSION_TYPE_NORMAL)) { if (!iscsi_add_text(session, pdu, data, max_data_length, "TargetName", session->target_name)) return 0; } if (!iscsi_add_text(session, pdu, data, max_data_length, "SessionType", (session->type == ISCSI_SESSION_TYPE_DISCOVERY) ? "Discovery" : "Normal")) return 0; if (auth_client) /* we're prepared to do authentication */ session->current_stage = session->next_stage = ISCSI_SECURITY_NEGOTIATION_STAGE; else /* can't do any authentication, skip that stage */ session->current_stage = session->next_stage = ISCSI_OP_PARMS_NEGOTIATION_STAGE; } /* fill in text based on the stage */ switch (session->current_stage) { case ISCSI_OP_PARMS_NEGOTIATION_STAGE:{ ret = fill_op_params_text(session, pdu, data, max_data_length, &transit); if (!ret) return ret; break; } case ISCSI_SECURITY_NEGOTIATION_STAGE:{ ret = fill_security_params_text(session, pdu, auth_client, data, max_data_length, &transit); if (!ret) return ret; break; } case ISCSI_FULL_FEATURE_PHASE: iscsi_host_err(session, "Can't send login PDUs in full " "feature phase\n"); return 0; default: iscsi_host_err(session, "Can't send login PDUs in unknown " "stage %d\n", session->current_stage); return 0; } /* fill in the flags */ login_pdu->flags = 0; login_pdu->flags |= session->current_stage << 2; if (transit) { /* transit to the next stage */ login_pdu->flags |= session->next_stage; login_pdu->flags |= ISCSI_FLAG_LOGIN_TRANSIT; } else /* next == current */ login_pdu->flags |= session->current_stage; return 1;}static enum iscsi_login_statuscheck_for_authentication(struct iscsi_session *session, struct iscsi_acl **auth_client){ /* prepare for authentication */ if (acl_init(TYPE_INITIATOR, session) != AUTH_STATUS_NO_ERROR) { iscsi_host_err(session, "Couldn't initialize authentication\n"); return LOGIN_FAILED; } *auth_client = session->auth_client_block; if (session->username && (acl_set_user_name(*auth_client, session->username) != AUTH_STATUS_NO_ERROR)) { iscsi_host_err(session, "Couldn't set username\n"); goto end; } if (session->password && (acl_set_passwd(*auth_client, session->password, session->password_length) != AUTH_STATUS_NO_ERROR)) { iscsi_host_err(session, "Couldn't set password\n"); goto end; } if (acl_set_ip_sec(*auth_client, 1) != AUTH_STATUS_NO_ERROR) { iscsi_host_err(session, "Couldn't set IPSec\n"); goto end; } if (acl_set_auth_rmt(*auth_client, session->bidirectional_auth) != AUTH_STATUS_NO_ERROR) { iscsi_host_err(session, "Couldn't set remote authentication\n"); goto end; } return LOGIN_OK; end: if (*auth_client && acl_finish(*auth_client) != AUTH_STATUS_NO_ERROR) iscsi_host_err(session, "Login failed, error finishing " "auth_client\n"); *auth_client = NULL; return LOGIN_FAILED;}static enum iscsi_login_statuscheck_status_login_response(struct iscsi_session *session, struct iscsi_login_rsp_hdr *login_rsp_pdu, char *data, int max_data_length, int *final){ enum iscsi_login_status ret; switch (login_rsp_pdu->status_class) { case ISCSI_STATUS_CLS_SUCCESS: /* process this response and possibly continue sending PDUs */ ret = iscsi_process_login_response(session, login_rsp_pdu, data, max_data_length); if (ret != LOGIN_OK) /* pass back whatever * error we discovered */ *final = 1; break; case ISCSI_STATUS_CLS_REDIRECT: /* * we need to process this response to get the * TargetAddress of the redirect, but we don't care * about the return code. */ iscsi_process_login_response(session, login_rsp_pdu, data, max_data_length); ret = LOGIN_OK; *final = 1; case ISCSI_STATUS_CLS_INITIATOR_ERR: if (login_rsp_pdu->status_detail == ISCSI_LOGIN_STATUS_AUTH_FAILED) { iscsi_host_err(session, "Login failed to authenticate " "with target %s\n", session->target_name); } ret = LOGIN_OK; *final = 1; default: /* * some sort of error, login terminated unsuccessfully, * though this function did it's job. * the caller must check the status_class and * status_detail and decide what to do next. */ ret = LOGIN_OK; *final = 1; } return ret;}/** * iscsi_login - attempt to login to the target. * @session: login is initiated over this session * @buffer: holds login pdu * @bufsize: size of login pdu * @status_class: holds either success or failure as status of login * @status_detail: contains details based on the login status * * Description: * The caller must check the status class to determine if the login * succeeded. A return of 1 does not mean the login succeeded, it just * means this function worked, and the status class is valid info. * This allows the caller to decide whether or not to retry logins, so * that we don't have any policy logic here. **/enum iscsi_login_statusiscsi_login(struct iscsi_session *session, char *buffer, size_t bufsize, uint8_t *status_class, uint8_t *status_detail){ struct iscsi_acl *auth_client = NULL; struct iscsi_hdr pdu; struct iscsi_login_rsp_hdr *login_rsp_pdu; char *data; int received_pdu = 0; int max_data_length; int timeout = 0; int final = 0; enum iscsi_login_status ret = LOGIN_FAILED; /* prepare the session */ session->cmd_sn = 1; session->exp_cmd_sn = 1; session->max_cmd_sn = 1; session->exp_stat_sn = 0; session->current_stage = ISCSI_INITIAL_LOGIN_STAGE; session->partial_response = 0; if (session->auth_client_block) { ret = check_for_authentication(session, &auth_client); if (ret != LOGIN_OK) return ret; } /* * exchange PDUs until the login stage is complete, or an error occurs */ do { final = 0; timeout = 0; login_rsp_pdu = (struct iscsi_login_rsp_hdr *)&pdu; ret = LOGIN_FAILED; memset(buffer, 0, bufsize); data = buffer; max_data_length = bufsize; /* * pick the appropriate timeout. If we know the target has * responded before, and we're in the security stage, we use a * longer timeout, since the authentication alogorithms can * take a while, especially if the target has to go talk to a * tacacs or RADIUS server (which may or may not be * responding). */ if (received_pdu && (session->current_stage == ISCSI_SECURITY_NEGOTIATION_STAGE)) timeout = session->auth_timeout; else timeout = session->login_timeout; /* * fill in the PDU header and text data based on the login * stage that we're in */ if (!iscsi_make_login_pdu(session, &pdu, data, max_data_length)) { iscsi_host_err(session, "login failed, couldn't make " "a login PDU\n"); ret = LOGIN_FAILED; goto done; } /* send a PDU to the target */ if (!iscsi_send_pdu(session, &pdu, ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, timeout)) { /* * FIXME: caller might want us to distinguish I/O * error and timeout. Might want to switch portals on * timeouts, but * not I/O errors. */ iscsi_host_err(session, "Login I/O error, failed to " "send a PDU\n"); ret = LOGIN_IO_ERROR; goto done; } /* read the target's response into the same buffer */ if (!iscsi_recv_pdu(session, &pdu, ISCSI_DIGEST_NONE, data, max_data_length, ISCSI_DIGEST_NONE, timeout)) { /* * FIXME: caller might want us to distinguish I/O * error and timeout. Might want to switch portals on * timeouts, but not I/O errors. */ iscsi_host_err(session, "Login I/O error, failed to " "receive a PDU\n"); ret = LOGIN_IO_ERROR; goto done; } received_pdu = 1; /* check the PDU response type */ if (pdu.opcode == (ISCSI_OP_LOGIN_RSP | 0xC0)) { /* * it's probably a draft 8 login response, * which we can't deal with */ iscsi_host_err(session, "Received iSCSI draft 8 login " "response opcode 0x%x, expected draft " "20 login response 0x%2x\n", pdu.opcode, ISCSI_OP_LOGIN_RSP); ret = LOGIN_VERSION_MISMATCH; goto done; } else if (pdu.opcode != ISCSI_OP_LOGIN_RSP) { ret = LOGIN_INVALID_PDU; goto done; } /* * give the caller the status class and detail from the last * login response PDU received */ if (status_class) *status_class = login_rsp_pdu->status_class; if (status_detail) *status_detail = login_rsp_pdu->status_detail; ret = check_status_login_response(session, login_rsp_pdu, data, max_data_length, &final); if (final) goto done; } while (session->current_stage != ISCSI_FULL_FEATURE_PHASE); ret = LOGIN_OK; done: if (auth_client && acl_finish(auth_client) != AUTH_STATUS_NO_ERROR) { iscsi_host_err(session, "Login failed, error finishing " "auth_client\n"); if (ret == LOGIN_OK) ret = LOGIN_FAILED; } return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -