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.
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.
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]
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/cpuvar.h>
26 #include <sys/types.h>
30 #include <sys/sunddi.h>
31 #include <sys/modctl.h>
33 #include <sys/socket.h>
34 #include <sys/strsubr.h>
35 #include <sys/sysmacros.h>
38 #include <sys/stmf_ioctl.h>
39 #include <sys/portif.h>
40 #include <sys/idm/idm.h>
41 #include <sys/idm/idm_text.h>
44 #include "iscsit_auth.h"
47 iscsit_select_auth(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
48 const idm_kv_xlate_t
*ikvx
);
51 auth_propose_chap(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
52 const idm_kv_xlate_t
*ikvx
);
55 auth_chap_select_alg(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
56 const idm_kv_xlate_t
*ikvx
);
59 auth_chap_recv_n(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
60 const idm_kv_xlate_t
*ikvx
);
63 auth_chap_recv_r(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
64 const idm_kv_xlate_t
*ikvx
);
67 auth_chap_recv_i(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
68 const idm_kv_xlate_t
*ikvx
);
71 auth_chap_recv_c(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
72 const idm_kv_xlate_t
*ikvx
);
75 iscsit_auth_propose(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
76 const idm_kv_xlate_t
*ikvx
);
79 iscsit_auth_expect_key(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
80 const idm_kv_xlate_t
*ikvx
);
83 auth_chap_expect_r(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
84 const idm_kv_xlate_t
*ikvx
);
87 auth_chap_done(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
88 const idm_kv_xlate_t
*ikvx
);
91 iscsit_auth_gen_challenge(iscsit_conn_t
*ict
);
94 iscsit_auth_gen_response(iscsit_conn_t
*ict
);
97 iscsit_auth_phase_t phase
;
99 iscsit_auth_handler_t handler
;
100 } auth_phase_entry_t
;
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.
109 static const auth_phase_entry_t apet
[] = {
111 { AP_AM_UNDECIDED
, KI_AUTH_METHOD
, iscsit_select_auth
},
112 { AP_AM_PROPOSED
, KI_CHAP_A
, auth_propose_chap
},
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
},
125 { AP_AM_UNDECIDED
, 0, iscsit_auth_propose
},
126 { AP_AM_DECIDED
, 0, iscsit_auth_expect_key
},
128 { AP_CHAP_A_RCVD
, 0, auth_chap_expect_r
},
129 { AP_CHAP_R_RCVD
, 0, auth_chap_done
}
133 iscsit_auth_method_t am_id
;
138 * a table of mapping from the authentication index to name.
140 static const auth_id_name_t aint
[] = {
143 /* { AM_KRB5, "KRB5" }, */ /* Not supported */
144 /* { AM_SPKM1, "SPKM1" }, */ /* Not supported */
145 /* { AM_SPKM2, "SPKM2" }, */ /* Not supported */
146 /* { AM_SRP, "SRP" }, */ /* Not supported */
149 #define ARRAY_LENGTH(ARRAY) (sizeof (ARRAY) / sizeof (ARRAY[0]))
152 * get the authentication method name for the method id.
155 am_id_to_name(int id
)
158 const auth_id_name_t
*p
;
160 while (i
< ARRAY_LENGTH(aint
)) {
162 if (id
== p
->am_id
) {
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.
178 iscsit_auth_handler_t
179 iscsit_auth_get_handler(iscsit_auth_client_t
*client
, iscsikey_id_t kv_id
)
181 iscsit_auth_phase_t phase
= client
->phase
;
183 const auth_phase_entry_t
*p
;
187 while (i
< ARRAY_LENGTH(apet
)) {
189 if (phase
== p
->phase
&&
196 /* No handler can be found, it must be an invalid requst. */
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.
206 iscsit_select_auth(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
207 const idm_kv_xlate_t
*ikvx
)
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
;
219 iscsit_auth_method_t am_id
;
222 client
->phase
= AP_AM_DECIDED
;
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
);
233 am_name
= am_id_to_name(am_id
);
234 if (strcasecmp(am
, am_name
) == 0) {
241 am_choice
= idm_get_next_listvalue(nvp
, am_choice
);
244 /* none of authentication method is valid */
246 text
= ISCSI_TEXT_REJECT
;
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
);
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.
264 auth_propose_chap(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
265 const idm_kv_xlate_t
*ikvx
)
267 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
268 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
270 client
->negotiatedMethod
= AM_CHAP
;
271 client
->phase
= AP_AM_DECIDED
;
273 return (auth_chap_select_alg(ict
, nvp
, ikvx
));
277 * Select a CHAP algorithm from a list of values proposed by
278 * initiator and shift the authentication phase to AP_CHAP_A_RCVD.
281 auth_chap_select_alg(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
282 const idm_kv_xlate_t
*ikvx
)
284 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
285 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
288 nvpair_t
*alg_choice
;
293 client
->phase
= AP_CHAP_A_RCVD
;
295 alg_choice
= idm_get_next_listvalue(nvp
, NULL
);
296 while (alg_choice
!= NULL
) {
297 nvrc
= nvpair_value_string(alg_choice
, &alg_string
);
299 rc
= ddi_strtoull(alg_string
, NULL
, 0, (u_longlong_t
*)&alg
);
300 if (rc
== 0 && alg
== 5) {
301 /* only MD5 is supported */
306 alg_choice
= idm_get_next_listvalue(nvp
, alg_choice
);
309 /* none of algorithm is selected */
311 text
= ISCSI_TEXT_REJECT
;
314 /* save the selected algorithm or zero for none is selected */
315 client_set_numeric_data(
316 &client
->recvKeyBlock
,
320 /* add the selected algorithm to the response nvlist */
321 nvrc
= nvlist_add_string(lsm
->icl_response_nvlist
,
322 ikvx
->ik_key_name
, text
);
324 kvrc
= KV_AUTH_FAILED
; /* No algorithm selected */
326 kvrc
= idm_nvstat_to_kvstat(nvrc
);
328 kvrc
= iscsit_auth_gen_challenge(ict
);
336 * Validate and save the the chap name which is sent by initiator
337 * and shift the authentication phase to AP_CHAP_R_RCVD.
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.
350 auth_chap_recv_n(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
351 const idm_kv_xlate_t
*ikvx
)
353 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
354 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
358 nvrc
= nvpair_value_string(nvp
, &chap_name
);
361 client_set_string_data(&client
->recvKeyBlock
,
365 client
->phase
= AP_CHAP_R_RCVD
;
371 * Validate and save the the chap response which is sent by initiator
372 * and shift the authentication phase to AP_CHAP_R_RCVD.
374 * Note: see function auth_chap_recv_n.
379 auth_chap_recv_r(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
380 const idm_kv_xlate_t
*ikvx
)
382 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
383 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
385 unsigned char *chap_resp
;
388 nvrc
= nvpair_value_byte_array(nvp
, &chap_resp
, &len
);
391 client_set_binary_data(&client
->recvKeyBlock
,
395 client
->phase
= AP_CHAP_R_RCVD
;
401 * Validate and save the the chap identifier which is sent by initiator
402 * and shift the authentication phase to AP_CHAP_R_RCVD.
404 * Note: see function auth_chap_recv_n.
409 auth_chap_recv_i(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
410 const idm_kv_xlate_t
*ikvx
)
412 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
413 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
417 nvrc
= nvpair_value_uint64(nvp
, &chap_id
);
420 client_set_numeric_data(&client
->recvKeyBlock
,
424 client
->phase
= AP_CHAP_R_RCVD
;
430 * Validate and save the the chap challenge which is sent by initiator
431 * and shift the authentication phase to AP_CHAP_R_RCVD.
433 * Note: see function auth_chap_recv_n.
438 auth_chap_recv_c(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
439 const idm_kv_xlate_t
*ikvx
)
441 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
442 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
444 unsigned char *chap_challenge
;
447 nvrc
= nvpair_value_byte_array(nvp
, &chap_challenge
, &len
);
450 client_set_binary_data(
451 &client
->recvKeyBlock
,
453 chap_challenge
, len
);
455 client
->phase
= AP_CHAP_R_RCVD
;
461 * Shift the authentication phase to AP_CHAP_R_WAITING after target
462 * has successfully selected a chap algorithm.
467 auth_chap_expect_r(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
468 const idm_kv_xlate_t
*ikvx
)
470 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
471 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
475 client_get_numeric_data(&client
->recvKeyBlock
,
480 client
->phase
= AP_CHAP_R_WAITING
;
482 /* none of proposed algorithm is supported or understood. */
483 client
->phase
= AP_CHAP_A_WAITING
;
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.
497 iscsit_auth_propose(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
498 const idm_kv_xlate_t
*ikvx
)
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
;
509 if (am_list
[0] == AM_NONE
|| am_list
[0] == 0) {
510 lsm
->icl_auth_pass
= 1;
513 if (lsm
->icl_auth_pass
== 0) {
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.
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
);
527 client
->phase
= AP_AM_PROPOSED
;
531 client
->phase
= AP_DONE
;
538 * Shift the authentication phase according to the authentication
539 * method once it is selected.
544 iscsit_auth_expect_key(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
545 const idm_kv_xlate_t
*ikvx
)
547 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
548 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
550 if (client
->negotiatedMethod
!= 0) {
551 /* Shift security negotiation phase. */
552 switch (client
->negotiatedMethod
) {
554 client
->phase
= AP_CHAP_A_WAITING
;
557 client
->phase
= AP_DONE
;
558 lsm
->icl_auth_pass
= 1;
565 /* None of proposed method is supported or understood. */
566 client
->phase
= AP_AM_UNDECIDED
;
573 * The last step of the chap authentication. We will validate the
574 * chap parameters we received and authenticate the client here.
579 auth_chap_done(iscsit_conn_t
*ict
, nvpair_t
*nvp
,
580 const idm_kv_xlate_t
*ikvx
)
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
;
586 conn_auth_t
*auth
= &lsm
->icl_auth
;
590 unsigned char *chap_challenge
;
591 unsigned int challenge_len
;
593 unsigned char *chap_resp
;
594 unsigned int resp_len
;
598 username_in
= auth
->ca_ini_chapuser
;
599 if (username_in
[0] == '\0')
600 return (KV_AUTH_FAILED
);
603 * Check if we have received a valid list of response keys.
605 if (!client_auth_key_present(&client
->recvKeyBlock
, AKT_CHAP_N
) ||
606 !client_auth_key_present(&client
->recvKeyBlock
, AKT_CHAP_R
) ||
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
);
613 client
->phase
= AP_DONE
;
615 client_get_string_data(&client
->recvKeyBlock
,
620 if (strcmp(username_in
, chap_name
) != 0) {
621 return (KV_AUTH_FAILED
);
624 client_get_numeric_data(&client
->sendKeyBlock
,
628 client_get_binary_data(&client
->sendKeyBlock
,
630 &chap_challenge
, &challenge_len
);
632 client_get_binary_data(&client
->recvKeyBlock
,
634 &chap_resp
, &resp_len
);
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
);
642 /* bi-direction authentication is required */
644 kvrc
= iscsit_auth_gen_response(ict
);
647 lsm
->icl_auth_pass
= 1;
653 iscsit_auth_gen_challenge(iscsit_conn_t
*ict
)
655 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
656 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
660 unsigned char idData
[1];
664 auth_random_set_data(idData
, 1);
665 client_set_numeric_data(&client
->sendKeyBlock
,
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
);
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
,
685 /* send chap challenge */
686 nvrc
= nvlist_add_byte_array(
687 lsm
->icl_response_nvlist
,
689 kvrc
= idm_nvstat_to_kvstat(nvrc
);
695 iscsit_auth_gen_response(iscsit_conn_t
*ict
)
697 iscsit_conn_login_t
*lsm
= &ict
->ict_login_sm
;
698 iscsit_auth_client_t
*client
= &lsm
->icl_auth_client
;
702 conn_auth_t
*auth
= &lsm
->icl_auth
;
704 uint8_t *tgt_password
;
705 int tgt_password_length
;
708 unsigned char *chap_challenge
;
709 unsigned int challenge_len
;
710 uchar_t resp
[iscsitAuthChapResponseLength
];
712 tgt_username
= auth
->ca_tgt_chapuser
;
713 tgt_password
= auth
->ca_tgt_chapsecret
;
714 tgt_password_length
= auth
->ca_tgt_chapsecretlen
;
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.
721 if (tgt_password_length
== 0) {
722 return (KV_AUTH_FAILED
);
725 client_get_numeric_data(&client
->recvKeyBlock
,
729 client_get_binary_data(&client
->recvKeyBlock
,
731 &chap_challenge
, &challenge_len
);
733 client_compute_chap_resp(
736 tgt_password
, tgt_password_length
,
737 chap_challenge
, challenge_len
);
739 nvrc
= nvlist_add_string(
740 lsm
->icl_response_nvlist
,
741 "CHAP_N", tgt_username
);
744 nvrc
= nvlist_add_byte_array(
745 lsm
->icl_response_nvlist
,
746 "CHAP_R", resp
, sizeof (resp
));
748 kvrc
= idm_nvstat_to_kvstat(nvrc
);