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