#include <sys/cpuvar.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/socket.h>
#include <sys/strsubr.h>
#include <sys/sysmacros.h>
#include <sys/stmf.h>
#include <sys/stmf_ioctl.h>
#include <sys/portif.h>
#include <sys/idm/idm.h>
#include <sys/idm/idm_text.h>
#include "iscsit.h"
#include "iscsit_auth.h"
static kv_status_t
iscsit_select_auth(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
auth_propose_chap(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
auth_chap_select_alg(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
auth_chap_recv_n(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
auth_chap_recv_r(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
auth_chap_recv_i(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
auth_chap_recv_c(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
iscsit_auth_propose(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
iscsit_auth_expect_key(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
auth_chap_expect_r(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
auth_chap_done(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx);
static kv_status_t
iscsit_auth_gen_challenge(iscsit_conn_t *ict);
static kv_status_t
iscsit_auth_gen_response(iscsit_conn_t *ict);
typedef struct {
iscsit_auth_phase_t phase;
iscsikey_id_t kv_id;
iscsit_auth_handler_t handler;
} auth_phase_entry_t;
static const auth_phase_entry_t apet[] = {
{ AP_AM_UNDECIDED, KI_AUTH_METHOD, iscsit_select_auth },
{ AP_AM_PROPOSED, KI_CHAP_A, auth_propose_chap },
{ AP_CHAP_A_WAITING, KI_CHAP_A, auth_chap_select_alg },
{ AP_CHAP_R_WAITING, KI_CHAP_N, auth_chap_recv_n },
{ AP_CHAP_R_WAITING, KI_CHAP_R, auth_chap_recv_r },
{ AP_CHAP_R_WAITING, KI_CHAP_I, auth_chap_recv_i },
{ AP_CHAP_R_WAITING, KI_CHAP_C, auth_chap_recv_c },
{ AP_CHAP_R_RCVD, KI_CHAP_N, auth_chap_recv_n },
{ AP_CHAP_R_RCVD, KI_CHAP_R, auth_chap_recv_r },
{ AP_CHAP_R_RCVD, KI_CHAP_I, auth_chap_recv_i },
{ AP_CHAP_R_RCVD, KI_CHAP_C, auth_chap_recv_c },
{ AP_AM_UNDECIDED, 0, iscsit_auth_propose },
{ AP_AM_DECIDED, 0, iscsit_auth_expect_key },
{ AP_CHAP_A_RCVD, 0, auth_chap_expect_r },
{ AP_CHAP_R_RCVD, 0, auth_chap_done }
};
typedef struct {
iscsit_auth_method_t am_id;
char *am_name;
} auth_id_name_t;
static const auth_id_name_t aint[] = {
{ AM_CHAP, "CHAP" },
{ AM_NONE, "None" },
};
#define ARRAY_LENGTH(ARRAY) (sizeof (ARRAY) / sizeof (ARRAY[0]))
static const char *
am_id_to_name(int id)
{
int i;
const auth_id_name_t *p;
i = 0;
while (i < ARRAY_LENGTH(aint)) {
p = &(aint[i]);
if (id == p->am_id) {
return (p->am_name);
}
i ++;
}
return (NULL);
}
iscsit_auth_handler_t
iscsit_auth_get_handler(iscsit_auth_client_t *client, iscsikey_id_t kv_id)
{
iscsit_auth_phase_t phase = client->phase;
int i;
const auth_phase_entry_t *p;
i = 0;
p = NULL;
while (i < ARRAY_LENGTH(apet)) {
p = &(apet[i]);
if (phase == p->phase &&
kv_id == p->kv_id) {
return (p->handler);
}
i ++;
}
return (NULL);
}
static kv_status_t
iscsit_select_auth(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
conn_auth_t *auth = &lsm->icl_auth;
iscsit_auth_method_t *am_list = &auth->ca_method_valid_list[0];
iscsit_auth_client_t *client = &lsm->icl_auth_client;
int nvrc;
kv_status_t kvrc;
nvpair_t *am_choice;
char *am;
const char *am_name;
const char *text;
iscsit_auth_method_t am_id;
int i;
client->phase = AP_AM_DECIDED;
am_choice = idm_get_next_listvalue(nvp, NULL);
while (am_choice != NULL) {
nvrc = nvpair_value_string(am_choice, &am);
ASSERT(nvrc == 0);
i = 0;
am_id = am_list[i];
while (am_id != 0) {
am_name = am_id_to_name(am_id);
if (strcasecmp(am, am_name) == 0) {
text = am;
goto am_decided;
}
i++;
am_id = am_list[i];
}
am_choice = idm_get_next_listvalue(nvp, am_choice);
}
am_id = 0;
text = ISCSI_TEXT_REJECT;
am_decided:
client->negotiatedMethod = am_id;
nvrc = nvlist_add_string(lsm->icl_response_nvlist,
ikvx->ik_key_name, text);
kvrc = idm_nvstat_to_kvstat(nvrc);
return (kvrc);
}
static kv_status_t
auth_propose_chap(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
client->negotiatedMethod = AM_CHAP;
client->phase = AP_AM_DECIDED;
return (auth_chap_select_alg(ict, nvp, ikvx));
}
static kv_status_t
auth_chap_select_alg(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
int nvrc, rc;
kv_status_t kvrc;
nvpair_t *alg_choice;
char *alg_string;
uint64_t alg;
const char *text;
client->phase = AP_CHAP_A_RCVD;
alg_choice = idm_get_next_listvalue(nvp, NULL);
while (alg_choice != NULL) {
nvrc = nvpair_value_string(alg_choice, &alg_string);
ASSERT(nvrc == 0);
rc = ddi_strtoull(alg_string, NULL, 0, (u_longlong_t *)&alg);
if (rc == 0 && alg == 5) {
text = alg_string;
goto alg_selected;
}
alg_choice = idm_get_next_listvalue(nvp, alg_choice);
}
alg = 0;
text = ISCSI_TEXT_REJECT;
alg_selected:
client_set_numeric_data(
&client->recvKeyBlock,
AKT_CHAP_A,
(uint32_t)alg);
nvrc = nvlist_add_string(lsm->icl_response_nvlist,
ikvx->ik_key_name, text);
if (alg == 0) {
kvrc = KV_AUTH_FAILED;
} else {
kvrc = idm_nvstat_to_kvstat(nvrc);
if (kvrc == 0) {
kvrc = iscsit_auth_gen_challenge(ict);
}
}
return (kvrc);
}
static kv_status_t
auth_chap_recv_n(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
int nvrc;
char *chap_name;
nvrc = nvpair_value_string(nvp, &chap_name);
ASSERT(nvrc == 0);
client_set_string_data(&client->recvKeyBlock,
AKT_CHAP_N,
chap_name);
client->phase = AP_CHAP_R_RCVD;
return (KV_HANDLED);
}
static kv_status_t
auth_chap_recv_r(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
int nvrc;
unsigned char *chap_resp;
uint_t len;
nvrc = nvpair_value_byte_array(nvp, &chap_resp, &len);
ASSERT(nvrc == 0);
client_set_binary_data(&client->recvKeyBlock,
AKT_CHAP_R,
chap_resp, len);
client->phase = AP_CHAP_R_RCVD;
return (KV_HANDLED);
}
static kv_status_t
auth_chap_recv_i(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
int nvrc;
uint64_t chap_id;
nvrc = nvpair_value_uint64(nvp, &chap_id);
ASSERT(nvrc == 0);
client_set_numeric_data(&client->recvKeyBlock,
AKT_CHAP_I,
chap_id);
client->phase = AP_CHAP_R_RCVD;
return (KV_HANDLED);
}
static kv_status_t
auth_chap_recv_c(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
int nvrc;
unsigned char *chap_challenge;
uint_t len;
nvrc = nvpair_value_byte_array(nvp, &chap_challenge, &len);
ASSERT(nvrc == 0);
client_set_binary_data(
&client->recvKeyBlock,
AKT_CHAP_C,
chap_challenge, len);
client->phase = AP_CHAP_R_RCVD;
return (KV_HANDLED);
}
static kv_status_t
auth_chap_expect_r(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
uint32_t alg;
client_get_numeric_data(&client->recvKeyBlock,
AKT_CHAP_A,
&alg);
if (alg != 0) {
client->phase = AP_CHAP_R_WAITING;
} else {
client->phase = AP_CHAP_A_WAITING;
}
return (KV_HANDLED);
}
static kv_status_t
iscsit_auth_propose(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
conn_auth_t *auth = &lsm->icl_auth;
iscsit_auth_method_t *am_list = &auth->ca_method_valid_list[0];
iscsit_auth_client_t *client = &lsm->icl_auth_client;
int nvrc;
kv_status_t kvrc;
const char *am_name;
if (am_list[0] == AM_NONE || am_list[0] == 0) {
lsm->icl_auth_pass = 1;
}
if (lsm->icl_auth_pass == 0) {
am_name = am_id_to_name(am_list[0]);
nvrc = nvlist_add_string(
lsm->icl_response_nvlist,
"AuthMethod", am_name);
kvrc = idm_nvstat_to_kvstat(nvrc);
client->phase = AP_AM_PROPOSED;
} else {
kvrc = KV_HANDLED;
client->phase = AP_DONE;
}
return (kvrc);
}
static kv_status_t
iscsit_auth_expect_key(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
if (client->negotiatedMethod != 0) {
switch (client->negotiatedMethod) {
case AM_CHAP:
client->phase = AP_CHAP_A_WAITING;
break;
case AM_NONE:
client->phase = AP_DONE;
lsm->icl_auth_pass = 1;
break;
default:
ASSERT(0);
break;
}
} else {
client->phase = AP_AM_UNDECIDED;
}
return (KV_HANDLED);
}
static kv_status_t
auth_chap_done(iscsit_conn_t *ict, nvpair_t *nvp,
const idm_kv_xlate_t *ikvx)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
kv_status_t kvrc = KV_HANDLED;
conn_auth_t *auth = &lsm->icl_auth;
char *username_in;
uint32_t chap_id;
unsigned char *chap_challenge;
unsigned int challenge_len;
char *chap_name;
unsigned char *chap_resp;
unsigned int resp_len;
int bi_auth;
username_in = auth->ca_ini_chapuser;
if (username_in[0] == '\0')
return (KV_AUTH_FAILED);
if (!client_auth_key_present(&client->recvKeyBlock, AKT_CHAP_N) ||
!client_auth_key_present(&client->recvKeyBlock, AKT_CHAP_R) ||
((bi_auth =
client_auth_key_present(&client->recvKeyBlock, AKT_CHAP_I)) ^
client_auth_key_present(&client->recvKeyBlock, AKT_CHAP_C))) {
return (KV_MISSING_FIELDS);
}
client->phase = AP_DONE;
client_get_string_data(&client->recvKeyBlock,
AKT_CHAP_N,
&chap_name);
if (strcmp(username_in, chap_name) != 0) {
return (KV_AUTH_FAILED);
}
client_get_numeric_data(&client->sendKeyBlock,
AKT_CHAP_I,
&chap_id);
client_get_binary_data(&client->sendKeyBlock,
AKT_CHAP_C,
&chap_challenge, &challenge_len);
client_get_binary_data(&client->recvKeyBlock,
AKT_CHAP_R,
&chap_resp, &resp_len);
if (iscsit_verify_chap_resp(lsm,
chap_id, chap_challenge, challenge_len,
chap_resp, resp_len) != ISCSI_AUTH_PASSED) {
return (KV_AUTH_FAILED);
}
if (bi_auth != 0) {
kvrc = iscsit_auth_gen_response(ict);
}
lsm->icl_auth_pass = 1;
return (kvrc);
}
static kv_status_t
iscsit_auth_gen_challenge(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
int nvrc;
kv_status_t kvrc;
unsigned char idData[1];
unsigned char *bin;
int len;
auth_random_set_data(idData, 1);
client_set_numeric_data(&client->sendKeyBlock,
AKT_CHAP_I,
idData[0]);
nvrc = nvlist_add_uint64(
lsm->icl_response_nvlist,
"CHAP_I", idData[0]);
kvrc = idm_nvstat_to_kvstat(nvrc);
if (kvrc != 0) {
return (kvrc);
}
bin = &(client->auth_send_binary_block.largeBinary[0]);
len = iscsitAuthChapResponseLength;
auth_random_set_data(bin, len);
client_set_binary_data(&client->sendKeyBlock,
AKT_CHAP_C,
bin, len);
nvrc = nvlist_add_byte_array(
lsm->icl_response_nvlist,
"CHAP_C", bin, len);
kvrc = idm_nvstat_to_kvstat(nvrc);
return (kvrc);
}
static kv_status_t
iscsit_auth_gen_response(iscsit_conn_t *ict)
{
iscsit_conn_login_t *lsm = &ict->ict_login_sm;
iscsit_auth_client_t *client = &lsm->icl_auth_client;
int nvrc;
kv_status_t kvrc;
conn_auth_t *auth = &lsm->icl_auth;
char *tgt_username;
uint8_t *tgt_password;
int tgt_password_length;
uint32_t chap_id;
unsigned char *chap_challenge;
unsigned int challenge_len;
uchar_t resp[iscsitAuthChapResponseLength];
tgt_username = auth->ca_tgt_chapuser;
tgt_password = auth->ca_tgt_chapsecret;
tgt_password_length = auth->ca_tgt_chapsecretlen;
if (tgt_password_length == 0) {
return (KV_AUTH_FAILED);
}
client_get_numeric_data(&client->recvKeyBlock,
AKT_CHAP_I,
&chap_id);
client_get_binary_data(&client->recvKeyBlock,
AKT_CHAP_C,
&chap_challenge, &challenge_len);
client_compute_chap_resp(
&resp[0],
chap_id,
tgt_password, tgt_password_length,
chap_challenge, challenge_len);
nvrc = nvlist_add_string(
lsm->icl_response_nvlist,
"CHAP_N", tgt_username);
if (nvrc == 0) {
nvrc = nvlist_add_byte_array(
lsm->icl_response_nvlist,
"CHAP_R", resp, sizeof (resp));
}
kvrc = idm_nvstat_to_kvstat(nvrc);
return (kvrc);
}