1/*	$NetBSD: mknetid.c,v 1.18 2011/08/30 21:10:28 joerg Exp $	*/
2
3/*
4 * Copyright (c) 1996 Mats O Jansson <moj@stacken.kth.se>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30#ifndef lint
31__RCSID("$NetBSD: mknetid.c,v 1.18 2011/08/30 21:10:28 joerg Exp $");
32#endif
33
34/*
35 * Originally written by Mats O Jansson <moj@stacken.kth.se>
36 * Simplified a bit by Jason R. Thorpe <thorpej@NetBSD.org>
37 */
38
39#include <sys/param.h>
40#include <sys/queue.h>
41#include <ctype.h>
42#include <err.h>
43#include <grp.h>
44#include <limits.h>
45#include <netdb.h>
46#include <pwd.h>
47#include <stdio.h>
48#include <string.h>
49#include <stdlib.h>
50#include <unistd.h>
51
52#include <rpcsvc/ypclnt.h>
53
54#include "protos.h"
55
56struct user {
57	char 	*usr_name;		/* user name */
58	int	usr_uid;		/* user uid */
59	int	usr_gid;		/* user gid */
60	int	gid_count;		/* number of gids */
61	int	gid[NGROUPS];		/* additional gids */
62	TAILQ_ENTRY(user) read;		/* links in read order */
63	TAILQ_ENTRY(user) hash;		/* links in hash order */
64};
65
66#define HASHMAX 55
67
68static void	add_group(const char *, const char *);
69static void	add_user(const char *, const char *, const char *);
70static int	hashidx(char);
71static int	isgsep(char);
72static void	print_hosts(const char *, const char *);
73static void	print_netid(const char *);
74static void	print_passwd_group(int, const char *);
75static void	read_group(const char *);
76static void	read_passwd(const char *);
77__dead static void	usage(void);
78
79TAILQ_HEAD(user_list, user);
80static struct user_list root;
81static struct user_list hroot[HASHMAX];
82
83int
84main(int argc, char *argv[])
85{
86	const char *HostFile = _PATH_HOSTS;
87	const char *PasswdFile = _PATH_PASSWD;
88	const char *GroupFile = _PATH_GROUP;
89	const char *NetidFile = "/etc/netid";
90
91	int qflag, ch;
92	char *domain;
93
94	TAILQ_INIT(&root);
95	for (ch = 0; ch < HASHMAX; ch++)
96		TAILQ_INIT((&hroot[ch]));
97
98	qflag = 0;
99	domain = NULL;
100
101	while ((ch = getopt(argc, argv, "d:g:h:m:p:q")) != -1) {
102		switch (ch) {
103		case 'd':
104			domain = optarg;
105			break;
106
107		case 'g':
108			GroupFile = optarg;
109			break;
110
111		case 'h':
112			HostFile = optarg;
113			break;
114
115		case 'm':
116			NetidFile = optarg;
117			break;
118
119		case 'p':
120			PasswdFile = optarg;
121			break;
122
123		case 'q':
124			qflag++;
125			break;
126
127		default:
128			usage();
129		}
130	}
131	if (argc != optind)
132		usage();
133
134	if (domain == NULL)
135		if (yp_get_default_domain(&domain))
136			errx(1, "Can't get YP domain name");
137
138	read_passwd(PasswdFile);
139	read_group(GroupFile);
140
141	print_passwd_group(qflag, domain);
142	print_hosts(HostFile, domain);
143	print_netid(NetidFile);
144
145	exit (0);
146}
147
148static int
149hashidx(char key)
150{
151	if (key < 'A')
152		return(0);
153
154	if (key <= 'Z')
155		return(1 + key - 'A');
156
157	if (key < 'a')
158		return(27);
159
160	if (key <= 'z')
161		return(28 + key - 'a');
162
163	return(54);
164}
165
166static void
167add_user(const char *username, const char *uid, const char *gid)
168{
169	struct user *u;
170	int idx;
171
172	idx = hashidx(username[0]);
173
174	u = (struct user *)malloc(sizeof(struct user));
175	if (u == NULL)
176		err(1, "can't allocate user");
177	memset(u, 0, sizeof(struct user));
178
179	u->usr_name = strdup(username);
180	if (u->usr_name == NULL)
181		err(1, "can't allocate user name");
182
183	u->usr_uid = atoi(uid);
184	u->usr_gid = atoi(gid);
185	u->gid_count = -1;
186
187	TAILQ_INSERT_TAIL(&root, u, read);
188	TAILQ_INSERT_TAIL((&hroot[idx]), u, hash);
189}
190
191static void
192add_group(const char *username, const char *gid)
193{
194	struct user *u;
195	int g, idx;
196
197	g = atoi(gid);
198	idx = hashidx(username[0]);
199
200	for (u = hroot[idx].tqh_first;
201	    u != NULL; u = u->hash.tqe_next) {
202		if (strcmp(username, u->usr_name) == 0) {
203			if (g != u->usr_gid) {
204				u->gid_count++;
205				if (u->gid_count < NGROUPS)
206					u->gid[u->gid_count] = g;
207			}
208			return;
209		}
210	}
211}
212
213static void
214read_passwd(const char *fname)
215{
216	FILE	*pfile;
217	size_t	 line_no;
218	int	 colon;
219	size_t	 len;
220	char	*line, *p, *k, *u, *g;
221
222	if ((pfile = fopen(fname, "r")) == NULL)
223		err(1, "%s", fname);
224
225	line_no = 0;
226	for (;
227	    (line = fparseln(pfile, &len, &line_no, NULL, FPARSELN_UNESCALL));
228	    free(line)) {
229		if (len == 0) {
230			warnx("%s line %lu: empty line", fname,
231			    (unsigned long)line_no);
232			continue;
233		}
234
235		p = line;
236		for (k = p, colon = 0; *k != '\0'; k++)
237			if (*k == ':')
238				colon++;
239
240		if (colon != 6) {
241			warnx("%s line %lu: incorrect number of fields",
242			    fname, (unsigned long)line_no);
243			continue;
244		}
245
246		k = p;
247		p = strchr(p, ':');
248		*p++ = '\0';
249
250		/* If it's a YP entry, skip it. */
251		if (*k == '+' || *k == '-')
252			continue;
253
254		/* terminate password */
255		p = strchr(p, ':');
256		*p++ = '\0';
257
258		/* terminate uid */
259		u = p;
260		p = strchr(p, ':');
261		*p++ = '\0';
262
263		/* terminate gid */
264		g = p;
265		p = strchr(p, ':');
266		*p++ = '\0';
267
268		add_user(k, u, g);
269	}
270	(void)fclose(pfile);
271}
272
273static int
274isgsep(char ch)
275{
276
277	switch (ch) {
278	case ',':
279	case ' ':
280	case '\t':
281	case '\0':
282		return (1);
283	}
284
285	return (0);
286}
287
288static void
289read_group(const char *fname)
290{
291	FILE	*gfile;
292	size_t	 line_no;
293	int	 colon;
294	size_t	 len;
295	char	*line, *p, *k, *u, *g;
296
297	if ((gfile = fopen(fname, "r")) == NULL)
298		err(1, "%s", fname);
299
300	line_no = 0;
301	for (;
302	    (line = fparseln(gfile, &len, &line_no, NULL, FPARSELN_UNESCALL));
303	    free(line)) {
304		if (len == 0) {
305			warnx("%s line %lu: empty line", fname,
306			    (unsigned long)line_no);
307			continue;
308		}
309
310		p = line;
311		for (k = p, colon = 0; *k != '\0'; k++)
312			if (*k == ':')
313				colon++;
314
315		if (colon != 3) {
316			warnx("%s line %lu: incorrect number of fields",
317			    fname, (unsigned long)line_no);
318			continue;
319		}
320
321		/* terminate key */
322		k = p;
323		p = strchr(p, ':');
324		*p++ = '\0';
325
326		if (*k == '+' || *k == '-')
327			continue;
328
329		/* terminate password */
330		p = strchr(p, ':');
331		*p++ = '\0';
332
333		/* terminate gid */
334		g = p;
335		p = strchr(p, ':');
336		*p++ = '\0';
337
338		/* get the group list */
339		for (u = p; *u != '\0'; u = p) {
340			/* find separator */
341			for (; isgsep(*p) == 0; p++)
342				;
343
344			if (*p != '\0') {
345				*p = '\0';
346				if (u != p)
347					add_group(u, g);
348				p++;
349			} else if (u != p)
350				add_group(u, g);
351		}
352	}
353	(void)fclose(gfile);
354}
355
356static void
357print_passwd_group(int qflag, const char *domain)
358{
359	struct user *u, *p;
360	int i;
361
362	for (u = root.tqh_first; u != NULL; u = u->read.tqe_next) {
363		for (p = root.tqh_first; p->usr_uid != u->usr_uid;
364		    p = p->read.tqe_next)
365			/* empty */ ;
366		if (p != u) {
367			if (!qflag) {
368				warnx("unix.%d@%s %s", u->usr_uid, domain,
369				 "multiply defined, ignoring duplicate");
370			}
371		} else {
372			printf("unix.%d@%s %d:%d", u->usr_uid, domain,
373			    u->usr_uid, u->usr_gid);
374			if (u->gid_count >= 0)
375				for (i = 0; i <= u->gid_count; i++)
376					printf(",%d", u->gid[i]);
377			printf("\n");
378		}
379	}
380}
381
382static void
383print_hosts(const char *fname, const char *domain)
384{
385	FILE	*hfile;
386	size_t	 len;
387	char	*line, *p, *u;
388
389	if ((hfile = fopen(fname, "r")) == NULL)
390		err(1, "%s", fname);
391
392	for (;
393	    (line = fparseln(hfile, &len, NULL, NULL, FPARSELN_UNESCALL));
394	    free(line)) {
395		if (len == 0)
396			continue;
397
398		p = line;
399		/* Find the key, replace trailing whitespace will <NUL> */
400		for (; *p && isspace((unsigned char)*p) == 0; p++)
401			;
402		while (*p && isspace((unsigned char)*p))
403			*p++ = '\0';
404
405		/* Get first hostname. */
406		for (u = p; *p && !isspace((unsigned char)*p); p++)
407			;
408		*p = '\0';
409
410		printf("unix.%s@%s 0:%s\n", u, domain, u);
411	}
412	(void) fclose(hfile);
413}
414
415static void
416print_netid(const char *fname)
417{
418	FILE	*mfile;
419	size_t	 len;
420	char	*line, *p, *k, *u;
421
422	mfile = fopen(fname, "r");
423	if (mfile == NULL)
424		return;
425
426	for (;
427	    (line = fparseln(mfile, &len, NULL, NULL, FPARSELN_UNESCALL));
428	    free(line)) {
429		if (len == 0)
430			continue;
431
432		p = line;
433		/* Find the key, replace trailing whitespace will <NUL> */
434		for (k = p; *p && !isspace((unsigned char)*p); p++)
435			;
436		while (*p && isspace((unsigned char)*p))
437			*p++ = '\0';
438
439		/* Get netid entry. */
440		for (u = p; *p && !isspace((unsigned char)*p); p++)
441			;
442		*p = '\0';
443
444		printf("%s %s\n", k, u);
445	}
446}
447
448static void
449usage(void)
450{
451
452	fprintf(stderr, "usage: %s %s\n", getprogname(),
453	    "[-d domain] [-q] [-p passwdfile] [-g groupfile]");
454	fprintf(stderr, "       %s  %s", getprogname(),
455	    "[-g groupfile] [-h hostfile] [-m netidfile]");
456	exit(1);
457}
458