xref: /freebsd/usr.sbin/ctld/uclparse.c (revision f3e7afe2d7b262ab55ab818445d4dfdb6e0c70a9)
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 	uint64_t tmp;
187 
188 	if (obj->type == UCL_INT) {
189 		char *name;
190 
191 		tmp = ucl_object_toint(obj);
192 		if (tmp >= MAX_LUNS) {
193 			log_warnx("LU number %ju in target \"%s\" is too big",
194 			    tmp, target->t_name);
195 			return (1);
196 		}
197 
198 		asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
199 		lun = lun_new(conf, name);
200 		if (lun == NULL)
201 			return (1);
202 
203 		lun_set_scsiname(lun, name);
204 		target->t_luns[tmp] = lun;
205 		return (0);
206 	}
207 
208 	if (obj->type == UCL_OBJECT) {
209 		const ucl_object_t *num = ucl_object_find_key(obj, "number");
210 		const ucl_object_t *name = ucl_object_find_key(obj, "name");
211 
212 		if (num == NULL || num->type != UCL_INT) {
213 			log_warnx("lun section in target \"%s\" is missing "
214 			    "\"number\" integer property", target->t_name);
215 			return (1);
216 		}
217 		tmp = ucl_object_toint(num);
218 		if (tmp >= MAX_LUNS) {
219 			log_warnx("LU number %ju in target \"%s\" is too big",
220 			    tmp, target->t_name);
221 			return (1);
222 		}
223 
224 		if (name == NULL || name->type != UCL_STRING) {
225 			log_warnx("lun section in target \"%s\" is missing "
226 			    "\"name\" string property", target->t_name);
227 			return (1);
228 		}
229 
230 		lun = lun_find(conf, ucl_object_tostring(name));
231 		if (lun == NULL)
232 			return (1);
233 
234 		target->t_luns[tmp] = lun;
235 	}
236 
237 	return (0);
238 }
239 
240 static int
241 uclparse_toplevel(const ucl_object_t *top)
242 {
243 	ucl_object_iter_t it = NULL, iter = NULL;
244 	const ucl_object_t *obj = NULL, *child = NULL;
245 	int err = 0;
246 
247 	/* Pass 1 - everything except targets */
248 	while ((obj = ucl_iterate_object(top, &it, true))) {
249 		const char *key = ucl_object_key(obj);
250 
251 		if (!strcmp(key, "debug")) {
252 			if (obj->type == UCL_INT)
253 				conf->conf_debug = ucl_object_toint(obj);
254 			else {
255 				log_warnx("\"debug\" property value is not integer");
256 				return (1);
257 			}
258 		}
259 
260 		if (!strcmp(key, "timeout")) {
261 			if (obj->type == UCL_INT)
262 				conf->conf_timeout = ucl_object_toint(obj);
263 			else {
264 				log_warnx("\"timeout\" property value is not integer");
265 				return (1);
266 			}
267 		}
268 
269 		if (!strcmp(key, "maxproc")) {
270 			if (obj->type == UCL_INT)
271 				conf->conf_maxproc = ucl_object_toint(obj);
272 			else {
273 				log_warnx("\"maxproc\" property value is not integer");
274 				return (1);
275 			}
276 		}
277 
278 		if (!strcmp(key, "pidfile")) {
279 			if (obj->type == UCL_STRING)
280 				conf->conf_pidfile_path = strdup(
281 				    ucl_object_tostring(obj));
282 			else {
283 				log_warnx("\"pidfile\" property value is not string");
284 				return (1);
285 			}
286 		}
287 
288 		if (!strcmp(key, "isns-server")) {
289 			if (obj->type == UCL_ARRAY) {
290 				iter = NULL;
291 				while ((child = ucl_iterate_object(obj, &iter,
292 				    true))) {
293 					if (child->type != UCL_STRING)
294 						return (1);
295 
296 					err = isns_new(conf,
297 					    ucl_object_tostring(child));
298 					if (err != 0) {
299 						return (1);
300 					}
301 				}
302 			} else {
303 				log_warnx("\"isns-server\" property value is "
304 				    "not an array");
305 				return (1);
306 			}
307 		}
308 
309 		if (!strcmp(key, "isns-period")) {
310 			if (obj->type == UCL_INT)
311 				conf->conf_timeout = ucl_object_toint(obj);
312 			else {
313 				log_warnx("\"isns-period\" property value is not integer");
314 				return (1);
315 			}
316 		}
317 
318 		if (!strcmp(key, "isns-timeout")) {
319 			if (obj->type == UCL_INT)
320 				conf->conf_timeout = ucl_object_toint(obj);
321 			else {
322 				log_warnx("\"isns-timeout\" property value is not integer");
323 				return (1);
324 			}
325 		}
326 
327 		if (!strcmp(key, "auth-group")) {
328 			if (obj->type == UCL_OBJECT) {
329 				iter = NULL;
330 				while ((child = ucl_iterate_object(obj, &iter, true))) {
331 					uclparse_auth_group(ucl_object_key(child), child);
332 				}
333 			} else {
334 				log_warnx("\"auth-group\" section is not an object");
335 				return (1);
336 			}
337 		}
338 
339 		if (!strcmp(key, "portal-group")) {
340 			if (obj->type == UCL_OBJECT) {
341 				iter = NULL;
342 				while ((child = ucl_iterate_object(obj, &iter, true))) {
343 					uclparse_portal_group(ucl_object_key(child), child);
344 				}
345 			} else {
346 				log_warnx("\"portal-group\" section is not an object");
347 				return (1);
348 			}
349 		}
350 
351 		if (!strcmp(key, "lun")) {
352 			if (obj->type == UCL_OBJECT) {
353 				iter = NULL;
354 				while ((child = ucl_iterate_object(obj, &iter, true))) {
355 					uclparse_lun(ucl_object_key(child), child);
356 				}
357 			} else {
358 				log_warnx("\"lun\" section is not an object");
359 				return (1);
360 			}
361 		}
362 	}
363 
364 	/* Pass 2 - targets */
365 	it = NULL;
366 	while ((obj = ucl_iterate_object(top, &it, true))) {
367 		const char *key = ucl_object_key(obj);
368 
369 		if (!strcmp(key, "target")) {
370 			if (obj->type == UCL_OBJECT) {
371 				iter = NULL;
372 				while ((child = ucl_iterate_object(obj, &iter,
373 				    true))) {
374 					uclparse_target(ucl_object_key(child),
375 					    child);
376 				}
377 			} else {
378 				log_warnx("\"target\" section is not an object");
379 				return (1);
380 			}
381 		}
382 	}
383 
384 	return (0);
385 }
386 
387 static int
388 uclparse_auth_group(const char *name, const ucl_object_t *top)
389 {
390 	struct auth_group *auth_group;
391 	const struct auth_name *an;
392 	const struct auth_portal *ap;
393 	ucl_object_iter_t it = NULL, it2 = NULL;
394 	const ucl_object_t *obj = NULL, *tmp = NULL;
395 	const char *key;
396 	int err;
397 
398 	if (!strcmp(name, "default") &&
399 	    conf->conf_default_ag_defined == false) {
400 		auth_group = auth_group_find(conf, name);
401 		conf->conf_default_ag_defined = true;
402 	} else {
403 		auth_group = auth_group_new(conf, name);
404 	}
405 
406 	if (auth_group == NULL)
407 		return (1);
408 
409 	while ((obj = ucl_iterate_object(top, &it, true))) {
410 		key = ucl_object_key(obj);
411 
412 		if (!strcmp(key, "auth-type")) {
413 			const char *value = ucl_object_tostring(obj);
414 
415 			err = auth_group_set_type(auth_group, value);
416 			if (err)
417 				return (1);
418 		}
419 
420 		if (!strcmp(key, "chap")) {
421 			if (obj->type != UCL_ARRAY) {
422 				log_warnx("\"chap\" property of "
423 				    "auth-group \"%s\" is not an array",
424 				    name);
425 				return (1);
426 			}
427 
428 			it2 = NULL;
429 			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
430 				if (uclparse_chap(auth_group, tmp) != 0)
431 					return (1);
432 			}
433 		}
434 
435 		if (!strcmp(key, "chap-mutual")) {
436 			if (obj->type != UCL_ARRAY) {
437 				log_warnx("\"chap-mutual\" property of "
438 				    "auth-group \"%s\" is not an array",
439 				    name);
440 				return (1);
441 			}
442 
443 			it2 = NULL;
444 			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
445 				if (uclparse_chap_mutual(auth_group, tmp) != 0)
446 					return (1);
447 			}
448 		}
449 
450 		if (!strcmp(key, "initiator-name")) {
451 			if (obj->type != UCL_ARRAY) {
452 				log_warnx("\"initiator-name\" property of "
453 				    "auth-group \"%s\" is not an array",
454 				    name);
455 				return (1);
456 			}
457 
458 			it2 = NULL;
459 			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
460 				const char *value = ucl_object_tostring(tmp);
461 
462 				an = auth_name_new(auth_group, value);
463 				if (an == NULL)
464 					return (1);
465 			}
466 		}
467 
468 		if (!strcmp(key, "initiator-portal")) {
469 			if (obj->type != UCL_ARRAY) {
470 				log_warnx("\"initiator-portal\" property of "
471 				    "auth-group \"%s\" is not an array",
472 				    name);
473 				return (1);
474 			}
475 
476 			it2 = NULL;
477 			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
478 				const char *value = ucl_object_tostring(tmp);
479 
480 				ap = auth_portal_new(auth_group, value);
481 				if (ap == NULL)
482 					return (1);
483 			}
484 		}
485 	}
486 
487 	return (0);
488 }
489 
490 static int
491 uclparse_portal_group(const char *name, const ucl_object_t *top)
492 {
493 	struct portal_group *portal_group;
494 	ucl_object_iter_t it = NULL, it2 = NULL;
495 	const ucl_object_t *obj = NULL, *tmp = NULL;
496 	const char *key;
497 
498 	if (strcmp(name, "default") == 0 &&
499 	    conf->conf_default_pg_defined == false) {
500 		portal_group = portal_group_find(conf, name);
501 		conf->conf_default_pg_defined = true;
502 	} else {
503 		portal_group = portal_group_new(conf, name);
504 	}
505 
506 	if (portal_group == NULL)
507 		return (1);
508 
509 	while ((obj = ucl_iterate_object(top, &it, true))) {
510 		key = ucl_object_key(obj);
511 
512 		if (!strcmp(key, "discovery-auth-group")) {
513 			portal_group->pg_discovery_auth_group =
514 			    auth_group_find(conf, ucl_object_tostring(obj));
515 			if (portal_group->pg_discovery_auth_group == NULL) {
516 				log_warnx("unknown discovery-auth-group \"%s\" "
517 				    "for portal-group \"%s\"",
518 				    ucl_object_tostring(obj),
519 				    portal_group->pg_name);
520 				return (1);
521 			}
522 		}
523 
524 		if (!strcmp(key, "discovery-filter")) {
525 			if (obj->type != UCL_STRING) {
526 				log_warnx("\"discovery-filter\" property of "
527 				    "portal-group \"%s\" is not a string",
528 				    portal_group->pg_name);
529 				return (1);
530 			}
531 
532 			if (portal_group_set_filter(portal_group,
533 			    ucl_object_tostring(obj)) != 0)
534 				return (1);
535 		}
536 
537 		if (!strcmp(key, "listen")) {
538 			if (obj->type == UCL_STRING) {
539 				if (portal_group_add_listen(portal_group,
540 				    ucl_object_tostring(obj), false) != 0)
541 					return (1);
542 			} else if (obj->type == UCL_ARRAY) {
543 				while ((tmp = ucl_iterate_object(obj, &it2,
544 				    true))) {
545 					if (portal_group_add_listen(
546 					    portal_group,
547 					    ucl_object_tostring(tmp),
548 					    false) != 0)
549 						return (1);
550 				}
551 			} else {
552 				log_warnx("\"listen\" property of "
553 				    "portal-group \"%s\" is not a string",
554 				    portal_group->pg_name);
555 				return (1);
556 			}
557 		}
558 
559 		if (!strcmp(key, "listen-iser")) {
560 			if (obj->type == UCL_STRING) {
561 				if (portal_group_add_listen(portal_group,
562 				    ucl_object_tostring(obj), true) != 0)
563 					return (1);
564 			} else if (obj->type == UCL_ARRAY) {
565 				while ((tmp = ucl_iterate_object(obj, &it2,
566 				    true))) {
567 					if (portal_group_add_listen(
568 					    portal_group,
569 					    ucl_object_tostring(tmp),
570 					    true) != 0)
571 						return (1);
572 				}
573 			} else {
574 				log_warnx("\"listen\" property of "
575 				    "portal-group \"%s\" is not a string",
576 				    portal_group->pg_name);
577 				return (1);
578 			}
579 		}
580 
581 		if (!strcmp(key, "redirect")) {
582 			if (obj->type != UCL_STRING) {
583 				log_warnx("\"listen\" property of "
584 				    "portal-group \"%s\" is not a string",
585 				    portal_group->pg_name);
586 				return (1);
587 			}
588 
589 			if (portal_group_set_redirection(portal_group,
590 			    ucl_object_tostring(obj)) != 0)
591 				return (1);
592 		}
593 
594 		if (!strcmp(key, "options")) {
595 			if (obj->type != UCL_OBJECT) {
596 				log_warnx("\"options\" property of portal group "
597 				    "\"%s\" is not an object", portal_group->pg_name);
598 				return (1);
599 			}
600 
601 			while ((tmp = ucl_iterate_object(obj, &it2,
602 			    true))) {
603 				option_new(&portal_group->pg_options,
604 				    ucl_object_key(tmp),
605 				    ucl_object_tostring_forced(tmp));
606 			}
607 		}
608 	}
609 
610 	return (0);
611 }
612 
613 static int
614 uclparse_target(const char *name, const ucl_object_t *top)
615 {
616 	struct target *target;
617 	ucl_object_iter_t it = NULL, it2 = NULL;
618 	const ucl_object_t *obj = NULL, *tmp = NULL;
619 	const char *key;
620 
621 	target = target_new(conf, name);
622 
623 	while ((obj = ucl_iterate_object(top, &it, true))) {
624 		key = ucl_object_key(obj);
625 
626 		if (!strcmp(key, "alias")) {
627 			if (obj->type != UCL_STRING) {
628 				log_warnx("\"alias\" property of target "
629 				    "\"%s\" is not a string", target->t_name);
630 				return (1);
631 			}
632 
633 			target->t_alias = strdup(ucl_object_tostring(obj));
634 		}
635 
636 		if (!strcmp(key, "auth-group")) {
637 			if (target->t_auth_group != NULL) {
638 				if (target->t_auth_group->ag_name != NULL)
639 					log_warnx("auth-group for target \"%s\" "
640 					    "specified more than once",
641 					    target->t_name);
642 				else
643 					log_warnx("cannot use both auth-group "
644 					    "and explicit authorisations for "
645 					    "target \"%s\"", target->t_name);
646 				return (1);
647 			}
648 			target->t_auth_group = auth_group_find(conf,
649 			    ucl_object_tostring(obj));
650 			if (target->t_auth_group == NULL) {
651 				log_warnx("unknown auth-group \"%s\" for target "
652 				    "\"%s\"", ucl_object_tostring(obj),
653 				    target->t_name);
654 				return (1);
655 			}
656 		}
657 
658 		if (!strcmp(key, "auth-type")) {
659 			int error;
660 
661 			if (target->t_auth_group != NULL) {
662 				if (target->t_auth_group->ag_name != NULL) {
663 					log_warnx("cannot use both auth-group and "
664 					    "auth-type for target \"%s\"",
665 					    target->t_name);
666 					return (1);
667 				}
668 			} else {
669 				target->t_auth_group = auth_group_new(conf, NULL);
670 				if (target->t_auth_group == NULL)
671 					return (1);
672 
673 				target->t_auth_group->ag_target = target;
674 			}
675 			error = auth_group_set_type(target->t_auth_group,
676 			    ucl_object_tostring(obj));
677 			if (error != 0)
678 				return (1);
679 		}
680 
681 		if (!strcmp(key, "chap")) {
682 			if (uclparse_chap(target->t_auth_group, obj) != 0)
683 				return (1);
684 		}
685 
686 		if (!strcmp(key, "chap-mutual")) {
687 			if (uclparse_chap_mutual(target->t_auth_group, obj) != 0)
688 				return (1);
689 		}
690 
691 		if (!strcmp(key, "initiator-name")) {
692 			const struct auth_name *an;
693 
694 			if (target->t_auth_group != NULL) {
695 				if (target->t_auth_group->ag_name != NULL) {
696 					log_warnx("cannot use both auth-group and "
697 					    "initiator-name for target \"%s\"",
698 					    target->t_name);
699 					return (1);
700 				}
701 			} else {
702 				target->t_auth_group = auth_group_new(conf, NULL);
703 				if (target->t_auth_group == NULL)
704 					return (1);
705 
706 				target->t_auth_group->ag_target = target;
707 			}
708 			an = auth_name_new(target->t_auth_group,
709 			    ucl_object_tostring(obj));
710 			if (an == NULL)
711 				return (1);
712 		}
713 
714 		if (!strcmp(key, "initiator-portal")) {
715 			const struct auth_portal *ap;
716 
717 			if (target->t_auth_group != NULL) {
718 				if (target->t_auth_group->ag_name != NULL) {
719 					log_warnx("cannot use both auth-group and "
720 					    "initiator-portal for target \"%s\"",
721 					    target->t_name);
722 					return (1);
723 				}
724 			} else {
725 				target->t_auth_group = auth_group_new(conf, NULL);
726 				if (target->t_auth_group == NULL)
727 					return (1);
728 
729 				target->t_auth_group->ag_target = target;
730 			}
731 			ap = auth_portal_new(target->t_auth_group,
732 			    ucl_object_tostring(obj));
733 			if (ap == NULL)
734 				return (1);
735 		}
736 
737 		if (!strcmp(key, "portal-group")) {
738 			if (obj->type == UCL_OBJECT) {
739 				if (uclparse_target_portal_group(target, obj) != 0)
740 					return (1);
741 			}
742 
743 			if (obj->type == UCL_ARRAY) {
744 				while ((tmp = ucl_iterate_object(obj, &it2,
745 				    true))) {
746 					if (uclparse_target_portal_group(target,
747 					    tmp) != 0)
748 						return (1);
749 				}
750 			}
751 		}
752 
753 		if (!strcmp(key, "port")) {
754 			struct pport *pp;
755 			struct port *tp;
756 			const char *value = ucl_object_tostring(obj);
757 
758 			pp = pport_find(conf, value);
759 			if (pp == NULL) {
760 				log_warnx("unknown port \"%s\" for target \"%s\"",
761 				    value, target->t_name);
762 				return (1);
763 			}
764 			if (!TAILQ_EMPTY(&pp->pp_ports)) {
765 				log_warnx("can't link port \"%s\" to target \"%s\", "
766 				    "port already linked to some target",
767 				    value, target->t_name);
768 				return (1);
769 			}
770 			tp = port_new_pp(conf, target, pp);
771 			if (tp == NULL) {
772 				log_warnx("can't link port \"%s\" to target \"%s\"",
773 				    value, target->t_name);
774 				return (1);
775 			}
776 		}
777 
778 		if (!strcmp(key, "redirect")) {
779 			if (obj->type != UCL_STRING) {
780 				log_warnx("\"redirect\" property of target "
781 				    "\"%s\" is not a string", target->t_name);
782 				return (1);
783 			}
784 
785 			if (target_set_redirection(target,
786 			    ucl_object_tostring(obj)) != 0)
787 				return (1);
788 		}
789 
790 		if (!strcmp(key, "lun")) {
791 			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
792 				if (uclparse_target_lun(target, tmp) != 0)
793 					return (1);
794 			}
795 		}
796 	}
797 
798 	return (0);
799 }
800 
801 static int
802 uclparse_lun(const char *name, const ucl_object_t *top)
803 {
804 	struct lun *lun;
805 	ucl_object_iter_t it = NULL, child_it = NULL;
806 	const ucl_object_t *obj = NULL, *child = NULL;
807 	const char *key;
808 
809 	lun = lun_new(conf, name);
810 
811 	while ((obj = ucl_iterate_object(top, &it, true))) {
812 		key = ucl_object_key(obj);
813 
814 		if (!strcmp(key, "backend")) {
815 			if (obj->type != UCL_STRING) {
816 				log_warnx("\"backend\" property of lun "
817 				    "\"%s\" is not a string",
818 				    lun->l_name);
819 				return (1);
820 			}
821 
822 			lun_set_backend(lun, ucl_object_tostring(obj));
823 		}
824 
825 		if (!strcmp(key, "blocksize")) {
826 			if (obj->type != UCL_INT) {
827 				log_warnx("\"blocksize\" property of lun "
828 				    "\"%s\" is not an integer", lun->l_name);
829 				return (1);
830 			}
831 
832 			lun_set_blocksize(lun, ucl_object_toint(obj));
833 		}
834 
835 		if (!strcmp(key, "device-id")) {
836 			if (obj->type != UCL_STRING) {
837 				log_warnx("\"device-id\" property of lun "
838 				    "\"%s\" is not an integer", lun->l_name);
839 				return (1);
840 			}
841 
842 			lun_set_device_id(lun, ucl_object_tostring(obj));
843 		}
844 
845 		if (!strcmp(key, "options")) {
846 			if (obj->type != UCL_OBJECT) {
847 				log_warnx("\"options\" property of lun "
848 				    "\"%s\" is not an object", lun->l_name);
849 				return (1);
850 			}
851 
852 			while ((child = ucl_iterate_object(obj, &child_it,
853 			    true))) {
854 				option_new(&lun->l_options,
855 				    ucl_object_key(child),
856 				    ucl_object_tostring_forced(child));
857 			}
858 		}
859 
860 		if (!strcmp(key, "path")) {
861 			if (obj->type != UCL_STRING) {
862 				log_warnx("\"path\" property of lun "
863 				    "\"%s\" is not a string", lun->l_name);
864 				return (1);
865 			}
866 
867 			lun_set_path(lun, ucl_object_tostring(obj));
868 		}
869 
870 		if (!strcmp(key, "serial")) {
871 			if (obj->type != UCL_STRING) {
872 				log_warnx("\"serial\" property of lun "
873 				    "\"%s\" is not a string", lun->l_name);
874 				return (1);
875 			}
876 
877 			lun_set_serial(lun, ucl_object_tostring(obj));
878 		}
879 
880 		if (!strcmp(key, "size")) {
881 			if (obj->type != UCL_INT) {
882 				log_warnx("\"size\" property of lun "
883 				    "\"%s\" is not an integer", lun->l_name);
884 				return (1);
885 			}
886 
887 			lun_set_size(lun, ucl_object_toint(obj));
888 		}
889 	}
890 
891 	return (0);
892 }
893 
894 int
895 uclparse_conf(struct conf *newconf, const char *path)
896 {
897 	struct ucl_parser *parser;
898 	int error;
899 
900 	conf = newconf;
901 	parser = ucl_parser_new(0);
902 
903 	if (!ucl_parser_add_file(parser, path)) {
904 		log_warn("unable to parse configuration file %s: %s", path,
905 		    ucl_parser_get_error(parser));
906 		return (1);
907 	}
908 
909 	error = uclparse_toplevel(ucl_parser_get_object(parser));
910 
911 	return (error);
912 }
913