1/*
2 * Copyright (c) 2008-2011 Apple Inc.  All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <si_module.h>
25#include <paths.h>
26#include <stdio.h>
27#include <unistd.h>
28#include <string.h>
29#include <time.h>
30#include <dirent.h>
31#include <errno.h>
32#include <notify.h>
33#include <pthread.h>
34#include <arpa/inet.h>
35#include <sys/param.h>
36#include <sys/mount.h>
37#include <sys/stat.h>
38#include <ils.h>
39#include <dispatch/dispatch.h>
40#include <TargetConditionals.h>
41
42/* notify SPI */
43uint32_t notify_peek(int token, uint32_t *val);
44
45extern uint32_t gL1CacheEnabled;
46
47/* These really should be in netdb.h & etc. */
48#define _PATH_RPCS "/etc/rpc"
49#define _PATH_ALIASES "/etc/aliases"
50#define _PATH_ETHERS "/etc/ethers"
51#define _PATH_NETGROUP "/etc/netgroup"
52
53static dispatch_once_t rootfs_once;
54static si_item_t *rootfs = NULL;
55
56#define CHUNK 256
57#define FNG_MEM 0x00000010
58#define FNG_GRP 0x00000020
59
60#define forever for(;;)
61
62#define VALIDATION_PASSWD 0
63#define VALIDATION_MASTER_PASSWD 1
64#define VALIDATION_GROUP 2
65#define VALIDATION_NETGROUP 3
66#define VALIDATION_ALIASES 4
67#define VALIDATION_HOSTS 5
68#define VALIDATION_NETWORKS 6
69#define VALIDATION_SERVICES 7
70#define VALIDATION_PROTOCOLS 8
71#define VALIDATION_RPC 9
72#define VALIDATION_FSTAB 10
73#define VALIDATION_ETHERS 11
74#define VALIDATION_COUNT 12
75
76#define VALIDATION_MASK_PASSWD			0x00000001
77#define VALIDATION_MASK_MASTER_PASSWD	0x00000002
78#define VALIDATION_MASK_MASK_GROUP		0x00000004
79#define VALIDATION_MASK_NETGROUP				0x00000008
80#define VALIDATION_MASK_ALIASES			0x00000010
81#define VALIDATION_MASK_HOSTS			0x00000020
82#define VALIDATION_MASK_NETWORKS		0x00000040
83#define VALIDATION_MASK_SERVICES		0x00000080
84#define VALIDATION_MASK_PROTOCOLS		0x00000100
85#define VALIDATION_MASK_RPC				0x00000200
86#define VALIDATION_MASK_FSTAB			0x00000400
87#define VALIDATION_MASK_ETHERS			0x00000800
88
89typedef struct file_netgroup_member_s
90{
91	uint32_t flags;
92	char *host;
93	char *user;
94	char *domain;
95	struct file_netgroup_member_s *next;
96} file_netgroup_member_t;
97
98typedef struct file_netgroup_s
99{
100	char *name;
101	uint32_t flags;
102	file_netgroup_member_t *members;
103	struct file_netgroup_s *next;
104} file_netgroup_t;
105
106typedef struct
107{
108	uint32_t validation_notify_mask;
109	int notify_token[VALIDATION_COUNT];
110	file_netgroup_t *file_netgroup_cache;
111	uint64_t netgroup_validation_a;
112	uint64_t netgroup_validation_b;
113} file_si_private_t;
114
115static pthread_mutex_t file_mutex = PTHREAD_MUTEX_INITIALIZER;
116
117static char *
118_fsi_copy_string(char *s)
119{
120	int len;
121	char *t;
122
123	if (s == NULL) return NULL;
124
125	len = strlen(s) + 1;
126	t = malloc(len);
127	if (t == NULL) return NULL;
128
129	bcopy(s, t, len);
130	return t;
131}
132
133static char **
134_fsi_append_string(char *s, char **l)
135{
136	int i, len;
137
138	if (s == NULL) return l;
139	if (l != NULL) {
140		for (i = 0; l[i] != NULL; i++);
141		len = i;
142	} else {
143		len = 0;
144	}
145
146	l = (char **) reallocf(l, (len + 2) * sizeof(char *));
147	if (l == NULL) return NULL;
148
149	l[len] = s;
150	l[len + 1] = NULL;
151	return l;
152}
153
154char **
155_fsi_tokenize(char *data, const char *sep, int trailing_empty, int *ntokens)
156{
157	char **tokens;
158	int p, i, start, end, more, len, end_on_sep;
159	int scanning;
160
161	tokens = NULL;
162	end_on_sep = 0;
163
164	if (data == NULL) return NULL;
165
166	if (ntokens != NULL) *ntokens = 0;
167	if (sep == NULL)
168	{
169		tokens = _fsi_append_string(data, tokens);
170		if (ntokens != NULL) *ntokens = *ntokens + 1;
171		return tokens;
172	}
173
174	len = strlen(sep);
175	p = 0;
176
177	while (data[p] != '\0')
178	{
179		end_on_sep = 1;
180		/* skip leading white space */
181		while ((data[p] == ' ') || (data[p] == '\t') || (data[p] == '\n')) p++;
182
183		/* check for end of line */
184		if (data[p] == '\0') break;
185
186		/* scan for separator */
187		start = p;
188		end = p;
189		scanning = 1;
190		end_on_sep = 0;
191
192		while (scanning == 1)
193		{
194			if (data[p] == '\0') break;
195
196			for (i = 0; i < len; i++)
197			{
198				if (data[p] == sep[i])
199				{
200					scanning = 0;
201					end_on_sep = 1;
202					break;
203				}
204			}
205
206			/* end is last non-whitespace character */
207			if ((scanning == 1) && (data[p] != ' ') && (data[p] != '\t') && (data[p] != '\n')) end = p;
208
209			p += scanning;
210		}
211
212		/* see if there's data left after p */
213		more = 0;
214		if (data[p] != '\0') more = 1;
215
216		/* set the character following the token to nul */
217		if (start == p) data[p] = '\0';
218		else data[end + 1] = '\0';
219
220		tokens = _fsi_append_string(data + start, tokens);
221		if (ntokens != NULL) *ntokens = *ntokens + 1;
222		p += more;
223	}
224
225	if ((end_on_sep == 1) && (trailing_empty != 0))
226	{
227		/* if the scan ended on an empty token, add a null string */
228		tokens = _fsi_append_string(data + p, tokens);
229		if (ntokens != NULL) *ntokens = *ntokens + 1;
230	}
231
232	return tokens;
233}
234
235char *
236_fsi_get_line(FILE *fp)
237{
238	char s[4096];
239	char *out;
240
241	s[0] = '\0';
242
243	fgets(s, sizeof(s), fp);
244	if ((s == NULL) || (s[0] == '\0')) return NULL;
245
246	if (s[0] != '#') s[strlen(s) - 1] = '\0';
247
248	out = _fsi_copy_string(s);
249	return out;
250}
251
252static const char *
253_fsi_validation_path(int vtype)
254{
255	if (vtype == VALIDATION_PASSWD) return _PATH_PASSWD;
256	else if (vtype == VALIDATION_MASTER_PASSWD) return _PATH_MASTERPASSWD;
257	else if (vtype == VALIDATION_GROUP) return _PATH_GROUP;
258	else if (vtype == VALIDATION_NETGROUP) return _PATH_NETGROUP;
259	else if (vtype == VALIDATION_ALIASES) return _PATH_ALIASES;
260	else if (vtype == VALIDATION_HOSTS) return _PATH_HOSTS;
261	else if (vtype == VALIDATION_NETWORKS) return _PATH_NETWORKS;
262	else if (vtype == VALIDATION_SERVICES) return _PATH_SERVICES;
263	else if (vtype == VALIDATION_PROTOCOLS) return _PATH_PROTOCOLS;
264	else if (vtype == VALIDATION_RPC) return _PATH_RPCS;
265	else if (vtype == VALIDATION_FSTAB) return _PATH_FSTAB;
266	else if (vtype == VALIDATION_ETHERS) return _PATH_ETHERS;
267
268	return NULL;
269}
270
271static void
272_fsi_get_validation(si_mod_t *si, int vtype, const char *path, FILE *f, uint64_t *a, uint64_t *b)
273{
274	struct stat sb;
275	file_si_private_t *pp;
276	uint32_t peek, bit;
277	int status;
278
279	if (a != NULL) *a = 0;
280	if (b != NULL) *b = 0;
281
282	if (si == NULL) return;
283	if (path == NULL) return;
284	if (gL1CacheEnabled == 0) return;
285
286	pp = (file_si_private_t *)si->private;
287	if (pp == NULL) return;
288
289	if (vtype >= VALIDATION_COUNT) return;
290
291	bit = 1 << vtype;
292	if (bit & pp->validation_notify_mask)
293	{
294		/* use notify validation for this type */
295		if (pp->notify_token[vtype] < 0)
296		{
297			char *str = NULL;
298			asprintf(&str, "com.apple.system.info:%s", path);
299			if (str == NULL) return;
300
301			status = notify_register_check(str, &(pp->notify_token[vtype]));
302			free(str);
303		}
304
305		if (a != NULL)
306		{
307			status = notify_peek(pp->notify_token[vtype], &peek);
308			if (status == NOTIFY_STATUS_OK) *a = ntohl(peek);
309		}
310
311		if (b != NULL) *b = vtype;
312	}
313	else
314	{
315		/* use stat() and last mod time for this type */
316		memset(&sb, 0, sizeof(struct stat));
317		if (f != NULL)
318		{
319			if (fstat(fileno(f), &sb) == 0)
320			{
321				if (a != NULL) *a = sb.st_mtimespec.tv_sec;
322				if (b != NULL) *b = sb.st_mtimespec.tv_nsec;
323			}
324		}
325		else
326		{
327			path = _fsi_validation_path(vtype);
328			if (path != NULL)
329			{
330				memset(&sb, 0, sizeof(struct stat));
331				if (stat(path, &sb) == 0)
332				{
333					if (a != NULL) *a = sb.st_mtimespec.tv_sec;
334					if (b != NULL) *b = sb.st_mtimespec.tv_nsec;
335				}
336			}
337		}
338	}
339}
340
341static int
342_fsi_validate(si_mod_t *si, int cat, uint64_t va, uint64_t vb)
343{
344	struct stat sb;
345	const char *path;
346	uint32_t item_val, curr_val, vtype;
347	file_si_private_t *pp;
348	int status;
349
350	if (si == NULL) return 0;
351
352#if TARGET_OS_EMBEDDED
353	/* /etc is on a read-only filesystem, so no validation is required */
354	return 1;
355#endif
356
357	pp = (file_si_private_t *)si->private;
358	if (pp == NULL) return 0;
359
360	vtype = UINT32_MAX;
361	switch (cat)
362	{
363		case CATEGORY_USER:
364		{
365			if (geteuid() == 0) vtype = VALIDATION_MASTER_PASSWD;
366			else vtype = VALIDATION_PASSWD;
367			break;
368		}
369		case CATEGORY_GROUP:
370		{
371			vtype = VALIDATION_GROUP;
372			break;
373		}
374		case CATEGORY_GROUPLIST:
375		{
376			vtype = VALIDATION_GROUP;
377			break;
378		}
379		case CATEGORY_NETGROUP:
380		{
381			vtype = VALIDATION_NETGROUP;
382			break;
383		}
384		case CATEGORY_ALIAS:
385		{
386			vtype = VALIDATION_ALIASES;
387			break;
388		}
389		case CATEGORY_HOST_IPV4:
390		{
391			vtype = VALIDATION_HOSTS;
392			break;
393		}
394		case CATEGORY_HOST_IPV6:
395		{
396			vtype = VALIDATION_HOSTS;
397			break;
398		}
399		case CATEGORY_NETWORK:
400		{
401			vtype = VALIDATION_NETWORKS;
402			break;
403		}
404		case CATEGORY_SERVICE:
405		{
406			vtype = VALIDATION_SERVICES;
407			break;
408		}
409		case CATEGORY_PROTOCOL:
410		{
411			vtype = VALIDATION_PROTOCOLS;
412			break;
413		}
414		case CATEGORY_RPC:
415		{
416			vtype = VALIDATION_RPC;
417			break;
418		}
419		case CATEGORY_FS:
420		{
421			vtype = VALIDATION_FSTAB;
422			break;
423		}
424		case CATEGORY_MAC:
425		{
426			vtype = VALIDATION_ETHERS;
427			break;
428		}
429		default: return 0;
430	}
431
432	if (pp->notify_token[vtype] < 0)
433	{
434		path = _fsi_validation_path(vtype);
435		if (path == NULL) return 0;
436
437		memset(&sb, 0, sizeof(struct stat));
438		if (stat(path, &sb) != 0) return 0;
439		if (va != sb.st_mtimespec.tv_sec) return 0;
440		if (vb != sb.st_mtimespec.tv_nsec) return 0;
441	}
442	else
443	{
444		item_val = va;
445		curr_val = -1;
446		status = notify_peek(pp->notify_token[vtype], &curr_val);
447		if (status != NOTIFY_STATUS_OK) return 0;
448
449		curr_val = ntohl(curr_val);
450		if (item_val != curr_val) return 0;
451	}
452
453	return 1;
454}
455
456/* netgroup support */
457static char *
458_fsi_append_char_to_line(char c, char *buf, size_t *x)
459{
460	if (x == NULL) return NULL;
461
462	if (buf == NULL) *x = 0;
463
464	if ((*x % CHUNK) == 0)
465	{
466		buf = reallocf(buf, *x + CHUNK);
467		memset(buf + *x, 0, CHUNK);
468	}
469
470	buf[*x] = c;
471	*x = *x + 1;
472
473	return buf;
474}
475
476static char *
477_fsi_read_netgroup_line(FILE *f)
478{
479	char *out = NULL;
480	size_t x = 0;
481	int white = 0;
482	int paren = 0;
483
484	if (f == NULL) return NULL;
485	forever
486	{
487		int c = getc(f);
488
489		if (c == EOF)
490		{
491			if (out == NULL) return NULL;
492			return _fsi_append_char_to_line('\0', out, &x);
493		}
494
495		if (c == '\n') return _fsi_append_char_to_line('\0', out, &x);
496		if (c == '(') paren = 1;
497		else if (c == ')') paren = 0;
498
499		if ((c == ' ') || (c == '\t'))
500		{
501			if ((white == 0) && (paren == 0)) out = _fsi_append_char_to_line(' ', out, &x);
502			white = 1;
503		}
504		else if (c == '\\')
505		{
506			forever
507			{
508				c = getc(f);
509				if (c == EOF) return _fsi_append_char_to_line('\0', out, &x);
510				if (c == '\n') break;
511			}
512		}
513		else
514		{
515			out = _fsi_append_char_to_line(c, out, &x);
516			white = 0;
517		}
518	}
519}
520
521static file_netgroup_t *
522_fsi_find_netgroup(file_netgroup_t **list, const char *name, int create)
523{
524	file_netgroup_t *n;
525
526	if (list == NULL) return NULL;
527
528	for (n = *list; n != NULL; n = n->next)
529	{
530		if (!strcmp(name, n->name)) return n;
531	}
532
533	if (create == 0) return NULL;
534
535	n = (file_netgroup_t *)calloc(1, sizeof(file_netgroup_t));
536	if (n == NULL) return NULL;
537
538	n->name = strdup(name);
539
540	n->next = *list;
541	*list = n;
542	return n;
543}
544
545void
546_fsi_free_file_netgroup(file_netgroup_t *n)
547{
548	file_netgroup_member_t *m;
549
550	if (n == NULL) return;
551	free(n->name);
552
553	m = n->members;
554	while (m != NULL)
555	{
556		file_netgroup_member_t *x = m;
557		m = m->next;
558		free(x->host);
559		free(x->user);
560		free(x->domain);
561		free(x);
562	}
563
564	free(n);
565}
566
567static void
568_fsi_add_netgroup_group(file_netgroup_t *n, char *grp)
569{
570	file_netgroup_member_t *g;
571
572	if (n == NULL) return;
573
574	g = (file_netgroup_member_t *)calloc(1, sizeof(file_netgroup_member_t));
575	if (g == NULL) return;
576
577	g->flags = FNG_GRP;
578	g->host = strdup(grp);
579
580	g->next = n->members;
581	n->members = g;
582
583	return;
584}
585
586static void
587_fsi_add_netgroup_member(file_netgroup_t *n, char *mem)
588{
589	char **tokens;
590	file_netgroup_member_t *m;
591	int ntokens;
592
593	if (n == NULL) return;
594
595	m = (file_netgroup_member_t *)calloc(1, sizeof(file_netgroup_member_t));
596	if (m == NULL) return;
597
598	tokens = _fsi_tokenize(mem + 1, ",)", 0, &ntokens);
599
600	if (tokens == NULL)
601	{
602		free(m);
603		return;
604	}
605
606	if ((ntokens > 0) && (tokens[0][0] != '\0')) m->host = strdup(tokens[0]);
607	if ((ntokens > 1) && (tokens[1][0] != '\0')) m->user = strdup(tokens[1]);
608	if ((ntokens > 2) && (tokens[2][0] != '\0')) m->domain = strdup(tokens[2]);
609
610	free(tokens);
611
612	m->flags = FNG_MEM;
613	m->next = n->members;
614	n->members = m;
615}
616
617static file_netgroup_t *
618_fsi_process_netgroup_line(file_netgroup_t **pass1, char *line)
619{
620	file_netgroup_t *n;
621	char **tokens;
622	int i, ntokens = 0;
623
624	tokens = _fsi_tokenize(line, " ", 0, &ntokens);
625	if (tokens == NULL) return NULL;
626	if (tokens[0] == NULL)
627	{
628		free(tokens);
629		return NULL;
630	}
631
632	n = _fsi_find_netgroup(pass1, tokens[0], 1);
633
634	for (i = 1; tokens[i] != NULL; i++)
635	{
636		if (tokens[i][0] == '(') _fsi_add_netgroup_member(n, tokens[i]);
637		else if (tokens[i][0] != '\0') _fsi_add_netgroup_group(n, tokens[i]);
638	}
639
640	free(tokens);
641	return n;
642}
643
644static void
645_fsi_flatten_netgroup(file_netgroup_t *pass1, file_netgroup_t **top, file_netgroup_t *n, file_netgroup_member_t *m)
646{
647	if (n == NULL) return;
648
649	if (n->flags == 1) return;
650	n->flags = 1;
651
652	if (*top == NULL)
653	{
654		*top = (file_netgroup_t *)calloc(1, sizeof(file_netgroup_t));
655		if (*top == NULL) return;
656		(*top)->name = strdup(n->name);
657		if ((*top)->name == NULL)
658		{
659			free(*top);
660			*top = NULL;
661			return;
662		}
663	}
664
665	while (m!= NULL)
666	{
667		if (m->flags & FNG_MEM)
668		{
669			file_netgroup_member_t *x = (file_netgroup_member_t *)calloc(1, sizeof(file_netgroup_member_t));
670			if (x == NULL) return;
671
672			x->flags = FNG_MEM;
673			if (m->host != NULL) x->host = strdup(m->host);
674			if (m->user != NULL) x->user = strdup(m->user);
675			if (m->domain != NULL) x->domain = strdup(m->domain);
676
677			x->next = (*top)->members;
678			(*top)->members = x;
679		}
680		else
681		{
682			file_netgroup_t *g = _fsi_find_netgroup(&pass1, m->host, 0);
683			if (g == NULL) continue;
684
685			_fsi_flatten_netgroup(pass1, top, g, g->members);
686		}
687
688		m = m->next;
689	}
690}
691
692static void
693_fsi_check_netgroup_cache(si_mod_t *si)
694{
695	file_netgroup_t *p1, *n, *x, *a;
696	char *line;
697	FILE *f;
698	file_si_private_t *pp;
699
700	if (si == NULL) return;
701
702	pp = (file_si_private_t *)si->private;
703	if (pp == NULL) return;
704
705	pthread_mutex_lock(&file_mutex);
706
707	if (_fsi_validate(si, CATEGORY_NETGROUP, pp->netgroup_validation_a, pp->netgroup_validation_b))
708	{
709		pthread_mutex_unlock(&file_mutex);
710		return;
711	}
712
713	n = pp->file_netgroup_cache;
714	while (n != NULL)
715	{
716		x = n;
717		n = n->next;
718		_fsi_free_file_netgroup(x);
719	}
720
721	pp->file_netgroup_cache = NULL;
722
723	f = fopen(_PATH_NETGROUP, "r");
724	if (f == NULL)
725	{
726		pthread_mutex_unlock(&file_mutex);
727		return;
728	}
729
730	_fsi_get_validation(si, VALIDATION_NETGROUP, _PATH_NETGROUP, f, &(pp->netgroup_validation_a), &(pp->netgroup_validation_b));
731
732	p1 = NULL;
733
734	line = _fsi_read_netgroup_line(f);
735	while (line != NULL)
736	{
737		n = _fsi_process_netgroup_line(&p1, line);
738
739		free(line);
740		line = _fsi_read_netgroup_line(f);
741	}
742
743	fclose(f);
744
745	for (n = p1; n != NULL; n = n->next)
746	{
747		a = NULL;
748		_fsi_flatten_netgroup(p1, &a, n, n->members);
749		for (x = p1; x != NULL; x = x->next) x->flags = 0;
750
751		if (a != NULL)
752		{
753			a->next = pp->file_netgroup_cache;
754			pp->file_netgroup_cache = a;
755		}
756	}
757
758	n = p1;
759	while (n != NULL)
760	{
761		x = n;
762		n = n->next;
763		_fsi_free_file_netgroup(x);
764	}
765
766	pthread_mutex_unlock(&file_mutex);
767}
768
769/* USERS */
770
771static si_item_t *
772_fsi_parse_user(si_mod_t *si, const char *name, uid_t uid, int which, char *data, int format, uint64_t va, uint64_t vb)
773{
774	char **tokens;
775	int ntokens, match;
776	time_t change, expire;
777	si_item_t *item;
778	uid_t xuid;
779
780	if (data == NULL) return NULL;
781
782	ntokens = 0;
783	tokens = _fsi_tokenize(data, ":", 1, &ntokens);
784	if (((format == 0) && (ntokens != 10)) || ((format == 1) && (ntokens !=  7)))
785	{
786		free(tokens);
787		return NULL;
788	}
789
790	xuid = atoi(tokens[2]);
791	match = 0;
792
793	/* XXX MATCH GECOS? XXX*/
794	if (which == SEL_ALL) match = 1;
795	else if ((which == SEL_NAME) && (string_equal(name, tokens[0]))) match = 1;
796	else if ((which == SEL_NUMBER) && (uid == xuid)) match = 1;
797
798	if (match == 0)
799	{
800		free(tokens);
801		return NULL;
802	}
803
804	if (format == 0)
805	{
806		/* master.passwd: name[0] passwd[1] uid[2] gid[3] class[4] change[5] expire[6] gecos[7] dir[8] shell[9] */
807		/* struct pwd: name[0] passwd[1] uid[2] gid[3] change[5] class[4] gecos[7] dir[8] shell[9] expire[6] */
808		change = atoi(tokens[5]);
809		expire = atoi(tokens[6]);
810		item = (si_item_t *)LI_ils_create("L4488ss44LssssL", (unsigned long)si, CATEGORY_USER, 1, va, vb, tokens[0], tokens[1], xuid, atoi(tokens[3]), change, tokens[4], tokens[7], tokens[8], tokens[9], expire);
811	}
812	else
813	{
814		/* passwd: name[0] passwd[1] uid[2] gid[3] gecos[4] dir[5] shell[6] */
815		/* struct pwd: name[0] passwd[1] uid[2] gid[3] change[-] class[-] gecos[4] dir[5] shell[6] expire[-] */
816		item = (si_item_t *)LI_ils_create("L4488ss44LssssL", (unsigned long)si, CATEGORY_USER, 1, va, vb, tokens[0], tokens[1], xuid, atoi(tokens[3]), 0, "", tokens[4], tokens[5], tokens[6], 0);
817	}
818
819	free(tokens);
820	return item;
821}
822
823static void *
824_fsi_get_user(si_mod_t *si, const char *name, uid_t uid, int which)
825{
826	char *line;
827	si_item_t *item;
828	int fmt;
829	FILE *f;
830	si_list_t *all;
831	uint64_t va, vb;
832
833	if ((which == SEL_NAME) && (name == NULL)) return NULL;
834
835	all = NULL;
836	f = NULL;
837	fmt = 0;
838	va = 0;
839	vb = 0;
840
841	if (geteuid() == 0)
842	{
843		f = fopen(_PATH_MASTERPASSWD, "r");
844		_fsi_get_validation(si, VALIDATION_MASTER_PASSWD, _PATH_MASTERPASSWD, f, &va, &vb);
845	}
846	else
847	{
848		f = fopen(_PATH_PASSWD, "r");
849		_fsi_get_validation(si, VALIDATION_PASSWD, _PATH_PASSWD, f, &va, &vb);
850		fmt = 1;
851	}
852
853	if (f == NULL) return NULL;
854
855
856	forever
857	{
858		line = _fsi_get_line(f);
859		if (line == NULL) break;
860
861		if (line[0] == '#')
862		{
863			free(line);
864			line = NULL;
865			continue;
866		}
867
868		item = _fsi_parse_user(si, name, uid, which, line, fmt, va, vb);
869		free(line);
870		line = NULL;
871
872		if (item == NULL) continue;
873
874		if (which == SEL_ALL)
875		{
876			all = si_list_add(all, item);
877			si_item_release(item);
878			continue;
879		}
880
881		fclose(f);
882		return item;
883	}
884	fclose(f);
885	return all;
886}
887
888/* GROUPS */
889
890static si_item_t *
891_fsi_parse_group(si_mod_t *si, const char *name, gid_t gid, int which, char *data, uint64_t va, uint64_t vb)
892{
893	char **tokens, **members;
894	int ntokens, match;
895	si_item_t *item;
896	gid_t xgid;
897
898	if (data == NULL) return NULL;
899
900	ntokens = 0;
901	tokens = _fsi_tokenize(data, ":", 1, &ntokens);
902	if (ntokens != 4)
903	{
904		free(tokens);
905		return NULL;
906	}
907
908	xgid = atoi(tokens[2]);
909	match = 0;
910
911	if (which == SEL_ALL) match = 1;
912	else if ((which == SEL_NAME) && (string_equal(name, tokens[0]))) match = 1;
913	else if ((which == SEL_NUMBER) && (gid == xgid)) match = 1;
914
915	if (match == 0)
916	{
917		free(tokens);
918		return NULL;
919	}
920
921	ntokens = 0;
922	members = _fsi_tokenize(tokens[3], ",", 1, &ntokens);
923
924	item = (si_item_t *)LI_ils_create("L4488ss4*", (unsigned long)si, CATEGORY_GROUP, 1, va, vb, tokens[0], tokens[1], xgid, members);
925
926	free(tokens);
927	free(members);
928
929	return item;
930}
931
932static void *
933_fsi_get_group(si_mod_t *si, const char *name, gid_t gid, int which)
934{
935	char *line;
936	si_item_t *item;
937	FILE *f;
938	si_list_t *all;
939	uint64_t va, vb;
940
941	if ((which == SEL_NAME) && (name == NULL)) return NULL;
942
943	all = NULL;
944	f = NULL;
945
946	f = fopen(_PATH_GROUP, "r");
947	if (f == NULL) return NULL;
948
949	_fsi_get_validation(si, VALIDATION_GROUP, _PATH_GROUP, f, &va, &vb);
950
951	forever
952	{
953		line = _fsi_get_line(f);
954		if (line == NULL) break;
955
956		if (line[0] == '#')
957		{
958			free(line);
959			line = NULL;
960			continue;
961		}
962
963		item = _fsi_parse_group(si, name, gid, which, line, va, vb);
964		free(line);
965		line = NULL;
966
967		if (item == NULL) continue;
968
969		if (which == SEL_ALL)
970		{
971			all = si_list_add(all, item);
972			si_item_release(item);
973			continue;
974		}
975
976		fclose(f);
977		return item;
978	}
979
980	fclose(f);
981	return all;
982}
983
984static void *
985_fsi_get_grouplist(si_mod_t *si, const char *user)
986{
987	char **tokens, **members;
988	int ntokens, i, match, gidcount;
989	char *line;
990	si_item_t *item;
991	FILE *f;
992	uint64_t va, vb;
993	gid_t gid, basegid;
994	gid_t *gidlist;
995	struct passwd *pw;
996
997	if (user == NULL) return NULL;
998
999	gidlist = NULL;
1000	gidcount = 0;
1001	f = NULL;
1002	basegid = -1;
1003
1004	item = si->vtable->sim_user_byname(si, user);
1005	if (item != NULL)
1006	{
1007		pw = (struct passwd *)((uintptr_t)item + sizeof(si_item_t));
1008		basegid = pw->pw_gid;
1009		si_item_release(item);
1010		item = NULL;
1011	}
1012
1013	f = fopen(_PATH_GROUP, "r");
1014	if (f == NULL) return NULL;
1015
1016	_fsi_get_validation(si, VALIDATION_GROUP, _PATH_GROUP, f, &va, &vb);
1017
1018	forever
1019	{
1020		line = _fsi_get_line(f);
1021		if (line == NULL) break;
1022
1023		if (line[0] == '#')
1024		{
1025			free(line);
1026			line = NULL;
1027			continue;
1028		}
1029
1030		ntokens = 0;
1031		tokens = _fsi_tokenize(line, ":", 1, &ntokens);
1032		if (ntokens != 4)
1033		{
1034			free(tokens);
1035			continue;
1036		}
1037
1038		ntokens = 0;
1039		members = _fsi_tokenize(tokens[3], ",", 1, &ntokens);
1040
1041		match = 0;
1042		gid = -2;
1043
1044		for (i = 0; i < ntokens; i++)
1045		{
1046			if (string_equal(user, members[i]))
1047			{
1048				gid = atoi(tokens[2]);
1049				match = 1;
1050				break;
1051			}
1052		}
1053
1054		free(tokens);
1055		free(members);
1056		free(line);
1057		line = NULL;
1058
1059		if (match == 1)
1060		{
1061			gidlist = (gid_t *) reallocf(gidlist, (gidcount + 1) * sizeof(gid_t));
1062			if (gidlist == NULL)
1063			{
1064				gidcount = 0;
1065				break;
1066			}
1067
1068			gidlist[gidcount++] = gid;
1069		}
1070	}
1071
1072	fclose(f);
1073
1074	if (gidcount != 0) {
1075		item = (si_item_t *)LI_ils_create("L4488s4@", (unsigned long)si, CATEGORY_GROUPLIST, 1, va, vb, user, gidcount,
1076										  gidcount * sizeof(gid_t), gidlist);
1077	}
1078
1079	free(gidlist);
1080
1081	return item;
1082}
1083
1084/* ALIASES */
1085
1086static si_item_t *
1087_fsi_parse_alias(si_mod_t *si, const char *name, int which, char *data, uint64_t va, uint64_t vb)
1088{
1089	char **tokens, **members;
1090	int ntokens, match;
1091	si_item_t *item;
1092
1093	if (data == NULL) return NULL;
1094
1095	ntokens = 0;
1096	tokens = _fsi_tokenize(data, ":", 1, &ntokens);
1097	if (ntokens < 2)
1098	{
1099		free(tokens);
1100		return NULL;
1101	}
1102
1103	match = 0;
1104
1105	if (which == SEL_ALL) match = 1;
1106	else if (string_equal(name, tokens[0])) match = 1;
1107
1108	if (match == 0)
1109	{
1110		free(tokens);
1111		return NULL;
1112	}
1113
1114	ntokens = 0;
1115	members = _fsi_tokenize(tokens[1], ",", 1, &ntokens);
1116
1117	item = (si_item_t *)LI_ils_create("L4488s4*4", (unsigned long)si, CATEGORY_ALIAS, 1, va, vb, tokens[0], ntokens, members, 1);
1118
1119	free(tokens);
1120	free(members);
1121
1122	return item;
1123}
1124
1125static void *
1126_fsi_get_alias(si_mod_t *si, const char *name, int which)
1127{
1128	char *line;
1129	si_item_t *item;
1130	FILE *f;
1131	si_list_t *all;
1132	uint64_t va, vb;
1133
1134	if ((which == SEL_NAME) && (name == NULL)) return NULL;
1135
1136	all = NULL;
1137	f = NULL;
1138
1139	f = fopen(_PATH_ALIASES, "r");
1140	if (f == NULL) return NULL;
1141
1142	_fsi_get_validation(si, VALIDATION_ALIASES, _PATH_ALIASES, f, &va, &vb);
1143
1144	forever
1145	{
1146		line = _fsi_get_line(f);
1147		if (line == NULL) break;
1148
1149		if (line[0] == '#')
1150		{
1151			free(line);
1152			line = NULL;
1153			continue;
1154		}
1155
1156		item = _fsi_parse_alias(si, name, which, line, va, vb);
1157		free(line);
1158		line = NULL;
1159
1160		if (item == NULL) continue;
1161
1162		if (which == SEL_ALL)
1163		{
1164			all = si_list_add(all, item);
1165			si_item_release(item);
1166			continue;
1167		}
1168
1169		fclose(f);
1170		return item;
1171	}
1172
1173	fclose(f);
1174	return all;
1175}
1176
1177/* ETHERS */
1178
1179static si_item_t *
1180_fsi_parse_ether(si_mod_t *si, const char *name, int which, char *data, uint64_t va, uint64_t vb)
1181{
1182	char **tokens;
1183	char *cmac;
1184	int ntokens, match;
1185	si_item_t *item;
1186
1187	if (data == NULL) return NULL;
1188
1189	ntokens = 0;
1190	tokens = _fsi_tokenize(data, " \t", 1, &ntokens);
1191	if (ntokens != 2)
1192	{
1193		free(tokens);
1194		return NULL;
1195	}
1196
1197	cmac = si_standardize_mac_address(tokens[0]);
1198	if (cmac == NULL)
1199	{
1200		free(tokens);
1201		return NULL;
1202	}
1203
1204	match = 0;
1205	if (which == SEL_ALL) match = 1;
1206	else if ((which == SEL_NAME) && (string_equal(name, tokens[1]))) match = 1;
1207	else if ((which == SEL_NUMBER) && (string_equal(name, cmac))) match = 1;
1208
1209	if (match == 0)
1210	{
1211		free(tokens);
1212		free(cmac);
1213		return NULL;
1214	}
1215
1216	item = (si_item_t *)LI_ils_create("L4488ss", (unsigned long)si, CATEGORY_MAC, 1, va, vb, tokens[1], cmac);
1217
1218	free(tokens);
1219	free(cmac);
1220
1221	return item;
1222}
1223
1224static void *
1225_fsi_get_ether(si_mod_t *si, const char *name, int which)
1226{
1227	char *line, *cmac;
1228	si_item_t *item;
1229	FILE *f;
1230	si_list_t *all;
1231	uint64_t va, vb;
1232
1233	if ((which != SEL_ALL) && (name == NULL)) return NULL;
1234
1235	cmac = NULL;
1236	if (which == SEL_NUMBER)
1237	{
1238		cmac = si_standardize_mac_address(name);
1239		if (cmac == NULL) return NULL;
1240	}
1241
1242	all = NULL;
1243	f = NULL;
1244
1245	f = fopen(_PATH_ETHERS, "r");
1246	if (f == NULL) return NULL;
1247
1248	_fsi_get_validation(si, VALIDATION_ETHERS, _PATH_ETHERS, f, &va, &vb);
1249
1250	forever
1251	{
1252		line = _fsi_get_line(f);
1253		if (line == NULL) break;
1254
1255		if (line[0] == '#')
1256		{
1257			free(line);
1258			line = NULL;
1259			continue;
1260		}
1261
1262		item = NULL;
1263		if (which == SEL_NUMBER) item = _fsi_parse_ether(si, cmac, which, line, va, vb);
1264		else item = _fsi_parse_ether(si, name, which, line, va, vb);
1265
1266		free(line);
1267		line = NULL;
1268
1269		if (item == NULL) continue;
1270
1271		if (which == SEL_ALL)
1272		{
1273			all = si_list_add(all, item);
1274			si_item_release(item);
1275			continue;
1276		}
1277
1278		fclose(f);
1279		return item;
1280	}
1281
1282	fclose(f);
1283	return all;
1284}
1285
1286/* HOSTS */
1287
1288static si_item_t *
1289_fsi_parse_host(si_mod_t *si, const char *name, const void *addr, int af, int which, char *data, uint64_t va, uint64_t vb)
1290{
1291	char **tokens, **h_aliases, *null_alias;
1292	int i, ntokens, match, h_addrtype, h_length;
1293	struct in_addr a4;
1294	struct in6_addr a6;
1295	si_item_t *item;
1296	char *h_addr_list[2];
1297	char h_addr_4[4], h_addr_6[16];
1298
1299	if (data == NULL) return NULL;
1300
1301	null_alias = NULL;
1302
1303	ntokens = 0;
1304	tokens = _fsi_tokenize(data, " 	", 0, &ntokens);
1305	if (ntokens < 2)
1306	{
1307		free(tokens);
1308		return NULL;
1309	}
1310
1311	h_addr_list[1] = NULL;
1312
1313	h_addrtype = AF_UNSPEC;
1314	if (inet_pton(AF_INET, tokens[0], &a4) == 1)
1315	{
1316		h_addrtype = AF_INET;
1317		h_length = sizeof(struct in_addr);
1318		memcpy(h_addr_4, &a4, 4);
1319		h_addr_list[0] = h_addr_4;
1320	}
1321	else if (inet_pton(AF_INET6, tokens[0], &a6) == 1)
1322	{
1323		h_addrtype = AF_INET6;
1324		h_length = sizeof(struct in6_addr);
1325		memcpy(h_addr_6, &a6, 16);
1326		h_addr_list[0] = h_addr_6;
1327	}
1328
1329	if (h_addrtype == AF_UNSPEC)
1330	{
1331		free(tokens);
1332		return NULL;
1333	}
1334
1335	h_aliases = NULL;
1336	if (ntokens > 2) h_aliases = &(tokens[2]);
1337
1338	match = 0;
1339
1340	if (which == SEL_ALL) match = 1;
1341	else
1342	{
1343		if (h_addrtype == af)
1344		{
1345			if (which == SEL_NAME)
1346			{
1347				if (string_equal(name, tokens[1])) match = 1;
1348				else if (h_aliases != NULL)
1349				{
1350					for (i = 0; (h_aliases[i] != NULL) && (match == 0); i++)
1351						if (string_equal(name, h_aliases[i])) match = 1;
1352				}
1353			}
1354			else if (which == SEL_NUMBER)
1355			{
1356				if (memcmp(addr, h_addr_list[0], h_length) == 0) match = 1;
1357			}
1358		}
1359	}
1360
1361	if (match == 0)
1362	{
1363		free(tokens);
1364		return NULL;
1365	}
1366
1367	item = NULL;
1368
1369	if (h_aliases == NULL) h_aliases = &null_alias;
1370
1371	if (h_addrtype == AF_INET)
1372	{
1373		item = (si_item_t *)LI_ils_create("L4488s*44a", (unsigned long)si, CATEGORY_HOST_IPV4, 1, va, vb, tokens[1], h_aliases, h_addrtype, h_length, h_addr_list);
1374	}
1375	else
1376	{
1377		item = (si_item_t *)LI_ils_create("L4488s*44c", (unsigned long)si, CATEGORY_HOST_IPV6, 1, va, vb, tokens[1], h_aliases, h_addrtype, h_length, h_addr_list);
1378	}
1379
1380	free(tokens);
1381
1382	return item;
1383}
1384
1385static void *
1386_fsi_get_host(si_mod_t *si, const char *name, const void *addr, int af, int which, uint32_t *err)
1387{
1388	char *line;
1389	si_item_t *item;
1390	FILE *f;
1391	si_list_t *all;
1392	uint64_t va, vb;
1393
1394	if ((which == SEL_NAME) && (name == NULL))
1395	{
1396		if (err != NULL) *err = NO_RECOVERY;
1397		return NULL;
1398	}
1399
1400	if ((which == SEL_NUMBER) && (addr == NULL))
1401	{
1402		if (err != NULL) *err = NO_RECOVERY;
1403		return NULL;
1404	}
1405
1406	f = fopen(_PATH_HOSTS, "r");
1407	if (f == NULL)
1408	{
1409		if (err != NULL) *err = NO_RECOVERY;
1410		return NULL;
1411	}
1412
1413	_fsi_get_validation(si, VALIDATION_HOSTS, _PATH_HOSTS, f, &va, &vb);
1414
1415	all = NULL;
1416
1417	forever
1418	{
1419		line = _fsi_get_line(f);
1420		if (line == NULL) break;
1421
1422		if (line[0] == '#')
1423		{
1424			free(line);
1425			line = NULL;
1426			continue;
1427		}
1428
1429		item = _fsi_parse_host(si, name, addr, af, which, line, va, vb);
1430		free(line);
1431		line = NULL;
1432
1433		if (item == NULL) continue;
1434
1435		if (which == SEL_ALL)
1436		{
1437			all = si_list_add(all, item);
1438			si_item_release(item);
1439			continue;
1440		}
1441
1442		fclose(f);
1443		return item;
1444	}
1445
1446	fclose(f);
1447	return all;
1448}
1449
1450/* SERVICE */
1451
1452static si_item_t *
1453_fsi_parse_service(si_mod_t *si, const char *name, const char *proto, int port, int which, char *data, uint64_t va, uint64_t vb)
1454{
1455	char **tokens, **s_aliases, *xproto;
1456	int i, ntokens, match;
1457	si_item_t *item;
1458	int xport;
1459
1460	if (data == NULL) return NULL;
1461
1462	port = ntohs(port);
1463
1464	ntokens = 0;
1465	tokens = _fsi_tokenize(data, " 	", 0, &ntokens);
1466	if (ntokens < 2)
1467	{
1468		free(tokens);
1469		return NULL;
1470	}
1471
1472	s_aliases = NULL;
1473	if (ntokens > 2) s_aliases = &(tokens[2]);
1474
1475	xport = atoi(tokens[1]);
1476
1477	xproto = strchr(tokens[1], '/');
1478
1479	if (xproto == NULL)
1480	{
1481		free(tokens);
1482		return NULL;
1483	}
1484
1485	*xproto++ = '\0';
1486	if ((proto != NULL) && (string_not_equal(proto, xproto)))
1487	{
1488		free(tokens);
1489		return NULL;
1490	}
1491
1492	match = 0;
1493	if (which == SEL_ALL) match = 1;
1494	else if (which == SEL_NAME)
1495	{
1496		if (string_equal(name, tokens[0])) match = 1;
1497		else if (s_aliases != NULL)
1498		{
1499			for (i = 0; (s_aliases[i] != NULL) && (match == 0); i++)
1500				if (string_equal(name, s_aliases[i])) match = 1;
1501		}
1502	}
1503	else if ((which == SEL_NUMBER) && (port == xport)) match = 1;
1504
1505	if (match == 0)
1506	{
1507		free(tokens);
1508		return NULL;
1509	}
1510
1511	/* strange but correct */
1512	xport = htons(xport);
1513
1514	item = (si_item_t *)LI_ils_create("L4488s*4s", (unsigned long)si, CATEGORY_SERVICE, 1, va, vb, tokens[0], s_aliases, xport, xproto);
1515
1516	free(tokens);
1517
1518	return item;
1519}
1520
1521static void *
1522_fsi_get_service(si_mod_t *si, const char *name, const char *proto, int port, int which)
1523{
1524	char *p, *line;
1525	si_item_t *item;
1526	FILE *f;
1527	si_list_t *all;
1528	uint64_t va, vb;
1529
1530	if ((which == SEL_NAME) && (name == NULL)) return NULL;
1531	if ((which == SEL_NUMBER) && (port == 0)) return NULL;
1532
1533	f = fopen(_PATH_SERVICES, "r");
1534	if (f == NULL) return NULL;
1535
1536	_fsi_get_validation(si, VALIDATION_SERVICES, _PATH_SERVICES, f, &va, &vb);
1537
1538	all = NULL;
1539
1540	forever
1541	{
1542		line = _fsi_get_line(f);
1543		if (line == NULL) break;
1544
1545		if (line[0] == '#')
1546		{
1547			free(line);
1548			line = NULL;
1549			continue;
1550		}
1551
1552		p = strchr(line, '#');
1553		if (p != NULL) *p = '\0';
1554
1555		item = _fsi_parse_service(si, name, proto, port, which, line, va, vb);
1556		free(line);
1557		line = NULL;
1558
1559		if (item == NULL) continue;
1560
1561		if (which == SEL_ALL)
1562		{
1563			all = si_list_add(all, item);
1564			si_item_release(item);
1565			continue;
1566		}
1567
1568		fclose(f);
1569		return item;
1570	}
1571
1572	fclose(f);
1573	return all;
1574}
1575
1576/*
1577 * Generic name/number/aliases lookup
1578 * Works for protocols, networks, and rpcs
1579 */
1580
1581static si_item_t *
1582_fsi_parse_name_num_aliases(si_mod_t *si, const char *name, int num, int which, char *data, uint64_t va, uint64_t vb, int cat)
1583{
1584	char **tokens, **aliases;
1585	int i, ntokens, match, xnum;
1586	si_item_t *item;
1587
1588	if (data == NULL) return NULL;
1589
1590	ntokens = 0;
1591	tokens = _fsi_tokenize(data, " 	", 0, &ntokens);
1592	if (ntokens < 2)
1593	{
1594		free(tokens);
1595		return NULL;
1596	}
1597
1598	xnum = atoi(tokens[1]);
1599
1600	aliases = NULL;
1601	if (ntokens > 2) aliases = &(tokens[2]);
1602
1603	match = 0;
1604
1605	if (which == SEL_ALL) match = 1;
1606	else if (which == SEL_NAME)
1607	{
1608		if (string_equal(name, tokens[0])) match = 1;
1609		else if (aliases != NULL)
1610		{
1611			for (i = 0; (aliases[i] != NULL) && (match == 0); i++)
1612				if (string_equal(name, aliases[i])) match = 1;
1613		}
1614	}
1615	else if ((which == SEL_NUMBER) && (num == xnum)) match = 1;
1616
1617	if (match == 0)
1618	{
1619		free(tokens);
1620		return NULL;
1621	}
1622
1623	switch (cat) {
1624		case CATEGORY_NETWORK:
1625			// struct netent
1626			item = (si_item_t *)LI_ils_create("L4488s*44", (unsigned long)si, cat, 1, va, vb, tokens[0], aliases, AF_INET, xnum);
1627			break;
1628		case CATEGORY_PROTOCOL:
1629		case CATEGORY_RPC:
1630			// struct protoent
1631			// struct rpcent
1632			item = (si_item_t *)LI_ils_create("L4488s*4", (unsigned long)si, cat, 1, va, vb, tokens[0], aliases, xnum);
1633			break;
1634		default:
1635			abort();
1636	}
1637
1638	free(tokens);
1639
1640	return item;
1641}
1642
1643static void *
1644_fsi_get_name_number_aliases(si_mod_t *si, const char *name, int num, int which, int cat)
1645{
1646	char *p, *line;
1647	si_item_t *item;
1648	FILE *f;
1649	si_list_t *all;
1650	uint64_t va, vb;
1651	const char *path;
1652	int vtype;
1653
1654	switch (cat) {
1655		case CATEGORY_NETWORK:
1656			vtype = VALIDATION_NETWORKS;
1657			path = _PATH_NETWORKS;
1658			break;
1659		case CATEGORY_PROTOCOL:
1660			vtype = VALIDATION_PROTOCOLS;
1661			path = _PATH_PROTOCOLS;
1662			break;
1663		case CATEGORY_RPC:
1664			vtype = VALIDATION_RPC;
1665			path = _PATH_RPCS;
1666			break;
1667		default:
1668			abort();
1669	}
1670
1671	f = fopen(path, "r");
1672	if (f == NULL) return NULL;
1673
1674	_fsi_get_validation(si, vtype, path, f, &va, &vb);
1675
1676	all = NULL;
1677
1678	forever
1679	{
1680		line = _fsi_get_line(f);
1681		if (line == NULL) break;
1682
1683		if (line[0] == '#')
1684		{
1685			free(line);
1686			line = NULL;
1687			continue;
1688		}
1689
1690		p = strchr(line, '#');
1691		if (p != NULL) *p = '\0';
1692
1693		item = _fsi_parse_name_num_aliases(si, name, num, which, line, va, vb, cat);
1694		free(line);
1695		line = NULL;
1696
1697		if (item == NULL) continue;
1698
1699		if (which == SEL_ALL)
1700		{
1701			all = si_list_add(all, item);
1702			si_item_release(item);
1703			continue;
1704		}
1705
1706		fclose(f);
1707		return item;
1708	}
1709
1710	fclose(f);
1711	return all;
1712}
1713
1714/* MOUNT */
1715
1716static si_item_t *
1717_fsi_parse_fs(si_mod_t *si, const char *name, int which, char *data, uint64_t va, uint64_t vb)
1718{
1719	char **tokens, *tmp, **opts, *fstype;
1720	int ntokens, match, i, freq, passno;
1721	si_item_t *item;
1722
1723	if (data == NULL) return NULL;
1724
1725	freq = 0;
1726	passno = 0;
1727	fstype = NULL;
1728
1729	ntokens = 0;
1730	tokens = _fsi_tokenize(data, " 	", 0, &ntokens);
1731	if ((ntokens < 4) || (ntokens > 6))
1732	{
1733		free(tokens);
1734		return NULL;
1735	}
1736
1737	if (ntokens >= 5) freq = atoi(tokens[4]);
1738	if (ntokens == 6) passno = atoi(tokens[5]);
1739
1740	tmp = strdup(tokens[3]);
1741	if (tmp == NULL)
1742	{
1743		free(tokens);
1744		return NULL;
1745	}
1746
1747	ntokens = 0;
1748	opts = _fsi_tokenize(tmp, ",", 0, &ntokens);
1749
1750	if (opts == NULL)
1751	{
1752		free(tokens);
1753		free(tmp);
1754		return NULL;
1755	}
1756
1757	for (i = 0; i < ntokens; i++)
1758	{
1759		if ((string_equal(opts[i], "rw")) || (string_equal(opts[i], "ro")) || (string_equal(opts[i], "sw")) || (string_equal(opts[i], "xx")))
1760		{
1761			fstype = opts[i];
1762			break;
1763		}
1764	}
1765
1766	match = 0;
1767
1768	if (which == SEL_ALL) match = 1;
1769	else if ((which == SEL_NAME) && (string_equal(name, tokens[0]))) match = 1;
1770	else if ((which == SEL_NUMBER) && (string_equal(name, tokens[1]))) match = 1;
1771
1772	if (match == 0)
1773	{
1774		free(tokens);
1775		return NULL;
1776	}
1777
1778	item = (si_item_t *)LI_ils_create("L4488sssss44", (unsigned long)si, CATEGORY_FS, 1, va, vb, tokens[0], tokens[1], tokens[2], tokens[3], (fstype == NULL) ? "rw" : fstype, freq, passno);
1779
1780	free(tokens);
1781	free(opts);
1782	free(tmp);
1783
1784	return item;
1785}
1786
1787static char *
1788_fsi_get_device_path(dev_t target_dev)
1789{
1790	char *result;
1791    char dev[PATH_MAX];
1792    char *name;
1793	char namebuf[PATH_MAX];
1794
1795	result = NULL;
1796
1797    strlcpy(dev, _PATH_DEV, sizeof(dev));
1798
1799    /* The root device in fstab should always be a block special device */
1800    name = devname_r(target_dev, S_IFBLK, namebuf, sizeof(namebuf));
1801    if (name == NULL)
1802	{
1803		DIR *dirp;
1804		struct stat devst;
1805		struct dirent *ent, entbuf;
1806
1807       /* No _PATH_DEVDB. We have to search for it the slow way */
1808        dirp = opendir(_PATH_DEV);
1809        if (dirp == NULL) return NULL;
1810
1811        while (readdir_r(dirp, &entbuf, &ent) == 0 && ent != NULL)
1812		{
1813            /* Look for a block special device */
1814            if (ent->d_type == DT_BLK)
1815			{
1816                strlcat(dev, ent->d_name, sizeof(dev));
1817                if (stat(dev, &devst) == 0)
1818				{
1819                    if (devst.st_rdev == target_dev) {
1820						result = strdup(dev);
1821						break;
1822					}
1823                }
1824            }
1825
1826            /* reset dev to _PATH_DEV and try again */
1827            dev[sizeof(_PATH_DEV) - 1] = '\0';
1828        }
1829
1830		if (dirp) closedir(dirp);
1831    }
1832	else
1833	{
1834        /* We found the _PATH_DEVDB entry */
1835		strlcat(dev, name, sizeof(dev));
1836		result = strdup(dev);
1837	}
1838
1839    return result;
1840}
1841
1842static si_item_t *
1843_fsi_fs_root(si_mod_t *si)
1844{
1845	dispatch_once(&rootfs_once, ^{
1846		struct stat rootstat;
1847		struct statfs rootfsinfo;
1848		char *root_spec;
1849		const char *root_path = "/";
1850
1851		if (stat(root_path, &rootstat) < 0) return;
1852 		if (statfs(root_path, &rootfsinfo) < 0) return;
1853
1854		// Check to make sure we're not looking at a synthetic root:
1855		if (string_equal(rootfsinfo.f_fstypename, "synthfs")) {
1856			root_path = "/root";
1857        		if (stat(root_path, &rootstat) < 0) return;
1858			if (statfs(root_path, &rootfsinfo) < 0) return;
1859		}
1860
1861		root_spec = _fsi_get_device_path(rootstat.st_dev);
1862
1863		rootfs = (si_item_t *)LI_ils_create("L4488sssss44", (unsigned long)si, CATEGORY_FS, 1, 0LL, 0LL, root_spec, root_path, rootfsinfo.f_fstypename, FSTAB_RW, FSTAB_RW, 0, 1);
1864	});
1865
1866	return si_item_retain(rootfs);
1867}
1868
1869static void *
1870_fsi_get_fs(si_mod_t *si, const char *name, int which)
1871{
1872	char *line;
1873	si_item_t *item;
1874	FILE *f;
1875	si_list_t *all;
1876	uint64_t va, vb;
1877	int synthesize_root;
1878	struct fstab *rfs;
1879
1880	if ((which != SEL_ALL) && (name == NULL)) return NULL;
1881
1882	all = NULL;
1883	f = NULL;
1884#ifdef SYNTH_ROOTFS
1885	synthesize_root = 1;
1886#else
1887	synthesize_root = 0;
1888#endif
1889
1890	f = fopen(_PATH_FSTAB, "r");
1891	if ((f == NULL) || (synthesize_root == 1))
1892	{
1893		item = _fsi_fs_root(si);
1894
1895		rfs = NULL;
1896		if (item != NULL) rfs = (struct fstab *)((uintptr_t)item + sizeof(si_item_t));
1897
1898		switch (which)
1899		{
1900			case SEL_NAME:
1901			{
1902				if ((rfs != NULL) && (string_equal(name, rfs->fs_spec)))
1903				{
1904					if (f != NULL) fclose(f);
1905					return item;
1906				}
1907
1908				break;
1909			}
1910
1911			case SEL_NUMBER:
1912			{
1913				if ((rfs != NULL) && (string_equal(name, rfs->fs_file)))
1914				{
1915					if (f != NULL) fclose(f);
1916					return item;
1917				}
1918
1919				break;
1920			}
1921
1922			case SEL_ALL:
1923			{
1924				all = si_list_add(all, item);
1925				si_item_release(item);
1926				break;
1927			}
1928		}
1929	}
1930
1931	if (f == NULL) return all;
1932
1933	_fsi_get_validation(si, VALIDATION_FSTAB, _PATH_FSTAB, f, &va, &vb);
1934
1935	forever
1936	{
1937		line = _fsi_get_line(f);
1938		if (line == NULL) break;
1939
1940		if (line[0] == '#')
1941		{
1942			free(line);
1943			line = NULL;
1944			continue;
1945		}
1946
1947		item = _fsi_parse_fs(si, name, which, line, va, vb);
1948		free(line);
1949		line = NULL;
1950
1951		if (item == NULL) continue;
1952
1953		if (which == SEL_ALL)
1954		{
1955			all = si_list_add(all, item);
1956			si_item_release(item);
1957			continue;
1958		}
1959
1960		fclose(f);
1961		return item;
1962	}
1963
1964	fclose(f);
1965	return all;
1966}
1967
1968static int
1969file_is_valid(si_mod_t *si, si_item_t *item)
1970{
1971	si_mod_t *src;
1972
1973	if (si == NULL) return 0;
1974	if (item == NULL) return 0;
1975	if (si->name == NULL) return 0;
1976	if (item->src == NULL) return 0;
1977
1978	src = (si_mod_t *)item->src;
1979
1980	if (src->name == NULL) return 0;
1981	if (string_not_equal(si->name, src->name)) return 0;
1982
1983	if (item == rootfs) return 1;
1984
1985	return _fsi_validate(si, item->type, item->validation_a, item->validation_b);
1986}
1987
1988static si_item_t *
1989file_user_byname(si_mod_t *si, const char *name)
1990{
1991	return _fsi_get_user(si, name, 0, SEL_NAME);
1992}
1993
1994static si_item_t *
1995file_user_byuid(si_mod_t *si, uid_t uid)
1996{
1997	return _fsi_get_user(si, NULL, uid, SEL_NUMBER);
1998}
1999
2000static si_list_t *
2001file_user_all(si_mod_t *si)
2002{
2003	return _fsi_get_user(si, NULL, 0, SEL_ALL);
2004}
2005
2006static si_item_t *
2007file_group_byname(si_mod_t *si, const char *name)
2008{
2009	return _fsi_get_group(si, name, 0, SEL_NAME);
2010}
2011
2012static si_item_t *
2013file_group_bygid(si_mod_t *si, gid_t gid)
2014{
2015	return _fsi_get_group(si, NULL, gid, SEL_NUMBER);
2016}
2017
2018static si_list_t *
2019file_group_all(si_mod_t *si)
2020{
2021	return _fsi_get_group(si, NULL, 0, SEL_ALL);
2022}
2023
2024static si_item_t *
2025file_grouplist(si_mod_t *si, const char *name, __unused uint32_t ignored)
2026{
2027	return _fsi_get_grouplist(si, name);
2028}
2029
2030static si_list_t *
2031file_netgroup_byname(si_mod_t *si, const char *name)
2032{
2033	si_list_t *list = NULL;
2034	si_item_t *item;
2035	uint64_t va=0, vb=0;
2036	file_netgroup_t *n;
2037	file_si_private_t *pp;
2038
2039	if (name == NULL) return NULL;
2040
2041	pp = (file_si_private_t *)si->private;
2042	if (pp == NULL) return NULL;
2043
2044	_fsi_check_netgroup_cache(si);
2045
2046	pthread_mutex_lock(&file_mutex);
2047
2048	n = _fsi_find_netgroup(&(pp->file_netgroup_cache), name, 0);
2049	if (n != NULL)
2050	{
2051		file_netgroup_member_t *m = n->members;
2052		while (m != NULL)
2053		{
2054			item = (si_item_t *)LI_ils_create("L4488sss", (unsigned long)si, CATEGORY_NETGROUP, 1, va, vb, m->host, m->user, m->domain);
2055			list = si_list_add(list, item);
2056			m = m->next;
2057		}
2058	}
2059
2060	pthread_mutex_unlock(&file_mutex);
2061
2062	return list;
2063}
2064
2065static int
2066file_in_netgroup(si_mod_t *si, const char *group, const char *host, const char *user, const char *domain)
2067{
2068	file_netgroup_t *n;
2069	file_netgroup_member_t *m;
2070	file_si_private_t *pp;
2071
2072	if (group == NULL) return 0;
2073
2074	pp = (file_si_private_t *)si->private;
2075	if (pp == NULL) return 0;
2076
2077	_fsi_check_netgroup_cache(si);
2078
2079	pthread_mutex_lock(&file_mutex);
2080
2081	n = _fsi_find_netgroup(&(pp->file_netgroup_cache), group, 0);
2082	if (n == NULL)
2083	{
2084		pthread_mutex_unlock(&file_mutex);
2085		return 0;
2086	}
2087
2088	m = n->members;
2089	while (m != NULL)
2090	{
2091		file_netgroup_member_t *x = m;
2092		m = m->next;
2093
2094		if (host != NULL)
2095		{
2096			if (x->host == NULL) continue;
2097			if (strcmp(host, x->host)) continue;
2098		}
2099
2100		if (user != NULL)
2101		{
2102			if (x->user == NULL) continue;
2103			if (strcmp(user, x->user)) continue;
2104		}
2105
2106		if (domain != NULL)
2107		{
2108			if (x->domain == NULL) continue;
2109			if (strcmp(domain, x->domain)) continue;
2110		}
2111
2112		pthread_mutex_unlock(&file_mutex);
2113		return 1;
2114	}
2115
2116	pthread_mutex_unlock(&file_mutex);
2117	return 0;
2118}
2119
2120static si_item_t *
2121file_host_byname(si_mod_t *si, const char *name, int af, const char *ignored, uint32_t *err)
2122{
2123	si_item_t *item;
2124
2125	if (err != NULL) *err = SI_STATUS_NO_ERROR;
2126
2127	item = _fsi_get_host(si, name, NULL, af, SEL_NAME, err);
2128	if ((item == NULL) && (err != NULL) && (*err == 0)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
2129
2130	return item;
2131}
2132
2133static si_item_t *
2134file_host_byaddr(si_mod_t *si, const void *addr, int af, const char *ignored, uint32_t *err)
2135{
2136	si_item_t *item;
2137
2138	if (err != NULL) *err = SI_STATUS_NO_ERROR;
2139
2140	item = _fsi_get_host(si, NULL, addr, af, SEL_NUMBER, err);
2141	if ((item == NULL) && (err != NULL) && (*err == 0)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
2142
2143	return item;
2144}
2145
2146static si_list_t *
2147file_host_all(si_mod_t *si)
2148{
2149	return _fsi_get_host(si, NULL, NULL, 0, SEL_ALL, NULL);
2150}
2151
2152static si_item_t *
2153file_network_byname(si_mod_t *si, const char *name)
2154{
2155	if (name == NULL) return NULL;
2156	return _fsi_get_name_number_aliases(si, name, 0, SEL_NAME, CATEGORY_NETWORK);
2157}
2158
2159static si_item_t *
2160file_network_byaddr(si_mod_t *si, uint32_t addr)
2161{
2162	return _fsi_get_name_number_aliases(si, NULL, (int)addr, SEL_NUMBER, CATEGORY_NETWORK);
2163}
2164
2165static si_list_t *
2166file_network_all(si_mod_t *si)
2167{
2168	return _fsi_get_name_number_aliases(si, NULL, 0, SEL_ALL, CATEGORY_NETWORK);
2169}
2170
2171static si_item_t *
2172file_service_byname(si_mod_t *si, const char *name, const char *proto)
2173{
2174	return _fsi_get_service(si, name, proto, 0, SEL_NAME);
2175}
2176
2177static si_item_t *
2178file_service_byport(si_mod_t *si, int port, const char *proto)
2179{
2180	return _fsi_get_service(si, NULL, proto, port, SEL_NUMBER);
2181}
2182
2183static si_list_t *
2184file_service_all(si_mod_t *si)
2185{
2186	return _fsi_get_service(si, NULL, NULL, 0, SEL_ALL);
2187}
2188
2189static si_item_t *
2190file_protocol_byname(si_mod_t *si, const char *name)
2191{
2192	if (name == NULL) return NULL;
2193	return _fsi_get_name_number_aliases(si, name, 0, SEL_NAME, CATEGORY_PROTOCOL);
2194}
2195
2196static si_item_t *
2197file_protocol_bynumber(si_mod_t *si, int number)
2198{
2199	return _fsi_get_name_number_aliases(si, NULL, number, SEL_NUMBER, CATEGORY_PROTOCOL);
2200}
2201
2202static si_list_t *
2203file_protocol_all(si_mod_t *si)
2204{
2205	return _fsi_get_name_number_aliases(si, NULL, 0, SEL_ALL, CATEGORY_PROTOCOL);
2206}
2207
2208static si_item_t *
2209file_rpc_byname(si_mod_t *si, const char *name)
2210{
2211	if (name == NULL) return NULL;
2212	return _fsi_get_name_number_aliases(si, name, 0, SEL_NAME, CATEGORY_RPC);
2213}
2214
2215static si_item_t *
2216file_rpc_bynumber(si_mod_t *si, int number)
2217{
2218	return _fsi_get_name_number_aliases(si, NULL, number, SEL_NUMBER, CATEGORY_RPC);
2219}
2220
2221static si_list_t *
2222file_rpc_all(si_mod_t *si)
2223{
2224	return _fsi_get_name_number_aliases(si, NULL, 0, SEL_ALL, CATEGORY_RPC);
2225}
2226
2227static si_item_t *
2228file_fs_byspec(si_mod_t *si, const char *spec)
2229{
2230	return _fsi_get_fs(si, spec, SEL_NAME);
2231}
2232
2233static si_item_t *
2234file_fs_byfile(si_mod_t *si, const char *file)
2235{
2236	return _fsi_get_fs(si, file, SEL_NUMBER);
2237}
2238
2239static si_list_t *
2240file_fs_all(si_mod_t *si)
2241{
2242	return _fsi_get_fs(si, NULL, SEL_ALL);
2243}
2244
2245static si_item_t *
2246file_alias_byname(si_mod_t *si, const char *name)
2247{
2248	return _fsi_get_alias(si, name, SEL_NAME);
2249}
2250
2251static si_list_t *
2252file_alias_all(si_mod_t *si)
2253{
2254	return _fsi_get_alias(si, NULL, SEL_ALL);
2255}
2256
2257static si_item_t *
2258file_mac_byname(si_mod_t *si, const char *name)
2259{
2260	return _fsi_get_ether(si, name, SEL_NAME);
2261}
2262
2263static si_item_t *
2264file_mac_bymac(si_mod_t *si, const char *mac)
2265{
2266	return _fsi_get_ether(si, mac, SEL_NUMBER);
2267}
2268
2269static si_list_t *
2270file_mac_all(si_mod_t *si)
2271{
2272	return _fsi_get_ether(si, NULL, SEL_ALL);
2273}
2274
2275static si_list_t *
2276file_addrinfo(si_mod_t *si, const void *node, const void *serv, uint32_t family, uint32_t socktype, uint32_t proto, uint32_t flags, const char *interface, uint32_t *err)
2277{
2278	if (err != NULL) *err = SI_STATUS_NO_ERROR;
2279	return _gai_simple(si, node, serv, family, socktype, proto, flags, interface, err);
2280}
2281
2282si_mod_t *
2283si_module_static_file(void)
2284{
2285	static const struct si_mod_vtable_s file_vtable =
2286	{
2287		.sim_is_valid = &file_is_valid,
2288
2289		.sim_user_byname = &file_user_byname,
2290		.sim_user_byuid = &file_user_byuid,
2291		.sim_user_byuuid = NULL,
2292		.sim_user_all = &file_user_all,
2293
2294		.sim_group_byname = &file_group_byname,
2295		.sim_group_bygid = &file_group_bygid,
2296		.sim_group_byuuid = NULL,
2297		.sim_group_all = &file_group_all,
2298
2299		.sim_grouplist = &file_grouplist,
2300
2301		.sim_netgroup_byname = &file_netgroup_byname,
2302		.sim_in_netgroup = &file_in_netgroup,
2303
2304		.sim_alias_byname = &file_alias_byname,
2305		.sim_alias_all = &file_alias_all,
2306
2307		.sim_host_byname = &file_host_byname,
2308		.sim_host_byaddr = &file_host_byaddr,
2309		.sim_host_all = &file_host_all,
2310
2311		.sim_network_byname = &file_network_byname,
2312		.sim_network_byaddr = &file_network_byaddr,
2313		.sim_network_all = &file_network_all,
2314
2315		.sim_service_byname = &file_service_byname,
2316		.sim_service_byport = &file_service_byport,
2317		.sim_service_all = &file_service_all,
2318
2319		.sim_protocol_byname = &file_protocol_byname,
2320		.sim_protocol_bynumber = &file_protocol_bynumber,
2321		.sim_protocol_all = &file_protocol_all,
2322
2323		.sim_rpc_byname = &file_rpc_byname,
2324		.sim_rpc_bynumber = &file_rpc_bynumber,
2325		.sim_rpc_all = &file_rpc_all,
2326
2327		.sim_fs_byspec = &file_fs_byspec,
2328		.sim_fs_byfile = &file_fs_byfile,
2329		.sim_fs_all = &file_fs_all,
2330
2331		.sim_mac_byname = &file_mac_byname,
2332		.sim_mac_bymac = &file_mac_bymac,
2333		.sim_mac_all = &file_mac_all,
2334
2335		.sim_wants_addrinfo = NULL,
2336		.sim_addrinfo = &file_addrinfo,
2337
2338		/* no nameinfo support */
2339		.sim_nameinfo = NULL,
2340	};
2341
2342	static si_mod_t si =
2343	{
2344		.vers = 1,
2345		.refcount = 1,
2346		.flags = SI_MOD_FLAG_STATIC,
2347
2348		.private = NULL,
2349		.vtable = &file_vtable,
2350	};
2351
2352	static dispatch_once_t once;
2353
2354	dispatch_once(&once, ^{
2355		si.name = strdup("file");
2356		file_si_private_t *pp = calloc(1, sizeof(file_si_private_t));
2357		if (pp != NULL)
2358		{
2359			int i;
2360			for (i = 0; i < VALIDATION_COUNT; i++) pp->notify_token[i] = -1;
2361
2362			/* hardwired for now, but we may want to make this configurable someday */
2363			pp->validation_notify_mask = VALIDATION_MASK_HOSTS | VALIDATION_MASK_SERVICES | VALIDATION_MASK_PROTOCOLS;
2364		}
2365
2366		si.private = pp;
2367	});
2368
2369	return (si_mod_t *)&si;
2370}
2371