📄 iscsi-login.c
字号:
/* * iSCSI login library * Copyright (C) 2001 Cisco Systems, Inc. * maintained by linux-iscsi@cisco.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * See the file COPYING included with this distribution for more details. * * $Id: iscsi-login.c,v 1.19 2002/10/08 20:32:33 smferris Exp $ * */#include "iscsi-platform.h"#include "iscsi-protocol.h"#include "iscsi-io.h"#include "iscsi-login.h"struct IscsiHdr *iscsi_align_pdu(iscsi_session_t *session, unsigned char *buffer, int buffersize){ struct IscsiHdr *header; unsigned long addr = (unsigned long)buffer; /* find a buffer location guaranteed to be reasonably aligned for the header */ addr += (addr % sizeof(*header)); header = (struct IscsiHdr *)addr; return header;}/* caller is assumed to be well-behaved and passing NUL terminated strings */int iscsi_add_text(iscsi_session_t *session, struct IscsiHdr *pdu, char *data, int max_data_length, char *param, char *value){ int param_len = strlen(param); int value_len = strlen(value); int length = param_len + 1 + value_len + 1; /* param, separator, value, and trailing NUL */ int pdu_length = ntoh24(pdu->dlength); char *text = data; char *end = data + max_data_length; char *pdu_text; /* find the end of the current text */ text += pdu_length; pdu_text = text; pdu_length += length; if (text + length >= end) { logmsg(AS_NOTICE, "failed to add login text '%s=%s'\n", param, value); return 0; } /* param */ iscsi_strncpy(text, param, param_len); text += param_len; /* separator */ *text++ = ISCSI_TEXT_SEPARATOR; /* value */ strncpy(text, value, value_len); text += value_len; /* NUL */ *text++ = '\0'; /* update the length in the PDU header */ hton24(pdu->dlength, pdu_length); return 1;}int iscsi_find_key_value(char *param, char *pdu, char *pdu_end, char **value_start, char **value_end){ char *str = param; char *text = pdu; char *value = NULL; if (value_start) *value_start = NULL; if (value_end) *value_end = NULL; /* make sure they contain the same bytes */ while (*str) { if (text >= pdu_end) return 0; if (*text == '\0') return 0; if (*str != *text) return 0; str++; text++; } if ((text >= pdu_end) || (*text == '\0') || (*text != ISCSI_TEXT_SEPARATOR)) { return 0; } /* find the value */ value = text + 1; /* find the end of the value */ while ((text < pdu_end) && (*text)) text++; if (value_start) *value_start = value; if (value_end) *value_end = text; return 1;}/* we never authenticate the target, so we don't need an AuthClient callback that does anything. * It would only be needed if we were authenticating the target, which we currently never do. */static void null_callback(void *user_handle, void *message_handle, int auth_status){}/* this assumes the text data is always NUL terminated. The caller can always arrange for that * by using a slightly larger buffer than the max PDU size, and then appending a NUL to the PDU. */int iscsi_process_login_response(iscsi_session_t *session, struct IscsiLoginRspHdr *login_rsp_pdu, char *data, int max_data_length){ int tbit = login_rsp_pdu->tbit; char *text = data; char *end; end = text + ntoh24(login_rsp_pdu->dlength) + 1; if (end >= (data + max_data_length)) { logmsg(AS_ERROR, "process_login_response - buffer too small to guarantee NUL termination\n"); return 0; } /* guarantee a trailing NUL */ *end = '\0'; /* if the response status was success, sanity check the response */ if (login_rsp_pdu->status_class == STATUS_CLASS_SUCCESS) { /* check the active version */ if (login_rsp_pdu->active_version != ISCSI_DRAFT16_VERSION) { logmsg(AS_ERROR, "received incompatible active iSCSI version 0x%02x during login, expected version 0x%02x\n", login_rsp_pdu->active_version, ISCSI_DRAFT16_VERSION); return 0; } /* make sure the current phase matches */ if (login_rsp_pdu->curr != session->current_phase) { logmsg(AS_ERROR, "current phase mismatch, session %d, response %d\n", session->current_phase, login_rsp_pdu->curr); return 0; } /* make sure that we're actually advancing if the T-bit is set */ if (login_rsp_pdu->tbit && (login_rsp_pdu->next <= session->current_phase)) { logmsg(AS_ERROR, "current phase %d, target wants to go to phase %d, but we want to go to phase %d\n", session->current_phase, login_rsp_pdu->next, session->next_phase); return 0; } } if (session->current_phase == ISCSI_SECURITY_NEGOTIATION_PHASE) { if (iscsiAuthClientRecvBegin(session->auth_client) != iscsiAuthStatusNoError) { logmsg(AS_ERROR, "authClientRecvBegin failed\n"); return 0; } if (iscsiAuthClientRecvTransitBit(session->auth_client, tbit) != iscsiAuthStatusNoError) { logmsg(AS_ERROR, "authClientRecvTransitBit failed\n"); return 0; } } /* scan the text data */ more_text: while (text && (text < end)) { char *value = NULL; char *value_end = NULL; /* skip any NULs separating each text key=value pair */ while ((text < end) && (*text == '\0')) text++; if (text >= end) break; /* handle keys appropriate for each phase */ switch (session->current_phase) { case ISCSI_SECURITY_NEGOTIATION_PHASE: { /* a few keys are possible in Security phase which the * auth code doesn't care about, but which we might * want to see, or at least not choke on. */ if (iscsi_find_key_value("TargetAlias", text, end, &value, &value_end)) { size_t size = sizeof(session->TargetAlias); if ((value_end - value) < size) size = value_end - value; memcpy(session->TargetAlias, value, size); session->TargetAlias[sizeof(session->TargetAlias)-1] = '\0'; text = value_end; } else if (iscsi_find_key_value("TargetAddress", text, end, &value, &value_end)) { /* if possible, change the session's ip_address and port to the new TargetAddress */ if (session->update_address && session->update_address(session, value)) { text = value_end; } else { logmsg(AS_ERROR, "session %p can't handle redirection to %s\n", session, value); return 0; } } else if (iscsi_find_key_value("TargetPortalGroupTag", text, end, &value, &value_end)) { /* We should have already obtained this via discovery. * We've already picked an isid, so the most we can do is confirm we reached * the portal group we were expecting to. */ int tag = iscsi_strtoul(value, NULL, 0); if (session->portal_group_tag >= 0) { if (tag != session->portal_group_tag) { logmsg(AS_ERROR, "session %p portal group tag mismatch, expected %u, actual %u\n", session, session->portal_group_tag, tag); return 0; } } else { /* we now know the tag */ session->portal_group_tag = tag; } text = value_end; } else { /* any key we don't recognize either goes to the auth code, or we choke on it */ int keytype = iscsiAuthKeyTypeNone; while (iscsiAuthClientGetNextKeyType(&keytype) == iscsiAuthStatusNoError) { char *key = (char *)iscsiAuthClientGetKeyName(keytype); if (key && iscsi_find_key_value(key, text, end, &value, &value_end)) { if (iscsiAuthClientRecvKeyValue(session->auth_client, keytype, value) != iscsiAuthStatusNoError) { logmsg(AS_ERROR, "authentication failed to process %s\n", text); return 0; } text = value_end; goto more_text; } } logmsg(AS_ERROR, "security phase failed to recognize text %s\n", text); return 0; } break; } case ISCSI_OP_PARMS_NEGOTIATION_PHASE: { /* FIXME: they're making base64 an encoding option for * all numbers in draft13, since some security * protocols use large numbers, and it was somehow * considered "simpler" to let them be used for any * number anywhere. */ if (iscsi_find_key_value("TargetAlias", text, end, &value, &value_end)) { size_t size = sizeof(session->TargetAlias); if ((value_end - value) < size) size = value_end - value; memcpy(session->TargetAlias, value, size); session->TargetAlias[sizeof(session->TargetAlias)-1] = '\0'; text = value_end; } else if (iscsi_find_key_value("TargetAddress", text, end, &value, &value_end)) { if (session->update_address && session->update_address(session, value)) { text = value_end; } else { logmsg(AS_ERROR, "session %p can't handle redirection to %s\n", session, value);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -