#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <fcntl.h>
#include <uuid/uuid.h>
#include <errno.h>
#include <unistd.h>
#include <strings.h>
#include <libintl.h>
#include <libscf.h>
#include <assert.h>
#include <libstmf.h>
#include <libiscsit.h>
#include <sys/iscsi_protocol.h>
#include <sys/iscsit/isns_protocol.h>
#define TARGET_NAME_VERS 2
#define ISCSI_NAME_LEN_MAX 223
#define MAX_BASE64_LEN 341
#define DEFAULT_RADIUS_PORT 1812
#define ISCSIT_FMRI "svc:/network/iscsi/target:default"
#define ISCSIT_DEFAULT_TPGT 1
#define MAXTAG 0xffff
#define PROPERR(lst, key, value) { \
if (lst) { \
(void) nvlist_add_string(lst, key, value); \
} \
}
static int
it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix);
static int
it_val_pass(char *name, char *val, nvlist_t *e);
static int
it_validate_configprops(nvlist_t *nvl, nvlist_t *errs);
static int
it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs);
static int
it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs);
static boolean_t
is_iscsit_enabled(void);
static void
iqnstr(char *s);
static void
euistr(char *s);
static void
free_empty_errlist(nvlist_t **errlist);
int
it_config_load(it_config_t **cfg)
{
int ret = 0;
nvlist_t *cfg_nv = NULL;
it_config_t *newcfg = NULL;
uint64_t stmf_token = 0;
if (!cfg) {
return (EINVAL);
}
*cfg = NULL;
ret = stmfGetProviderDataProt(ISCSIT_MODNAME, &cfg_nv,
STMF_PORT_PROVIDER_TYPE, &stmf_token);
if ((ret == STMF_STATUS_SUCCESS) ||
(ret == STMF_ERROR_NOT_FOUND)) {
ret = it_nv_to_config(cfg_nv, &newcfg);
}
if (ret == 0) {
newcfg->stmf_token = stmf_token;
*cfg = newcfg;
}
if (cfg_nv) {
nvlist_free(cfg_nv);
}
return (ret);
}
int
it_config_commit(it_config_t *cfg)
{
int ret;
nvlist_t *cfgnv = NULL;
char *packednv = NULL;
int iscsit_fd = -1;
size_t pnv_size;
iscsit_ioc_set_config_t iop;
it_tgt_t *tgtp;
if (!cfg) {
return (EINVAL);
}
ret = it_config_to_nv(cfg, &cfgnv);
if (ret == 0) {
ret = nvlist_size(cfgnv, &pnv_size, NV_ENCODE_NATIVE);
}
if ((ret == 0) && is_iscsit_enabled()) {
packednv = malloc(pnv_size);
if (!packednv) {
ret = ENOMEM;
} else {
ret = nvlist_pack(cfgnv, &packednv, &pnv_size,
NV_ENCODE_NATIVE, 0);
}
if (ret == 0) {
iscsit_fd = open(ISCSIT_NODE, O_RDWR|O_EXCL);
if (iscsit_fd != -1) {
iop.set_cfg_vers = ISCSIT_API_VERS0;
iop.set_cfg_pnvlist = packednv;
iop.set_cfg_pnvlist_len = pnv_size;
if ((ioctl(iscsit_fd, ISCSIT_IOC_SET_CONFIG,
&iop)) != 0) {
ret = errno;
}
(void) close(iscsit_fd);
} else {
ret = errno;
}
}
if (packednv != NULL) {
free(packednv);
}
}
if (ret == 0) {
boolean_t changed = B_FALSE;
tgtp = cfg->config_tgt_list;
for (; tgtp != NULL; tgtp = tgtp->tgt_next) {
if (!tgtp->tgt_properties) {
continue;
}
if (nvlist_exists(tgtp->tgt_properties,
PROP_OLD_TARGET_NAME)) {
(void) nvlist_remove_all(tgtp->tgt_properties,
PROP_OLD_TARGET_NAME);
changed = B_TRUE;
}
}
if (changed) {
nvlist_free(cfgnv);
cfgnv = NULL;
ret = it_config_to_nv(cfg, &cfgnv);
}
}
if (ret == 0) {
ret = stmfSetProviderDataProt(ISCSIT_MODNAME, cfgnv,
STMF_PORT_PROVIDER_TYPE, &(cfg->stmf_token));
if (ret == STMF_STATUS_SUCCESS) {
ret = 0;
} else if (ret == STMF_ERROR_NOMEM) {
ret = ENOMEM;
} else if (ret == STMF_ERROR_PROV_DATA_STALE) {
int st;
it_config_t *rcfg = NULL;
st = it_config_load(&rcfg);
if (st == 0) {
(void) it_config_commit(rcfg);
it_config_free(rcfg);
}
}
}
if (cfgnv) {
nvlist_free(cfgnv);
}
return (ret);
}
int
it_config_setprop(it_config_t *cfg, nvlist_t *proplist, nvlist_t **errlist)
{
int ret;
nvlist_t *errs = NULL;
it_portal_t *isns = NULL;
it_portal_t *pnext = NULL;
it_portal_t *newisnslist = NULL;
char **arr;
uint32_t count;
uint32_t newcount;
nvlist_t *cprops = NULL;
char *val = NULL;
if (!cfg || !proplist) {
return (EINVAL);
}
if (errlist) {
(void) nvlist_alloc(&errs, 0, 0);
*errlist = errs;
}
if (cfg->config_global_properties) {
ret = nvlist_dup(cfg->config_global_properties, &cprops, 0);
} else {
ret = nvlist_alloc(&cprops, NV_UNIQUE_NAME, 0);
}
if (ret != 0) {
return (ret);
}
ret = nvlist_merge(cprops, proplist, 0);
if (ret != 0) {
nvlist_free(cprops);
return (ret);
}
val = NULL;
(void) nvlist_lookup_string(proplist, PROP_RADIUS_SECRET, &val);
if (val) {
char bsecret[MAX_BASE64_LEN];
ret = it_val_pass(PROP_RADIUS_SECRET, val, errs);
if (ret == 0) {
(void) memset(bsecret, 0, MAX_BASE64_LEN);
ret = iscsi_binary_to_base64_str((uint8_t *)val,
strlen(val), bsecret, MAX_BASE64_LEN);
if (ret == 0) {
ret = nvlist_add_string(cprops,
PROP_RADIUS_SECRET, bsecret);
}
}
}
if (ret != 0) {
nvlist_free(cprops);
return (ret);
}
val = NULL;
(void) nvlist_lookup_string(cprops, PROP_RADIUS_SERVER, &val);
if (val && (strcasecmp(val, "none") == 0)) {
(void) nvlist_remove_all(cprops, PROP_RADIUS_SERVER);
}
val = NULL;
(void) nvlist_lookup_string(cprops, PROP_ALIAS, &val);
if (val && (strcasecmp(val, "none") == 0)) {
(void) nvlist_remove_all(cprops, PROP_ALIAS);
}
ret = it_validate_configprops(cprops, errs);
if (ret != 0) {
if (cprops) {
nvlist_free(cprops);
}
return (ret);
}
ret = nvlist_lookup_string_array(proplist, PROP_ISNS_SERVER,
&arr, &count);
if (ret == 0) {
if (strcasecmp(arr[0], "none") != 0) {
ret = it_array_to_portallist(arr, count,
ISNS_DEFAULT_SERVER_PORT, &newisnslist, &newcount);
} else {
newisnslist = NULL;
newcount = 0;
(void) nvlist_remove_all(cprops, PROP_ISNS_SERVER);
}
if (ret == 0) {
isns = cfg->config_isns_svr_list;
while (isns) {
pnext = isns->portal_next;
free(isns);
isns = pnext;
}
cfg->config_isns_svr_list = newisnslist;
cfg->config_isns_svr_count = newcount;
if (newcount > 0) {
int i = 0;
char **newarray;
newarray = malloc(sizeof (char *) * newcount);
if (newarray == NULL) {
ret = ENOMEM;
} else {
for (isns = newisnslist; isns != NULL;
isns = isns->portal_next) {
(void) sockaddr_to_str(
&(isns->portal_addr),
&(newarray[i++]));
}
(void) nvlist_add_string_array(cprops,
PROP_ISNS_SERVER, newarray,
newcount);
for (i = 0; i < newcount; i++) {
if (newarray[i]) {
free(newarray[i]);
}
}
free(newarray);
}
}
}
} else if (ret == ENOENT) {
ret = 0;
}
if (ret == 0) {
nvlist_free(cfg->config_global_properties);
cfg->config_global_properties = cprops;
} else {
if (cprops) {
nvlist_free(cprops);
}
}
if (ret == 0)
free_empty_errlist(errlist);
return (ret);
}
void
it_config_free(it_config_t *cfg)
{
it_config_free_cmn(cfg);
}
int
it_tgt_create(it_config_t *cfg, it_tgt_t **tgt, char *tgt_name)
{
int ret = 0;
it_tgt_t *ptr;
it_tgt_t *cfgtgt;
char *namep;
char buf[ISCSI_NAME_LEN_MAX + 1];
if (!cfg || !tgt) {
return (EINVAL);
}
if (!tgt_name) {
ret = it_iqn_generate(buf, sizeof (buf), NULL);
if (ret != 0) {
return (ret);
}
} else {
if (!validate_iscsi_name(tgt_name)) {
return (EFAULT);
}
(void) strlcpy(buf, tgt_name, sizeof (buf));
canonical_iscsi_name(buf);
}
namep = buf;
cfgtgt = cfg->config_tgt_list;
while (cfgtgt != NULL) {
if (strcasecmp(namep, cfgtgt->tgt_name) == 0) {
return (EEXIST);
}
cfgtgt = cfgtgt->tgt_next;
}
if (cfg->config_tgt_count >= MAX_TARGETS) {
return (E2BIG);
}
ptr = calloc(1, sizeof (it_tgt_t));
if (ptr == NULL) {
return (ENOMEM);
}
(void) strlcpy(ptr->tgt_name, namep, sizeof (ptr->tgt_name));
ptr->tgt_generation = 1;
ptr->tgt_next = cfg->config_tgt_list;
cfg->config_tgt_list = ptr;
cfg->config_tgt_count++;
*tgt = ptr;
return (0);
}
int
it_tgt_setprop(it_config_t *cfg, it_tgt_t *tgt, nvlist_t *proplist,
nvlist_t **errlist)
{
int ret;
nvlist_t *errs = NULL;
nvlist_t *tprops = NULL;
char *val = NULL;
if (!cfg || !tgt || !proplist) {
return (EINVAL);
}
if (!validate_iscsi_name(tgt->tgt_name)) {
return (EINVAL);
}
canonical_iscsi_name(tgt->tgt_name);
if (errlist) {
(void) nvlist_alloc(&errs, 0, 0);
*errlist = errs;
}
if (tgt->tgt_properties) {
ret = nvlist_dup(tgt->tgt_properties, &tprops, 0);
} else {
ret = nvlist_alloc(&tprops, NV_UNIQUE_NAME, 0);
}
if (ret != 0) {
return (ret);
}
ret = nvlist_merge(tprops, proplist, 0);
if (ret != 0) {
nvlist_free(tprops);
return (ret);
}
val = NULL;
(void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_USER, &val);
if (val && (strcasecmp(val, "none") == 0)) {
(void) nvlist_remove_all(tprops, PROP_TARGET_CHAP_USER);
}
val = NULL;
(void) nvlist_lookup_string(proplist, PROP_ALIAS, &val);
if (val && (strcasecmp(val, "none") == 0)) {
(void) nvlist_remove_all(tprops, PROP_ALIAS);
}
val = NULL;
(void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_SECRET, &val);
if (val) {
char bsecret[MAX_BASE64_LEN];
ret = it_val_pass(PROP_TARGET_CHAP_SECRET, val, errs);
if (ret == 0) {
(void) memset(bsecret, 0, MAX_BASE64_LEN);
ret = iscsi_binary_to_base64_str((uint8_t *)val,
strlen(val), bsecret, MAX_BASE64_LEN);
if (ret == 0) {
ret = nvlist_add_string(tprops,
PROP_TARGET_CHAP_SECRET, bsecret);
}
}
}
if (ret == 0) {
ret = it_validate_tgtprops(tprops, errs);
}
if (ret != 0) {
if (tprops) {
nvlist_free(tprops);
}
return (ret);
}
if (tgt->tgt_properties) {
nvlist_free(tgt->tgt_properties);
}
tgt->tgt_properties = tprops;
free_empty_errlist(errlist);
return (0);
}
int
it_tgt_delete(it_config_t *cfg, it_tgt_t *tgt, boolean_t force)
{
int ret;
it_tgt_t *ptgt;
it_tgt_t *prev = NULL;
stmfDevid devid;
stmfTargetProperties props;
if (!cfg || !tgt) {
return (0);
}
ptgt = cfg->config_tgt_list;
while (ptgt != NULL) {
if (strcasecmp(tgt->tgt_name, ptgt->tgt_name) == 0) {
break;
}
prev = ptgt;
ptgt = ptgt->tgt_next;
}
if (!ptgt) {
return (0);
}
ret = stmfDevidFromIscsiName(ptgt->tgt_name, &devid);
if (ret != STMF_STATUS_SUCCESS) {
return (EINVAL);
}
ret = stmfGetTargetProperties(&devid, &props);
if (ret == STMF_STATUS_SUCCESS) {
if (props.status == STMF_TARGET_PORT_ONLINE) {
if (!force) {
return (EBUSY);
}
ret = stmfOfflineTarget(&devid);
if (ret != 0) {
return (EBUSY);
}
}
}
if (prev) {
prev->tgt_next = ptgt->tgt_next;
} else {
cfg->config_tgt_list = ptgt->tgt_next;
}
ptgt->tgt_next = NULL;
cfg->config_tgt_count--;
it_tgt_free(ptgt);
return (0);
}
void
it_tgt_free(it_tgt_t *tgt)
{
it_tgt_free_cmn(tgt);
}
int
it_tpgt_create(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t **tpgt,
char *tpg_name, uint16_t tpgt_tag)
{
it_tpgt_t *ptr = NULL;
it_tpgt_t *cfgt;
char tagid_used[MAXTAG + 1];
uint16_t tagid = ISCSIT_DEFAULT_TPGT;
if (!cfg || !tgt || !tpgt || !tpg_name) {
return (EINVAL);
}
(void) memset(&(tagid_used[0]), 0, sizeof (tagid_used));
cfgt = tgt->tgt_tpgt_list;
while (cfgt != NULL) {
tagid_used[cfgt->tpgt_tag] = 1;
if (strcmp(tpg_name, cfgt->tpgt_tpg_name) == 0) {
return (EEXIST);
}
if (cfgt->tpgt_tag > tagid) {
tagid = cfgt->tpgt_tag;
}
cfgt = cfgt->tpgt_next;
}
if ((tpgt_tag > ISCSIT_DEFAULT_TPGT) && (tpgt_tag < MAXTAG) &&
(tagid_used[tpgt_tag] == 0)) {
tagid = tpgt_tag;
} else if (tagid == MAXTAG) {
tagid = ISCSIT_DEFAULT_TPGT + 1;
for (; tagid < MAXTAG; tagid++) {
if (tagid_used[tagid] == 0) {
break;
}
}
if (tagid >= MAXTAG) {
return (E2BIG);
}
} else {
tagid++;
}
ptr = calloc(1, sizeof (it_tpgt_t));
if (!ptr) {
return (ENOMEM);
}
(void) strlcpy(ptr->tpgt_tpg_name, tpg_name,
sizeof (ptr->tpgt_tpg_name));
ptr->tpgt_generation = 1;
ptr->tpgt_tag = tagid;
ptr->tpgt_next = tgt->tgt_tpgt_list;
tgt->tgt_tpgt_list = ptr;
tgt->tgt_tpgt_count++;
tgt->tgt_generation++;
*tpgt = ptr;
return (0);
}
void
it_tpgt_delete(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t *tpgt)
{
it_tpgt_t *ptr;
it_tpgt_t *prev = NULL;
if (!cfg || !tgt || !tpgt) {
return;
}
ptr = tgt->tgt_tpgt_list;
while (ptr) {
if (ptr->tpgt_tag == tpgt->tpgt_tag) {
break;
}
prev = ptr;
ptr = ptr->tpgt_next;
}
if (!ptr) {
return;
}
if (prev) {
prev->tpgt_next = ptr->tpgt_next;
} else {
tgt->tgt_tpgt_list = ptr->tpgt_next;
}
ptr->tpgt_next = NULL;
tgt->tgt_tpgt_count--;
tgt->tgt_generation++;
it_tpgt_free(ptr);
}
void
it_tpgt_free(it_tpgt_t *tpgt)
{
it_tpgt_free_cmn(tpgt);
}
int
it_tpg_create(it_config_t *cfg, it_tpg_t **tpg, char *tpg_name,
char *portal_ip_port)
{
int ret;
it_tpg_t *ptr;
it_portal_t *portal = NULL;
if (!cfg || !tpg || !tpg_name || !portal_ip_port) {
return (EINVAL);
}
*tpg = NULL;
ptr = cfg->config_tpg_list;
while (ptr) {
if (strcmp(tpg_name, ptr->tpg_name) == 0) {
break;
}
ptr = ptr->tpg_next;
}
if (ptr) {
return (EEXIST);
}
ptr = calloc(1, sizeof (it_tpg_t));
if (!ptr) {
return (ENOMEM);
}
ptr->tpg_generation = 1;
(void) strlcpy(ptr->tpg_name, tpg_name, sizeof (ptr->tpg_name));
ret = it_portal_create(cfg, ptr, &portal, portal_ip_port);
if (ret != 0) {
free(ptr);
return (ret);
}
ptr->tpg_next = cfg->config_tpg_list;
cfg->config_tpg_list = ptr;
cfg->config_tpg_count++;
*tpg = ptr;
return (0);
}
int
it_tpg_delete(it_config_t *cfg, it_tpg_t *tpg, boolean_t force)
{
it_tpg_t *ptr;
it_tpg_t *prev = NULL;
it_tgt_t *tgt;
it_tpgt_t *tpgt;
it_tpgt_t *ntpgt;
if (!cfg || !tpg) {
return (EINVAL);
}
ptr = cfg->config_tpg_list;
while (ptr) {
if (strcmp(ptr->tpg_name, tpg->tpg_name) == 0) {
break;
}
prev = ptr;
ptr = ptr->tpg_next;
}
if (!ptr) {
return (0);
}
tgt = cfg->config_tgt_list;
while (tgt) {
tpgt = tgt->tgt_tpgt_list;
while (tpgt) {
ntpgt = tpgt->tpgt_next;
if (strcmp(tpgt->tpgt_tpg_name, tpg->tpg_name)
== 0) {
if (!force) {
return (EBUSY);
}
it_tpgt_delete(cfg, tgt, tpgt);
}
tpgt = ntpgt;
}
tgt = tgt->tgt_next;
}
if (prev) {
prev->tpg_next = ptr->tpg_next;
} else {
cfg->config_tpg_list = ptr->tpg_next;
}
ptr->tpg_next = NULL;
cfg->config_tpg_count--;
it_tpg_free(ptr);
return (0);
}
void
it_tpg_free(it_tpg_t *tpg)
{
it_tpg_free_cmn(tpg);
}
int
it_portal_create(it_config_t *cfg, it_tpg_t *tpg, it_portal_t **portal,
char *portal_ip_port)
{
struct sockaddr_storage sa;
it_portal_t *ptr;
it_tpg_t *ctpg = NULL;
if (!cfg || !tpg || !portal || !portal_ip_port) {
return (EINVAL);
}
if ((it_common_convert_sa(portal_ip_port, &sa, ISCSI_LISTEN_PORT))
== NULL) {
return (EINVAL);
}
ctpg = cfg->config_tpg_list;
while (ctpg) {
ptr = ctpg->tpg_portal_list;
for (; ptr != NULL; ptr = ptr->portal_next) {
if (it_sa_compare(&(ptr->portal_addr), &sa) != 0) {
continue;
}
if (strcmp(ctpg->tpg_name, tpg->tpg_name) == 0) {
return (0);
} else {
return (EEXIST);
}
}
ctpg = ctpg->tpg_next;
}
ptr = calloc(1, sizeof (it_portal_t));
if (!ptr) {
return (ENOMEM);
}
(void) memcpy(&(ptr->portal_addr), &sa,
sizeof (struct sockaddr_storage));
ptr->portal_next = tpg->tpg_portal_list;
tpg->tpg_portal_list = ptr;
tpg->tpg_portal_count++;
tpg->tpg_generation++;
return (0);
}
void
it_portal_delete(it_config_t *cfg, it_tpg_t *tpg, it_portal_t *portal)
{
it_portal_t *ptr;
it_portal_t *prev = NULL;
if (!cfg || !tpg || !portal) {
return;
}
ptr = tpg->tpg_portal_list;
while (ptr) {
if (memcmp(&(ptr->portal_addr), &(portal->portal_addr),
sizeof (ptr->portal_addr)) == 0) {
break;
}
prev = ptr;
ptr = ptr->portal_next;
}
if (!ptr) {
return;
}
if (prev) {
prev->portal_next = ptr->portal_next;
} else {
tpg->tpg_portal_list = ptr->portal_next;
}
tpg->tpg_portal_count--;
tpg->tpg_generation++;
free(ptr);
}
int
it_ini_create(it_config_t *cfg, it_ini_t **ini, char *ini_node_name)
{
it_ini_t *ptr;
if (!cfg || !ini || !ini_node_name) {
return (EINVAL);
}
if (!validate_iscsi_name(ini_node_name)) {
return (EFAULT);
}
ptr = cfg->config_ini_list;
while (ptr) {
if (strcasecmp(ptr->ini_name, ini_node_name) == 0) {
break;
}
ptr = ptr->ini_next;
}
if (ptr) {
return (EEXIST);
}
ptr = calloc(1, sizeof (it_ini_t));
if (!ptr) {
return (ENOMEM);
}
(void) strlcpy(ptr->ini_name, ini_node_name, sizeof (ptr->ini_name));
ptr->ini_generation = 1;
ptr->ini_next = cfg->config_ini_list;
cfg->config_ini_list = ptr;
cfg->config_ini_count++;
*ini = ptr;
return (0);
}
int
it_ini_setprop(it_ini_t *ini, nvlist_t *proplist, nvlist_t **errlist)
{
int ret;
nvlist_t *errs = NULL;
nvlist_t *iprops = NULL;
char *val = NULL;
if (!ini || !proplist) {
return (EINVAL);
}
if (errlist) {
(void) nvlist_alloc(&errs, 0, 0);
*errlist = errs;
}
if (ini->ini_properties) {
ret = nvlist_dup(ini->ini_properties, &iprops, 0);
} else {
ret = nvlist_alloc(&iprops, NV_UNIQUE_NAME, 0);
}
if (ret != 0) {
return (ret);
}
ret = nvlist_merge(iprops, proplist, 0);
if (ret != 0) {
nvlist_free(iprops);
return (ret);
}
if ((nvlist_lookup_string(proplist, PROP_CHAP_USER, &val)) == 0) {
if (strcasecmp(val, "none") == 0) {
(void) nvlist_remove_all(iprops, PROP_CHAP_USER);
}
}
if ((nvlist_lookup_string(proplist, PROP_CHAP_SECRET, &val)) == 0) {
char bsecret[MAX_BASE64_LEN];
ret = it_val_pass(PROP_CHAP_SECRET, val, errs);
if (ret == 0) {
(void) memset(bsecret, 0, MAX_BASE64_LEN);
ret = iscsi_binary_to_base64_str((uint8_t *)val,
strlen(val), bsecret, MAX_BASE64_LEN);
if (ret == 0) {
ret = nvlist_add_string(iprops,
PROP_CHAP_SECRET, bsecret);
}
}
}
if (ret == 0) {
ret = it_validate_iniprops(iprops, errs);
}
if (ret != 0) {
if (iprops) {
nvlist_free(iprops);
}
return (ret);
}
if (ini->ini_properties) {
nvlist_free(ini->ini_properties);
}
ini->ini_properties = iprops;
free_empty_errlist(errlist);
return (0);
}
void
it_ini_delete(it_config_t *cfg, it_ini_t *ini)
{
it_ini_t *ptr;
it_ini_t *prev = NULL;
if (!cfg || !ini) {
return;
}
ptr = cfg->config_ini_list;
while (ptr) {
if (strcasecmp(ptr->ini_name, ini->ini_name) == 0) {
break;
}
prev = ptr;
ptr = ptr->ini_next;
}
if (!ptr) {
return;
}
if (prev) {
prev->ini_next = ptr->ini_next;
} else {
cfg->config_ini_list = ptr->ini_next;
}
ptr->ini_next = NULL;
cfg->config_ini_count--;
it_ini_free(ptr);
}
void
it_ini_free(it_ini_t *ini)
{
it_ini_free_cmn(ini);
}
static int
it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs)
{
int errcnt = 0;
nvpair_t *nvp = NULL;
data_type_t nvtype;
char *name;
char *val;
char *auth = NULL;
if (!nvl) {
return (0);
}
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
name = nvpair_name(nvp);
nvtype = nvpair_type(nvp);
if (!name) {
continue;
}
val = NULL;
if (strcmp(name, PROP_TARGET_CHAP_USER) == 0) {
if (nvtype != DATA_TYPE_STRING) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
continue;
}
} else if (strcmp(name, PROP_TARGET_CHAP_SECRET) == 0) {
if (nvtype == DATA_TYPE_STRING) {
(void) nvpair_value_string(nvp, &val);
}
if (!val) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
continue;
}
} else if (strcmp(name, PROP_ALIAS) == 0) {
if (nvtype != DATA_TYPE_STRING) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
continue;
}
} else if (strcmp(name, PROP_AUTH) == 0) {
if (nvtype == DATA_TYPE_STRING) {
val = NULL;
(void) nvpair_value_string(nvp, &val);
}
if (!val) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
continue;
}
if ((strcmp(val, PA_AUTH_NONE) != 0) &&
(strcmp(val, PA_AUTH_CHAP) != 0) &&
(strcmp(val, PA_AUTH_RADIUS) != 0) &&
(strcmp(val, "default") != 0)) {
PROPERR(errs, val, gettext(
"must be none, chap, radius or default"));
errcnt++;
}
auth = val;
continue;
} else if (strcmp(name, PROP_OLD_TARGET_NAME) == 0) {
continue;
} else {
PROPERR(errs, name, gettext("unrecognized property"));
errcnt++;
}
}
if (errcnt) {
return (EINVAL);
}
if (auth && (strcmp(auth, "default") == 0)) {
(void) nvlist_remove_all(nvl, PROP_AUTH);
}
return (0);
}
static int
it_validate_configprops(nvlist_t *nvl, nvlist_t *errs)
{
int errcnt = 0;
nvpair_t *nvp = NULL;
data_type_t nvtype;
char *name;
char *val;
struct sockaddr_storage sa;
boolean_t update_rad_server = B_FALSE;
char *rad_server;
char *auth = NULL;
if (!nvl) {
return (0);
}
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
name = nvpair_name(nvp);
nvtype = nvpair_type(nvp);
if (!name) {
continue;
}
val = NULL;
if (nvtype == DATA_TYPE_STRING) {
(void) nvpair_value_string(nvp, &val);
}
if (strcmp(name, PROP_ALIAS) == 0) {
if (!val) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
}
} else if (strcmp(name, PROP_AUTH) == 0) {
if (!val) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
continue;
}
if ((strcmp(val, PA_AUTH_NONE) != 0) &&
(strcmp(val, PA_AUTH_CHAP) != 0) &&
(strcmp(val, PA_AUTH_RADIUS) != 0)) {
PROPERR(errs, PROP_AUTH,
gettext("must be none, chap or radius"));
errcnt++;
}
auth = val;
} else if (strcmp(name, PROP_ISNS_ENABLED) == 0) {
if (nvtype != DATA_TYPE_BOOLEAN_VALUE) {
PROPERR(errs, name,
gettext("must be a boolean value"));
errcnt++;
}
} else if (strcmp(name, PROP_ISNS_SERVER) == 0) {
char **arr = NULL;
uint32_t acount = 0;
(void) nvlist_lookup_string_array(nvl, name,
&arr, &acount);
while (acount > 0) {
if (strcasecmp(arr[acount - 1], "none") == 0) {
break;
}
if ((it_common_convert_sa(arr[acount - 1],
&sa, 0)) == NULL) {
PROPERR(errs, arr[acount - 1],
gettext("invalid address"));
errcnt++;
}
acount--;
}
} else if (strcmp(name, PROP_RADIUS_SECRET) == 0) {
if (!val) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
continue;
}
} else if (strcmp(name, PROP_RADIUS_SERVER) == 0) {
struct sockaddr_storage sa;
if (!val) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
continue;
}
if ((it_common_convert_sa(val, &sa,
DEFAULT_RADIUS_PORT)) == NULL) {
PROPERR(errs, name,
gettext("invalid address"));
errcnt++;
} else {
if (sockaddr_to_str(&sa, &rad_server) == 0) {
update_rad_server = B_TRUE;
}
}
} else {
PROPERR(errs, name, gettext("unrecognized property"));
errcnt++;
}
}
if (update_rad_server) {
(void) nvlist_add_string(nvl, PROP_RADIUS_SERVER, rad_server);
free(rad_server);
}
if (auth) {
if (strcmp(auth, PA_AUTH_RADIUS) == 0) {
if (!nvlist_exists(nvl, PROP_RADIUS_SERVER)) {
PROPERR(errs, PROP_RADIUS_SERVER,
gettext("missing required property"));
errcnt++;
}
if (!nvlist_exists(nvl, PROP_RADIUS_SECRET)) {
PROPERR(errs, PROP_RADIUS_SECRET,
gettext("missing required property"));
errcnt++;
}
}
}
if (errcnt) {
return (EINVAL);
}
return (0);
}
static int
it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs)
{
int errcnt = 0;
nvpair_t *nvp = NULL;
data_type_t nvtype;
char *name;
char *val;
if (!nvl) {
return (0);
}
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
name = nvpair_name(nvp);
nvtype = nvpair_type(nvp);
if (!name) {
continue;
}
if (strcmp(name, PROP_CHAP_USER) == 0) {
if (nvtype != DATA_TYPE_STRING) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
continue;
}
} else if (strcmp(name, PROP_CHAP_SECRET) == 0) {
if (nvtype == DATA_TYPE_STRING) {
val = NULL;
(void) nvpair_value_string(nvp, &val);
}
if (!val) {
PROPERR(errs, name,
gettext("must be a string value"));
errcnt++;
continue;
}
} else {
PROPERR(errs, name, gettext("unrecognized property"));
errcnt++;
}
}
if (errcnt) {
return (EINVAL);
}
return (0);
}
static int
it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix)
{
int ret;
uuid_t id;
char id_str[UUID_PRINTABLE_STRING_LENGTH];
uuid_generate_random(id);
uuid_unparse(id, id_str);
if (opt_iqn_suffix) {
ret = snprintf(iqn_buf, iqn_buf_len, DEFAULT_IQN
"%02d:%s.%s", TARGET_NAME_VERS, id_str, opt_iqn_suffix);
} else {
ret = snprintf(iqn_buf, iqn_buf_len, DEFAULT_IQN
"%02d:%s", TARGET_NAME_VERS, id_str);
}
if (ret > iqn_buf_len) {
return (1);
}
return (0);
}
static int
it_val_pass(char *name, char *val, nvlist_t *e)
{
size_t sz;
if (!name || !val) {
return (EINVAL);
}
sz = strlen(val);
if (sz < 12) {
PROPERR(e, name, gettext("secret too short"));
} else if (sz > 255) {
PROPERR(e, name, gettext("secret too long"));
} else {
return (0);
}
return (1);
}
boolean_t
validate_iscsi_name(char *in_name)
{
size_t in_len;
int i;
char month[3];
if (in_name == NULL) {
return (B_FALSE);
}
in_len = strlen(in_name);
if (in_len < 12) {
return (B_FALSE);
}
if (IS_IQN_NAME(in_name)) {
if ((!isdigit(in_name[4])) ||
(!isdigit(in_name[5])) ||
(!isdigit(in_name[6])) ||
(!isdigit(in_name[7])) ||
(in_name[8] != '-') ||
(!isdigit(in_name[9])) ||
(!isdigit(in_name[10])) ||
(in_name[11] != '.')) {
return (B_FALSE);
}
(void) strncpy(month, &(in_name[9]), 2);
month[2] = '\0';
i = atoi(month);
if ((i < 0) || (i > 12)) {
return (B_FALSE);
}
for (i = 12; i < in_len; i++) {
char c = in_name[i];
if ((c != '-') && (c != '.') && (c != ':') &&
!isalpha(c) && !isdigit(c)) {
return (B_FALSE);
}
}
in_len = mbstowcs(NULL, in_name, 0);
if (in_len > ISCSI_NAME_LEN_MAX) {
return (B_FALSE);
}
} else if (IS_EUI_NAME(in_name)) {
if (in_len != 20) {
return (B_FALSE);
}
for (i = 4; i < in_len; i++) {
if (!isxdigit(in_name[i])) {
return (B_FALSE);
}
}
} else {
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
is_iscsit_enabled(void)
{
char *state;
state = smf_get_state(ISCSIT_FMRI);
if (state != NULL) {
if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
free(state);
return (B_TRUE);
}
free(state);
}
return (B_FALSE);
}
void
canonical_iscsi_name(char *tgt)
{
if (IS_IQN_NAME(tgt)) {
iqnstr(tgt);
} else {
euistr(tgt);
}
}
static void
iqnstr(char *s)
{
if (s != NULL) {
while (*s) {
*s = tolower(*s);
s++;
}
}
}
static void
euistr(char *s)
{
if (s != NULL) {
char *l = s + 4;
while (*l) {
*l = toupper(*l);
l++;
}
}
}
static void
free_empty_errlist(nvlist_t **errlist)
{
if (errlist != NULL && *errlist != NULL) {
assert(fnvlist_num_pairs(*errlist) == 0);
nvlist_free(*errlist);
*errlist = NULL;
}
}