1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2015 iXsystems Inc.
5 * All rights reserved.
6 *
7 * This software was developed by Jakub Klama <jceel@FreeBSD.org>
8 * under sponsorship from iXsystems Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/queue.h>
33#include <sys/types.h>
34#include <assert.h>
35#include <stdio.h>
36#include <stdint.h>
37#include <stdlib.h>
38#include <string.h>
39#include <ucl.h>
40#include <netinet/in.h>
41#include <netinet/ip.h>
42
43#include "ctld.h"
44
45static struct conf *conf = NULL;
46
47static int uclparse_toplevel(const ucl_object_t *);
48static int uclparse_chap(struct auth_group *, const ucl_object_t *);
49static int uclparse_chap_mutual(struct auth_group *, const ucl_object_t *);
50static int uclparse_lun(const char *, const ucl_object_t *);
51static int uclparse_auth_group(const char *, const ucl_object_t *);
52static int uclparse_portal_group(const char *, const ucl_object_t *);
53static int uclparse_target(const char *, const ucl_object_t *);
54static int uclparse_target_portal_group(struct target *, const ucl_object_t *);
55static int uclparse_target_lun(struct target *, const ucl_object_t *);
56
57static int
58uclparse_chap(struct auth_group *auth_group, const ucl_object_t *obj)
59{
60	const struct auth *ca;
61	const ucl_object_t *user, *secret;
62
63	assert(auth_group != NULL);
64	user = ucl_object_find_key(obj, "user");
65	if (!user || user->type != UCL_STRING) {
66		log_warnx("chap section in auth-group \"%s\" is missing "
67		    "\"user\" string key", auth_group->ag_name);
68		return (1);
69	}
70
71	secret = ucl_object_find_key(obj, "secret");
72	if (!secret || secret->type != UCL_STRING) {
73		log_warnx("chap section in auth-group \"%s\" is missing "
74		    "\"secret\" string key", auth_group->ag_name);
75	}
76
77	ca = auth_new_chap(auth_group,
78	    ucl_object_tostring(user),
79	    ucl_object_tostring(secret));
80
81	if (ca == NULL)
82		return (1);
83
84	return (0);
85}
86
87static int
88uclparse_chap_mutual(struct auth_group *auth_group, const ucl_object_t *obj)
89{
90	const struct auth *ca;
91	const ucl_object_t *user, *secret, *mutual_user;
92	const ucl_object_t *mutual_secret;
93
94	assert(auth_group != NULL);
95	user = ucl_object_find_key(obj, "user");
96	if (!user || user->type != UCL_STRING) {
97		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
98		    "\"user\" string key", auth_group->ag_name);
99		return (1);
100	}
101
102	secret = ucl_object_find_key(obj, "secret");
103	if (!secret || secret->type != UCL_STRING) {
104		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
105		    "\"secret\" string key", auth_group->ag_name);
106		return (1);
107	}
108
109	mutual_user = ucl_object_find_key(obj, "mutual-user");
110	if (!user || user->type != UCL_STRING) {
111		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
112		    "\"mutual-user\" string key", auth_group->ag_name);
113		return (1);
114	}
115
116	mutual_secret = ucl_object_find_key(obj, "mutual-secret");
117	if (!secret || secret->type != UCL_STRING) {
118		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
119		    "\"mutual-secret\" string key", auth_group->ag_name);
120		return (1);
121	}
122
123	ca = auth_new_chap_mutual(auth_group,
124	    ucl_object_tostring(user),
125	    ucl_object_tostring(secret),
126	    ucl_object_tostring(mutual_user),
127	    ucl_object_tostring(mutual_secret));
128
129	if (ca == NULL)
130		return (1);
131
132	return (0);
133}
134
135static int
136uclparse_target_portal_group(struct target *target, const ucl_object_t *obj)
137{
138	struct portal_group *tpg;
139	struct auth_group *tag = NULL;
140	struct port *tp;
141	const ucl_object_t *portal_group, *auth_group;
142
143	portal_group = ucl_object_find_key(obj, "name");
144	if (!portal_group || portal_group->type != UCL_STRING) {
145		log_warnx("portal-group section in target \"%s\" is missing "
146		    "\"name\" string key", target->t_name);
147		return (1);
148	}
149
150	auth_group = ucl_object_find_key(obj, "auth-group-name");
151	if (auth_group && auth_group->type != UCL_STRING) {
152		log_warnx("portal-group section in target \"%s\" is missing "
153		    "\"auth-group-name\" string key", target->t_name);
154		return (1);
155	}
156
157
158	tpg = portal_group_find(conf, ucl_object_tostring(portal_group));
159	if (tpg == NULL) {
160		log_warnx("unknown portal-group \"%s\" for target "
161		    "\"%s\"", ucl_object_tostring(portal_group), target->t_name);
162		return (1);
163	}
164
165	if (auth_group) {
166		tag = auth_group_find(conf, ucl_object_tostring(auth_group));
167		if (tag == NULL) {
168			log_warnx("unknown auth-group \"%s\" for target "
169			    "\"%s\"", ucl_object_tostring(auth_group),
170			    target->t_name);
171			return (1);
172		}
173	}
174
175	tp = port_new(conf, target, tpg);
176	if (tp == NULL) {
177		log_warnx("can't link portal-group \"%s\" to target "
178		    "\"%s\"", ucl_object_tostring(portal_group), target->t_name);
179		return (1);
180	}
181	tp->p_auth_group = tag;
182
183	return (0);
184}
185
186static int
187uclparse_target_lun(struct target *target, const ucl_object_t *obj)
188{
189	struct lun *lun;
190	uint64_t tmp;
191
192	if (obj->type == UCL_INT) {
193		char *name;
194
195		tmp = ucl_object_toint(obj);
196		if (tmp >= MAX_LUNS) {
197			log_warnx("LU number %ju in target \"%s\" is too big",
198			    tmp, target->t_name);
199			return (1);
200		}
201
202		asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
203		lun = lun_new(conf, name);
204		if (lun == NULL)
205			return (1);
206
207		lun_set_scsiname(lun, name);
208		target->t_luns[tmp] = lun;
209		return (0);
210	}
211
212	if (obj->type == UCL_OBJECT) {
213		const ucl_object_t *num = ucl_object_find_key(obj, "number");
214		const ucl_object_t *name = ucl_object_find_key(obj, "name");
215
216		if (num == NULL || num->type != UCL_INT) {
217			log_warnx("lun section in target \"%s\" is missing "
218			    "\"number\" integer property", target->t_name);
219			return (1);
220		}
221		tmp = ucl_object_toint(num);
222		if (tmp >= MAX_LUNS) {
223			log_warnx("LU number %ju in target \"%s\" is too big",
224			    tmp, target->t_name);
225			return (1);
226		}
227
228		if (name == NULL || name->type != UCL_STRING) {
229			log_warnx("lun section in target \"%s\" is missing "
230			    "\"name\" string property", target->t_name);
231			return (1);
232		}
233
234		lun = lun_find(conf, ucl_object_tostring(name));
235		if (lun == NULL)
236			return (1);
237
238		target->t_luns[tmp] = lun;
239	}
240
241	return (0);
242}
243
244static int
245uclparse_toplevel(const ucl_object_t *top)
246{
247	ucl_object_iter_t it = NULL, iter = NULL;
248	const ucl_object_t *obj = NULL, *child = NULL;
249	int err = 0;
250
251	/* Pass 1 - everything except targets */
252	while ((obj = ucl_iterate_object(top, &it, true))) {
253		const char *key = ucl_object_key(obj);
254
255		if (!strcmp(key, "debug")) {
256			if (obj->type == UCL_INT)
257				conf->conf_debug = ucl_object_toint(obj);
258			else {
259				log_warnx("\"debug\" property value is not integer");
260				return (1);
261			}
262		}
263
264		if (!strcmp(key, "timeout")) {
265			if (obj->type == UCL_INT)
266				conf->conf_timeout = ucl_object_toint(obj);
267			else {
268				log_warnx("\"timeout\" property value is not integer");
269				return (1);
270			}
271		}
272
273		if (!strcmp(key, "maxproc")) {
274			if (obj->type == UCL_INT)
275				conf->conf_maxproc = ucl_object_toint(obj);
276			else {
277				log_warnx("\"maxproc\" property value is not integer");
278				return (1);
279			}
280		}
281
282		if (!strcmp(key, "pidfile")) {
283			if (obj->type == UCL_STRING)
284				conf->conf_pidfile_path = strdup(
285				    ucl_object_tostring(obj));
286			else {
287				log_warnx("\"pidfile\" property value is not string");
288				return (1);
289			}
290		}
291
292		if (!strcmp(key, "isns-server")) {
293			if (obj->type == UCL_ARRAY) {
294				iter = NULL;
295				while ((child = ucl_iterate_object(obj, &iter,
296				    true))) {
297					if (child->type != UCL_STRING)
298						return (1);
299
300					err = isns_new(conf,
301					    ucl_object_tostring(child));
302					if (err != 0) {
303						return (1);
304					}
305				}
306			} else {
307				log_warnx("\"isns-server\" property value is "
308				    "not an array");
309				return (1);
310			}
311		}
312
313		if (!strcmp(key, "isns-period")) {
314			if (obj->type == UCL_INT)
315				conf->conf_timeout = ucl_object_toint(obj);
316			else {
317				log_warnx("\"isns-period\" property value is not integer");
318				return (1);
319			}
320		}
321
322		if (!strcmp(key, "isns-timeout")) {
323			if (obj->type == UCL_INT)
324				conf->conf_timeout = ucl_object_toint(obj);
325			else {
326				log_warnx("\"isns-timeout\" property value is not integer");
327				return (1);
328			}
329		}
330
331		if (!strcmp(key, "auth-group")) {
332			if (obj->type == UCL_OBJECT) {
333				iter = NULL;
334				while ((child = ucl_iterate_object(obj, &iter, true))) {
335					uclparse_auth_group(ucl_object_key(child), child);
336				}
337			} else {
338				log_warnx("\"auth-group\" section is not an object");
339				return (1);
340			}
341		}
342
343		if (!strcmp(key, "portal-group")) {
344			if (obj->type == UCL_OBJECT) {
345				iter = NULL;
346				while ((child = ucl_iterate_object(obj, &iter, true))) {
347					uclparse_portal_group(ucl_object_key(child), child);
348				}
349			} else {
350				log_warnx("\"portal-group\" section is not an object");
351				return (1);
352			}
353		}
354
355		if (!strcmp(key, "lun")) {
356			if (obj->type == UCL_OBJECT) {
357				iter = NULL;
358				while ((child = ucl_iterate_object(obj, &iter, true))) {
359					uclparse_lun(ucl_object_key(child), child);
360				}
361			} else {
362				log_warnx("\"lun\" section is not an object");
363				return (1);
364			}
365		}
366	}
367
368	/* Pass 2 - targets */
369	it = NULL;
370	while ((obj = ucl_iterate_object(top, &it, true))) {
371		const char *key = ucl_object_key(obj);
372
373		if (!strcmp(key, "target")) {
374			if (obj->type == UCL_OBJECT) {
375				iter = NULL;
376				while ((child = ucl_iterate_object(obj, &iter,
377				    true))) {
378					uclparse_target(ucl_object_key(child),
379					    child);
380				}
381			} else {
382				log_warnx("\"target\" section is not an object");
383				return (1);
384			}
385		}
386	}
387
388	return (0);
389}
390
391static int
392uclparse_auth_group(const char *name, const ucl_object_t *top)
393{
394	struct auth_group *auth_group;
395	const struct auth_name *an;
396	const struct auth_portal *ap;
397	ucl_object_iter_t it = NULL, it2 = NULL;
398	const ucl_object_t *obj = NULL, *tmp = NULL;
399	const char *key;
400	int err;
401
402	if (!strcmp(name, "default") &&
403	    conf->conf_default_ag_defined == false) {
404		auth_group = auth_group_find(conf, name);
405		conf->conf_default_ag_defined = true;
406	} else {
407		auth_group = auth_group_new(conf, name);
408	}
409
410	if (auth_group == NULL)
411		return (1);
412
413	while ((obj = ucl_iterate_object(top, &it, true))) {
414		key = ucl_object_key(obj);
415
416		if (!strcmp(key, "auth-type")) {
417			const char *value = ucl_object_tostring(obj);
418
419			err = auth_group_set_type(auth_group, value);
420			if (err)
421				return (1);
422		}
423
424		if (!strcmp(key, "chap")) {
425			if (obj->type != UCL_ARRAY) {
426				log_warnx("\"chap\" property of "
427				    "auth-group \"%s\" is not an array",
428				    name);
429				return (1);
430			}
431
432			it2 = NULL;
433			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
434				if (uclparse_chap(auth_group, tmp) != 0)
435					return (1);
436			}
437		}
438
439		if (!strcmp(key, "chap-mutual")) {
440			if (obj->type != UCL_ARRAY) {
441				log_warnx("\"chap-mutual\" property of "
442				    "auth-group \"%s\" is not an array",
443				    name);
444				return (1);
445			}
446
447			it2 = NULL;
448			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
449				if (uclparse_chap_mutual(auth_group, tmp) != 0)
450					return (1);
451			}
452		}
453
454		if (!strcmp(key, "initiator-name")) {
455			if (obj->type != UCL_ARRAY) {
456				log_warnx("\"initiator-name\" property of "
457				    "auth-group \"%s\" is not an array",
458				    name);
459				return (1);
460			}
461
462			it2 = NULL;
463			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
464				const char *value = ucl_object_tostring(tmp);
465
466				an = auth_name_new(auth_group, value);
467				if (an == NULL)
468					return (1);
469			}
470		}
471
472		if (!strcmp(key, "initiator-portal")) {
473			if (obj->type != UCL_ARRAY) {
474				log_warnx("\"initiator-portal\" property of "
475				    "auth-group \"%s\" is not an array",
476				    name);
477				return (1);
478			}
479
480			it2 = NULL;
481			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
482				const char *value = ucl_object_tostring(tmp);
483
484				ap = auth_portal_new(auth_group, value);
485				if (ap == NULL)
486					return (1);
487			}
488		}
489	}
490
491	return (0);
492}
493
494static int
495uclparse_portal_group(const char *name, const ucl_object_t *top)
496{
497	struct portal_group *portal_group;
498	ucl_object_iter_t it = NULL, it2 = NULL;
499	const ucl_object_t *obj = NULL, *tmp = NULL;
500	const char *key;
501
502	if (strcmp(name, "default") == 0 &&
503	    conf->conf_default_pg_defined == false) {
504		portal_group = portal_group_find(conf, name);
505		conf->conf_default_pg_defined = true;
506	} else {
507		portal_group = portal_group_new(conf, name);
508	}
509
510	if (portal_group == NULL)
511		return (1);
512
513	while ((obj = ucl_iterate_object(top, &it, true))) {
514		key = ucl_object_key(obj);
515
516		if (!strcmp(key, "discovery-auth-group")) {
517			portal_group->pg_discovery_auth_group =
518			    auth_group_find(conf, ucl_object_tostring(obj));
519			if (portal_group->pg_discovery_auth_group == NULL) {
520				log_warnx("unknown discovery-auth-group \"%s\" "
521				    "for portal-group \"%s\"",
522				    ucl_object_tostring(obj),
523				    portal_group->pg_name);
524				return (1);
525			}
526		}
527
528		if (!strcmp(key, "discovery-filter")) {
529			if (obj->type != UCL_STRING) {
530				log_warnx("\"discovery-filter\" property of "
531				    "portal-group \"%s\" is not a string",
532				    portal_group->pg_name);
533				return (1);
534			}
535
536			if (portal_group_set_filter(portal_group,
537			    ucl_object_tostring(obj)) != 0)
538				return (1);
539		}
540
541		if (!strcmp(key, "listen")) {
542			if (obj->type == UCL_STRING) {
543				if (portal_group_add_listen(portal_group,
544				    ucl_object_tostring(obj), false) != 0)
545					return (1);
546			} else if (obj->type == UCL_ARRAY) {
547				while ((tmp = ucl_iterate_object(obj, &it2,
548				    true))) {
549					if (portal_group_add_listen(
550					    portal_group,
551					    ucl_object_tostring(tmp),
552					    false) != 0)
553						return (1);
554				}
555			} else {
556				log_warnx("\"listen\" property of "
557				    "portal-group \"%s\" is not a string",
558				    portal_group->pg_name);
559				return (1);
560			}
561		}
562
563		if (!strcmp(key, "listen-iser")) {
564			if (obj->type == UCL_STRING) {
565				if (portal_group_add_listen(portal_group,
566				    ucl_object_tostring(obj), true) != 0)
567					return (1);
568			} else if (obj->type == UCL_ARRAY) {
569				while ((tmp = ucl_iterate_object(obj, &it2,
570				    true))) {
571					if (portal_group_add_listen(
572					    portal_group,
573					    ucl_object_tostring(tmp),
574					    true) != 0)
575						return (1);
576				}
577			} else {
578				log_warnx("\"listen\" property of "
579				    "portal-group \"%s\" is not a string",
580				    portal_group->pg_name);
581				return (1);
582			}
583		}
584
585		if (!strcmp(key, "redirect")) {
586			if (obj->type != UCL_STRING) {
587				log_warnx("\"listen\" property of "
588				    "portal-group \"%s\" is not a string",
589				    portal_group->pg_name);
590				return (1);
591			}
592
593			if (portal_group_set_redirection(portal_group,
594			    ucl_object_tostring(obj)) != 0)
595				return (1);
596		}
597
598		if (!strcmp(key, "options")) {
599			if (obj->type != UCL_OBJECT) {
600				log_warnx("\"options\" property of portal group "
601				    "\"%s\" is not an object", portal_group->pg_name);
602				return (1);
603			}
604
605			while ((tmp = ucl_iterate_object(obj, &it2,
606			    true))) {
607				option_new(&portal_group->pg_options,
608				    ucl_object_key(tmp),
609				    ucl_object_tostring_forced(tmp));
610			}
611		}
612
613		if (!strcmp(key, "dscp")) {
614			if ((obj->type != UCL_STRING) && (obj->type != UCL_INT)) {
615				log_warnx("\"dscp\" property of portal group "
616				    "\"%s\" is not a string or integer", portal_group->pg_name);
617				return(1);
618			}
619			if (obj->type == UCL_INT)
620				portal_group->pg_dscp = ucl_object_toint(obj);
621			else {
622				key = ucl_object_tostring(obj);
623				if (strcmp(key, "0x") == 0)
624					portal_group->pg_dscp = strtol(key + 2, NULL, 16);
625				else if (strcmp(key, "be") || strcmp(key, "cs0"))
626					portal_group->pg_dscp = IPTOS_DSCP_CS0 >> 2;
627				else if (strcmp(key, "ef"))
628					portal_group->pg_dscp = IPTOS_DSCP_EF >> 2;
629				else if (strcmp(key, "cs0"))
630					portal_group->pg_dscp = IPTOS_DSCP_CS0 >> 2;
631				else if (strcmp(key, "cs1"))
632					portal_group->pg_dscp = IPTOS_DSCP_CS1 >> 2;
633				else if (strcmp(key, "cs2"))
634					portal_group->pg_dscp = IPTOS_DSCP_CS2 >> 2;
635				else if (strcmp(key, "cs3"))
636					portal_group->pg_dscp = IPTOS_DSCP_CS3 >> 2;
637				else if (strcmp(key, "cs4"))
638					portal_group->pg_dscp = IPTOS_DSCP_CS4 >> 2;
639				else if (strcmp(key, "cs5"))
640					portal_group->pg_dscp = IPTOS_DSCP_CS5 >> 2;
641				else if (strcmp(key, "cs6"))
642					portal_group->pg_dscp = IPTOS_DSCP_CS6 >> 2;
643				else if (strcmp(key, "cs7"))
644					portal_group->pg_dscp = IPTOS_DSCP_CS7 >> 2;
645				else if (strcmp(key, "af11"))
646					portal_group->pg_dscp = IPTOS_DSCP_AF11 >> 2;
647				else if (strcmp(key, "af12"))
648					portal_group->pg_dscp = IPTOS_DSCP_AF12 >> 2;
649				else if (strcmp(key, "af13"))
650					portal_group->pg_dscp = IPTOS_DSCP_AF13 >> 2;
651				else if (strcmp(key, "af21"))
652					portal_group->pg_dscp = IPTOS_DSCP_AF21 >> 2;
653				else if (strcmp(key, "af22"))
654					portal_group->pg_dscp = IPTOS_DSCP_AF22 >> 2;
655				else if (strcmp(key, "af23"))
656					portal_group->pg_dscp = IPTOS_DSCP_AF23 >> 2;
657				else if (strcmp(key, "af31"))
658					portal_group->pg_dscp = IPTOS_DSCP_AF31 >> 2;
659				else if (strcmp(key, "af32"))
660					portal_group->pg_dscp = IPTOS_DSCP_AF32 >> 2;
661				else if (strcmp(key, "af33"))
662					portal_group->pg_dscp = IPTOS_DSCP_AF33 >> 2;
663				else if (strcmp(key, "af41"))
664					portal_group->pg_dscp = IPTOS_DSCP_AF41 >> 2;
665				else if (strcmp(key, "af42"))
666					portal_group->pg_dscp = IPTOS_DSCP_AF42 >> 2;
667				else if (strcmp(key, "af43"))
668					portal_group->pg_dscp = IPTOS_DSCP_AF43 >> 2;
669				else {
670					log_warnx("\"dscp\" property value is not a supported textual value");
671					return (1);
672				}
673			}
674		}
675
676		if (!strcmp(key, "pcp")) {
677			if (obj->type != UCL_INT) {
678				log_warnx("\"pcp\" property of portal group "
679				    "\"%s\" is not an integer", portal_group->pg_name);
680				return(1);
681			}
682			portal_group->pg_pcp = ucl_object_toint(obj);
683			if (!((portal_group->pg_pcp >= 0) && (portal_group->pg_pcp <= 7))) {
684				log_warnx("invalid \"pcp\" value %d, using default", portal_group->pg_pcp);
685				portal_group->pg_pcp = -1;
686			}
687		}
688	}
689
690	return (0);
691}
692
693static int
694uclparse_target(const char *name, const ucl_object_t *top)
695{
696	struct target *target;
697	ucl_object_iter_t it = NULL, it2 = NULL;
698	const ucl_object_t *obj = NULL, *tmp = NULL;
699	const char *key;
700
701	target = target_new(conf, name);
702	if (target == NULL)
703		return (1);
704
705	while ((obj = ucl_iterate_object(top, &it, true))) {
706		key = ucl_object_key(obj);
707
708		if (!strcmp(key, "alias")) {
709			if (obj->type != UCL_STRING) {
710				log_warnx("\"alias\" property of target "
711				    "\"%s\" is not a string", target->t_name);
712				return (1);
713			}
714
715			target->t_alias = strdup(ucl_object_tostring(obj));
716		}
717
718		if (!strcmp(key, "auth-group")) {
719			const char *ag;
720
721			if (target->t_auth_group != NULL) {
722				if (target->t_auth_group->ag_name != NULL)
723					log_warnx("auth-group for target \"%s\" "
724					    "specified more than once",
725					    target->t_name);
726				else
727					log_warnx("cannot use both auth-group "
728					    "and explicit authorisations for "
729					    "target \"%s\"", target->t_name);
730				return (1);
731			}
732			ag = ucl_object_tostring(obj);
733			if (!ag) {
734				log_warnx("auth-group must be a string");
735				return (1);
736			}
737			target->t_auth_group = auth_group_find(conf, ag);
738			if (target->t_auth_group == NULL) {
739				log_warnx("unknown auth-group \"%s\" for target "
740				    "\"%s\"", ucl_object_tostring(obj),
741				    target->t_name);
742				return (1);
743			}
744		}
745
746		if (!strcmp(key, "auth-type")) {
747			int error;
748
749			if (target->t_auth_group != NULL) {
750				if (target->t_auth_group->ag_name != NULL) {
751					log_warnx("cannot use both auth-group and "
752					    "auth-type for target \"%s\"",
753					    target->t_name);
754					return (1);
755				}
756			} else {
757				target->t_auth_group = auth_group_new(conf, NULL);
758				if (target->t_auth_group == NULL)
759					return (1);
760
761				target->t_auth_group->ag_target = target;
762			}
763			error = auth_group_set_type(target->t_auth_group,
764			    ucl_object_tostring(obj));
765			if (error != 0)
766				return (1);
767		}
768
769		if (!strcmp(key, "chap")) {
770			if (target->t_auth_group != NULL) {
771				if (target->t_auth_group->ag_name != NULL) {
772					log_warnx("cannot use both auth-group "
773					    "and chap for target \"%s\"",
774					    target->t_name);
775					return (1);
776				}
777			} else {
778				target->t_auth_group = auth_group_new(conf, NULL);
779				if (target->t_auth_group == NULL) {
780					return (1);
781				}
782				target->t_auth_group->ag_target = target;
783			}
784			if (uclparse_chap(target->t_auth_group, obj) != 0)
785				return (1);
786		}
787
788		if (!strcmp(key, "chap-mutual")) {
789			if (uclparse_chap_mutual(target->t_auth_group, obj) != 0)
790				return (1);
791		}
792
793		if (!strcmp(key, "initiator-name")) {
794			const struct auth_name *an;
795
796			if (target->t_auth_group != NULL) {
797				if (target->t_auth_group->ag_name != NULL) {
798					log_warnx("cannot use both auth-group and "
799					    "initiator-name for target \"%s\"",
800					    target->t_name);
801					return (1);
802				}
803			} else {
804				target->t_auth_group = auth_group_new(conf, NULL);
805				if (target->t_auth_group == NULL)
806					return (1);
807
808				target->t_auth_group->ag_target = target;
809			}
810			an = auth_name_new(target->t_auth_group,
811			    ucl_object_tostring(obj));
812			if (an == NULL)
813				return (1);
814		}
815
816		if (!strcmp(key, "initiator-portal")) {
817			const struct auth_portal *ap;
818
819			if (target->t_auth_group != NULL) {
820				if (target->t_auth_group->ag_name != NULL) {
821					log_warnx("cannot use both auth-group and "
822					    "initiator-portal for target \"%s\"",
823					    target->t_name);
824					return (1);
825				}
826			} else {
827				target->t_auth_group = auth_group_new(conf, NULL);
828				if (target->t_auth_group == NULL)
829					return (1);
830
831				target->t_auth_group->ag_target = target;
832			}
833			ap = auth_portal_new(target->t_auth_group,
834			    ucl_object_tostring(obj));
835			if (ap == NULL)
836				return (1);
837		}
838
839		if (!strcmp(key, "portal-group")) {
840			if (obj->type == UCL_OBJECT) {
841				if (uclparse_target_portal_group(target, obj) != 0)
842					return (1);
843			}
844
845			if (obj->type == UCL_ARRAY) {
846				while ((tmp = ucl_iterate_object(obj, &it2,
847				    true))) {
848					if (uclparse_target_portal_group(target,
849					    tmp) != 0)
850						return (1);
851				}
852			}
853		}
854
855		if (!strcmp(key, "port")) {
856			struct pport *pp;
857			struct port *tp;
858			const char *value = ucl_object_tostring(obj);
859			int ret, i_pp, i_vp = 0;
860
861			ret = sscanf(value, "ioctl/%d/%d", &i_pp, &i_vp);
862			if (ret > 0) {
863				tp = port_new_ioctl(conf, target, i_pp, i_vp);
864				if (tp == NULL) {
865					log_warnx("can't create new ioctl port "
866					    "for target \"%s\"", target->t_name);
867					return (1);
868				}
869
870				continue;
871			}
872
873			pp = pport_find(conf, value);
874			if (pp == NULL) {
875				log_warnx("unknown port \"%s\" for target \"%s\"",
876				    value, target->t_name);
877				return (1);
878			}
879			if (!TAILQ_EMPTY(&pp->pp_ports)) {
880				log_warnx("can't link port \"%s\" to target \"%s\", "
881				    "port already linked to some target",
882				    value, target->t_name);
883				return (1);
884			}
885			tp = port_new_pp(conf, target, pp);
886			if (tp == NULL) {
887				log_warnx("can't link port \"%s\" to target \"%s\"",
888				    value, target->t_name);
889				return (1);
890			}
891		}
892
893		if (!strcmp(key, "redirect")) {
894			if (obj->type != UCL_STRING) {
895				log_warnx("\"redirect\" property of target "
896				    "\"%s\" is not a string", target->t_name);
897				return (1);
898			}
899
900			if (target_set_redirection(target,
901			    ucl_object_tostring(obj)) != 0)
902				return (1);
903		}
904
905		if (!strcmp(key, "lun")) {
906			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
907				if (uclparse_target_lun(target, tmp) != 0)
908					return (1);
909			}
910		}
911	}
912
913	return (0);
914}
915
916static int
917uclparse_lun(const char *name, const ucl_object_t *top)
918{
919	struct lun *lun;
920	ucl_object_iter_t it = NULL, child_it = NULL;
921	const ucl_object_t *obj = NULL, *child = NULL;
922	const char *key;
923
924	lun = lun_new(conf, name);
925	if (lun == NULL)
926		return (1);
927
928	while ((obj = ucl_iterate_object(top, &it, true))) {
929		key = ucl_object_key(obj);
930
931		if (!strcmp(key, "backend")) {
932			if (obj->type != UCL_STRING) {
933				log_warnx("\"backend\" property of lun "
934				    "\"%s\" is not a string",
935				    lun->l_name);
936				return (1);
937			}
938
939			lun_set_backend(lun, ucl_object_tostring(obj));
940		}
941
942		if (!strcmp(key, "blocksize")) {
943			if (obj->type != UCL_INT) {
944				log_warnx("\"blocksize\" property of lun "
945				    "\"%s\" is not an integer", lun->l_name);
946				return (1);
947			}
948
949			lun_set_blocksize(lun, ucl_object_toint(obj));
950		}
951
952		if (!strcmp(key, "device-id")) {
953			if (obj->type != UCL_STRING) {
954				log_warnx("\"device-id\" property of lun "
955				    "\"%s\" is not an integer", lun->l_name);
956				return (1);
957			}
958
959			lun_set_device_id(lun, ucl_object_tostring(obj));
960		}
961
962		if (!strcmp(key, "options")) {
963			if (obj->type != UCL_OBJECT) {
964				log_warnx("\"options\" property of lun "
965				    "\"%s\" is not an object", lun->l_name);
966				return (1);
967			}
968
969			while ((child = ucl_iterate_object(obj, &child_it,
970			    true))) {
971				option_new(&lun->l_options,
972				    ucl_object_key(child),
973				    ucl_object_tostring_forced(child));
974			}
975		}
976
977		if (!strcmp(key, "path")) {
978			if (obj->type != UCL_STRING) {
979				log_warnx("\"path\" property of lun "
980				    "\"%s\" is not a string", lun->l_name);
981				return (1);
982			}
983
984			lun_set_path(lun, ucl_object_tostring(obj));
985		}
986
987		if (!strcmp(key, "serial")) {
988			if (obj->type != UCL_STRING) {
989				log_warnx("\"serial\" property of lun "
990				    "\"%s\" is not a string", lun->l_name);
991				return (1);
992			}
993
994			lun_set_serial(lun, ucl_object_tostring(obj));
995		}
996
997		if (!strcmp(key, "size")) {
998			if (obj->type != UCL_INT) {
999				log_warnx("\"size\" property of lun "
1000				    "\"%s\" is not an integer", lun->l_name);
1001				return (1);
1002			}
1003
1004			lun_set_size(lun, ucl_object_toint(obj));
1005		}
1006	}
1007
1008	return (0);
1009}
1010
1011int
1012uclparse_conf(struct conf *newconf, const char *path)
1013{
1014	struct ucl_parser *parser;
1015	ucl_object_t *top;
1016	int error;
1017
1018	conf = newconf;
1019	parser = ucl_parser_new(0);
1020
1021	if (!ucl_parser_add_file(parser, path)) {
1022		log_warn("unable to parse configuration file %s: %s", path,
1023		    ucl_parser_get_error(parser));
1024		ucl_parser_free(parser);
1025		return (1);
1026	}
1027
1028	top = ucl_parser_get_object(parser);
1029	error = uclparse_toplevel(top);
1030	ucl_object_unref(top);
1031	ucl_parser_free(parser);
1032
1033	return (error);
1034}
1035