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