1 /*
2  * Trivial smb.conf parsing code
3  * iniparser compatibility layer.
4  *
5  * Copyright Jeremy Allison <jra@samba.org> 2014
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, and the entire permission notice in its entirety,
12  *    including the disclaimer of warranties.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote
17  *    products derived from this software without specific prior
18  *    written permission.
19  *
20  * ALTERNATIVELY, this product may be distributed under the terms of
21  * the GNU Public License Version 3 or later, in which case the provisions
22  * of the GPL are required INSTEAD OF the above restrictions.  (This clause is
23  * necessary due to a potential bad interaction between the GPL and
24  * the restrictions contained in a BSD-style copyright.)
25  *
26  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
27  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
30  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36  * OF THE POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdbool.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <string.h>
45 #include <stddef.h>
46 #include "tini.h"
47 #include "tiniparser.h"
48 
49 struct tiniparser_entry {
50 	struct tiniparser_entry *next_entry;
51 	char *key;
52 	char *value;
53 };
54 
55 struct tiniparser_section {
56 	struct tiniparser_section *next_section;
57 	struct tiniparser_entry *entry_list;
58 	char section_name[];
59 };
60 
61 struct tiniparser_dictionary {
62 	struct tiniparser_section *section_list;
63 };
64 
65 /*
66  * Find a section from a given key.
67  * Also return start of subkey.
68  * Return NULL if section name can't be found,
69  * if no section name given, or no subkey given.
70  */
71 
find_section(struct tiniparser_dictionary * d,const char * key,const char ** subkey)72 static struct tiniparser_section *find_section(struct tiniparser_dictionary *d,
73 					const char *key,
74 					const char **subkey)
75 {
76 	struct tiniparser_section *curr_section;
77 	const char *p;
78 	size_t section_len;
79 
80 	if (key == NULL) {
81 		return NULL;
82 	}
83 	p = strchr(key, ':');
84 	if (p == NULL) {
85 		/* No section. */
86 		return NULL;
87 	}
88 
89 	section_len = p - key;
90 	/* Ensure we have at least one character of section name. */
91 	if (section_len == 0) {
92 		return NULL;
93 	}
94 	/* Ensure we have at least one character of subkey. */
95 	if (p[1] == '\0') {
96 		return NULL;
97 	}
98 
99 	for (curr_section = d->section_list;
100 			curr_section;
101 			curr_section = curr_section->next_section) {
102 		/*
103 		 * Check if the key section matches the
104 		 * section name *exactly* (with terminating
105 		 * null after section_len characters.
106 		 */
107 		if ((strncasecmp(key, curr_section->section_name, section_len) == 0) &&
108 				(curr_section->section_name[section_len] == '\0')) {
109 			*subkey = p+1;
110 			return curr_section;
111 		}
112 	}
113 	return NULL;
114 }
115 
find_entry(struct tiniparser_section * section,const char * key)116 static struct tiniparser_entry *find_entry(struct tiniparser_section *section,
117 					const char *key)
118 {
119 	struct tiniparser_entry *curr_entry;
120 
121 	for (curr_entry = section->entry_list;
122 			curr_entry;
123 			curr_entry = curr_entry->next_entry) {
124 		if (strcasecmp(key,
125 				curr_entry->key) == 0) {
126 			return curr_entry;
127 		}
128 	}
129 	return NULL;
130 }
131 
tiniparser_getstring(struct tiniparser_dictionary * d,const char * key,const char * default_value)132 const char *tiniparser_getstring(struct tiniparser_dictionary *d,
133 			const char *key,
134 			const char *default_value)
135 {
136 	struct tiniparser_section *section;
137 	struct tiniparser_entry *entry;
138 	const char *subkey;
139 
140 	section = find_section(d, key, &subkey);
141 	if (section == NULL) {
142 		return default_value;
143 	}
144 
145 	entry = find_entry(section, subkey);
146 	if (entry == NULL) {
147 		return default_value;
148 	}
149 
150 	return entry->value;
151 }
152 
153 
tiniparser_getboolean(struct tiniparser_dictionary * d,const char * key,bool default_value)154 bool tiniparser_getboolean(struct tiniparser_dictionary *d,
155 			const char *key,
156 			bool default_value)
157 {
158 	const char *value = tiniparser_getstring(d, key, NULL);
159 
160 	if (value == NULL) {
161 		return default_value;
162 	}
163 
164 	switch(value[0]) {
165 		case '1':
166 		case 'T':
167 		case 't':
168 		case 'y':
169 		case 'Y':
170 			return true;
171 		case '0':
172 		case 'F':
173 		case 'f':
174 		case 'n':
175 		case 'N':
176 			return false;
177 		default:
178 			break;
179 	}
180 
181 	return default_value;
182 }
183 
tiniparser_getint(struct tiniparser_dictionary * d,const char * key,int default_value)184 int tiniparser_getint(struct tiniparser_dictionary *d,
185 			const char *key,
186 			int default_value)
187 {
188 	const char *value = tiniparser_getstring(d, key, NULL);
189 
190 	if (value == NULL) {
191 		return default_value;
192 	}
193 
194 	return (int)strtol(value, NULL, 0);
195 }
196 
value_parser(const char * key,const char * value,void * private_data)197 static bool value_parser(const char *key,
198 			const char *value,
199 			void *private_data)
200 {
201 	struct tiniparser_dictionary *d =
202 		(struct tiniparser_dictionary *)private_data;
203 	struct tiniparser_section *section = d->section_list;
204 	struct tiniparser_entry *entry = NULL;
205 	size_t val_len;
206 	size_t key_len;
207 
208 	if (section == NULL) {
209 		return false;
210 	}
211 	if (key == NULL) {
212 		return false;
213 	}
214 	if (value == NULL) {
215 		return false;
216 	}
217 
218 	key_len = strlen(key) + 1;
219 	val_len = strlen(value) + 1;
220 
221 	entry = find_entry(section, key);
222 	if (entry) {
223 		/* Replace current value. */
224 		char *new_val = malloc(val_len);
225 		if (new_val == NULL) {
226 			return false;
227 		}
228 		memcpy(new_val, value, val_len);
229 		free(entry->value);
230 		entry->value = new_val;
231 		return true;
232 	}
233 
234 	/* Create a new entry. */
235 	entry = malloc(sizeof(struct tiniparser_entry));
236 	if (entry == NULL) {
237 		return false;
238 	}
239 	entry->key = malloc(key_len);
240 	if (entry->key == NULL) {
241 		free(entry);
242 		return false;
243 	}
244 	memcpy(entry->key, key, key_len);
245 
246 	entry->value = malloc(val_len);
247 	if (entry->value == NULL) {
248 		free(entry->key);
249 		free(entry);
250 		return false;
251 	}
252 	memcpy(entry->value, value, val_len);
253 
254 	entry->next_entry = section->entry_list;
255 	section->entry_list = entry;
256 	return true;
257 }
258 
section_parser(const char * section_name,void * private_data)259 static bool section_parser(const char *section_name,
260 			void *private_data)
261 {
262 	struct tiniparser_section **pp_section;
263 	struct tiniparser_section *new_section;
264 	struct tiniparser_dictionary *d =
265 		(struct tiniparser_dictionary *)private_data;
266 	size_t section_name_len;
267 
268 	if (section_name == NULL) {
269 		return false;
270 	}
271 
272 	/* Section names can't contain ':' */
273 	if (strchr(section_name, ':') != NULL) {
274 		return false;
275 	}
276 
277 	/* Do we already have this section ? */
278 	for (pp_section = &d->section_list;
279 			*pp_section;
280 			pp_section = &(*pp_section)->next_section) {
281 		if (strcasecmp(section_name,
282 				(*pp_section)->section_name) == 0) {
283 			/*
284 			 * Move to the front of the list for
285 			 * value_parser() to find it.
286 			 */
287 
288 			/* First save current entry. */
289 			struct tiniparser_section *curr_section = *pp_section;
290 
291 			/* Now unlink current entry from list. */
292 			*pp_section = curr_section->next_section;
293 
294 			/* Make current entry next point to whole list. */
295 			curr_section->next_section = d->section_list;
296 
297 			/* And replace list with current entry at start. */
298 			d->section_list = curr_section;
299 
300 			return true;
301 		}
302 	}
303 
304 	section_name_len = strlen(section_name) + 1;
305 
306 	/* Create new section. */
307 	new_section = malloc(
308 		offsetof(struct tiniparser_section, section_name) +
309 		section_name_len);
310 	if (new_section == NULL) {
311 		return false;
312 	}
313 
314 	memcpy(new_section->section_name, section_name, section_name_len);
315 
316 	new_section->entry_list = NULL;
317 
318 	/* Add it to the head of the singly linked list. */
319 	new_section->next_section = d->section_list;
320 	d->section_list = new_section;
321 	return true;
322 }
323 
tiniparser_load_stream(FILE * fp)324 struct tiniparser_dictionary *tiniparser_load_stream(FILE *fp)
325 {
326 	bool ret;
327 	struct tiniparser_dictionary *d = NULL;
328 
329 	d = malloc(sizeof(struct tiniparser_dictionary));
330 	if (d == NULL) {
331 		return NULL;
332 	}
333 	d->section_list = NULL;
334 
335 	ret = tini_parse(fp,
336 			false,
337 			section_parser,
338 			value_parser,
339 			d);
340 	if (ret == false) {
341 		tiniparser_freedict(d);
342 		d = NULL;
343 	}
344 	return d;
345 }
346 
tiniparser_load(const char * filename)347 struct tiniparser_dictionary *tiniparser_load(const char *filename)
348 {
349 	struct tiniparser_dictionary *d;
350 	FILE *fp = fopen(filename, "r");
351 
352 	if (fp == NULL) {
353 		return NULL;
354 	}
355 
356 	d = tiniparser_load_stream(fp);
357 
358 	fclose(fp);
359 
360 	return d;
361 }
362 
tiniparser_freedict(struct tiniparser_dictionary * d)363 void tiniparser_freedict(struct tiniparser_dictionary *d)
364 {
365 	struct tiniparser_section *curr_section, *next_section;
366 
367 	if (d == NULL) {
368 		return;
369 	}
370 
371 	for (curr_section = d->section_list;
372 			curr_section;
373 			curr_section = next_section) {
374 		struct tiniparser_entry *curr_entry, *next_entry;
375 
376 		next_section = curr_section->next_section;
377 
378 		for (curr_entry = curr_section->entry_list;
379 				curr_entry;
380 				curr_entry = next_entry) {
381 			next_entry = curr_entry->next_entry;
382 
383 			free(curr_entry->key);
384 			free(curr_entry->value);
385 			free(curr_entry);
386 		}
387 		free(curr_section);
388 	}
389 	free(d);
390 }
391