1255570Strasz%{
2255570Strasz/*-
3255570Strasz * Copyright (c) 2012 The FreeBSD Foundation
4255570Strasz * All rights reserved.
5255570Strasz *
6255570Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
7255570Strasz * from the FreeBSD Foundation.
8255570Strasz *
9255570Strasz * Redistribution and use in source and binary forms, with or without
10255570Strasz * modification, are permitted provided that the following conditions
11255570Strasz * are met:
12255570Strasz * 1. Redistributions of source code must retain the above copyright
13255570Strasz *    notice, this list of conditions and the following disclaimer.
14255570Strasz * 2. Redistributions in binary form must reproduce the above copyright
15255570Strasz *    notice, this list of conditions and the following disclaimer in the
16255570Strasz *    documentation and/or other materials provided with the distribution.
17255570Strasz *
18255570Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21255570Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28255570Strasz * SUCH DAMAGE.
29255570Strasz *
30255570Strasz * $FreeBSD$
31255570Strasz */
32255570Strasz
33255570Strasz#include <sys/queue.h>
34255570Strasz#include <sys/types.h>
35255570Strasz#include <sys/stat.h>
36255570Strasz#include <assert.h>
37255570Strasz#include <stdio.h>
38255570Strasz#include <stdint.h>
39255570Strasz#include <stdlib.h>
40255570Strasz#include <string.h>
41255570Strasz
42255570Strasz#include "ctld.h"
43255570Strasz
44255570Straszextern FILE *yyin;
45255570Straszextern char *yytext;
46255570Straszextern int lineno;
47255570Strasz
48255570Straszstatic struct conf *conf = NULL;
49255570Straszstatic struct auth_group *auth_group = NULL;
50255570Straszstatic struct portal_group *portal_group = NULL;
51255570Straszstatic struct target *target = NULL;
52255570Straszstatic struct lun *lun = NULL;
53255570Strasz
54255570Straszextern void	yyerror(const char *);
55255570Straszextern int	yylex(void);
56255570Straszextern void	yyrestart(FILE *);
57255570Strasz
58255570Strasz%}
59255570Strasz
60255570Strasz%token ALIAS AUTH_GROUP BACKEND BLOCKSIZE CHAP CHAP_MUTUAL CLOSING_BRACKET
61255570Strasz%token DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP LISTEN LISTEN_ISER LUN MAXPROC NUM
62255570Strasz%token OPENING_BRACKET OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR TARGET
63255570Strasz%token TIMEOUT
64255570Strasz
65255570Strasz%union
66255570Strasz{
67255570Strasz	uint64_t num;
68255570Strasz	char *str;
69255570Strasz}
70255570Strasz
71255570Strasz%token <num> NUM
72255570Strasz%token <str> STR
73255570Strasz
74255570Strasz%%
75255570Strasz
76255570Straszstatements:
77255570Strasz	|
78255570Strasz	statements statement
79255570Strasz	;
80255570Strasz
81255570Straszstatement:
82255570Strasz	debug_statement
83255570Strasz	|
84255570Strasz	timeout_statement
85255570Strasz	|
86255570Strasz	maxproc_statement
87255570Strasz	|
88255570Strasz	pidfile_statement
89255570Strasz	|
90255570Strasz	auth_group_definition
91255570Strasz	|
92255570Strasz	portal_group_definition
93255570Strasz	|
94255570Strasz	target_statement
95255570Strasz	;
96255570Strasz
97255570Straszdebug_statement:	DEBUG NUM
98255570Strasz	{
99255570Strasz		conf->conf_debug = $2;
100255570Strasz	}
101255570Strasz	;
102255570Strasz
103255570Strasztimeout_statement:	TIMEOUT NUM
104255570Strasz	{
105255570Strasz		conf->conf_timeout = $2;
106255570Strasz	}
107255570Strasz	;
108255570Strasz
109255570Straszmaxproc_statement:	MAXPROC NUM
110255570Strasz	{
111255570Strasz		conf->conf_maxproc = $2;
112255570Strasz	}
113255570Strasz	;
114255570Strasz
115255570Straszpidfile_statement:	PIDFILE STR
116255570Strasz	{
117255570Strasz		if (conf->conf_pidfile_path != NULL) {
118255570Strasz			log_warnx("pidfile specified more than once");
119255570Strasz			free($2);
120255570Strasz			return (1);
121255570Strasz		}
122255570Strasz		conf->conf_pidfile_path = $2;
123255570Strasz	}
124255570Strasz	;
125255570Strasz
126255570Straszauth_group_definition:	AUTH_GROUP auth_group_name
127255570Strasz    OPENING_BRACKET auth_group_entries CLOSING_BRACKET
128255570Strasz	{
129255570Strasz		auth_group = NULL;
130255570Strasz	}
131255570Strasz	;
132255570Strasz
133255570Straszauth_group_name:	STR
134255570Strasz	{
135255570Strasz		auth_group = auth_group_new(conf, $1);
136255570Strasz		free($1);
137255570Strasz		if (auth_group == NULL)
138255570Strasz			return (1);
139255570Strasz	}
140255570Strasz	;
141255570Strasz
142255570Straszauth_group_entries:
143255570Strasz	|
144255570Strasz	auth_group_entries auth_group_entry
145255570Strasz	;
146255570Strasz
147255570Straszauth_group_entry:
148255570Strasz	auth_group_chap
149255570Strasz	|
150255570Strasz	auth_group_chap_mutual
151255570Strasz	;
152255570Strasz
153255570Straszauth_group_chap:	CHAP STR STR
154255570Strasz	{
155255570Strasz		const struct auth *ca;
156255570Strasz
157255570Strasz		ca = auth_new_chap(auth_group, $2, $3);
158255570Strasz		free($2);
159255570Strasz		free($3);
160255570Strasz		if (ca == NULL)
161255570Strasz			return (1);
162255570Strasz	}
163255570Strasz	;
164255570Strasz
165255570Straszauth_group_chap_mutual:	CHAP_MUTUAL STR STR STR STR
166255570Strasz	{
167255570Strasz		const struct auth *ca;
168255570Strasz
169255570Strasz		ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5);
170255570Strasz		free($2);
171255570Strasz		free($3);
172255570Strasz		free($4);
173255570Strasz		free($5);
174255570Strasz		if (ca == NULL)
175255570Strasz			return (1);
176255570Strasz	}
177255570Strasz	;
178255570Strasz
179255570Straszportal_group_definition:	PORTAL_GROUP portal_group_name
180255570Strasz    OPENING_BRACKET portal_group_entries CLOSING_BRACKET
181255570Strasz	{
182255570Strasz		portal_group = NULL;
183255570Strasz	}
184255570Strasz	;
185255570Strasz
186255570Straszportal_group_name:	STR
187255570Strasz	{
188255570Strasz		portal_group = portal_group_new(conf, $1);
189255570Strasz		free($1);
190255570Strasz		if (portal_group == NULL)
191255570Strasz			return (1);
192255570Strasz	}
193255570Strasz	;
194255570Strasz
195255570Straszportal_group_entries:
196255570Strasz	|
197255570Strasz	portal_group_entries portal_group_entry
198255570Strasz	;
199255570Strasz
200255570Straszportal_group_entry:
201255570Strasz	portal_group_discovery_auth_group
202255570Strasz	|
203255570Strasz	portal_group_listen
204255570Strasz	|
205255570Strasz	portal_group_listen_iser
206255570Strasz	;
207255570Strasz
208255570Straszportal_group_discovery_auth_group:	DISCOVERY_AUTH_GROUP STR
209255570Strasz	{
210255570Strasz		if (portal_group->pg_discovery_auth_group != NULL) {
211255570Strasz			log_warnx("discovery-auth-group for portal-group "
212255570Strasz			    "\"%s\" specified more than once",
213255570Strasz			    portal_group->pg_name);
214255570Strasz			return (1);
215255570Strasz		}
216255570Strasz		portal_group->pg_discovery_auth_group =
217255570Strasz		    auth_group_find(conf, $2);
218255570Strasz		if (portal_group->pg_discovery_auth_group == NULL) {
219255570Strasz			log_warnx("unknown discovery-auth-group \"%s\" "
220255570Strasz			    "for portal-group \"%s\"",
221255570Strasz			    $2, portal_group->pg_name);
222255570Strasz			return (1);
223255570Strasz		}
224255570Strasz		free($2);
225255570Strasz	}
226255570Strasz	;
227255570Strasz
228255570Straszportal_group_listen:	LISTEN STR
229255570Strasz	{
230255570Strasz		int error;
231255570Strasz
232255570Strasz		error = portal_group_add_listen(portal_group, $2, false);
233255570Strasz		free($2);
234255570Strasz		if (error != 0)
235255570Strasz			return (1);
236255570Strasz	}
237255570Strasz	;
238255570Strasz
239255570Straszportal_group_listen_iser:	LISTEN_ISER STR
240255570Strasz	{
241255570Strasz		int error;
242255570Strasz
243255570Strasz		error = portal_group_add_listen(portal_group, $2, true);
244255570Strasz		free($2);
245255570Strasz		if (error != 0)
246255570Strasz			return (1);
247255570Strasz	}
248255570Strasz	;
249255570Strasz
250255570Strasztarget_statement:	TARGET target_iqn
251255570Strasz    OPENING_BRACKET target_entries CLOSING_BRACKET
252255570Strasz	{
253255570Strasz		target = NULL;
254255570Strasz	}
255255570Strasz	;
256255570Strasz
257255570Strasztarget_iqn:	STR
258255570Strasz	{
259255570Strasz		target = target_new(conf, $1);
260255570Strasz		free($1);
261255570Strasz		if (target == NULL)
262255570Strasz			return (1);
263255570Strasz	}
264255570Strasz	;
265255570Strasz
266255570Strasztarget_entries:
267255570Strasz	|
268255570Strasz	target_entries target_entry
269255570Strasz	;
270255570Strasz
271255570Strasztarget_entry:
272255570Strasz	alias_statement
273255570Strasz	|
274255570Strasz	auth_group_statement
275255570Strasz	|
276255570Strasz	chap_statement
277255570Strasz	|
278255570Strasz	chap_mutual_statement
279255570Strasz	|
280255570Strasz	portal_group_statement
281255570Strasz	|
282255570Strasz	lun_statement
283255570Strasz	;
284255570Strasz
285255570Straszalias_statement:	ALIAS STR
286255570Strasz	{
287255570Strasz		if (target->t_alias != NULL) {
288255570Strasz			log_warnx("alias for target \"%s\" "
289255570Strasz			    "specified more than once", target->t_iqn);
290255570Strasz			return (1);
291255570Strasz		}
292255570Strasz		target->t_alias = $2;
293255570Strasz	}
294255570Strasz	;
295255570Strasz
296255570Straszauth_group_statement:	AUTH_GROUP STR
297255570Strasz	{
298255570Strasz		if (target->t_auth_group != NULL) {
299255570Strasz			if (target->t_auth_group->ag_name != NULL)
300255570Strasz				log_warnx("auth-group for target \"%s\" "
301255570Strasz				    "specified more than once", target->t_iqn);
302255570Strasz			else
303258989Strasz				log_warnx("cannot mix auth-group with explicit "
304255570Strasz				    "authorisations for target \"%s\"",
305255570Strasz				    target->t_iqn);
306255570Strasz			return (1);
307255570Strasz		}
308255570Strasz		target->t_auth_group = auth_group_find(conf, $2);
309255570Strasz		if (target->t_auth_group == NULL) {
310255570Strasz			log_warnx("unknown auth-group \"%s\" for target "
311255570Strasz			    "\"%s\"", $2, target->t_iqn);
312255570Strasz			return (1);
313255570Strasz		}
314255570Strasz		free($2);
315255570Strasz	}
316255570Strasz	;
317255570Strasz
318255570Straszchap_statement:	CHAP STR STR
319255570Strasz	{
320255570Strasz		const struct auth *ca;
321255570Strasz
322255570Strasz		if (target->t_auth_group != NULL) {
323255570Strasz			if (target->t_auth_group->ag_name != NULL) {
324258989Strasz				log_warnx("cannot mix auth-group with explicit "
325255570Strasz				    "authorisations for target \"%s\"",
326255570Strasz				    target->t_iqn);
327255570Strasz				free($2);
328255570Strasz				free($3);
329255570Strasz				return (1);
330255570Strasz			}
331255570Strasz		} else {
332255570Strasz			target->t_auth_group = auth_group_new(conf, NULL);
333255570Strasz			if (target->t_auth_group == NULL) {
334255570Strasz				free($2);
335255570Strasz				free($3);
336255570Strasz				return (1);
337255570Strasz			}
338255570Strasz			target->t_auth_group->ag_target = target;
339255570Strasz		}
340255570Strasz		ca = auth_new_chap(target->t_auth_group, $2, $3);
341255570Strasz		free($2);
342255570Strasz		free($3);
343255570Strasz		if (ca == NULL)
344255570Strasz			return (1);
345255570Strasz	}
346255570Strasz	;
347255570Strasz
348255570Straszchap_mutual_statement:	CHAP_MUTUAL STR STR STR STR
349255570Strasz	{
350255570Strasz		const struct auth *ca;
351255570Strasz
352255570Strasz		if (target->t_auth_group != NULL) {
353255570Strasz			if (target->t_auth_group->ag_name != NULL) {
354258989Strasz				log_warnx("cannot mix auth-group with explicit "
355255570Strasz				    "authorisations for target \"%s\"",
356255570Strasz				    target->t_iqn);
357255570Strasz				free($2);
358255570Strasz				free($3);
359255570Strasz				free($4);
360255570Strasz				free($5);
361255570Strasz				return (1);
362255570Strasz			}
363255570Strasz		} else {
364255570Strasz			target->t_auth_group = auth_group_new(conf, NULL);
365255570Strasz			if (target->t_auth_group == NULL) {
366255570Strasz				free($2);
367255570Strasz				free($3);
368255570Strasz				free($4);
369255570Strasz				free($5);
370255570Strasz				return (1);
371255570Strasz			}
372255570Strasz			target->t_auth_group->ag_target = target;
373255570Strasz		}
374255570Strasz		ca = auth_new_chap_mutual(target->t_auth_group,
375255570Strasz		    $2, $3, $4, $5);
376255570Strasz		free($2);
377255570Strasz		free($3);
378255570Strasz		free($4);
379255570Strasz		free($5);
380255570Strasz		if (ca == NULL)
381255570Strasz			return (1);
382255570Strasz	}
383255570Strasz	;
384255570Strasz
385255570Straszportal_group_statement:	PORTAL_GROUP STR
386255570Strasz	{
387255570Strasz		if (target->t_portal_group != NULL) {
388255570Strasz			log_warnx("portal-group for target \"%s\" "
389255570Strasz			    "specified more than once", target->t_iqn);
390255570Strasz			free($2);
391255570Strasz			return (1);
392255570Strasz		}
393255570Strasz		target->t_portal_group = portal_group_find(conf, $2);
394255570Strasz		if (target->t_portal_group == NULL) {
395255570Strasz			log_warnx("unknown portal-group \"%s\" for target "
396255570Strasz			    "\"%s\"", $2, target->t_iqn);
397255570Strasz			free($2);
398255570Strasz			return (1);
399255570Strasz		}
400255570Strasz		free($2);
401255570Strasz	}
402255570Strasz	;
403255570Strasz
404255570Straszlun_statement:	LUN lun_number
405255570Strasz    OPENING_BRACKET lun_statement_entries CLOSING_BRACKET
406255570Strasz	{
407255570Strasz		lun = NULL;
408255570Strasz	}
409255570Strasz	;
410255570Strasz
411255570Straszlun_number:	NUM
412255570Strasz	{
413255570Strasz		lun = lun_new(target, $1);
414255570Strasz		if (lun == NULL)
415255570Strasz			return (1);
416255570Strasz	}
417255570Strasz	;
418255570Strasz
419255570Straszlun_statement_entries:
420255570Strasz	|
421255570Strasz	lun_statement_entries lun_statement_entry
422255570Strasz	;
423255570Strasz
424255570Straszlun_statement_entry:
425255570Strasz	backend_statement
426255570Strasz	|
427255570Strasz	blocksize_statement
428255570Strasz	|
429255570Strasz	device_id_statement
430255570Strasz	|
431255570Strasz	option_statement
432255570Strasz	|
433255570Strasz	path_statement
434255570Strasz	|
435255570Strasz	serial_statement
436255570Strasz	|
437255570Strasz	size_statement
438255570Strasz	;
439255570Strasz
440255570Straszbackend_statement:	BACKEND STR
441255570Strasz	{
442255570Strasz		if (lun->l_backend != NULL) {
443255570Strasz			log_warnx("backend for lun %d, target \"%s\" "
444255570Strasz			    "specified more than once",
445255570Strasz			    lun->l_lun, target->t_iqn);
446255570Strasz			free($2);
447255570Strasz			return (1);
448255570Strasz		}
449255570Strasz		lun_set_backend(lun, $2);
450255570Strasz		free($2);
451255570Strasz	}
452255570Strasz	;
453255570Strasz
454255570Straszblocksize_statement:	BLOCKSIZE NUM
455255570Strasz	{
456255570Strasz		if (lun->l_blocksize != 0) {
457255570Strasz			log_warnx("blocksize for lun %d, target \"%s\" "
458255570Strasz			    "specified more than once",
459255570Strasz			    lun->l_lun, target->t_iqn);
460255570Strasz			return (1);
461255570Strasz		}
462255570Strasz		lun_set_blocksize(lun, $2);
463255570Strasz	}
464255570Strasz	;
465255570Strasz
466255570Straszdevice_id_statement:	DEVICE_ID STR
467255570Strasz	{
468255570Strasz		if (lun->l_device_id != NULL) {
469255570Strasz			log_warnx("device_id for lun %d, target \"%s\" "
470255570Strasz			    "specified more than once",
471255570Strasz			    lun->l_lun, target->t_iqn);
472255570Strasz			free($2);
473255570Strasz			return (1);
474255570Strasz		}
475255570Strasz		lun_set_device_id(lun, $2);
476255570Strasz		free($2);
477255570Strasz	}
478255570Strasz	;
479255570Strasz
480255570Straszoption_statement:	OPTION STR STR
481255570Strasz	{
482255570Strasz		struct lun_option *clo;
483255570Strasz
484255570Strasz		clo = lun_option_new(lun, $2, $3);
485255570Strasz		free($2);
486255570Strasz		free($3);
487255570Strasz		if (clo == NULL)
488255570Strasz			return (1);
489255570Strasz	}
490255570Strasz	;
491255570Strasz
492255570Straszpath_statement:	PATH STR
493255570Strasz	{
494255570Strasz		if (lun->l_path != NULL) {
495255570Strasz			log_warnx("path for lun %d, target \"%s\" "
496255570Strasz			    "specified more than once",
497255570Strasz			    lun->l_lun, target->t_iqn);
498255570Strasz			free($2);
499255570Strasz			return (1);
500255570Strasz		}
501255570Strasz		lun_set_path(lun, $2);
502255570Strasz		free($2);
503255570Strasz	}
504255570Strasz	;
505255570Strasz
506255570Straszserial_statement:	SERIAL STR
507255570Strasz	{
508255570Strasz		if (lun->l_serial != NULL) {
509255570Strasz			log_warnx("serial for lun %d, target \"%s\" "
510255570Strasz			    "specified more than once",
511255570Strasz			    lun->l_lun, target->t_iqn);
512255570Strasz			free($2);
513255570Strasz			return (1);
514255570Strasz		}
515255570Strasz		lun_set_serial(lun, $2);
516255570Strasz		free($2);
517255570Strasz	}
518255570Strasz	;
519255570Strasz
520255570Straszsize_statement:	SIZE NUM
521255570Strasz	{
522255570Strasz		if (lun->l_size != 0) {
523255570Strasz			log_warnx("size for lun %d, target \"%s\" "
524255570Strasz			    "specified more than once",
525255570Strasz			    lun->l_lun, target->t_iqn);
526255570Strasz			return (1);
527255570Strasz		}
528255570Strasz		lun_set_size(lun, $2);
529255570Strasz	}
530255570Strasz	;
531255570Strasz%%
532255570Strasz
533255570Straszvoid
534255570Straszyyerror(const char *str)
535255570Strasz{
536255570Strasz
537255570Strasz	log_warnx("error in configuration file at line %d near '%s': %s",
538255570Strasz	    lineno, yytext, str);
539255570Strasz}
540255570Strasz
541255570Straszstatic void
542255570Straszcheck_perms(const char *path)
543255570Strasz{
544255570Strasz	struct stat sb;
545255570Strasz	int error;
546255570Strasz
547255570Strasz	error = stat(path, &sb);
548255570Strasz	if (error != 0) {
549255570Strasz		log_warn("stat");
550255570Strasz		return;
551255570Strasz	}
552255570Strasz	if (sb.st_mode & S_IWOTH) {
553255570Strasz		log_warnx("%s is world-writable", path);
554255570Strasz	} else if (sb.st_mode & S_IROTH) {
555255570Strasz		log_warnx("%s is world-readable", path);
556255570Strasz	} else if (sb.st_mode & S_IXOTH) {
557255570Strasz		/*
558255570Strasz		 * Ok, this one doesn't matter, but still do it,
559255570Strasz		 * just for consistency.
560255570Strasz		 */
561255570Strasz		log_warnx("%s is world-executable", path);
562255570Strasz	}
563255570Strasz
564255570Strasz	/*
565255570Strasz	 * XXX: Should we also check for owner != 0?
566255570Strasz	 */
567255570Strasz}
568255570Strasz
569255570Straszstruct conf *
570255570Straszconf_new_from_file(const char *path)
571255570Strasz{
572255570Strasz	struct auth_group *ag;
573255570Strasz	struct portal_group *pg;
574255570Strasz	int error;
575255570Strasz
576255570Strasz	log_debugx("obtaining configuration from %s", path);
577255570Strasz
578255570Strasz	conf = conf_new();
579255570Strasz
580255570Strasz	ag = auth_group_new(conf, "no-authentication");
581255570Strasz	ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
582255570Strasz
583255570Strasz	/*
584255570Strasz	 * Here, the type doesn't really matter, as the group doesn't contain
585255570Strasz	 * any entries and thus will always deny access.
586255570Strasz	 */
587255570Strasz	ag = auth_group_new(conf, "no-access");
588255570Strasz	ag->ag_type = AG_TYPE_CHAP;
589255570Strasz
590255570Strasz	pg = portal_group_new(conf, "default");
591255570Strasz	portal_group_add_listen(pg, "0.0.0.0:3260", false);
592255570Strasz	portal_group_add_listen(pg, "[::]:3260", false);
593255570Strasz
594255570Strasz	yyin = fopen(path, "r");
595255570Strasz	if (yyin == NULL) {
596255570Strasz		log_warn("unable to open configuration file %s", path);
597255570Strasz		conf_delete(conf);
598255570Strasz		return (NULL);
599255570Strasz	}
600255570Strasz	check_perms(path);
601255570Strasz	lineno = 0;
602255570Strasz	yyrestart(yyin);
603255570Strasz	error = yyparse();
604255570Strasz	auth_group = NULL;
605255570Strasz	portal_group = NULL;
606255570Strasz	target = NULL;
607255570Strasz	lun = NULL;
608255570Strasz	fclose(yyin);
609255570Strasz	if (error != 0) {
610255570Strasz		conf_delete(conf);
611255570Strasz		return (NULL);
612255570Strasz	}
613255570Strasz
614255570Strasz	error = conf_verify(conf);
615255570Strasz	if (error != 0) {
616255570Strasz		conf_delete(conf);
617255570Strasz		return (NULL);
618255570Strasz	}
619255570Strasz
620255570Strasz	return (conf);
621255570Strasz}
622