Index: head/usr.sbin/ctld/chap.c =================================================================== --- head/usr.sbin/ctld/chap.c (revision 328336) +++ head/usr.sbin/ctld/chap.c (revision 328337) @@ -1,422 +1,424 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "ctld.h" static void chap_compute_md5(const char id, const char *secret, const void *challenge, size_t challenge_len, void *response, size_t response_len) { MD5_CTX ctx; assert(response_len == CHAP_DIGEST_LEN); MD5Init(&ctx); MD5Update(&ctx, &id, sizeof(id)); MD5Update(&ctx, secret, strlen(secret)); MD5Update(&ctx, challenge, challenge_len); MD5Final(response, &ctx); } static int chap_hex2int(const char hex) { switch (hex) { case '0': return (0x00); case '1': return (0x01); case '2': return (0x02); case '3': return (0x03); case '4': return (0x04); case '5': return (0x05); case '6': return (0x06); case '7': return (0x07); case '8': return (0x08); case '9': return (0x09); case 'a': case 'A': return (0x0a); case 'b': case 'B': return (0x0b); case 'c': case 'C': return (0x0c); case 'd': case 'D': return (0x0d); case 'e': case 'E': return (0x0e); case 'f': case 'F': return (0x0f); default: return (-1); } } static int chap_b642bin(const char *b64, void **binp, size_t *bin_lenp) { char *bin; int b64_len, bin_len; b64_len = strlen(b64); bin_len = (b64_len + 3) / 4 * 3; bin = calloc(bin_len, 1); if (bin == NULL) log_err(1, "calloc"); bin_len = b64_pton(b64, bin, bin_len); if (bin_len < 0) { log_warnx("malformed base64 variable"); free(bin); return (-1); } *binp = bin; *bin_lenp = bin_len; return (0); } /* * XXX: Review this _carefully_. */ static int chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp) { int i, hex_len, nibble; bool lo = true; /* As opposed to 'hi'. */ char *bin; size_t bin_off, bin_len; if (strncasecmp(hex, "0b", strlen("0b")) == 0) return (chap_b642bin(hex + 2, binp, bin_lenp)); if (strncasecmp(hex, "0x", strlen("0x")) != 0) { log_warnx("malformed variable, should start with \"0x\"" " or \"0b\""); return (-1); } hex += strlen("0x"); hex_len = strlen(hex); if (hex_len < 1) { log_warnx("malformed variable; doesn't contain anything " "but \"0x\""); return (-1); } bin_len = hex_len / 2 + hex_len % 2; bin = calloc(bin_len, 1); if (bin == NULL) log_err(1, "calloc"); bin_off = bin_len - 1; for (i = hex_len - 1; i >= 0; i--) { nibble = chap_hex2int(hex[i]); if (nibble < 0) { log_warnx("malformed variable, invalid char \"%c\"", hex[i]); free(bin); return (-1); } assert(bin_off < bin_len); if (lo) { bin[bin_off] = nibble; lo = false; } else { bin[bin_off] |= nibble << 4; bin_off--; lo = true; } } *binp = bin; *bin_lenp = bin_len; return (0); } #ifdef USE_BASE64 static char * chap_bin2hex(const char *bin, size_t bin_len) { unsigned char *b64, *tmp; size_t b64_len; b64_len = (bin_len + 2) / 3 * 4 + 3; /* +2 for "0b", +1 for '\0'. */ b64 = malloc(b64_len); if (b64 == NULL) log_err(1, "malloc"); tmp = b64; tmp += sprintf(tmp, "0b"); b64_ntop(bin, bin_len, tmp, b64_len - 2); return (b64); } #else static char * chap_bin2hex(const char *bin, size_t bin_len) { unsigned char *hex, *tmp, ch; size_t hex_len; size_t i; hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ hex = malloc(hex_len); if (hex == NULL) log_err(1, "malloc"); tmp = hex; tmp += sprintf(tmp, "0x"); for (i = 0; i < bin_len; i++) { ch = bin[i]; tmp += sprintf(tmp, "%02x", ch); } return (hex); } #endif /* !USE_BASE64 */ struct chap * chap_new(void) { struct chap *chap; chap = calloc(1, sizeof(*chap)); if (chap == NULL) log_err(1, "calloc"); /* * Generate the challenge. */ arc4random_buf(chap->chap_challenge, sizeof(chap->chap_challenge)); arc4random_buf(&chap->chap_id, sizeof(chap->chap_id)); return (chap); } char * chap_get_id(const struct chap *chap) { char *chap_i; int ret; ret = asprintf(&chap_i, "%d", chap->chap_id); if (ret < 0) log_err(1, "asprintf"); return (chap_i); } char * chap_get_challenge(const struct chap *chap) { char *chap_c; chap_c = chap_bin2hex(chap->chap_challenge, sizeof(chap->chap_challenge)); return (chap_c); } static int chap_receive_bin(struct chap *chap, void *response, size_t response_len) { if (response_len != sizeof(chap->chap_response)) { log_debugx("got CHAP response with invalid length; " "got %zd, should be %zd", response_len, sizeof(chap->chap_response)); return (1); } memcpy(chap->chap_response, response, response_len); return (0); } int chap_receive(struct chap *chap, const char *response) { void *response_bin; size_t response_bin_len; int error; error = chap_hex2bin(response, &response_bin, &response_bin_len); if (error != 0) { log_debugx("got incorrectly encoded CHAP response \"%s\"", response); return (1); } error = chap_receive_bin(chap, response_bin, response_bin_len); free(response_bin); return (error); } int chap_authenticate(struct chap *chap, const char *secret) { char expected_response[CHAP_DIGEST_LEN]; chap_compute_md5(chap->chap_id, secret, chap->chap_challenge, sizeof(chap->chap_challenge), expected_response, sizeof(expected_response)); if (memcmp(chap->chap_response, expected_response, sizeof(expected_response)) != 0) { return (-1); } return (0); } void chap_delete(struct chap *chap) { free(chap); } struct rchap * rchap_new(const char *secret) { struct rchap *rchap; rchap = calloc(1, sizeof(*rchap)); if (rchap == NULL) log_err(1, "calloc"); rchap->rchap_secret = checked_strdup(secret); return (rchap); } static void rchap_receive_bin(struct rchap *rchap, const unsigned char id, const void *challenge, size_t challenge_len) { rchap->rchap_id = id; rchap->rchap_challenge = calloc(challenge_len, 1); if (rchap->rchap_challenge == NULL) log_err(1, "calloc"); memcpy(rchap->rchap_challenge, challenge, challenge_len); rchap->rchap_challenge_len = challenge_len; } int rchap_receive(struct rchap *rchap, const char *id, const char *challenge) { unsigned char id_bin; void *challenge_bin; size_t challenge_bin_len; int error; id_bin = strtoul(id, NULL, 10); error = chap_hex2bin(challenge, &challenge_bin, &challenge_bin_len); if (error != 0) { log_debugx("got incorrectly encoded CHAP challenge \"%s\"", challenge); return (1); } rchap_receive_bin(rchap, id_bin, challenge_bin, challenge_bin_len); free(challenge_bin); return (0); } static void rchap_get_response_bin(struct rchap *rchap, void **responsep, size_t *response_lenp) { void *response_bin; size_t response_bin_len = CHAP_DIGEST_LEN; response_bin = calloc(response_bin_len, 1); if (response_bin == NULL) log_err(1, "calloc"); chap_compute_md5(rchap->rchap_id, rchap->rchap_secret, rchap->rchap_challenge, rchap->rchap_challenge_len, response_bin, response_bin_len); *responsep = response_bin; *response_lenp = response_bin_len; } char * rchap_get_response(struct rchap *rchap) { void *response; size_t response_len; char *chap_r; rchap_get_response_bin(rchap, &response, &response_len); chap_r = chap_bin2hex(response, response_len); free(response); return (chap_r); } void rchap_delete(struct rchap *rchap) { free(rchap->rchap_secret); free(rchap->rchap_challenge); free(rchap); } Index: head/usr.sbin/ctld/isns.c =================================================================== --- head/usr.sbin/ctld/isns.c (revision 328336) +++ head/usr.sbin/ctld/isns.c (revision 328337) @@ -1,252 +1,254 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 Alexander Motin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "ctld.h" #include "isns.h" struct isns_req * isns_req_alloc(void) { struct isns_req *req; req = calloc(sizeof(struct isns_req), 1); if (req == NULL) { log_err(1, "calloc"); return (NULL); } req->ir_buflen = sizeof(struct isns_hdr); req->ir_usedlen = 0; req->ir_buf = calloc(req->ir_buflen, 1); if (req->ir_buf == NULL) { free(req); log_err(1, "calloc"); return (NULL); } return (req); } struct isns_req * isns_req_create(uint16_t func, uint16_t flags) { struct isns_req *req; struct isns_hdr *hdr; req = isns_req_alloc(); req->ir_usedlen = sizeof(struct isns_hdr); hdr = (struct isns_hdr *)req->ir_buf; be16enc(hdr->ih_version, ISNS_VERSION); be16enc(hdr->ih_function, func); be16enc(hdr->ih_flags, flags); return (req); } void isns_req_free(struct isns_req *req) { free(req->ir_buf); free(req); } static int isns_req_getspace(struct isns_req *req, uint32_t len) { void *newbuf; int newlen; if (req->ir_usedlen + len <= req->ir_buflen) return (0); newlen = 1 << flsl(req->ir_usedlen + len); newbuf = realloc(req->ir_buf, newlen); if (newbuf == NULL) { log_err(1, "realloc"); return (1); } req->ir_buf = newbuf; req->ir_buflen = newlen; return (0); } void isns_req_add(struct isns_req *req, uint32_t tag, uint32_t len, const void *value) { struct isns_tlv *tlv; uint32_t vlen; vlen = len + ((len & 3) ? (4 - (len & 3)) : 0); isns_req_getspace(req, sizeof(*tlv) + vlen); tlv = (struct isns_tlv *)&req->ir_buf[req->ir_usedlen]; be32enc(tlv->it_tag, tag); be32enc(tlv->it_length, vlen); memcpy(tlv->it_value, value, len); if (vlen != len) memset(&tlv->it_value[len], 0, vlen - len); req->ir_usedlen += sizeof(*tlv) + vlen; } void isns_req_add_delim(struct isns_req *req) { isns_req_add(req, 0, 0, NULL); } void isns_req_add_str(struct isns_req *req, uint32_t tag, const char *value) { isns_req_add(req, tag, strlen(value) + 1, value); } void isns_req_add_32(struct isns_req *req, uint32_t tag, uint32_t value) { uint32_t beval; be32enc(&beval, value); isns_req_add(req, tag, sizeof(value), &beval); } void isns_req_add_addr(struct isns_req *req, uint32_t tag, struct addrinfo *ai) { struct sockaddr_in *in4; struct sockaddr_in6 *in6; uint8_t buf[16]; switch (ai->ai_addr->sa_family) { case AF_INET: in4 = (struct sockaddr_in *)(void *)ai->ai_addr; memset(buf, 0, 10); buf[10] = 0xff; buf[11] = 0xff; memcpy(&buf[12], &in4->sin_addr, sizeof(in4->sin_addr)); isns_req_add(req, tag, sizeof(buf), buf); break; case AF_INET6: in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; isns_req_add(req, tag, sizeof(in6->sin6_addr), &in6->sin6_addr); break; default: log_errx(1, "Unsupported address family %d", ai->ai_addr->sa_family); } } void isns_req_add_port(struct isns_req *req, uint32_t tag, struct addrinfo *ai) { struct sockaddr_in *in4; struct sockaddr_in6 *in6; uint32_t buf; switch (ai->ai_addr->sa_family) { case AF_INET: in4 = (struct sockaddr_in *)(void *)ai->ai_addr; be32enc(&buf, ntohs(in4->sin_port)); isns_req_add(req, tag, sizeof(buf), &buf); break; case AF_INET6: in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; be32enc(&buf, ntohs(in6->sin6_port)); isns_req_add(req, tag, sizeof(buf), &buf); break; default: log_errx(1, "Unsupported address family %d", ai->ai_addr->sa_family); } } int isns_req_send(int s, struct isns_req *req) { struct isns_hdr *hdr; int res; hdr = (struct isns_hdr *)req->ir_buf; be16enc(hdr->ih_length, req->ir_usedlen - sizeof(*hdr)); be16enc(hdr->ih_flags, be16dec(hdr->ih_flags) | ISNS_FLAG_LAST | ISNS_FLAG_FIRST); be16enc(hdr->ih_transaction, 0); be16enc(hdr->ih_sequence, 0); res = write(s, req->ir_buf, req->ir_usedlen); return ((res < 0) ? -1 : 0); } int isns_req_receive(int s, struct isns_req *req) { struct isns_hdr *hdr; ssize_t res, len; req->ir_usedlen = 0; isns_req_getspace(req, sizeof(*hdr)); res = read(s, req->ir_buf, sizeof(*hdr)); if (res < (ssize_t)sizeof(*hdr)) return (-1); req->ir_usedlen = sizeof(*hdr); hdr = (struct isns_hdr *)req->ir_buf; if (be16dec(hdr->ih_version) != ISNS_VERSION) return (-1); if ((be16dec(hdr->ih_flags) & (ISNS_FLAG_LAST | ISNS_FLAG_FIRST)) != (ISNS_FLAG_LAST | ISNS_FLAG_FIRST)) return (-1); len = be16dec(hdr->ih_length); isns_req_getspace(req, len); res = read(s, &req->ir_buf[req->ir_usedlen], len); if (res < len) return (-1); req->ir_usedlen += len; return (0); } uint32_t isns_req_get_status(struct isns_req *req) { if (req->ir_usedlen < sizeof(struct isns_hdr) + 4) return (-1); return (be32dec(&req->ir_buf[sizeof(struct isns_hdr)])); } Index: head/usr.sbin/ctld/uclparse.c =================================================================== --- head/usr.sbin/ctld/uclparse.c (revision 328336) +++ head/usr.sbin/ctld/uclparse.c (revision 328337) @@ -1,916 +1,918 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2015 iXsystems Inc. * All rights reserved. * * This software was developed by Jakub Klama * under sponsorship from iXsystems Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include "ctld.h" static struct conf *conf = NULL; static int uclparse_toplevel(const ucl_object_t *); static int uclparse_chap(struct auth_group *, const ucl_object_t *); static int uclparse_chap_mutual(struct auth_group *, const ucl_object_t *); static int uclparse_lun(const char *, const ucl_object_t *); static int uclparse_auth_group(const char *, const ucl_object_t *); static int uclparse_portal_group(const char *, const ucl_object_t *); static int uclparse_target(const char *, const ucl_object_t *); static int uclparse_target_portal_group(struct target *, const ucl_object_t *); static int uclparse_target_lun(struct target *, const ucl_object_t *); static int uclparse_chap(struct auth_group *auth_group, const ucl_object_t *obj) { const struct auth *ca; const ucl_object_t *user, *secret; user = ucl_object_find_key(obj, "user"); if (!user || user->type != UCL_STRING) { log_warnx("chap section in auth-group \"%s\" is missing " "\"user\" string key", auth_group->ag_name); return (1); } secret = ucl_object_find_key(obj, "secret"); if (!secret || secret->type != UCL_STRING) { log_warnx("chap section in auth-group \"%s\" is missing " "\"secret\" string key", auth_group->ag_name); } ca = auth_new_chap(auth_group, ucl_object_tostring(user), ucl_object_tostring(secret)); if (ca == NULL) return (1); return (0); } static int uclparse_chap_mutual(struct auth_group *auth_group, const ucl_object_t *obj) { const struct auth *ca; const ucl_object_t *user, *secret, *mutual_user; const ucl_object_t *mutual_secret; user = ucl_object_find_key(obj, "user"); if (!user || user->type != UCL_STRING) { log_warnx("chap-mutual section in auth-group \"%s\" is missing " "\"user\" string key", auth_group->ag_name); return (1); } secret = ucl_object_find_key(obj, "secret"); if (!secret || secret->type != UCL_STRING) { log_warnx("chap-mutual section in auth-group \"%s\" is missing " "\"secret\" string key", auth_group->ag_name); return (1); } mutual_user = ucl_object_find_key(obj, "mutual-user"); if (!user || user->type != UCL_STRING) { log_warnx("chap-mutual section in auth-group \"%s\" is missing " "\"mutual-user\" string key", auth_group->ag_name); return (1); } mutual_secret = ucl_object_find_key(obj, "mutual-secret"); if (!secret || secret->type != UCL_STRING) { log_warnx("chap-mutual section in auth-group \"%s\" is missing " "\"mutual-secret\" string key", auth_group->ag_name); return (1); } ca = auth_new_chap_mutual(auth_group, ucl_object_tostring(user), ucl_object_tostring(secret), ucl_object_tostring(mutual_user), ucl_object_tostring(mutual_secret)); if (ca == NULL) return (1); return (0); } static int uclparse_target_portal_group(struct target *target, const ucl_object_t *obj) { struct portal_group *tpg; struct auth_group *tag = NULL; struct port *tp; const ucl_object_t *portal_group, *auth_group; portal_group = ucl_object_find_key(obj, "name"); if (!portal_group || portal_group->type != UCL_STRING) { log_warnx("portal-group section in target \"%s\" is missing " "\"name\" string key", target->t_name); return (1); } auth_group = ucl_object_find_key(obj, "auth-group-name"); if (auth_group && auth_group->type != UCL_STRING) { log_warnx("portal-group section in target \"%s\" is missing " "\"auth-group-name\" string key", target->t_name); return (1); } tpg = portal_group_find(conf, ucl_object_tostring(portal_group)); if (tpg == NULL) { log_warnx("unknown portal-group \"%s\" for target " "\"%s\"", ucl_object_tostring(portal_group), target->t_name); return (1); } if (auth_group) { tag = auth_group_find(conf, ucl_object_tostring(auth_group)); if (tag == NULL) { log_warnx("unknown auth-group \"%s\" for target " "\"%s\"", ucl_object_tostring(auth_group), target->t_name); return (1); } } tp = port_new(conf, target, tpg); if (tp == NULL) { log_warnx("can't link portal-group \"%s\" to target " "\"%s\"", ucl_object_tostring(portal_group), target->t_name); return (1); } tp->p_auth_group = tag; return (0); } static int uclparse_target_lun(struct target *target, const ucl_object_t *obj) { struct lun *lun; uint64_t tmp; if (obj->type == UCL_INT) { char *name; tmp = ucl_object_toint(obj); if (tmp >= MAX_LUNS) { log_warnx("LU number %ju in target \"%s\" is too big", tmp, target->t_name); return (1); } asprintf(&name, "%s,lun,%ju", target->t_name, tmp); lun = lun_new(conf, name); if (lun == NULL) return (1); lun_set_scsiname(lun, name); target->t_luns[tmp] = lun; return (0); } if (obj->type == UCL_OBJECT) { const ucl_object_t *num = ucl_object_find_key(obj, "number"); const ucl_object_t *name = ucl_object_find_key(obj, "name"); if (num == NULL || num->type != UCL_INT) { log_warnx("lun section in target \"%s\" is missing " "\"number\" integer property", target->t_name); return (1); } tmp = ucl_object_toint(num); if (tmp >= MAX_LUNS) { log_warnx("LU number %ju in target \"%s\" is too big", tmp, target->t_name); return (1); } if (name == NULL || name->type != UCL_STRING) { log_warnx("lun section in target \"%s\" is missing " "\"name\" string property", target->t_name); return (1); } lun = lun_find(conf, ucl_object_tostring(name)); if (lun == NULL) return (1); target->t_luns[tmp] = lun; } return (0); } static int uclparse_toplevel(const ucl_object_t *top) { ucl_object_iter_t it = NULL, iter = NULL; const ucl_object_t *obj = NULL, *child = NULL; int err = 0; /* Pass 1 - everything except targets */ while ((obj = ucl_iterate_object(top, &it, true))) { const char *key = ucl_object_key(obj); if (!strcmp(key, "debug")) { if (obj->type == UCL_INT) conf->conf_debug = ucl_object_toint(obj); else { log_warnx("\"debug\" property value is not integer"); return (1); } } if (!strcmp(key, "timeout")) { if (obj->type == UCL_INT) conf->conf_timeout = ucl_object_toint(obj); else { log_warnx("\"timeout\" property value is not integer"); return (1); } } if (!strcmp(key, "maxproc")) { if (obj->type == UCL_INT) conf->conf_maxproc = ucl_object_toint(obj); else { log_warnx("\"maxproc\" property value is not integer"); return (1); } } if (!strcmp(key, "pidfile")) { if (obj->type == UCL_STRING) conf->conf_pidfile_path = strdup( ucl_object_tostring(obj)); else { log_warnx("\"pidfile\" property value is not string"); return (1); } } if (!strcmp(key, "isns-server")) { if (obj->type == UCL_ARRAY) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { if (child->type != UCL_STRING) return (1); err = isns_new(conf, ucl_object_tostring(child)); if (err != 0) { return (1); } } } else { log_warnx("\"isns-server\" property value is " "not an array"); return (1); } } if (!strcmp(key, "isns-period")) { if (obj->type == UCL_INT) conf->conf_timeout = ucl_object_toint(obj); else { log_warnx("\"isns-period\" property value is not integer"); return (1); } } if (!strcmp(key, "isns-timeout")) { if (obj->type == UCL_INT) conf->conf_timeout = ucl_object_toint(obj); else { log_warnx("\"isns-timeout\" property value is not integer"); return (1); } } if (!strcmp(key, "auth-group")) { if (obj->type == UCL_OBJECT) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { uclparse_auth_group(ucl_object_key(child), child); } } else { log_warnx("\"auth-group\" section is not an object"); return (1); } } if (!strcmp(key, "portal-group")) { if (obj->type == UCL_OBJECT) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { uclparse_portal_group(ucl_object_key(child), child); } } else { log_warnx("\"portal-group\" section is not an object"); return (1); } } if (!strcmp(key, "lun")) { if (obj->type == UCL_OBJECT) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { uclparse_lun(ucl_object_key(child), child); } } else { log_warnx("\"lun\" section is not an object"); return (1); } } } /* Pass 2 - targets */ it = NULL; while ((obj = ucl_iterate_object(top, &it, true))) { const char *key = ucl_object_key(obj); if (!strcmp(key, "target")) { if (obj->type == UCL_OBJECT) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { uclparse_target(ucl_object_key(child), child); } } else { log_warnx("\"target\" section is not an object"); return (1); } } } return (0); } static int uclparse_auth_group(const char *name, const ucl_object_t *top) { struct auth_group *auth_group; const struct auth_name *an; const struct auth_portal *ap; ucl_object_iter_t it = NULL, it2 = NULL; const ucl_object_t *obj = NULL, *tmp = NULL; const char *key; int err; if (!strcmp(name, "default") && conf->conf_default_ag_defined == false) { auth_group = auth_group_find(conf, name); conf->conf_default_ag_defined = true; } else { auth_group = auth_group_new(conf, name); } if (auth_group == NULL) return (1); while ((obj = ucl_iterate_object(top, &it, true))) { key = ucl_object_key(obj); if (!strcmp(key, "auth-type")) { const char *value = ucl_object_tostring(obj); err = auth_group_set_type(auth_group, value); if (err) return (1); } if (!strcmp(key, "chap")) { if (obj->type != UCL_ARRAY) { log_warnx("\"chap\" property of " "auth-group \"%s\" is not an array", name); return (1); } it2 = NULL; while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (uclparse_chap(auth_group, tmp) != 0) return (1); } } if (!strcmp(key, "chap-mutual")) { if (obj->type != UCL_ARRAY) { log_warnx("\"chap-mutual\" property of " "auth-group \"%s\" is not an array", name); return (1); } it2 = NULL; while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (uclparse_chap_mutual(auth_group, tmp) != 0) return (1); } } if (!strcmp(key, "initiator-name")) { if (obj->type != UCL_ARRAY) { log_warnx("\"initiator-name\" property of " "auth-group \"%s\" is not an array", name); return (1); } it2 = NULL; while ((tmp = ucl_iterate_object(obj, &it2, true))) { const char *value = ucl_object_tostring(tmp); an = auth_name_new(auth_group, value); if (an == NULL) return (1); } } if (!strcmp(key, "initiator-portal")) { if (obj->type != UCL_ARRAY) { log_warnx("\"initiator-portal\" property of " "auth-group \"%s\" is not an array", name); return (1); } it2 = NULL; while ((tmp = ucl_iterate_object(obj, &it2, true))) { const char *value = ucl_object_tostring(tmp); ap = auth_portal_new(auth_group, value); if (ap == NULL) return (1); } } } return (0); } static int uclparse_portal_group(const char *name, const ucl_object_t *top) { struct portal_group *portal_group; ucl_object_iter_t it = NULL, it2 = NULL; const ucl_object_t *obj = NULL, *tmp = NULL; const char *key; if (strcmp(name, "default") == 0 && conf->conf_default_pg_defined == false) { portal_group = portal_group_find(conf, name); conf->conf_default_pg_defined = true; } else { portal_group = portal_group_new(conf, name); } if (portal_group == NULL) return (1); while ((obj = ucl_iterate_object(top, &it, true))) { key = ucl_object_key(obj); if (!strcmp(key, "discovery-auth-group")) { portal_group->pg_discovery_auth_group = auth_group_find(conf, ucl_object_tostring(obj)); if (portal_group->pg_discovery_auth_group == NULL) { log_warnx("unknown discovery-auth-group \"%s\" " "for portal-group \"%s\"", ucl_object_tostring(obj), portal_group->pg_name); return (1); } } if (!strcmp(key, "discovery-filter")) { if (obj->type != UCL_STRING) { log_warnx("\"discovery-filter\" property of " "portal-group \"%s\" is not a string", portal_group->pg_name); return (1); } if (portal_group_set_filter(portal_group, ucl_object_tostring(obj)) != 0) return (1); } if (!strcmp(key, "listen")) { if (obj->type == UCL_STRING) { if (portal_group_add_listen(portal_group, ucl_object_tostring(obj), false) != 0) return (1); } else if (obj->type == UCL_ARRAY) { while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (portal_group_add_listen( portal_group, ucl_object_tostring(tmp), false) != 0) return (1); } } else { log_warnx("\"listen\" property of " "portal-group \"%s\" is not a string", portal_group->pg_name); return (1); } } if (!strcmp(key, "listen-iser")) { if (obj->type == UCL_STRING) { if (portal_group_add_listen(portal_group, ucl_object_tostring(obj), true) != 0) return (1); } else if (obj->type == UCL_ARRAY) { while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (portal_group_add_listen( portal_group, ucl_object_tostring(tmp), true) != 0) return (1); } } else { log_warnx("\"listen\" property of " "portal-group \"%s\" is not a string", portal_group->pg_name); return (1); } } if (!strcmp(key, "redirect")) { if (obj->type != UCL_STRING) { log_warnx("\"listen\" property of " "portal-group \"%s\" is not a string", portal_group->pg_name); return (1); } if (portal_group_set_redirection(portal_group, ucl_object_tostring(obj)) != 0) return (1); } if (!strcmp(key, "options")) { if (obj->type != UCL_OBJECT) { log_warnx("\"options\" property of portal group " "\"%s\" is not an object", portal_group->pg_name); return (1); } while ((tmp = ucl_iterate_object(obj, &it2, true))) { option_new(&portal_group->pg_options, ucl_object_key(tmp), ucl_object_tostring_forced(tmp)); } } } return (0); } static int uclparse_target(const char *name, const ucl_object_t *top) { struct target *target; ucl_object_iter_t it = NULL, it2 = NULL; const ucl_object_t *obj = NULL, *tmp = NULL; const char *key; target = target_new(conf, name); if (target == NULL) return (1); while ((obj = ucl_iterate_object(top, &it, true))) { key = ucl_object_key(obj); if (!strcmp(key, "alias")) { if (obj->type != UCL_STRING) { log_warnx("\"alias\" property of target " "\"%s\" is not a string", target->t_name); return (1); } target->t_alias = strdup(ucl_object_tostring(obj)); } if (!strcmp(key, "auth-group")) { if (target->t_auth_group != NULL) { if (target->t_auth_group->ag_name != NULL) log_warnx("auth-group for target \"%s\" " "specified more than once", target->t_name); else log_warnx("cannot use both auth-group " "and explicit authorisations for " "target \"%s\"", target->t_name); return (1); } target->t_auth_group = auth_group_find(conf, ucl_object_tostring(obj)); if (target->t_auth_group == NULL) { log_warnx("unknown auth-group \"%s\" for target " "\"%s\"", ucl_object_tostring(obj), target->t_name); return (1); } } if (!strcmp(key, "auth-type")) { int error; if (target->t_auth_group != NULL) { if (target->t_auth_group->ag_name != NULL) { log_warnx("cannot use both auth-group and " "auth-type for target \"%s\"", target->t_name); return (1); } } else { target->t_auth_group = auth_group_new(conf, NULL); if (target->t_auth_group == NULL) return (1); target->t_auth_group->ag_target = target; } error = auth_group_set_type(target->t_auth_group, ucl_object_tostring(obj)); if (error != 0) return (1); } if (!strcmp(key, "chap")) { if (uclparse_chap(target->t_auth_group, obj) != 0) return (1); } if (!strcmp(key, "chap-mutual")) { if (uclparse_chap_mutual(target->t_auth_group, obj) != 0) return (1); } if (!strcmp(key, "initiator-name")) { const struct auth_name *an; if (target->t_auth_group != NULL) { if (target->t_auth_group->ag_name != NULL) { log_warnx("cannot use both auth-group and " "initiator-name for target \"%s\"", target->t_name); return (1); } } else { target->t_auth_group = auth_group_new(conf, NULL); if (target->t_auth_group == NULL) return (1); target->t_auth_group->ag_target = target; } an = auth_name_new(target->t_auth_group, ucl_object_tostring(obj)); if (an == NULL) return (1); } if (!strcmp(key, "initiator-portal")) { const struct auth_portal *ap; if (target->t_auth_group != NULL) { if (target->t_auth_group->ag_name != NULL) { log_warnx("cannot use both auth-group and " "initiator-portal for target \"%s\"", target->t_name); return (1); } } else { target->t_auth_group = auth_group_new(conf, NULL); if (target->t_auth_group == NULL) return (1); target->t_auth_group->ag_target = target; } ap = auth_portal_new(target->t_auth_group, ucl_object_tostring(obj)); if (ap == NULL) return (1); } if (!strcmp(key, "portal-group")) { if (obj->type == UCL_OBJECT) { if (uclparse_target_portal_group(target, obj) != 0) return (1); } if (obj->type == UCL_ARRAY) { while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (uclparse_target_portal_group(target, tmp) != 0) return (1); } } } if (!strcmp(key, "port")) { struct pport *pp; struct port *tp; const char *value = ucl_object_tostring(obj); pp = pport_find(conf, value); if (pp == NULL) { log_warnx("unknown port \"%s\" for target \"%s\"", value, target->t_name); return (1); } if (!TAILQ_EMPTY(&pp->pp_ports)) { log_warnx("can't link port \"%s\" to target \"%s\", " "port already linked to some target", value, target->t_name); return (1); } tp = port_new_pp(conf, target, pp); if (tp == NULL) { log_warnx("can't link port \"%s\" to target \"%s\"", value, target->t_name); return (1); } } if (!strcmp(key, "redirect")) { if (obj->type != UCL_STRING) { log_warnx("\"redirect\" property of target " "\"%s\" is not a string", target->t_name); return (1); } if (target_set_redirection(target, ucl_object_tostring(obj)) != 0) return (1); } if (!strcmp(key, "lun")) { while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (uclparse_target_lun(target, tmp) != 0) return (1); } } } return (0); } static int uclparse_lun(const char *name, const ucl_object_t *top) { struct lun *lun; ucl_object_iter_t it = NULL, child_it = NULL; const ucl_object_t *obj = NULL, *child = NULL; const char *key; lun = lun_new(conf, name); if (lun == NULL) return (1); while ((obj = ucl_iterate_object(top, &it, true))) { key = ucl_object_key(obj); if (!strcmp(key, "backend")) { if (obj->type != UCL_STRING) { log_warnx("\"backend\" property of lun " "\"%s\" is not a string", lun->l_name); return (1); } lun_set_backend(lun, ucl_object_tostring(obj)); } if (!strcmp(key, "blocksize")) { if (obj->type != UCL_INT) { log_warnx("\"blocksize\" property of lun " "\"%s\" is not an integer", lun->l_name); return (1); } lun_set_blocksize(lun, ucl_object_toint(obj)); } if (!strcmp(key, "device-id")) { if (obj->type != UCL_STRING) { log_warnx("\"device-id\" property of lun " "\"%s\" is not an integer", lun->l_name); return (1); } lun_set_device_id(lun, ucl_object_tostring(obj)); } if (!strcmp(key, "options")) { if (obj->type != UCL_OBJECT) { log_warnx("\"options\" property of lun " "\"%s\" is not an object", lun->l_name); return (1); } while ((child = ucl_iterate_object(obj, &child_it, true))) { option_new(&lun->l_options, ucl_object_key(child), ucl_object_tostring_forced(child)); } } if (!strcmp(key, "path")) { if (obj->type != UCL_STRING) { log_warnx("\"path\" property of lun " "\"%s\" is not a string", lun->l_name); return (1); } lun_set_path(lun, ucl_object_tostring(obj)); } if (!strcmp(key, "serial")) { if (obj->type != UCL_STRING) { log_warnx("\"serial\" property of lun " "\"%s\" is not a string", lun->l_name); return (1); } lun_set_serial(lun, ucl_object_tostring(obj)); } if (!strcmp(key, "size")) { if (obj->type != UCL_INT) { log_warnx("\"size\" property of lun " "\"%s\" is not an integer", lun->l_name); return (1); } lun_set_size(lun, ucl_object_toint(obj)); } } return (0); } int uclparse_conf(struct conf *newconf, const char *path) { struct ucl_parser *parser; int error; conf = newconf; parser = ucl_parser_new(0); if (!ucl_parser_add_file(parser, path)) { log_warn("unable to parse configuration file %s: %s", path, ucl_parser_get_error(parser)); return (1); } error = uclparse_toplevel(ucl_parser_get_object(parser)); return (error); }