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