xref: /freebsd/usr.sbin/ctld/uclparse.c (revision 315ee00f)
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 
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 		if (!strcmp(key, "dscp")) {
612 			if ((obj->type != UCL_STRING) && (obj->type != UCL_INT)) {
613 				log_warnx("\"dscp\" property of portal group "
614 				    "\"%s\" is not a string or integer", portal_group->pg_name);
615 				return(1);
616 			}
617 			if (obj->type == UCL_INT)
618 				portal_group->pg_dscp = ucl_object_toint(obj);
619 			else {
620 				key = ucl_object_tostring(obj);
621 				if (strcmp(key, "0x") == 0)
622 					portal_group->pg_dscp = strtol(key + 2, NULL, 16);
623 				else if (strcmp(key, "be") || strcmp(key, "cs0"))
624 					portal_group->pg_dscp = IPTOS_DSCP_CS0 >> 2;
625 				else if (strcmp(key, "ef"))
626 					portal_group->pg_dscp = IPTOS_DSCP_EF >> 2;
627 				else if (strcmp(key, "cs0"))
628 					portal_group->pg_dscp = IPTOS_DSCP_CS0 >> 2;
629 				else if (strcmp(key, "cs1"))
630 					portal_group->pg_dscp = IPTOS_DSCP_CS1 >> 2;
631 				else if (strcmp(key, "cs2"))
632 					portal_group->pg_dscp = IPTOS_DSCP_CS2 >> 2;
633 				else if (strcmp(key, "cs3"))
634 					portal_group->pg_dscp = IPTOS_DSCP_CS3 >> 2;
635 				else if (strcmp(key, "cs4"))
636 					portal_group->pg_dscp = IPTOS_DSCP_CS4 >> 2;
637 				else if (strcmp(key, "cs5"))
638 					portal_group->pg_dscp = IPTOS_DSCP_CS5 >> 2;
639 				else if (strcmp(key, "cs6"))
640 					portal_group->pg_dscp = IPTOS_DSCP_CS6 >> 2;
641 				else if (strcmp(key, "cs7"))
642 					portal_group->pg_dscp = IPTOS_DSCP_CS7 >> 2;
643 				else if (strcmp(key, "af11"))
644 					portal_group->pg_dscp = IPTOS_DSCP_AF11 >> 2;
645 				else if (strcmp(key, "af12"))
646 					portal_group->pg_dscp = IPTOS_DSCP_AF12 >> 2;
647 				else if (strcmp(key, "af13"))
648 					portal_group->pg_dscp = IPTOS_DSCP_AF13 >> 2;
649 				else if (strcmp(key, "af21"))
650 					portal_group->pg_dscp = IPTOS_DSCP_AF21 >> 2;
651 				else if (strcmp(key, "af22"))
652 					portal_group->pg_dscp = IPTOS_DSCP_AF22 >> 2;
653 				else if (strcmp(key, "af23"))
654 					portal_group->pg_dscp = IPTOS_DSCP_AF23 >> 2;
655 				else if (strcmp(key, "af31"))
656 					portal_group->pg_dscp = IPTOS_DSCP_AF31 >> 2;
657 				else if (strcmp(key, "af32"))
658 					portal_group->pg_dscp = IPTOS_DSCP_AF32 >> 2;
659 				else if (strcmp(key, "af33"))
660 					portal_group->pg_dscp = IPTOS_DSCP_AF33 >> 2;
661 				else if (strcmp(key, "af41"))
662 					portal_group->pg_dscp = IPTOS_DSCP_AF41 >> 2;
663 				else if (strcmp(key, "af42"))
664 					portal_group->pg_dscp = IPTOS_DSCP_AF42 >> 2;
665 				else if (strcmp(key, "af43"))
666 					portal_group->pg_dscp = IPTOS_DSCP_AF43 >> 2;
667 				else {
668 					log_warnx("\"dscp\" property value is not a supported textual value");
669 					return (1);
670 				}
671 			}
672 		}
673 
674 		if (!strcmp(key, "pcp")) {
675 			if (obj->type != UCL_INT) {
676 				log_warnx("\"pcp\" property of portal group "
677 				    "\"%s\" is not an integer", portal_group->pg_name);
678 				return(1);
679 			}
680 			portal_group->pg_pcp = ucl_object_toint(obj);
681 			if (!((portal_group->pg_pcp >= 0) && (portal_group->pg_pcp <= 7))) {
682 				log_warnx("invalid \"pcp\" value %d, using default", portal_group->pg_pcp);
683 				portal_group->pg_pcp = -1;
684 			}
685 		}
686 	}
687 
688 	return (0);
689 }
690 
691 static int
692 uclparse_target(const char *name, const ucl_object_t *top)
693 {
694 	struct target *target;
695 	ucl_object_iter_t it = NULL, it2 = NULL;
696 	const ucl_object_t *obj = NULL, *tmp = NULL;
697 	const char *key;
698 
699 	target = target_new(conf, name);
700 	if (target == NULL)
701 		return (1);
702 
703 	while ((obj = ucl_iterate_object(top, &it, true))) {
704 		key = ucl_object_key(obj);
705 
706 		if (!strcmp(key, "alias")) {
707 			if (obj->type != UCL_STRING) {
708 				log_warnx("\"alias\" property of target "
709 				    "\"%s\" is not a string", target->t_name);
710 				return (1);
711 			}
712 
713 			target->t_alias = strdup(ucl_object_tostring(obj));
714 		}
715 
716 		if (!strcmp(key, "auth-group")) {
717 			if (target->t_auth_group != NULL) {
718 				if (target->t_auth_group->ag_name != NULL)
719 					log_warnx("auth-group for target \"%s\" "
720 					    "specified more than once",
721 					    target->t_name);
722 				else
723 					log_warnx("cannot use both auth-group "
724 					    "and explicit authorisations for "
725 					    "target \"%s\"", target->t_name);
726 				return (1);
727 			}
728 			target->t_auth_group = auth_group_find(conf,
729 			    ucl_object_tostring(obj));
730 			if (target->t_auth_group == NULL) {
731 				log_warnx("unknown auth-group \"%s\" for target "
732 				    "\"%s\"", ucl_object_tostring(obj),
733 				    target->t_name);
734 				return (1);
735 			}
736 		}
737 
738 		if (!strcmp(key, "auth-type")) {
739 			int error;
740 
741 			if (target->t_auth_group != NULL) {
742 				if (target->t_auth_group->ag_name != NULL) {
743 					log_warnx("cannot use both auth-group and "
744 					    "auth-type for target \"%s\"",
745 					    target->t_name);
746 					return (1);
747 				}
748 			} else {
749 				target->t_auth_group = auth_group_new(conf, NULL);
750 				if (target->t_auth_group == NULL)
751 					return (1);
752 
753 				target->t_auth_group->ag_target = target;
754 			}
755 			error = auth_group_set_type(target->t_auth_group,
756 			    ucl_object_tostring(obj));
757 			if (error != 0)
758 				return (1);
759 		}
760 
761 		if (!strcmp(key, "chap")) {
762 			if (uclparse_chap(target->t_auth_group, obj) != 0)
763 				return (1);
764 		}
765 
766 		if (!strcmp(key, "chap-mutual")) {
767 			if (uclparse_chap_mutual(target->t_auth_group, obj) != 0)
768 				return (1);
769 		}
770 
771 		if (!strcmp(key, "initiator-name")) {
772 			const struct auth_name *an;
773 
774 			if (target->t_auth_group != NULL) {
775 				if (target->t_auth_group->ag_name != NULL) {
776 					log_warnx("cannot use both auth-group and "
777 					    "initiator-name for target \"%s\"",
778 					    target->t_name);
779 					return (1);
780 				}
781 			} else {
782 				target->t_auth_group = auth_group_new(conf, NULL);
783 				if (target->t_auth_group == NULL)
784 					return (1);
785 
786 				target->t_auth_group->ag_target = target;
787 			}
788 			an = auth_name_new(target->t_auth_group,
789 			    ucl_object_tostring(obj));
790 			if (an == NULL)
791 				return (1);
792 		}
793 
794 		if (!strcmp(key, "initiator-portal")) {
795 			const struct auth_portal *ap;
796 
797 			if (target->t_auth_group != NULL) {
798 				if (target->t_auth_group->ag_name != NULL) {
799 					log_warnx("cannot use both auth-group and "
800 					    "initiator-portal for target \"%s\"",
801 					    target->t_name);
802 					return (1);
803 				}
804 			} else {
805 				target->t_auth_group = auth_group_new(conf, NULL);
806 				if (target->t_auth_group == NULL)
807 					return (1);
808 
809 				target->t_auth_group->ag_target = target;
810 			}
811 			ap = auth_portal_new(target->t_auth_group,
812 			    ucl_object_tostring(obj));
813 			if (ap == NULL)
814 				return (1);
815 		}
816 
817 		if (!strcmp(key, "portal-group")) {
818 			if (obj->type == UCL_OBJECT) {
819 				if (uclparse_target_portal_group(target, obj) != 0)
820 					return (1);
821 			}
822 
823 			if (obj->type == UCL_ARRAY) {
824 				while ((tmp = ucl_iterate_object(obj, &it2,
825 				    true))) {
826 					if (uclparse_target_portal_group(target,
827 					    tmp) != 0)
828 						return (1);
829 				}
830 			}
831 		}
832 
833 		if (!strcmp(key, "port")) {
834 			struct pport *pp;
835 			struct port *tp;
836 			const char *value = ucl_object_tostring(obj);
837 			int ret, i_pp, i_vp = 0;
838 
839 			ret = sscanf(value, "ioctl/%d/%d", &i_pp, &i_vp);
840 			if (ret > 0) {
841 				tp = port_new_ioctl(conf, target, i_pp, i_vp);
842 				if (tp == NULL) {
843 					log_warnx("can't create new ioctl port "
844 					    "for target \"%s\"", target->t_name);
845 					return (1);
846 				}
847 
848 				continue;
849 			}
850 
851 			pp = pport_find(conf, value);
852 			if (pp == NULL) {
853 				log_warnx("unknown port \"%s\" for target \"%s\"",
854 				    value, target->t_name);
855 				return (1);
856 			}
857 			if (!TAILQ_EMPTY(&pp->pp_ports)) {
858 				log_warnx("can't link port \"%s\" to target \"%s\", "
859 				    "port already linked to some target",
860 				    value, target->t_name);
861 				return (1);
862 			}
863 			tp = port_new_pp(conf, target, pp);
864 			if (tp == NULL) {
865 				log_warnx("can't link port \"%s\" to target \"%s\"",
866 				    value, target->t_name);
867 				return (1);
868 			}
869 		}
870 
871 		if (!strcmp(key, "redirect")) {
872 			if (obj->type != UCL_STRING) {
873 				log_warnx("\"redirect\" property of target "
874 				    "\"%s\" is not a string", target->t_name);
875 				return (1);
876 			}
877 
878 			if (target_set_redirection(target,
879 			    ucl_object_tostring(obj)) != 0)
880 				return (1);
881 		}
882 
883 		if (!strcmp(key, "lun")) {
884 			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
885 				if (uclparse_target_lun(target, tmp) != 0)
886 					return (1);
887 			}
888 		}
889 	}
890 
891 	return (0);
892 }
893 
894 static int
895 uclparse_lun(const char *name, const ucl_object_t *top)
896 {
897 	struct lun *lun;
898 	ucl_object_iter_t it = NULL, child_it = NULL;
899 	const ucl_object_t *obj = NULL, *child = NULL;
900 	const char *key;
901 
902 	lun = lun_new(conf, name);
903 	if (lun == NULL)
904 		return (1);
905 
906 	while ((obj = ucl_iterate_object(top, &it, true))) {
907 		key = ucl_object_key(obj);
908 
909 		if (!strcmp(key, "backend")) {
910 			if (obj->type != UCL_STRING) {
911 				log_warnx("\"backend\" property of lun "
912 				    "\"%s\" is not a string",
913 				    lun->l_name);
914 				return (1);
915 			}
916 
917 			lun_set_backend(lun, ucl_object_tostring(obj));
918 		}
919 
920 		if (!strcmp(key, "blocksize")) {
921 			if (obj->type != UCL_INT) {
922 				log_warnx("\"blocksize\" property of lun "
923 				    "\"%s\" is not an integer", lun->l_name);
924 				return (1);
925 			}
926 
927 			lun_set_blocksize(lun, ucl_object_toint(obj));
928 		}
929 
930 		if (!strcmp(key, "device-id")) {
931 			if (obj->type != UCL_STRING) {
932 				log_warnx("\"device-id\" property of lun "
933 				    "\"%s\" is not an integer", lun->l_name);
934 				return (1);
935 			}
936 
937 			lun_set_device_id(lun, ucl_object_tostring(obj));
938 		}
939 
940 		if (!strcmp(key, "options")) {
941 			if (obj->type != UCL_OBJECT) {
942 				log_warnx("\"options\" property of lun "
943 				    "\"%s\" is not an object", lun->l_name);
944 				return (1);
945 			}
946 
947 			while ((child = ucl_iterate_object(obj, &child_it,
948 			    true))) {
949 				option_new(&lun->l_options,
950 				    ucl_object_key(child),
951 				    ucl_object_tostring_forced(child));
952 			}
953 		}
954 
955 		if (!strcmp(key, "path")) {
956 			if (obj->type != UCL_STRING) {
957 				log_warnx("\"path\" property of lun "
958 				    "\"%s\" is not a string", lun->l_name);
959 				return (1);
960 			}
961 
962 			lun_set_path(lun, ucl_object_tostring(obj));
963 		}
964 
965 		if (!strcmp(key, "serial")) {
966 			if (obj->type != UCL_STRING) {
967 				log_warnx("\"serial\" property of lun "
968 				    "\"%s\" is not a string", lun->l_name);
969 				return (1);
970 			}
971 
972 			lun_set_serial(lun, ucl_object_tostring(obj));
973 		}
974 
975 		if (!strcmp(key, "size")) {
976 			if (obj->type != UCL_INT) {
977 				log_warnx("\"size\" property of lun "
978 				    "\"%s\" is not an integer", lun->l_name);
979 				return (1);
980 			}
981 
982 			lun_set_size(lun, ucl_object_toint(obj));
983 		}
984 	}
985 
986 	return (0);
987 }
988 
989 int
990 uclparse_conf(struct conf *newconf, const char *path)
991 {
992 	struct ucl_parser *parser;
993 	ucl_object_t *top;
994 	int error;
995 
996 	conf = newconf;
997 	parser = ucl_parser_new(0);
998 
999 	if (!ucl_parser_add_file(parser, path)) {
1000 		log_warn("unable to parse configuration file %s: %s", path,
1001 		    ucl_parser_get_error(parser));
1002 		ucl_parser_free(parser);
1003 		return (1);
1004 	}
1005 
1006 	top = ucl_parser_get_object(parser);
1007 	error = uclparse_toplevel(top);
1008 	ucl_object_unref(top);
1009 	ucl_parser_free(parser);
1010 
1011 	return (error);
1012 }
1013