1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
25 */
26
27/*
28 * Network name to unix credential database generator.
29 * Uses /etc/passwd, /etc/group, /etc/hosts and /etc/netid to
30 * create the database.
31 *
32 * If some user appears in passwd, they get an entry like:
33 *	sun.<uid>@<domainname>	<uid>:<gid1>,<gid2>,...
34 * If some host appears in hosts, it gets an entry like:
35 *	sun.<hostname>@<domainname>	0:<hostname>
36 *
37 * The file /etc/netid is used to add other domains (possibly non-unix)
38 * to the database.
39 */
40#include <stdio.h>
41#include <pwd.h>
42#include <limits.h>
43#include <sys/param.h>
44#include <rpc/rpc.h>
45#include <rpc/key_prot.h>
46
47
48#define	MAXNAMELEN	256
49#define	MAXLINELEN	1024
50#define	MAXDOMAINLEN	32
51
52#define	GRPTABSIZE	256		/* size of group table */
53#define	PRNTABSIZE	4096		/* size of printed item table */
54
55#define	NUMGIDS	(NGROUPS_MAX + 1)	/* group-access-list + gid */
56
57extern char **getaline();
58extern char *malloc();
59extern char *strcpy();
60
61/*
62 * The group list
63 * Store username and groups to which they belong
64 */
65struct group_list {
66	char *user;
67	int group_len;
68	int groups[NUMGIDS];
69	struct group_list *next;
70};
71
72/*
73 * General purpose list of strings
74 */
75struct string_list {
76	char *str;
77	struct string_list *next;
78};
79
80static FILE *openfile();
81static char *scanargs();
82static int atoi();
83
84static char *cmdname;	/* name of this program */
85static int quietmode;	/* quiet mode: don't print error messages */
86static char *curfile;	/* name of file we are parsing */
87static int curline;		/* current line parsed in this file */
88
89static struct group_list *groups[GRPTABSIZE];	/* group table */
90static struct string_list *printed[PRNTABSIZE];	/* printed item table */
91static char domain[MAXDOMAINLEN];	/* name of our domain */
92
93static char PASSWD[] = "/etc/passwd";	/* default passwd database */
94static char IDMAP[] = "/etc/idmap";	/* default net-id map database */
95static char GROUP[] = "/etc/group";	/* default group database */
96static char HOSTS[] = "/etc/hosts";	/* default hosts database */
97
98static char *pwdfile = PASSWD;	/* password file to parse */
99static char *grpfile = GROUP;	/* group file */
100static char *hostfile = HOSTS;	/* hosts file */
101static char *mapfile = IDMAP;	/* network id file */
102
103/*
104 * Various separaters
105 */
106static char WHITE[] = "\t ";
107static char COLON[] = ":";
108static char COMMA[] = ",";
109
110void domapfile(char *, FILE *);
111void dogrpfile(char *, FILE *);
112void dopwdfile(char *, FILE *);
113void dohostfile(char *, FILE *);
114static int Atoi(char *);
115void check_getname(char **, char *, char *, char *, char *);
116void multdef(char *);
117static int wasprinted(char *);
118void storegid(int, char *);
119void printgroups(char *, int);
120int parseargs(int, char *[]);
121void put_s(char *);
122void put_d(int);
123
124
125int
126main(argc, argv)
127	int argc;
128	char *argv[];
129{
130	FILE *pf, *mf, *gf, *hf;
131
132	cmdname = argv[0];
133	if (!parseargs(argc, argv)) {
134		(void) fprintf(stderr,
135			"usage: %s [-q] [-pghm filename]\n", cmdname);
136		exit(1);
137	}
138	(void) getdomainname(domain, sizeof (domain));
139
140	pf = openfile(pwdfile);
141	gf = openfile(grpfile);
142	hf = openfile(hostfile);
143	mf = fopen(mapfile, "r");
144
145
146	if (mf != NULL) {
147		domapfile(mapfile, mf);
148	}
149	dogrpfile(grpfile, gf);
150	dopwdfile(pwdfile, pf);
151	dohostfile(hostfile, hf);
152
153	return (0);
154	/* NOTREACHED */
155}
156
157/*
158 * Parse the network id mapping file
159 */
160void
161domapfile(mapfile, mf)
162	char *mapfile;
163	FILE *mf;
164{
165	char **lp;
166	char line[MAXLINELEN];
167	char name[MAXNAMELEN];
168	int uid, gid;
169
170	curfile = mapfile;
171	curline = 0;
172	while (lp = getaline(line, sizeof (line), mf, &curline, "#")) {
173		check_getname(lp, name, WHITE, WHITE, "#");
174		if (wasprinted(name)) {
175			multdef(name);
176			continue;
177		}
178		put_s(name);
179		(void) putchar(' ');
180		check_getname(lp, name, WHITE, COLON, "#");
181		uid = Atoi(name);
182		put_d(uid);
183		(void) putchar(':');
184		if (uid == 0) {
185			check_getname(lp, name, WHITE, "\t\n ", "#");
186			put_s(name);
187			(void) putchar(' ');
188		} else {
189			check_getname(lp, name, WHITE, ",\n", "#");
190			gid = Atoi(name);
191			put_d(gid);
192			while (getname(name, sizeof (name), WHITE, ",\n", lp,
193					"#") >= 0) {
194				gid = Atoi(name);
195				(void) putchar(',');
196				put_d(gid);
197			}
198		}
199		(void) putchar('\n');
200	}
201}
202
203
204/*
205 * Parse the groups file
206 */
207void
208dogrpfile(grpfile, gf)
209	char *grpfile;
210	FILE *gf;
211{
212	char **lp;
213	char line[MAXLINELEN];
214	char name[MAXNAMELEN];
215	int gid;
216
217	curfile = grpfile;
218	curline = 0;
219	while (lp = getaline(line, sizeof (line), gf, &curline, "")) {
220		check_getname(lp, name, WHITE, COLON, "");
221		if (name[0] == '+') {
222			continue;
223		}
224		check_getname(lp, name, WHITE, COLON, ""); /* ignore passwd */
225		check_getname(lp, name, WHITE, COLON, "");
226		gid = Atoi(name);
227		while (getname(name, sizeof (name), WHITE, COMMA, lp,
228				"") >= 0) {
229			storegid(gid, name);
230		}
231	}
232}
233
234
235/*
236 * Parse the password file
237 */
238void
239dopwdfile(pwdfile, pf)
240	char *pwdfile;
241	FILE *pf;
242{
243	char **lp;
244	char line[MAXLINELEN];
245	char name[MAXNAMELEN];
246	char user[MAXNAMELEN];
247	int uid, gid;
248
249	curfile = pwdfile;
250	curline = 0;
251
252	while (lp = getaline(line, sizeof (line), pf, &curline, "")) {
253		check_getname(lp, user, WHITE, COLON, ""); 	/* username */
254		if (user[0] == '-' || user[0] == '+') {
255			continue;	/* NIS entry */
256		}
257		check_getname(lp, name, WHITE, COLON, ""); /* ignore passwd */
258		check_getname(lp, name, WHITE, COLON, ""); /* but get uid */
259		uid = Atoi(name);
260		user2netname(name, uid, domain);
261		if (wasprinted(name)) {
262			multdef(name);
263			continue;
264		}
265		put_s(name);
266		(void) putchar(' ');
267		check_getname(lp, name, WHITE, COLON, "");
268		gid = Atoi(name);
269		put_d(uid);
270		(void) putchar(':');
271		printgroups(user, gid);
272	}
273}
274
275
276/*
277 * Parse the hosts file
278 */
279void
280dohostfile(hostfile, hf)
281	char *hostfile;
282	FILE *hf;
283{
284	char **lp;
285	char line[MAXLINELEN];
286	char name[MAXNAMELEN];
287	char netname[MAXNETNAMELEN];
288
289	curfile = hostfile;
290	curline = 0;
291	while (lp = getaline(line, sizeof (line), hf, &curline, "#")) {
292		check_getname(lp, name, WHITE, WHITE, "#");
293		if (getname(name, MAXNAMELEN, WHITE, WHITE, lp, "#") != 1) {
294			continue;
295		}
296		host2netname(netname, name, domain);
297		if (wasprinted(netname)) {
298			multdef(netname);
299			continue;
300		}
301		(void) printf("%s 0:%.*s\n", netname, sizeof (name), name);
302	}
303}
304
305/*
306 * Open a file, exit on failure
307 */
308static FILE *
309openfile(fname)
310	char *fname;
311{
312	FILE *f;
313
314	f = fopen(fname, "r");
315	if (f == NULL) {
316		(void) fprintf(stderr, "%s: can't open %s\n", cmdname, fname);
317		exit(1);
318	}
319	return (f);
320}
321
322/*
323 * Print syntax error message, then exit
324 */
325void
326syntaxerror()
327{
328	(void) fprintf(stderr, "%s: syntax error in file \"%s\", line %d\n",
329	    cmdname, curfile, curline);
330	exit(1);
331}
332
333
334/*
335 * an atoi() that prints a message and exits upong failure
336 */
337static int
338Atoi(str)
339	char *str;
340{
341	int res;
342
343	if (!sscanf(str, "%d", &res)) {
344		syntaxerror();
345	}
346	return (res);
347}
348
349
350/*
351 * Attempt to get a token from a file, print a message and exit upon failure
352 */
353void
354check_getname(lp, name, skip, term, com)
355	char **lp;
356	char *name;
357	char *skip;
358	char *term;
359	char *com;
360{
361	if (getname(name, MAXNAMELEN, skip, term, lp, com) != 1) {
362		syntaxerror();
363	}
364}
365
366/*
367 * Something was defined more than once
368 */
369void
370multdef(name)
371	char *name;
372{
373	if (!quietmode) {
374		(void) fprintf(stderr,
375			"%s: %s multiply defined, other definitions ignored\n",
376			cmdname, name);
377	}
378}
379
380static int
381hash(str, size)
382	unsigned char *str;
383	int size;
384{
385	unsigned val;
386	int flip;
387
388	val = 0;
389	flip = 0;
390	while (*str) {
391		if (flip) {
392			val ^= (*str++ << 6);
393		} else {
394			val ^= *str++;
395		}
396		flip = !flip;
397	}
398	return (val % size);
399}
400
401
402/*
403 * Check if an item has been printed
404 * If not, store the item into the printed item table
405 */
406static int
407wasprinted(name)
408	char *name;
409{
410	struct string_list *s;
411	int val;
412
413	val = hash((unsigned char *) name, PRNTABSIZE);
414	for (s = printed[val]; s != NULL && strcmp(s->str, name); s = s->next)
415		;
416	if (s != NULL) {
417		return (1);
418	}
419	s = (struct string_list *)malloc(sizeof (struct string_list));
420	s->str = malloc((unsigned)strlen(name) + 1);
421	(void) strcpy(s->str, name);
422	s->next = printed[val];
423	printed[val] = s;
424	return (0);
425}
426
427/*
428 * Add gid to the list of a user's groups
429 */
430void
431storegid(gid, user)
432	int gid;
433	char *user;
434{
435	struct group_list *g;
436	int i;
437	int val;
438
439	val = hash((unsigned char *) user, GRPTABSIZE);
440	for (g = groups[val]; g != NULL && strcmp(g->user, user); g = g->next)
441		;
442	if (g == NULL) {
443		g = (struct group_list *)malloc(sizeof (struct group_list));
444		g->user = malloc((unsigned)strlen(user) + 1);
445		(void) strcpy(g->user, user);
446		g->group_len = 1;
447		g->groups[0] = gid;
448		g->next = groups[val];
449		groups[val] = g;
450	} else {
451		for (i = 0; i < g->group_len; i++) {
452			if (g->groups[i] == gid) {
453				return;
454			}
455		}
456		if (g->group_len >= NUMGIDS) {
457			(void) fprintf(stderr, "%s: %s's groups exceed %d\n",
458				cmdname, user, NGROUPS_MAX);
459			return;
460		}
461		g->groups[g->group_len++] = gid;
462	}
463}
464
465/*
466 * print out a user's groups
467 */
468void
469printgroups(user, gid)
470	char *user;
471	int gid;
472{
473	struct group_list *g;
474	int i;
475	int val;
476
477	val = hash((unsigned char *) user, GRPTABSIZE);
478	for (g = groups[val]; g != NULL && strcmp(g->user, user); g = g->next)
479		;
480	put_d(gid);
481	if (g != NULL) {
482		for (i = 0; i < g->group_len; i++) {
483			if (gid != g->groups[i]) {
484				(void) putchar(',');
485				put_d(g->groups[i]);
486			}
487		}
488	}
489	(void) putchar('\n');
490}
491
492
493/*
494 * Parse command line arguments
495 */
496int
497parseargs(argc, argv)
498	int argc;
499	char *argv[];
500{
501	int i;
502	int j;
503	static struct {
504		char letter;
505		char *standard;
506		char **filename;
507	} whattodo[] = {
508		{ 'p', PASSWD, &pwdfile },
509		{ 'g', GROUP, &grpfile },
510		{ 'm', IDMAP, &mapfile },
511		{ 'h', HOSTS, &hostfile }
512	};
513
514#define	TABSIZE  sizeof (whattodo)/sizeof (whattodo[0])
515
516	for (i = 1; i < argc; i++) {
517		if (argv[i][0] == '-') {
518			if (argv[i][2] != 0) {
519				return (0);
520			}
521			if (argv[i][1] == 'q') {
522				quietmode = 1;
523				continue;
524			}
525			for (j = 0; j < TABSIZE; j++) {
526				if (whattodo[j].letter == argv[i][1]) {
527					if (*whattodo[j].filename !=
528							whattodo[j].standard) {
529						return (0);
530					}
531					if (++i == argc) {
532						return (0);
533					}
534					*whattodo[j].filename = argv[i];
535					break;
536				}
537			}
538			if (j == TABSIZE) {
539				return (0);
540			}
541		}
542	}
543	return (1);
544}
545
546/*
547 * Print a string, quickly
548 */
549void
550put_s(s)
551	char *s;
552{
553	(void) fwrite(s, strlen(s), 1, stdout);
554}
555
556/*
557 * Print an integer, quickly
558 */
559void
560put_d(d)
561	int d;
562{
563	char buf[20];
564	char *p;
565
566	if (d == 0) {
567		(void) putchar('0');
568		return;
569	}
570	if (d < 0) {
571		(void) putchar('-');
572		d = -d;
573	}
574	p = buf + sizeof (buf);
575	*--p = 0;
576	while (d > 0) {
577		*--p = (d % 10) + '0';
578		d /= 10;
579	}
580	put_s(p);
581}
582