Home | History | Annotate | Download | only in iscsit
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/cpuvar.h>
     27 #include <sys/types.h>
     28 #include <sys/conf.h>
     29 #include <sys/file.h>
     30 #include <sys/ddi.h>
     31 #include <sys/sunddi.h>
     32 #include <sys/modctl.h>
     33 
     34 #include <sys/socket.h>
     35 #include <sys/strsubr.h>
     36 #include <sys/sysmacros.h>
     37 
     38 #include <sys/stmf.h>
     39 #include <sys/stmf_ioctl.h>
     40 #include <sys/portif.h>
     41 #include <sys/idm/idm.h>
     42 #include <sys/idm/idm_text.h>
     43 #include <iscsit.h>
     44 #include <iscsit_auth.h>
     45 
     46 static kv_status_t
     47 iscsit_select_auth(iscsit_conn_t *ict, nvpair_t *nvp,
     48     const idm_kv_xlate_t *ikvx);
     49 
     50 static kv_status_t
     51 auth_propose_chap(iscsit_conn_t *ict, nvpair_t *nvp,
     52     const idm_kv_xlate_t *ikvx);
     53 
     54 static kv_status_t
     55 auth_chap_select_alg(iscsit_conn_t *ict, nvpair_t *nvp,
     56     const idm_kv_xlate_t *ikvx);
     57 
     58 static kv_status_t
     59 auth_chap_recv_n(iscsit_conn_t *ict, nvpair_t *nvp,
     60     const idm_kv_xlate_t *ikvx);
     61 
     62 static kv_status_t
     63 auth_chap_recv_r(iscsit_conn_t *ict, nvpair_t *nvp,
     64     const idm_kv_xlate_t *ikvx);
     65 
     66 static kv_status_t
     67 auth_chap_recv_i(iscsit_conn_t *ict, nvpair_t *nvp,
     68     const idm_kv_xlate_t *ikvx);
     69 
     70 static kv_status_t
     71 auth_chap_recv_c(iscsit_conn_t *ict, nvpair_t *nvp,
     72     const idm_kv_xlate_t *ikvx);
     73 
     74 static kv_status_t
     75 iscsit_auth_propose(iscsit_conn_t *ict, nvpair_t *nvp,
     76     const idm_kv_xlate_t *ikvx);
     77 
     78 static kv_status_t
     79 iscsit_auth_expect_key(iscsit_conn_t *ict, nvpair_t *nvp,
     80     const idm_kv_xlate_t *ikvx);
     81 
     82 static kv_status_t
     83 auth_chap_expect_r(iscsit_conn_t *ict, nvpair_t *nvp,
     84     const idm_kv_xlate_t *ikvx);
     85 
     86 static kv_status_t
     87 auth_chap_done(iscsit_conn_t *ict, nvpair_t *nvp,
     88     const idm_kv_xlate_t *ikvx);
     89 
     90 static kv_status_t
     91 iscsit_auth_gen_challenge(iscsit_conn_t *ict);
     92 
     93 static kv_status_t
     94 iscsit_auth_gen_response(iscsit_conn_t *ict);
     95 
     96 typedef struct {
     97 	iscsit_auth_phase_t	phase;
     98 	iscsikey_id_t		kv_id;
     99 	iscsit_auth_handler_t	handler;
    100 } auth_phase_entry_t;
    101 
    102 /*
    103  * This table defines all authentication phases which have valid
    104  * handler. The entries which have a non-zero key index are for
    105  * a key/value pair handling when a key/value is being received,
    106  * the rest of entries are for target checking the authentication
    107  * phase after all key/value pair(s) are handled.
    108  */
    109 static const auth_phase_entry_t	apet[] = {
    110 	/* by key */
    111 	{ AP_AM_UNDECIDED,	KI_AUTH_METHOD,	iscsit_select_auth },
    112 	{ AP_AM_PROPOSED,	KI_CHAP_A,	auth_propose_chap  },
    113 
    114 	{ AP_CHAP_A_WAITING,	KI_CHAP_A,	auth_chap_select_alg },
    115 	{ AP_CHAP_R_WAITING,	KI_CHAP_N,	auth_chap_recv_n },
    116 	{ AP_CHAP_R_WAITING,	KI_CHAP_R,	auth_chap_recv_r },
    117 	{ AP_CHAP_R_WAITING,	KI_CHAP_I,	auth_chap_recv_i },
    118 	{ AP_CHAP_R_WAITING,	KI_CHAP_C,	auth_chap_recv_c },
    119 	{ AP_CHAP_R_RCVD,	KI_CHAP_N,	auth_chap_recv_n },
    120 	{ AP_CHAP_R_RCVD,	KI_CHAP_R,	auth_chap_recv_r },
    121 	{ AP_CHAP_R_RCVD,	KI_CHAP_I,	auth_chap_recv_i },
    122 	{ AP_CHAP_R_RCVD,	KI_CHAP_C,	auth_chap_recv_c },
    123 
    124 	/* by target */
    125 	{ AP_AM_UNDECIDED,	0,		iscsit_auth_propose },
    126 	{ AP_AM_DECIDED,	0,		iscsit_auth_expect_key },
    127 
    128 	{ AP_CHAP_A_RCVD,	0,		auth_chap_expect_r },
    129 	{ AP_CHAP_R_RCVD,	0,		auth_chap_done }
    130 };
    131 
    132 typedef struct {
    133 	iscsit_auth_method_t	am_id;
    134 	char			*am_name;
    135 } auth_id_name_t;
    136 
    137 /*
    138  * a table of mapping from the authentication index to name.
    139  */
    140 static const auth_id_name_t aint[] = {
    141 	{ AM_CHAP,	"CHAP" },
    142 	{ AM_NONE,	"None" },
    143 	/* { AM_KRB5,	"KRB5" }, */	/* Not supported */
    144 	/* { AM_SPKM1,	"SPKM1" }, */	/* Not supported */
    145 	/* { AM_SPKM2,	"SPKM2" }, */	/* Not supported */
    146 	/* { AM_SRP,	"SRP" },  */	/* Not supported */
    147 };
    148 
    149 #define	ARRAY_LENGTH(ARRAY)	(sizeof (ARRAY) / sizeof (ARRAY[0]))
    150 
    151 /*
    152  * get the authentication method name for the method id.
    153  */
    154 static const char *
    155 am_id_to_name(int id)
    156 {
    157 	int			i;
    158 	const auth_id_name_t	*p;
    159 	i = 0;
    160 	while (i < ARRAY_LENGTH(aint)) {
    161 		p = &(aint[i]);
    162 		if (id == p->am_id) {
    163 			return (p->am_name);
    164 		}
    165 		i ++;
    166 	}
    167 
    168 	return (NULL);
    169 }
    170 
    171 /*
    172  * Look for an apporiate function handler which is defined for
    173  * current authentication phase and matches the key which is
    174  * being handled. The key index is passed in as zero when it
    175  * is looking for an handler for checking the authentication phase
    176  * after all security keys are handled.
    177  */
    178 iscsit_auth_handler_t
    179 iscsit_auth_get_handler(iscsit_auth_client_t *client, iscsikey_id_t kv_id)
    180 {
    181 	iscsit_auth_phase_t		phase = client->phase;
    182 	int				i;
    183 	const auth_phase_entry_t	*p;
    184 
    185 	i = 0;
    186 	p = NULL;
    187 	while (i < ARRAY_LENGTH(apet)) {
    188 		p = &(apet[i]);
    189 		if (phase == p->phase &&
    190 		    kv_id == p->kv_id) {
    191 			return (p->handler);
    192 		}
    193 		i ++;
    194 	}
    195 
    196 	/* No handler can be found, it must be an invalid requst. */
    197 	return (NULL);
    198 }
    199 
    200 /*
    201  * Select an authentication method from a list of values proposed
    202  * by initiator. After a valid method is selected, shift the
    203  * authentication phase to AP_AM_DECIDED.
    204  */
    205 static kv_status_t
    206 iscsit_select_auth(iscsit_conn_t *ict, nvpair_t *nvp,
    207     const idm_kv_xlate_t *ikvx)
    208 {
    209 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    210 	conn_auth_t		*auth = &lsm->icl_auth;
    211 	iscsit_auth_method_t	*am_list = &auth->ca_method_valid_list[0];
    212 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    213 	int			nvrc;
    214 	kv_status_t		kvrc;
    215 	nvpair_t		*am_choice;
    216 	char			*am;
    217 	const char		*am_name;
    218 	const char		*text;
    219 	iscsit_auth_method_t	am_id;
    220 	int			i;
    221 
    222 	client->phase = AP_AM_DECIDED;
    223 
    224 	/* select a valid authentication method */
    225 	am_choice = idm_get_next_listvalue(nvp, NULL);
    226 	while (am_choice != NULL) {
    227 		nvrc = nvpair_value_string(am_choice, &am);
    228 		ASSERT(nvrc == 0);
    229 
    230 		i = 0;
    231 		am_id = am_list[i];
    232 		while (am_id != 0) {
    233 			am_name = am_id_to_name(am_id);
    234 			if (strcasecmp(am, am_name) == 0) {
    235 				text = am;
    236 				goto am_decided;
    237 			}
    238 			i++;
    239 			am_id = am_list[i];
    240 		}
    241 		am_choice = idm_get_next_listvalue(nvp, am_choice);
    242 	}
    243 
    244 	/* none of authentication method is valid */
    245 	am_id = 0;
    246 	text = ISCSI_TEXT_REJECT;
    247 
    248 am_decided:
    249 	client->negotiatedMethod = am_id;
    250 	/* add the selected method to the response nvlist */
    251 	nvrc = nvlist_add_string(lsm->icl_response_nvlist,
    252 	    ikvx->ik_key_name, text);
    253 	kvrc = idm_nvstat_to_kvstat(nvrc);
    254 
    255 	return (kvrc);
    256 }
    257 
    258 /*
    259  * Initiator chooses to use CHAP after target proposed a list of
    260  * authentication method. Set the authentication method to CHAP and
    261  * continue on chap authentication phase.
    262  */
    263 static kv_status_t
    264 auth_propose_chap(iscsit_conn_t *ict, nvpair_t *nvp,
    265     const idm_kv_xlate_t *ikvx)
    266 {
    267 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    268 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    269 
    270 	client->negotiatedMethod = AM_CHAP;
    271 	client->phase = AP_AM_DECIDED;
    272 
    273 	return (auth_chap_select_alg(ict, nvp, ikvx));
    274 }
    275 
    276 /*
    277  * Select a CHAP algorithm from a list of values proposed by
    278  * initiator and shift the authentication phase to AP_CHAP_A_RCVD.
    279  */
    280 static kv_status_t
    281 auth_chap_select_alg(iscsit_conn_t *ict, nvpair_t *nvp,
    282     const idm_kv_xlate_t *ikvx)
    283 {
    284 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    285 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    286 	int			nvrc, rc;
    287 	kv_status_t		kvrc;
    288 	nvpair_t		*alg_choice;
    289 	char			*alg_string;
    290 	uint64_t		alg;
    291 	const char		*text;
    292 
    293 	client->phase = AP_CHAP_A_RCVD;
    294 
    295 	alg_choice = idm_get_next_listvalue(nvp, NULL);
    296 	while (alg_choice != NULL) {
    297 		nvrc = nvpair_value_string(alg_choice, &alg_string);
    298 		ASSERT(nvrc == 0);
    299 		rc = ddi_strtoull(alg_string, NULL, 0, (u_longlong_t *)&alg);
    300 		if (rc == 0 && alg == 5) {
    301 			/* only MD5 is supported */
    302 			text = alg_string;
    303 			goto alg_selected;
    304 		}
    305 
    306 		alg_choice = idm_get_next_listvalue(nvp, alg_choice);
    307 	}
    308 
    309 	/* none of algorithm is selected */
    310 	alg = 0;
    311 	text = ISCSI_TEXT_REJECT;
    312 
    313 alg_selected:
    314 	/* save the selected algorithm or zero for none is selected */
    315 	client_set_numeric_data(
    316 	    &client->recvKeyBlock,
    317 	    AKT_CHAP_A,
    318 	    (uint32_t)alg);
    319 
    320 	/* add the selected algorithm to the response nvlist */
    321 	nvrc = nvlist_add_string(lsm->icl_response_nvlist,
    322 	    ikvx->ik_key_name, text);
    323 	if (alg == 0) {
    324 		kvrc = KV_AUTH_FAILED; /* No algorithm selected */
    325 	} else {
    326 		kvrc = idm_nvstat_to_kvstat(nvrc);
    327 		if (kvrc == 0) {
    328 			kvrc = iscsit_auth_gen_challenge(ict);
    329 		}
    330 	}
    331 
    332 	return (kvrc);
    333 }
    334 
    335 /*
    336  * Validate and save the the chap name which is sent by initiator
    337  * and shift the authentication phase to AP_CHAP_R_RCVD.
    338  *
    339  * Note: the CHAP_N, CHAP_R, optionally CHAP_I and CHAP_C key/value
    340  * pairs need to be received in one packet, we handle each of them
    341  * separately, in order to track the authentication phase, we set
    342  * the authentication phase to AP_CHAP_R_RCVD once one of them is
    343  * handled. So both of AP_CHAP_R_WAITING and AP_CHAP_R_RCVD phases
    344  * are valid for these keys. The function auth_chap_done is going
    345  * to detect if any of these keys is missing.
    346  */
    347 
    348 /*ARGSUSED*/
    349 static kv_status_t
    350 auth_chap_recv_n(iscsit_conn_t *ict, nvpair_t *nvp,
    351     const idm_kv_xlate_t *ikvx)
    352 {
    353 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    354 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    355 	int			nvrc;
    356 	char			*chap_name;
    357 
    358 	nvrc = nvpair_value_string(nvp, &chap_name);
    359 	ASSERT(nvrc == 0);
    360 
    361 	client_set_string_data(&client->recvKeyBlock,
    362 	    AKT_CHAP_N,
    363 	    chap_name);
    364 
    365 	client->phase = AP_CHAP_R_RCVD;
    366 
    367 	return (KV_HANDLED);
    368 }
    369 
    370 /*
    371  * Validate and save the the chap response which is sent by initiator
    372  * and shift the authentication phase to AP_CHAP_R_RCVD.
    373  *
    374  * Note: see function auth_chap_recv_n.
    375  */
    376 
    377 /*ARGSUSED*/
    378 static kv_status_t
    379 auth_chap_recv_r(iscsit_conn_t *ict, nvpair_t *nvp,
    380     const idm_kv_xlate_t *ikvx)
    381 {
    382 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    383 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    384 	int			nvrc;
    385 	unsigned char		*chap_resp;
    386 	uint_t			len;
    387 
    388 	nvrc = nvpair_value_byte_array(nvp, &chap_resp, &len);
    389 	ASSERT(nvrc == 0);
    390 
    391 	client_set_binary_data(&client->recvKeyBlock,
    392 	    AKT_CHAP_R,
    393 	    chap_resp, len);
    394 
    395 	client->phase = AP_CHAP_R_RCVD;
    396 
    397 	return (KV_HANDLED);
    398 }
    399 
    400 /*
    401  * Validate and save the the chap identifier which is sent by initiator
    402  * and shift the authentication phase to AP_CHAP_R_RCVD.
    403  *
    404  * Note: see function auth_chap_recv_n.
    405  */
    406 
    407 /*ARGSUSED*/
    408 static kv_status_t
    409 auth_chap_recv_i(iscsit_conn_t *ict, nvpair_t *nvp,
    410     const idm_kv_xlate_t *ikvx)
    411 {
    412 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    413 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    414 	int			nvrc;
    415 	uint64_t		chap_id;
    416 
    417 	nvrc = nvpair_value_uint64(nvp, &chap_id);
    418 	ASSERT(nvrc == 0);
    419 
    420 	client_set_numeric_data(&client->recvKeyBlock,
    421 	    AKT_CHAP_I,
    422 	    chap_id);
    423 
    424 	client->phase = AP_CHAP_R_RCVD;
    425 
    426 	return (KV_HANDLED);
    427 }
    428 
    429 /*
    430  * Validate and save the the chap challenge which is sent by initiator
    431  * and shift the authentication phase to AP_CHAP_R_RCVD.
    432  *
    433  * Note: see function auth_chap_recv_n.
    434  */
    435 
    436 /*ARGSUSED*/
    437 static kv_status_t
    438 auth_chap_recv_c(iscsit_conn_t *ict, nvpair_t *nvp,
    439     const idm_kv_xlate_t *ikvx)
    440 {
    441 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    442 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    443 	int			nvrc;
    444 	unsigned char		*chap_challenge;
    445 	uint_t			len;
    446 
    447 	nvrc = nvpair_value_byte_array(nvp, &chap_challenge, &len);
    448 	ASSERT(nvrc == 0);
    449 
    450 	client_set_binary_data(
    451 	    &client->recvKeyBlock,
    452 	    AKT_CHAP_C,
    453 	    chap_challenge, len);
    454 
    455 	client->phase = AP_CHAP_R_RCVD;
    456 
    457 	return (KV_HANDLED);
    458 }
    459 
    460 /*
    461  * Shift the authentication phase to AP_CHAP_R_WAITING after target
    462  * has successfully selected a chap algorithm.
    463  */
    464 
    465 /*ARGSUSED*/
    466 static kv_status_t
    467 auth_chap_expect_r(iscsit_conn_t *ict, nvpair_t *nvp,
    468     const idm_kv_xlate_t *ikvx)
    469 {
    470 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    471 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    472 
    473 	uint32_t		alg;
    474 
    475 	client_get_numeric_data(&client->recvKeyBlock,
    476 	    AKT_CHAP_A,
    477 	    &alg);
    478 
    479 	if (alg != 0) {
    480 		client->phase = AP_CHAP_R_WAITING;
    481 	} else {
    482 		/* none of proposed algorithm is supported or understood. */
    483 		client->phase = AP_CHAP_A_WAITING;
    484 	}
    485 
    486 	return (KV_HANDLED);
    487 }
    488 
    489 /*
    490  * Initiator does not propose security negotiation, target needs to
    491  * verify if we can bypass the security negotiation phase or propose
    492  * a security negotiation for the initiator.
    493  */
    494 
    495 /*ARGSUSED*/
    496 static kv_status_t
    497 iscsit_auth_propose(iscsit_conn_t *ict, nvpair_t *nvp,
    498     const idm_kv_xlate_t *ikvx)
    499 {
    500 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    501 	conn_auth_t		*auth = &lsm->icl_auth;
    502 	iscsit_auth_method_t	*am_list = &auth->ca_method_valid_list[0];
    503 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    504 
    505 	int			nvrc;
    506 	kv_status_t		kvrc;
    507 	const char		*am_name;
    508 
    509 	if (am_list[0] == AM_NONE || am_list[0] == 0) {
    510 		lsm->icl_auth_pass = 1;
    511 	}
    512 
    513 	if (lsm->icl_auth_pass == 0) {
    514 		/*
    515 		 * It should be noted that the negotiation might also
    516 		 * be directed by the target if the initiator does
    517 		 * support security, but is not ready to direct the
    518 		 * negotiation (propose options).
    519 		 * - RFC3720 section 5.3.2.
    520 		 */
    521 		am_name = am_id_to_name(am_list[0]);
    522 		nvrc = nvlist_add_string(
    523 		    lsm->icl_response_nvlist,
    524 		    "AuthMethod", am_name);
    525 		kvrc = idm_nvstat_to_kvstat(nvrc);
    526 
    527 		client->phase = AP_AM_PROPOSED;
    528 	} else {
    529 		kvrc = KV_HANDLED;
    530 
    531 		client->phase = AP_DONE;
    532 	}
    533 
    534 	return (kvrc);
    535 }
    536 
    537 /*
    538  * Shift the authentication phase according to the authentication
    539  * method once it is selected.
    540  */
    541 
    542 /*ARGSUSED*/
    543 static kv_status_t
    544 iscsit_auth_expect_key(iscsit_conn_t *ict, nvpair_t *nvp,
    545     const idm_kv_xlate_t *ikvx)
    546 {
    547 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    548 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    549 
    550 	if (client->negotiatedMethod != 0) {
    551 		/* Shift security negotiation phase. */
    552 		switch (client->negotiatedMethod) {
    553 		case AM_CHAP:
    554 			client->phase = AP_CHAP_A_WAITING;
    555 			break;
    556 		case AM_NONE:
    557 			client->phase = AP_DONE;
    558 			lsm->icl_auth_pass = 1;
    559 			break;
    560 		default:
    561 			ASSERT(0);
    562 			break;
    563 		}
    564 	} else {
    565 		/* None of proposed method is supported or understood. */
    566 		client->phase = AP_AM_UNDECIDED;
    567 	}
    568 
    569 	return (KV_HANDLED);
    570 }
    571 
    572 /*
    573  * The last step of the chap authentication. We will validate the
    574  * chap parameters we received and authenticate the client here.
    575  */
    576 
    577 /*ARGSUSED*/
    578 static kv_status_t
    579 auth_chap_done(iscsit_conn_t *ict, nvpair_t *nvp,
    580     const idm_kv_xlate_t *ikvx)
    581 {
    582 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    583 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    584 	kv_status_t		kvrc = KV_HANDLED;
    585 
    586 	conn_auth_t		*auth = &lsm->icl_auth;
    587 	char			*username_in;
    588 
    589 	uint32_t		chap_id;
    590 	unsigned char		*chap_challenge;
    591 	unsigned int		challenge_len;
    592 	char			*chap_name;
    593 	unsigned char		*chap_resp;
    594 	unsigned int		resp_len;
    595 
    596 	int			bi_auth;
    597 
    598 	username_in = auth->ca_ini_chapuser;
    599 	if (username_in[0] == '\0')
    600 		return (KV_AUTH_FAILED);
    601 
    602 	/*
    603 	 * Check if we have received a valid list of response keys.
    604 	 */
    605 	if (!client_auth_key_present(&client->recvKeyBlock, AKT_CHAP_N) ||
    606 	    !client_auth_key_present(&client->recvKeyBlock, AKT_CHAP_R) ||
    607 	    ((bi_auth =
    608 	    client_auth_key_present(&client->recvKeyBlock, AKT_CHAP_I)) ^
    609 	    client_auth_key_present(&client->recvKeyBlock, AKT_CHAP_C))) {
    610 		return (KV_MISSING_FIELDS);
    611 	}
    612 
    613 	client->phase = AP_DONE;
    614 
    615 	client_get_string_data(&client->recvKeyBlock,
    616 	    AKT_CHAP_N,
    617 	    &chap_name);
    618 
    619 	/* check username */
    620 	if (strcmp(username_in, chap_name) != 0) {
    621 		return (KV_AUTH_FAILED);
    622 	}
    623 
    624 	client_get_numeric_data(&client->sendKeyBlock,
    625 	    AKT_CHAP_I,
    626 	    &chap_id);
    627 
    628 	client_get_binary_data(&client->sendKeyBlock,
    629 	    AKT_CHAP_C,
    630 	    &chap_challenge, &challenge_len);
    631 
    632 	client_get_binary_data(&client->recvKeyBlock,
    633 	    AKT_CHAP_R,
    634 	    &chap_resp, &resp_len);
    635 
    636 	if (iscsit_verify_chap_resp(lsm,
    637 	    chap_id, chap_challenge, challenge_len,
    638 	    chap_resp, resp_len) != ISCSI_AUTH_PASSED) {
    639 		return (KV_AUTH_FAILED);
    640 	}
    641 
    642 	/* bi-direction authentication is required */
    643 	if (bi_auth != 0) {
    644 		kvrc = iscsit_auth_gen_response(ict);
    645 	}
    646 
    647 	lsm->icl_auth_pass = 1;
    648 
    649 	return (kvrc);
    650 }
    651 
    652 static kv_status_t
    653 iscsit_auth_gen_challenge(iscsit_conn_t *ict)
    654 {
    655 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    656 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    657 	int			nvrc;
    658 	kv_status_t		kvrc;
    659 
    660 	unsigned char		idData[1];
    661 	unsigned char		*bin;
    662 	int			len;
    663 
    664 	auth_random_set_data(idData, 1);
    665 	client_set_numeric_data(&client->sendKeyBlock,
    666 	    AKT_CHAP_I,
    667 	    idData[0]);
    668 
    669 	/* send chap identifier */
    670 	nvrc = nvlist_add_uint64(
    671 	    lsm->icl_response_nvlist,
    672 	    "CHAP_I", idData[0]);
    673 	kvrc = idm_nvstat_to_kvstat(nvrc);
    674 	if (kvrc != 0) {
    675 		return (kvrc);
    676 	}
    677 
    678 	bin = &(client->auth_send_binary_block.largeBinary[0]);
    679 	len = iscsitAuthChapResponseLength;
    680 	auth_random_set_data(bin, len);
    681 	client_set_binary_data(&client->sendKeyBlock,
    682 	    AKT_CHAP_C,
    683 	    bin, len);
    684 
    685 	/* send chap challenge */
    686 	nvrc = nvlist_add_byte_array(
    687 	    lsm->icl_response_nvlist,
    688 	    "CHAP_C", bin, len);
    689 	kvrc = idm_nvstat_to_kvstat(nvrc);
    690 
    691 	return (kvrc);
    692 }
    693 
    694 static kv_status_t
    695 iscsit_auth_gen_response(iscsit_conn_t *ict)
    696 {
    697 	iscsit_conn_login_t	*lsm = &ict->ict_login_sm;
    698 	iscsit_auth_client_t	*client = &lsm->icl_auth_client;
    699 	int			nvrc;
    700 	kv_status_t		kvrc;
    701 
    702 	conn_auth_t		*auth = &lsm->icl_auth;
    703 	char			*tgt_username;
    704 	uint8_t			*tgt_password;
    705 	int			tgt_password_length;
    706 
    707 	uint32_t		chap_id;
    708 	unsigned char		*chap_challenge;
    709 	unsigned int		challenge_len;
    710 	uchar_t			resp[iscsitAuthChapResponseLength];
    711 
    712 	tgt_username = auth->ca_tgt_chapuser;
    713 	tgt_password = auth->ca_tgt_chapsecret;
    714 	tgt_password_length = auth->ca_tgt_chapsecretlen;
    715 
    716 	/*
    717 	 * We can't know in advance whether the initiator will attempt
    718 	 * mutual authentication, so now we need to check whether we
    719 	 * have a target CHAP secret configured.
    720 	 */
    721 	if (tgt_password_length == 0) {
    722 		return (KV_AUTH_FAILED);
    723 	}
    724 
    725 	client_get_numeric_data(&client->recvKeyBlock,
    726 	    AKT_CHAP_I,
    727 	    &chap_id);
    728 
    729 	client_get_binary_data(&client->recvKeyBlock,
    730 	    AKT_CHAP_C,
    731 	    &chap_challenge, &challenge_len);
    732 
    733 	client_compute_chap_resp(
    734 	    &resp[0],
    735 	    chap_id,
    736 	    tgt_password, tgt_password_length,
    737 	    chap_challenge, challenge_len);
    738 
    739 	nvrc = nvlist_add_string(
    740 	    lsm->icl_response_nvlist,
    741 	    "CHAP_N", tgt_username);
    742 
    743 	if (nvrc == 0) {
    744 		nvrc = nvlist_add_byte_array(
    745 		    lsm->icl_response_nvlist,
    746 		    "CHAP_R", resp, sizeof (resp));
    747 	}
    748 	kvrc = idm_nvstat_to_kvstat(nvrc);
    749 
    750 	return (kvrc);
    751 }
    752