iscsi_login.c revision 9e8164f5f5658c6e38a931780d499f5cb622908a
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/iscsi_protocol.h>
#include <iscsitgt_impl.h>
#include "queue.h"
#include "iscsi_conn.h"
#include "iscsi_sess.h"
#include "iscsi_login.h"
#include "iscsi_provider_impl.h"
#include "utility.h"
#include "target.h"
#include "isns_client.h"
typedef enum auth_action {
/*
* Forward declarations
*/
int err_code);
/*
* iscsi_null_callback - This callback may be used under certain
* conditions when authenticating a target, but I'm not sure what
* we need to do here.
*/
/* ARGSUSED */
static void
{
}
/*
* iscsi_find_key_value -
*/
static int
char **value_start, char **value_end)
{
if (value_start)
*value_start = NULL;
if (value_end)
/*
* make sure they contain the same bytes
*/
while (*str) {
return (0);
}
if (*text == '\0') {
return (0);
}
return (0);
}
str++;
text++;
}
(*text == '\0') ||
(*text != ISCSI_TEXT_SEPARATOR)) {
return (0);
}
/*
* find the value
*/
/*
* find the end of the value
*/
text++;
if (value_start)
*value_start = value;
if (value_end)
return (1);
}
{
char debug[128];
int debug_status = 0;
int errcode = 0;
int text_length = 0;
int keytype = 0;
int transit = 0;
int rc = 0;
"Header to small");
return (False);
}
"CON%x Wrong OP code for state (Got 0x%x, Expected 0x%x)",
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
conn_state(c, T7);
return (True);
}
if (ISCSI_LOGIN_COMMAND_ENABLED()) {
char nil = '\0';
info.uip_datasn = 0;
}
conn_state(c, T7);
return (True);
}
/*
* Is this a new session or an attempt to add a connection to
* an existing session.
*/
/* Multiple connections per session not handled right now */
conn_state(c, T7);
return (True);
}
"CON%x Failed make_login_response", c->c_num);
return (False);
}
/* default is ISCSI_FLAG_LOGIN_TRANSIT, not good for login */
"CON%x Version: Active %d, min %d, max %d", c->c_num,
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
conn_state(c, T7);
return (True);
}
"CON%x Continuation pkt", c->c_num);
}
NULL;
c->auth_text_length = 0;
/*
* Grab the parameters and create the response
* text.
*/
"CON%x SecurityNegotiation: parse_text"
" failed", c->c_num);
conn_state(c, T7);
break;
}
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
"CON%x SecurityNegotiation: invalid I "
"or T", c->c_num);
conn_state(c, T7);
break;
}
if (auth_action == LOGIN_NO_AUTH) {
"None");
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_TARGET_ERR << 8) |
"CON%x Norm: Failed to add AuthMethod=None",
c->c_num);
debug);
conn_state(c, T7);
}
break;
}
if (auth_action == LOGIN_DROP) {
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
"CON%x SecurityNegotiation: access denied",
c->c_num);
conn_state(c, T7);
break;
}
if (iscsiAuthClientRecvBegin(auth_client) !=
"login failed - authentication receive failed",
c->c_num);
break;
}
transit) != iscsiAuthStatusNoError) {
"iscsi connection(%u) login failed - "
"authentication transmit failed", c->c_num);
break;
}
/*
* scan the text data
*/
/*
* skip any NULs separating each text key=value pair
*/
text++;
}
break;
}
while (iscsiAuthClientGetNextKeyType(&keytype) ==
char *key =
(char *)iscsiAuthClientGetKeyName(keytype);
if ((key) &&
!= iscsiAuthStatusNoError) {
sizeof (debug),
"iscsi connection(%u) login"
"failed - can't accept "
"%s in security stage",
}
goto more_text;
}
}
}
case iscsiAuthStatusContinue:
/*
* continue sending PDUs
*/
break;
case iscsiAuthStatusPass:
c->c_auth_pass = 1;
break;
/*
* this should only occur if we were authenticating the
* target, which we don't do yet, so treat this as an
* error.
*/
case iscsiAuthStatusNoError:
/*
* treat this as an error, since we should get a
* different code
*/
case iscsiAuthStatusError:
case iscsiAuthStatusFail:
default:
debug_status = 0;
&debug_status);
"iscsi connection(%u) authentication failed (%s)",
debug_status));
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
conn_state(c, T7);
break;
}
break;
/*
* see if we're ready for a stage change
*/
if (rc == iscsiAuthStatusNoError) {
if (transit) {
}
} else {
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
"CON%x SecurityNegotiation: wants", c->c_num);
conn_state(c, T7);
}
/*
* enumerate all the keys the auth code might want to send
*/
while (iscsiAuthClientGetNextKeyType(&keytype) ==
int present = 0;
char *auth_value = NULL;
unsigned int max_length = ISCSI_DEFAULT_MAX_XMIT_SEG_LEN
/*
* send directly to the PDU, since they could in
* theory be large.
*/
NULL) {
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_TARGET_ERR << 8) |
"CON%x Norm: Failed alloc auth_key %S=%s",
debug);
conn_state(c, T7);
break;
}
debug);
key, auth_value);
send_login_reject(c, &lh,
8) |
"CON%x Norm: Failed to add %S=%s",
conn_state(c, T7);
}
}
if (auth_value != NULL)
}
break;
/*
* Gather up the parameters sent across and build a response
* based on any selection required.
*/
"CON%x Norm: parse_text failed", c->c_num);
conn_state(c, T7);
break;
}
/*
* If the connection hasn't passed authentication and
* it's a normal session see if this connection MUST
* have gone through authentication first. If the
* initiator has a CHAP secret stored that means we
* want to validate.
*/
if ((c->c_auth_pass == 0) &&
NULL) {
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_TARGET_ERR << 8) |
"CON%x No target node in login",
c->c_num);
debug);
conn_state(c, T7);
}
/*
* check_access will return True if the initiator
* is required to use CHAP authentication. So if
* true and we're here it means that the initiator
* is trying to skip the authentication step.
*/
False) {
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
"CON%x Authentication required for %s",
debug);
conn_state(c, T7);
}
}
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
"CON%x Norm: bad I or T", c->c_num);
conn_state(c, T7);
break;
}
/*
* We accept transition and stage information as is
* and echo it back because at this point there's no need
* to send a parameter to the Initiator and expect a
* reply.
*/
break;
case ISCSI_FULL_FEATURE_PHASE:
/* can't jump directly to full feature phase */
"CON%x Protocol error: wrong stage", c->c_num);
send_login_reject(c, &lh,
(ISCSI_STATUS_CLASS_INITIATOR_ERR << 8) |
conn_state(c, T7);
break;
default:
/* just drop the connection since we don't know what's up */
break;
}
if (ISCSI_LOGIN_RESPONSE_ENABLED()) {
char nil = '\0';
info.uip_datasn = 0;
}
conn_state(c, T5);
/*
* At this point we've completed the negotiation
* of all login parameters. Now we need to perform
* some quick boundary checks and then send a couple
* pieces of information to STE for it's use.
*/
c->c_max_recv_data);
}
}
return (rval);
}
/*
* check_for_valid_I_T -- check to see if we have valid names
*
* This routine checks to see if we have received a valid InitiatorName
* and TargetName which is the bare minimum which an Initiator must send
* across during the login phase.
*/
static Boolean_t
{
iscsi_sess_t *s = c->c_sess;
if (s->s_type == SessionDiscovery)
else
}
static iscsi_login_rsp_hdr_t *
{
/* don't except existing sessions for now */
return (NULL);
r = (iscsi_login_rsp_hdr_t *)calloc(sizeof (*r), sizeof (char));
if (r == NULL)
return (NULL);
r->opcode = ISCSI_OP_LOGIN_RSP;
r->max_version = ISCSI_MAX_VERSION;
(void) pthread_mutex_lock(&c->c_mutex);
(void) pthread_mutex_unlock(&c->c_mutex);
/* ---- cmdsn is not advanced during login ---- */
return (r);
}
static void
{
return;
if (ISCSI_LOGIN_RESPONSE_ENABLED()) {
char nil = '\0';
info.uip_datasn = 0;
}
free(r);
}
static auth_action_t
{
char *szIniAlias = NULL;
char *szIscsiName = NULL;
char *szChapName = NULL;
char *szChapSecret = NULL;
int comp = 0;
sess_auth->password_length_in = 0;
/* Load alias, iscsi-name, chap-name, chap-secret from config file */
xnInitiator)) != NULL) {
&szIniAlias);
&szIscsiName) == True) {
szIscsiName = NULL;
if (comp == 0) {
&szChapName) == True) {
/*CSTYLED*/
}
&szChapSecret) == True) {
/*CSTYLED*/
}
break;
}
}
}
if (s->s_type == SessionDiscovery) {
return (LOGIN_NO_AUTH);
}
/*
* Should not happen for non-discovery session
*/
return (LOGIN_DROP);
}
/*
* If iSNS enabled set LOGIN_AUTH
*/
if (isns_enabled() == True) {
if (sess_auth->password_length_in == 0)
return (LOGIN_NO_AUTH);
return (LOGIN_AUTH);
}
/*
* If no acc_list for current target, transit.
* If acc_list exists for the target, and
* If the initiator not in the list, drop it.
* If the initiator in the list, and
* If no CHAP secret for the initiator, transit.
* If a CHAP secret exists for the initiator, it must be authed.
*/
return (LOGIN_DROP);
}
szIscsiName = NULL;
if (comp == 0) {
XML_ELEMENT_ACLLIST, 0)) == NULL) {
/*
* No acl_list found, return True for no auth
*/
return (LOGIN_NO_AUTH);
}
/*
* This target has an access_list. Now compare
* those entries against the initiator who started
* this session.
*/
xnInitiator = NULL;
continue;
/*
* Found the initiator in acl-list,
* authentication needed
*/
if (sess_auth->password_length_in == 0)
return (LOGIN_NO_AUTH);
else
return (LOGIN_AUTH);
}
}
/*
* Acl-list exists, while the initiator is not found in
* the list, we should drop the connection
*/
return (LOGIN_DROP);
}
}
/* False means Need authentication */
return (LOGIN_AUTH);
}