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