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