xref: /openbsd/usr.sbin/ypserv/mknetid/mknetid.c (revision 73471bf0)
1 /*	$OpenBSD: mknetid.c,v 1.22 2015/02/09 23:00:15 deraadt 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 <stdio.h>
30 #include <unistd.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <err.h>
37 #include <netdb.h>
38 #include <limits.h>
39 
40 #include <rpcsvc/ypclnt.h>
41 
42 struct user {
43 	char	*usr_name;		/* user name */
44 	int	usr_uid;		/* user uid */
45 	int	usr_gid;		/* user gid */
46 	int	gid_count;		/* number of gids */
47 	int	gid[NGROUPS_MAX];	/* additional gids */
48 	struct user *prev, *next;	/* links in read order */
49 	struct user *hprev, *hnext;	/* links in hash order */
50 };
51 
52 char *HostFile = _PATH_HOSTS;
53 char *PasswdFile = _PATH_PASSWD;
54 char *MasterPasswdFile = _PATH_MASTERPASSWD;
55 char *GroupFile = _PATH_GROUP;
56 char *NetidFile = "/etc/netid";
57 
58 #define HASHMAX 55
59 
60 struct user *root = NULL, *tail = NULL;
61 struct user *hroot[HASHMAX], *htail[HASHMAX];
62 
63 static int
64 read_line(FILE *fp, char *buf, int size)
65 {
66 	int done = 0;
67 
68 	do {
69 		while (fgets(buf, size, fp)) {
70 			int len = strlen(buf);
71 
72 			done += len;
73 			if (len > 1 && buf[len-2] == '\\' &&
74 			    buf[len-1] == '\n') {
75 				int ch;
76 
77 				buf += len - 2;
78 				size -= len - 2;
79 				*buf = '\n'; buf[1] = '\0';
80 
81 				/*
82 				 * Skip leading white space on next line
83 				 */
84 				while ((ch = getc(fp)) != EOF &&
85 				    isascii(ch) && isspace(ch))
86 					;
87 				(void) ungetc(ch, fp);
88 			} else {
89 				return done;
90 			}
91 		}
92 	} while (size > 0 && !feof(fp));
93 
94 	return done;
95 }
96 
97 static int
98 hashidx(char key)
99 {
100 	if (key < 'A')
101 		return (0);
102 	if (key <= 'Z')
103 		return (1 + key - 'A');
104 	if (key < 'a')
105 		return (27);
106 	if (key <= 'z')
107 		return (28 + key - 'a');
108 	return (54);
109 }
110 
111 static void
112 add_user(char *username, char *uid, char *gid)
113 {
114 	struct user *u;
115 	int idx;
116 
117 	u = malloc(sizeof(struct user));
118 	if (u == NULL)
119 		err(1, "malloc");
120 	bzero(u, sizeof(struct user));
121 	u->usr_name = strdup(username);
122 	if (u->usr_name == NULL)
123 		err(1, "strdup");
124 	u->usr_uid = atoi(uid);
125 	u->usr_gid = atoi(gid);
126 	u->gid_count = -1;
127 	if (root == NULL) {
128 		root = tail = u;
129 	} else {
130 		u->prev = tail;
131 		tail->next = u;
132 		tail = u;
133 	}
134 	idx = hashidx(username[0]);
135 	if (hroot[idx] == NULL) {
136 		hroot[idx] = htail[idx] = u;
137 	} else {
138 		u->hprev = htail[idx];
139 		htail[idx]->hnext = u;
140 		htail[idx] = u;
141 	}
142 }
143 
144 static void
145 add_group(char *username, char *gid)
146 {
147 	struct user *u;
148 	int idx, g;
149 
150 	idx = hashidx(username[0]);
151 	u = hroot[idx];
152 	g = atoi(gid);
153 
154 	while (u != NULL) {
155 		if (strcmp(username, u->usr_name) == 0) {
156 			if (g != u->usr_gid) {
157 				u->gid_count++;
158 				if (u->gid_count < NGROUPS_MAX)
159 					u->gid[u->gid_count] = atoi(gid);
160 			}
161 			u = htail[idx];
162 		}
163 		u = u->hnext;
164 	}
165 }
166 
167 static void
168 read_passwd(FILE *pfile, char *fname)
169 {
170 	char  line[1024], *p, *k, *u, *g;
171 	int   line_no = 0, len, colon;
172 
173 	while (read_line(pfile, line, sizeof(line))) {
174 		line_no++;
175 		len = strlen(line);
176 
177 		if (len > 0) {
178 			if (line[0] == '#')
179 				continue;
180 		}
181 
182 		/*
183 		 * Check if we have the whole line
184 		 */
185 		if (line[len-1] != '\n') {
186 			fprintf(stderr, "line %d in \"%s\" is too long\n",
187 			    line_no, fname);
188 		} else {
189 			line[len-1] = '\0';
190 		}
191 
192 		p = (char *)&line;
193 
194 		k = p; colon = 0;
195 		while (*k != '\0') {
196 			if (*k == ':')
197 				colon++;
198 			k++;
199 		}
200 
201 		if (colon > 0) {
202 			k = p;			/* save start of key  */
203 			while (*p != ':')
204 				p++;		/* find first "colon" */
205 			if (*p==':')
206 				*p++ = '\0';	/* terminate key */
207 			if (strlen(k) == 1) {
208 				if (*k == '+')
209 					continue;
210 			}
211 		}
212 
213 		if (colon < 4) {
214 			fprintf(stderr, "syntax error at line %d in \"%s\"\n",
215 			    line_no, fname);
216 			continue;
217 		}
218 
219 		while (*p != ':')
220 			p++;			/* find second "colon" */
221 		if (*p==':')
222 			*p++ = '\0';		/* terminate passwd */
223 		u = p;
224 		while (*p != ':')
225 			p++;			/* find third "colon" */
226 		if (*p==':')
227 			*p++ = '\0';		/* terminate uid */
228 		g = p;
229 		while (*p != ':')
230 			p++;			/* find fourth "colon" */
231 		if (*p==':')
232 			*p++ = '\0';		/* terminate gid */
233 		while (*p != '\0')
234 			p++;	/* find end of string */
235 
236 		add_user(k, u, g);
237 	}
238 }
239 
240 static int
241 isgsep(char ch)
242 {
243 	switch (ch)  {
244 	case ',':
245 	case ' ':
246 	case '\t':
247 	case '\0':
248 		return (1);
249 	default:
250 		return (0);
251 	}
252 }
253 
254 static void
255 read_group(FILE *gfile, char *fname)
256 {
257 	char  line[2048], *p, *k, *u, *g;
258 	int   line_no = 0, len, colon;
259 
260 	while (read_line(gfile, line, sizeof(line))) {
261 		line_no++;
262 		len = strlen(line);
263 
264 		if (len > 0) {
265 			if (line[0] == '#')
266 				continue;
267 		}
268 
269 		/*
270 		 * Check if we have the whole line
271 		 */
272 		if (line[len-1] != '\n') {
273 			fprintf(stderr, "line %d in \"%s\" is too long\n",
274 			    line_no, fname);
275 		} else {
276 			line[len-1] = '\0';
277 		}
278 
279 		p = (char *)&line;
280 
281 		k = p; colon = 0;
282 		while (*k != '\0') {
283 			if (*k == ':')
284 				colon++;
285 			k++;
286 		}
287 
288 		if (colon > 0) {
289 			k = p;			/* save start of key  */
290 			while (*p != ':')
291 				p++;		/* find first "colon" */
292 			if (*p==':')
293 				*p++ = '\0';	/* terminate key */
294 			if (strlen(k) == 1) {
295 				if (*k == '+')
296 					continue;
297 			}
298 		}
299 
300 		if (colon < 3) {
301 			fprintf(stderr, "syntax error at line %d in \"%s\"\n",
302 			    line_no, fname);
303 			continue;
304 		}
305 
306 		while (*p != ':')
307 			p++;			/* find second "colon" */
308 		if (*p==':')
309 			*p++ = '\0';		/* terminate passwd */
310 		g = p;
311 		while (*p != ':')
312 			p++;			/* find third "colon" */
313 		if (*p==':')
314 			*p++ = '\0';		/* terminate gid */
315 
316 		u = p;
317 
318 		while (*u != '\0') {
319 			while (!isgsep(*p))
320 				p++;		/* find separator */
321 			if (*p != '\0') {
322 				*p = '\0';
323 				if (u != p)
324 					add_group(u, g);
325 				p++;
326 			} else {
327 				if (u != p)
328 					add_group(u, g);
329 			}
330 			u = p;
331 		}
332 	}
333 }
334 
335 static void
336 print_passwd_group(int qflag, char *domain)
337 {
338 	struct user *u, *p;
339 	int i;
340 
341 	u = root;
342 
343 	while (u != NULL) {
344 		p = root;
345 		while (p->usr_uid != u->usr_uid)
346 			p = p->next;
347 
348 		if (p != u) {
349 			if (!qflag) {
350 				fprintf(stderr, "mknetid: unix.%d@%s %s\n",
351 				    u->usr_uid, domain,
352 				    "multiply defined, other definitions ignored");
353 			}
354 		} else {
355 			printf("unix.%d@%s %d:%d",
356 			    u->usr_uid, domain, u->usr_uid, u->usr_gid);
357 			if (u->gid_count >= 0) {
358 				i = 0;
359 				while (i <= u->gid_count) {
360 					printf(",%d", u->gid[i]);
361 					i++;
362 				}
363 			}
364 			printf("\n");
365 		}
366 		u = u->next;
367 	}
368 }
369 
370 static void
371 print_hosts(FILE *pfile, char *fname, char *domain)
372 {
373 	char  line[1024], *p, *u;
374 	int   line_no = 0, len;
375 
376 	while (read_line(pfile, line, sizeof(line))) {
377 		line_no++;
378 		len = strlen(line);
379 
380 		if (len > 0) {
381 			if (line[0] == '#')
382 				continue;
383 		}
384 
385 		/*
386 		 * Check if we have the whole line
387 		 */
388 		if (line[len-1] != '\n') {
389 			fprintf(stderr, "line %d in \"%s\" is too long\n",
390 			    line_no, fname);
391 		} else {
392 			line[len-1] = '\0';
393 		}
394 
395 		p = (char *)&line;
396 
397 		while (!isspace((unsigned char)*p))
398 			p++;			/* find first "space" */
399 		while (isspace((unsigned char)*p))
400 			*p++ = '\0';		/* replace space with <NUL> */
401 
402 		u = p;
403 		while (p != NULL) {
404 			if (*p == '\0') {
405 				p = NULL;
406 			} else {
407 				if (!isspace((unsigned char)*p)) {
408 					p++;
409 				} else {
410 					*p = '\0';
411 					p = NULL;
412 				}
413 			}
414 		}
415 
416 		printf("unix.%s@%s 0:%s\n", u, domain, u);
417 	}
418 }
419 
420 static void
421 print_netid(FILE *mfile, char *fname)
422 {
423 	char  line[1024], *p, *k, *u;
424 	int   line_no = 0, len;
425 
426 	while (read_line(mfile, line, sizeof(line))) {
427 		line_no++;
428 		len = strlen(line);
429 
430 		if (len > 0) {
431 			if (line[0] == '#')
432 				continue;
433 		}
434 
435 		/*
436 		 * Check if we have the whole line
437 		 */
438 		if (line[len-1] != '\n') {
439 			fprintf(stderr, "line %d in \"%s\" is too long\n",
440 			    line_no, fname);
441 		} else {
442 			line[len-1] = '\0';
443 		}
444 
445 		p = (char *)&line;
446 
447 		k = p;				/* save start of key  */
448 		while (!isspace((unsigned char)*p))
449 			p++;			/* find first "space" */
450 		while (isspace((unsigned char)*p))
451 			*p++ = '\0';		/* replace space with <NUL> */
452 
453 		u = p;
454 		while (p != NULL) {
455 			if (*p == '\0') {
456 				p = NULL;
457 			} else {
458 				if (!isspace((unsigned char)*p)) {
459 					p++;
460 				} else {
461 					*p = '\0';
462 					p = NULL;
463 				}
464 			}
465 		}
466 
467 		printf("%s %s\n", k, u);
468 	}
469 }
470 
471 static void
472 usage(void)
473 {
474 	fprintf(stderr, "usage: mknetid [-q] [-d domain] [-g groupfile] "
475 	    "[-h hostfile] [-m netidfile]\n"
476 	    "               [-P master.passwdfile] [-p passwdfile]\n");
477 	exit(1);
478 }
479 
480 int
481 main(int argc, char *argv[])
482 {
483 	FILE   *pfile, *gfile, *hfile, *mfile;
484 	int	qflag = 0, ch;
485 	char   *domain = NULL;
486 
487 	while ((ch = getopt(argc, argv, "d:g:h:m:p:P:q")) != -1)
488 		switch (ch) {
489 		case 'd':
490 			domain = optarg;
491 			break;
492 		case 'g':
493 			GroupFile = optarg;
494 			break;
495 		case 'h':
496 			HostFile = optarg;
497 			break;
498 		case 'm':
499 			NetidFile = optarg;
500 			break;
501 		case 'p':
502 			PasswdFile = optarg;
503 			break;
504 		case 'P':
505 			MasterPasswdFile = optarg;
506 			break;
507 		case 'q':
508 			qflag = 1;
509 			break;
510 		default:
511 			usage();
512 			break;
513 		}
514 
515 	if (argc > optind)
516 		usage();
517 
518 	if (domain == NULL)
519 		yp_get_default_domain(&domain);
520 
521 	pfile = fopen(PasswdFile, "r");
522 	if (pfile == NULL)
523 		pfile = fopen(MasterPasswdFile, "r");
524 	if (pfile == NULL)
525 		err(1, "%s", MasterPasswdFile);
526 
527 	gfile = fopen(GroupFile, "r");
528 	if (gfile == NULL)
529 		err(1, "%s", GroupFile);
530 
531 	hfile = fopen(HostFile, "r");
532 	if (hfile == NULL)
533 		err(1, "%s", HostFile);
534 
535 	mfile = fopen(NetidFile, "r");
536 
537 	read_passwd(pfile, PasswdFile);
538 	read_group(gfile, GroupFile);
539 
540 	print_passwd_group(qflag, domain);
541 	print_hosts(hfile, HostFile, domain);
542 
543 	if (mfile != NULL)
544 		print_netid(mfile, NetidFile);
545 
546 	return 0;
547 }
548