xref: /netbsd-src/usr.sbin/ypserv/mknetid/mknetid.c (revision 10ad5ffa714ce1a679dcc9dd8159648df2d67b5a)
1 /*	$NetBSD: mknetid.c,v 1.16 2009/04/19 06:06:40 lukem 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.16 2009/04/19 06:06:40 lukem 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 
57 #include <rpcsvc/ypclnt.h>
58 
59 #include "protos.h"
60 
61 struct user {
62 	char 	*usr_name;		/* user name */
63 	int	usr_uid;		/* user uid */
64 	int	usr_gid;		/* user gid */
65 	int	gid_count;		/* number of gids */
66 	int	gid[NGROUPS];		/* additional gids */
67 	TAILQ_ENTRY(user) read;		/* links in read order */
68 	TAILQ_ENTRY(user) hash;		/* links in hash order */
69 };
70 
71 #define HASHMAX 55
72 
73 void	add_group(const char *, const char *);
74 void	add_user(const char *, const char *, const char *);
75 int	hashidx(char);
76 int	isgsep(char);
77 int	main(int, char *[]);
78 void	print_hosts(const char *, const char *);
79 void	print_netid(const char *);
80 void	print_passwd_group(int, const char *);
81 void	read_group(const char *);
82 void	read_passwd(const char *);
83 void	usage(void);
84 
85 TAILQ_HEAD(user_list, user);
86 struct user_list root;
87 struct user_list hroot[HASHMAX];
88 
89 int
90 main(int argc, char *argv[])
91 {
92 	const char *HostFile = _PATH_HOSTS;
93 	const char *PasswdFile = _PATH_PASSWD;
94 	const char *GroupFile = _PATH_GROUP;
95 	const char *NetidFile = "/etc/netid";
96 
97 	int qflag, ch;
98 	char *domain;
99 
100 	TAILQ_INIT(&root);
101 	for (ch = 0; ch < HASHMAX; ch++)
102 		TAILQ_INIT((&hroot[ch]));
103 
104 	qflag = 0;
105 	domain = NULL;
106 
107 	while ((ch = getopt(argc, argv, "d:g:h:m:p:q")) != -1) {
108 		switch (ch) {
109 		case 'd':
110 			domain = optarg;
111 			break;
112 
113 		case 'g':
114 			GroupFile = optarg;
115 			break;
116 
117 		case 'h':
118 			HostFile = optarg;
119 			break;
120 
121 		case 'm':
122 			NetidFile = optarg;
123 			break;
124 
125 		case 'p':
126 			PasswdFile = optarg;
127 			break;
128 
129 		case 'q':
130 			qflag++;
131 			break;
132 
133 		default:
134 			usage();
135 		}
136 	}
137 	if (argc != optind)
138 		usage();
139 
140 	if (domain == NULL)
141 		if (yp_get_default_domain(&domain))
142 			errx(1, "Can't get YP domain name");
143 
144 	read_passwd(PasswdFile);
145 	read_group(GroupFile);
146 
147 	print_passwd_group(qflag, domain);
148 	print_hosts(HostFile, domain);
149 	print_netid(NetidFile);
150 
151 	exit (0);
152 }
153 
154 int
155 hashidx(char key)
156 {
157 	if (key < 'A')
158 		return(0);
159 
160 	if (key <= 'Z')
161 		return(1 + key - 'A');
162 
163 	if (key < 'a')
164 		return(27);
165 
166 	if (key <= 'z')
167 		return(28 + key - 'a');
168 
169 	return(54);
170 }
171 
172 void
173 add_user(const char *username, const char *uid, const char *gid)
174 {
175 	struct user *u;
176 	int idx;
177 
178 	idx = hashidx(username[0]);
179 
180 	u = (struct user *)malloc(sizeof(struct user));
181 	if (u == NULL)
182 		err(1, "can't allocate user");
183 	memset(u, 0, sizeof(struct user));
184 
185 	u->usr_name = strdup(username);
186 	if (u->usr_name == NULL)
187 		err(1, "can't allocate user name");
188 
189 	u->usr_uid = atoi(uid);
190 	u->usr_gid = atoi(gid);
191 	u->gid_count = -1;
192 
193 	TAILQ_INSERT_TAIL(&root, u, read);
194 	TAILQ_INSERT_TAIL((&hroot[idx]), u, hash);
195 }
196 
197 void
198 add_group(const char *username, const char *gid)
199 {
200 	struct user *u;
201 	int g, idx;
202 
203 	g = atoi(gid);
204 	idx = hashidx(username[0]);
205 
206 	for (u = hroot[idx].tqh_first;
207 	    u != NULL; u = u->hash.tqe_next) {
208 		if (strcmp(username, u->usr_name) == 0) {
209 			if (g != u->usr_gid) {
210 				u->gid_count++;
211 				if (u->gid_count < NGROUPS)
212 					u->gid[u->gid_count] = g;
213 			}
214 			return;
215 		}
216 	}
217 }
218 
219 void
220 read_passwd(const char *fname)
221 {
222 	FILE	*pfile;
223 	size_t	 line_no;
224 	int	 colon;
225 	size_t	 len;
226 	char	*line, *p, *k, *u, *g;
227 
228 	if ((pfile = fopen(fname, "r")) == NULL)
229 		err(1, "%s", fname);
230 
231 	line_no = 0;
232 	for (;
233 	    (line = fparseln(pfile, &len, &line_no, NULL, FPARSELN_UNESCALL));
234 	    free(line)) {
235 		if (len == 0) {
236 			warnx("%s line %lu: empty line", fname,
237 			    (unsigned long)line_no);
238 			continue;
239 		}
240 
241 		p = line;
242 		for (k = p, colon = 0; *k != '\0'; k++)
243 			if (*k == ':')
244 				colon++;
245 
246 		if (colon != 6) {
247 			warnx("%s line %lu: incorrect number of fields",
248 			    fname, (unsigned long)line_no);
249 			continue;
250 		}
251 
252 		k = p;
253 		p = strchr(p, ':');
254 		*p++ = '\0';
255 
256 		/* If it's a YP entry, skip it. */
257 		if (*k == '+' || *k == '-')
258 			continue;
259 
260 		/* terminate password */
261 		p = strchr(p, ':');
262 		*p++ = '\0';
263 
264 		/* terminate uid */
265 		u = p;
266 		p = strchr(p, ':');
267 		*p++ = '\0';
268 
269 		/* terminate gid */
270 		g = p;
271 		p = strchr(p, ':');
272 		*p++ = '\0';
273 
274 		add_user(k, u, g);
275 	}
276 	(void)fclose(pfile);
277 }
278 
279 int
280 isgsep(char ch)
281 {
282 
283 	switch (ch) {
284 	case ',':
285 	case ' ':
286 	case '\t':
287 	case '\0':
288 		return (1);
289 	}
290 
291 	return (0);
292 }
293 
294 void
295 read_group(const char *fname)
296 {
297 	FILE	*gfile;
298 	size_t	 line_no;
299 	int	 colon;
300 	size_t	 len;
301 	char	*line, *p, *k, *u, *g;
302 
303 	if ((gfile = fopen(fname, "r")) == NULL)
304 		err(1, "%s", fname);
305 
306 	line_no = 0;
307 	for (;
308 	    (line = fparseln(gfile, &len, &line_no, NULL, FPARSELN_UNESCALL));
309 	    free(line)) {
310 		if (len == 0) {
311 			warnx("%s line %lu: empty line", fname,
312 			    (unsigned long)line_no);
313 			continue;
314 		}
315 
316 		p = line;
317 		for (k = p, colon = 0; *k != '\0'; k++)
318 			if (*k == ':')
319 				colon++;
320 
321 		if (colon != 3) {
322 			warnx("%s line %lu: incorrect number of fields",
323 			    fname, (unsigned long)line_no);
324 			continue;
325 		}
326 
327 		/* terminate key */
328 		k = p;
329 		p = strchr(p, ':');
330 		*p++ = '\0';
331 
332 		if (*k == '+' || *k == '-')
333 			continue;
334 
335 		/* terminate password */
336 		p = strchr(p, ':');
337 		*p++ = '\0';
338 
339 		/* terminate gid */
340 		g = p;
341 		p = strchr(p, ':');
342 		*p++ = '\0';
343 
344 		/* get the group list */
345 		for (u = p; *u != '\0'; u = p) {
346 			/* find separator */
347 			for (; isgsep(*p) == 0; p++)
348 				;
349 
350 			if (*p != '\0') {
351 				*p = '\0';
352 				if (u != p)
353 					add_group(u, g);
354 				p++;
355 			} else if (u != p)
356 				add_group(u, g);
357 		}
358 	}
359 	(void)fclose(gfile);
360 }
361 
362 void
363 print_passwd_group(int qflag, const char *domain)
364 {
365 	struct user *u, *p;
366 	int i;
367 
368 	for (u = root.tqh_first; u != NULL; u = u->read.tqe_next) {
369 		for (p = root.tqh_first; p->usr_uid != u->usr_uid;
370 		    p = p->read.tqe_next)
371 			/* empty */ ;
372 		if (p != u) {
373 			if (!qflag) {
374 				warnx("unix.%d@%s %s", u->usr_uid, domain,
375 				 "multiply defined, ignoring duplicate");
376 			}
377 		} else {
378 			printf("unix.%d@%s %d:%d", u->usr_uid, domain,
379 			    u->usr_uid, u->usr_gid);
380 			if (u->gid_count >= 0)
381 				for (i = 0; i <= u->gid_count; i++)
382 					printf(",%d", u->gid[i]);
383 			printf("\n");
384 		}
385 	}
386 }
387 
388 void
389 print_hosts(const char *fname, const char *domain)
390 {
391 	FILE	*hfile;
392 	size_t	 len;
393 	char	*line, *p, *k, *u;
394 
395 	if ((hfile = fopen(fname, "r")) == NULL)
396 		err(1, "%s", fname);
397 
398 	for (;
399 	    (line = fparseln(hfile, &len, NULL, NULL, FPARSELN_UNESCALL));
400 	    free(line)) {
401 		if (len == 0)
402 			continue;
403 
404 		p = line;
405 		/* Find the key, replace trailing whitespace will <NUL> */
406 		for (k = p; *p && isspace((unsigned char)*p) == 0; p++)
407 			;
408 		while (*p && isspace((unsigned char)*p))
409 			*p++ = '\0';
410 
411 		/* Get first hostname. */
412 		for (u = p; *p && !isspace((unsigned char)*p); p++)
413 			;
414 		*p = '\0';
415 
416 		printf("unix.%s@%s 0:%s\n", u, domain, u);
417 	}
418 	(void) fclose(hfile);
419 }
420 
421 void
422 print_netid(const char *fname)
423 {
424 	FILE	*mfile;
425 	size_t	 len;
426 	char	*line, *p, *k, *u;
427 
428 	mfile = fopen(fname, "r");
429 	if (mfile == NULL)
430 		return;
431 
432 	for (;
433 	    (line = fparseln(mfile, &len, NULL, NULL, FPARSELN_UNESCALL));
434 	    free(line)) {
435 		if (len == 0)
436 			continue;
437 
438 		p = line;
439 		/* Find the key, replace trailing whitespace will <NUL> */
440 		for (k = p; *p && !isspace((unsigned char)*p); p++)
441 			;
442 		while (*p && isspace((unsigned char)*p))
443 			*p++ = '\0';
444 
445 		/* Get netid entry. */
446 		for (u = p; *p && !isspace((unsigned char)*p); p++)
447 			;
448 		*p = '\0';
449 
450 		printf("%s %s\n", k, u);
451 	}
452 }
453 
454 void
455 usage(void)
456 {
457 
458 	fprintf(stderr, "usage: %s %s\n", getprogname(),
459 	    "[-d domain] [-q] [-p passwdfile] [-g groupfile]");
460 	fprintf(stderr, "       %s  %s", getprogname(),
461 	    "[-g groupfile] [-h hostfile] [-m netidfile]");
462 	exit(1);
463 }
464