xref: /freebsd/usr.sbin/ypldap/yp.c (revision 895f86f15fbf6540071feb9328c3c50ed1f027b8)
1 /*	$OpenBSD: yp.c,v 1.14 2015/02/11 01:26:00 pelikan Exp $ */
2 /*	$FreeBSD$ */
3 /*
4  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/param.h>
21 #include <sys/queue.h>
22 #include <sys/socket.h>
23 #include <sys/select.h>
24 #include <sys/tree.h>
25 
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 
29 #include <errno.h>
30 #include <event.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <pwd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <limits.h>
38 
39 #include <rpc/rpc.h>
40 #include <rpc/xdr.h>
41 #include <rpc/pmap_clnt.h>
42 #include <rpc/pmap_prot.h>
43 #include <rpc/pmap_rmt.h>
44 #include <rpcsvc/yp.h>
45 #include <rpcsvc/ypclnt.h>
46 
47 #include "ypldap.h"
48 
49 void	yp_dispatch(struct svc_req *, SVCXPRT *);
50 void	yp_disable_events(void);
51 void	yp_fd_event(int, short, void *);
52 int	yp_check(struct svc_req *);
53 int	yp_valid_domain(char *, struct ypresp_val *);
54 void	yp_make_val(struct ypresp_val *, char *, int);
55 void	yp_make_keyval(struct ypresp_key_val *, char *, char *);
56 
57 static struct env	*env;
58 
59 struct yp_event {
60 	TAILQ_ENTRY(yp_event)	 ye_entry;
61 	struct event		 ye_event;
62 };
63 
64 struct yp_data {
65 	SVCXPRT			*yp_trans_udp;
66 	SVCXPRT			*yp_trans_tcp;
67 	TAILQ_HEAD(, yp_event)	 yd_events;
68 };
69 
70 void
71 yp_disable_events(void)
72 {
73 	struct yp_event	*ye;
74 
75 	while ((ye = TAILQ_FIRST(&env->sc_yp->yd_events)) != NULL) {
76 		TAILQ_REMOVE(&env->sc_yp->yd_events, ye, ye_entry);
77 		event_del(&ye->ye_event);
78 		free(ye);
79 	}
80 }
81 
82 void
83 yp_enable_events(void)
84 {
85 	int i;
86 	extern fd_set	svc_fdset;
87 	struct yp_event	*ye;
88 
89 	for (i = 0; i < getdtablesize(); i++) {
90 		if (FD_ISSET(i, &svc_fdset)) {
91 			if ((ye = calloc(1, sizeof(*ye))) == NULL)
92 				fatal(NULL);
93 			event_set(&ye->ye_event, i, EV_READ, yp_fd_event, NULL);
94 			event_add(&ye->ye_event, NULL);
95 			TAILQ_INSERT_TAIL(&env->sc_yp->yd_events, ye, ye_entry);
96 		}
97 	}
98 }
99 
100 void
101 yp_fd_event(int fd, short event, void *p)
102 {
103 	svc_getreq_common(fd);
104 	yp_disable_events();
105 	yp_enable_events();
106 }
107 
108 void
109 yp_init(struct env *x_env)
110 {
111 	struct yp_data	*yp;
112 
113 	if ((yp = calloc(1, sizeof(*yp))) == NULL)
114 		fatal(NULL);
115 	TAILQ_INIT(&yp->yd_events);
116 
117 	env = x_env;
118 	env->sc_yp = yp;
119 
120 	(void)pmap_unset(YPPROG, YPVERS);
121 
122 	if ((yp->yp_trans_udp = svcudp_create(RPC_ANYSOCK)) == NULL)
123 		fatal("cannot create udp service");
124 	if ((yp->yp_trans_tcp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL)
125 		fatal("cannot create tcp service");
126 
127 	if (!svc_register(yp->yp_trans_udp, YPPROG, YPVERS,
128 	    yp_dispatch, IPPROTO_UDP)) {
129 		fatal("unable to register (YPPROG, YPVERS, udp)");
130 	}
131 	if (!svc_register(yp->yp_trans_tcp, YPPROG, YPVERS,
132 	    yp_dispatch, IPPROTO_TCP)) {
133 		fatal("unable to register (YPPROG, YPVERS, tcp)");
134 	}
135 }
136 
137 /*
138  * lots of inspiration from ypserv by Mats O Jansson
139  */
140 void
141 yp_dispatch(struct svc_req *req, SVCXPRT *trans)
142 {
143 	xdrproc_t		 xdr_argument;
144 	xdrproc_t		 xdr_result;
145 	char			*result;
146 	char			*(*cb)(char *, struct svc_req *);
147         union {
148 		domainname	 ypproc_domain_2_arg;
149 		domainname	 ypproc_domain_nonack_2_arg;
150 		ypreq_key	 ypproc_match_2_arg;
151 		ypreq_nokey	 ypproc_first_2_arg;
152 		ypreq_key	 ypproc_next_2_arg;
153 		ypreq_xfr	 ypproc_xfr_2_arg;
154 		ypreq_nokey	 ypproc_all_2_arg;
155 		ypreq_nokey	 ypproc_master_2_arg;
156 		ypreq_nokey	 ypproc_order_2_arg;
157 		domainname	 ypproc_maplist_2_arg;
158 	} argument;
159 
160 	xdr_argument = (xdrproc_t) xdr_void;
161 	xdr_result = (xdrproc_t) xdr_void;
162 	cb = NULL;
163 	switch (req->rq_proc) {
164 	case YPPROC_NULL:
165 		xdr_argument = (xdrproc_t) xdr_void;
166 		xdr_result = (xdrproc_t) xdr_void;
167 		if (yp_check(req) == -1)
168 			return;
169 		result = NULL;
170 		if (!svc_sendreply(trans, (xdrproc_t) xdr_void,
171 		    (void *)&result))
172 			svcerr_systemerr(trans);
173 		return;
174 	case YPPROC_DOMAIN:
175 		xdr_argument = (xdrproc_t) xdr_domainname;
176 		xdr_result = (xdrproc_t) xdr_bool;
177 		if (yp_check(req) == -1)
178 			return;
179 		cb = (void *)ypproc_domain_2_svc;
180 		break;
181 	case YPPROC_DOMAIN_NONACK:
182 		xdr_argument = (xdrproc_t) xdr_domainname;
183 		xdr_result = (xdrproc_t) xdr_bool;
184 		if (yp_check(req) == -1)
185 			return;
186 		cb = (void *)ypproc_domain_nonack_2_svc;
187 		break;
188 	case YPPROC_MATCH:
189 		xdr_argument = (xdrproc_t) xdr_ypreq_key;
190 		xdr_result = (xdrproc_t) xdr_ypresp_val;
191 		if (yp_check(req) == -1)
192 			return;
193 		cb = (void *)ypproc_match_2_svc;
194 		break;
195 	case YPPROC_FIRST:
196 		xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
197 		xdr_result = (xdrproc_t) xdr_ypresp_key_val;
198 		if (yp_check(req) == -1)
199 			return;
200 		cb = (void *)ypproc_first_2_svc;
201 		break;
202 	case YPPROC_NEXT:
203 		xdr_argument = (xdrproc_t) xdr_ypreq_key;
204 		xdr_result = (xdrproc_t) xdr_ypresp_key_val;
205 		if (yp_check(req) == -1)
206 			return;
207 		cb = (void *)ypproc_next_2_svc;
208 		break;
209 	case YPPROC_XFR:
210 		if (yp_check(req) == -1)
211 			return;
212 		svcerr_noproc(trans);
213 		return;
214 	case YPPROC_CLEAR:
215 		log_debug("ypproc_clear");
216 		if (yp_check(req) == -1)
217 			return;
218 		svcerr_noproc(trans);
219 		return;
220 	case YPPROC_ALL:
221 		log_debug("ypproc_all");
222 		if (yp_check(req) == -1)
223 			return;
224 		cb = (void *)ypproc_all_2_svc;
225 		break;
226 	case YPPROC_MASTER:
227 		log_debug("ypproc_master");
228 		if (yp_check(req) == -1)
229 			return;
230 		cb = (void *)ypproc_master_2_svc;
231 		break;
232 	case YPPROC_ORDER:
233 		log_debug("ypproc_order");
234 		if (yp_check(req) == -1)
235 			return;
236 		svcerr_noproc(trans);
237 		return;
238 	case YPPROC_MAPLIST:
239 		log_debug("ypproc_maplist");
240 		if (yp_check(req) == -1)
241 			return;
242 		cb = (void *)ypproc_maplist_2_svc;
243 		break;
244 	default:
245 		svcerr_noproc(trans);
246 		return;
247 	}
248 	(void)memset(&argument, 0, sizeof(argument));
249 
250 	if (!svc_getargs(trans, xdr_argument, (caddr_t)&argument)) {
251 		svcerr_decode(trans);
252 		return;
253 	}
254 	result = (*cb)((char *)&argument, req);
255 	if (result != NULL && !svc_sendreply(trans, xdr_result, result))
256 		svcerr_systemerr(trans);
257 	if (!svc_freeargs(trans, xdr_argument, (caddr_t)&argument)) {
258 		/*
259 		 * ypserv does it too.
260 		 */
261 		fatal("unable to free arguments");
262 	}
263 }
264 
265 int
266 yp_check(struct svc_req *req)
267 {
268 	struct sockaddr_in	*caller;
269 
270 	caller = svc_getcaller(req->rq_xprt);
271 	/*
272 	 * We might want to know who we allow here.
273 	 */
274 	return (0);
275 }
276 
277 int
278 yp_valid_domain(char *domain, struct ypresp_val *res)
279 {
280 	if (domain == NULL) {
281 		log_debug("NULL domain !");
282 		return (-1);
283 	}
284 	if (strcmp(domain, env->sc_domainname) != 0) {
285 		res->stat = YP_NODOM;
286 		return (-1);
287 	}
288 	return (0);
289 }
290 
291 bool_t *
292 ypproc_domain_2_svc(domainname *arg, struct svc_req *req)
293 {
294 	static bool_t	res;
295 
296 	res = (bool_t)1;
297 	if (strcmp(*arg, env->sc_domainname) != 0)
298 		res = (bool_t)0;
299 	return (&res);
300 }
301 
302 bool_t *
303 ypproc_domain_nonack_2_svc(domainname *arg, struct svc_req *req)
304 {
305 	static bool_t	res;
306 
307 	if (strcmp(*arg, env->sc_domainname) != 0)
308 		return NULL;
309 	res = (bool_t)1;
310 	return (&res);
311 }
312 
313 ypresp_val *
314 ypproc_match_2_svc(ypreq_key *arg, struct svc_req *req)
315 {
316 	struct userent		 ukey;
317 	struct userent		*ue;
318 	struct groupent		 gkey;
319 	struct groupent		*ge;
320 	static struct ypresp_val res;
321 	const char		*estr;
322 	char			*bp, *cp;
323 	char			 key[YPMAXRECORD+1];
324 
325 	log_debug("matching '%.*s' in map %s", arg->key.keydat_len,
326 	   arg->key.keydat_val, arg->map);
327 
328 	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
329 		return (&res);
330 
331 	if (env->sc_user_names == NULL) {
332 		/*
333 		 * tree not ready.
334 		 */
335 		return (NULL);
336 	}
337 
338 	if (arg->key.keydat_len > YPMAXRECORD) {
339 		log_debug("argument too long");
340 		return (NULL);
341 	}
342 	bzero(key, sizeof(key));
343 	(void)strncpy(key, arg->key.keydat_val, arg->key.keydat_len);
344 
345 	if (strcmp(arg->map, "passwd.byname") == 0 ||
346 	    strcmp(arg->map, "master.passwd.byname") == 0) {
347 		ukey.ue_line = key;
348 		if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
349 		    &ukey)) == NULL) {
350 			res.stat = YP_NOKEY;
351 			return (&res);
352 		}
353 
354 		yp_make_val(&res, ue->ue_line, 1);
355 		return (&res);
356 	} else if (strcmp(arg->map, "passwd.byuid") == 0 ||
357 		   strcmp(arg->map, "master.passwd.byuid") == 0) {
358 		ukey.ue_uid = strtonum(key, 0, UID_MAX, &estr);
359 		if (estr) {
360 			res.stat = YP_BADARGS;
361 			return (&res);
362 		}
363 
364 		if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
365 		    &ukey)) == NULL) {
366 			res.stat = YP_NOKEY;
367 			return (&res);
368 		}
369 
370 		yp_make_val(&res, ue->ue_line, 1);
371 		return (&res);
372 	} else if (strcmp(arg->map, "group.bygid") == 0) {
373 		gkey.ge_gid = strtonum(key, 0, GID_MAX, &estr);
374 		if (estr) {
375 			res.stat = YP_BADARGS;
376 			return (&res);
377 		}
378 		if ((ge = RB_FIND(group_gid_tree, &env->sc_group_gids,
379 		    &gkey)) == NULL) {
380 			res.stat = YP_NOKEY;
381 			return (&res);
382 		}
383 
384 		yp_make_val(&res, ge->ge_line, 1);
385 		return (&res);
386 	} else if (strcmp(arg->map, "group.byname") == 0) {
387 		gkey.ge_line = key;
388 		if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
389 		    &gkey)) == NULL) {
390 			res.stat = YP_NOKEY;
391 			return (&res);
392 		}
393 
394 		yp_make_val(&res, ge->ge_line, 1);
395 		return (&res);
396 	} else if (strcmp(arg->map, "netid.byname") == 0) {
397 		bp = cp = key;
398 
399 		if (strncmp(bp, "unix.", strlen("unix.")) != 0) {
400 			res.stat = YP_BADARGS;
401 			return (&res);
402 		}
403 
404 		bp += strlen("unix.");
405 
406 		if (*bp == '\0') {
407 			res.stat = YP_BADARGS;
408 			return (&res);
409 		}
410 
411 		if (!(cp = strsep(&bp, "@"))) {
412 			res.stat = YP_BADARGS;
413 			return (&res);
414 		}
415 
416 		if (strcmp(bp, arg->domain) != 0) {
417 			res.stat = YP_BADARGS;
418 			return (&res);
419 		}
420 
421 		ukey.ue_uid = strtonum(cp, 0, UID_MAX, &estr);
422 		if (estr) {
423 			res.stat = YP_BADARGS;
424 			return (&res);
425 		}
426 
427 		if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
428 		    &ukey)) == NULL) {
429 			res.stat = YP_NOKEY;
430 			return (&res);
431 		}
432 
433 		yp_make_val(&res, ue->ue_netid_line, 0);
434 		return (&res);
435 
436 	} else {
437 		log_debug("unknown map %s", arg->map);
438 		res.stat = YP_NOMAP;
439 		return (&res);
440 	}
441 }
442 
443 ypresp_key_val *
444 ypproc_first_2_svc(ypreq_nokey *arg, struct svc_req *req)
445 {
446 	static struct ypresp_key_val	res;
447 
448 	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
449 		return (&res);
450 
451 	if (strcmp(arg->map, "passwd.byname") == 0 ||
452 	    strcmp(arg->map, "master.passwd.byname") == 0) {
453 		if (env->sc_user_lines == NULL)
454 			return (NULL);
455 
456 		yp_make_keyval(&res, env->sc_user_lines, env->sc_user_lines);
457 	} else if (strcmp(arg->map, "group.byname") == 0) {
458 		if (env->sc_group_lines == NULL)
459 			return (NULL);
460 
461 		yp_make_keyval(&res, env->sc_group_lines, env->sc_group_lines);
462 	} else {
463 		log_debug("unknown map %s", arg->map);
464 		res.stat = YP_NOMAP;
465 	}
466 
467 	return (&res);
468 }
469 
470 ypresp_key_val *
471 ypproc_next_2_svc(ypreq_key *arg, struct svc_req *req)
472 {
473 	struct userent			 ukey;
474 	struct userent			*ue;
475 	struct groupent			 gkey;
476 	struct groupent			*ge;
477 	char				*line;
478 	static struct ypresp_key_val	 res;
479 	char				 key[YPMAXRECORD+1];
480 
481 	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
482 		return (&res);
483 
484 	if (strcmp(arg->map, "passwd.byname") == 0 ||
485 	    strcmp(arg->map, "master.passwd.byname") == 0) {
486 		bzero(key, sizeof(key));
487 		(void)strncpy(key, arg->key.keydat_val,
488 		    arg->key.keydat_len);
489 		ukey.ue_line = key;
490 		if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
491 		    &ukey)) == NULL) {
492 			/*
493 			 * canacar's trick:
494 			 * the user might have been deleted in between calls
495 			 * to next since the tree may be modified by a reload.
496 			 * next should still return the next user in
497 			 * lexicographical order, hence insert the search key
498 			 * and look up the next field, then remove it again.
499 			 */
500 			RB_INSERT(user_name_tree, env->sc_user_names, &ukey);
501 			if ((ue = RB_NEXT(user_name_tree, &env->sc_user_names,
502 			    &ukey)) == NULL) {
503 				RB_REMOVE(user_name_tree, env->sc_user_names,
504 				    &ukey);
505 				res.stat = YP_NOKEY;
506 				return (&res);
507 			}
508 			RB_REMOVE(user_name_tree, env->sc_user_names, &ukey);
509 		}
510 		line = ue->ue_line + (strlen(ue->ue_line) + 1);
511 		line = line + (strlen(line) + 1);
512 		yp_make_keyval(&res, line, line);
513 		return (&res);
514 
515 
516 	} else if (strcmp(arg->map, "group.byname") == 0) {
517 		bzero(key, sizeof(key));
518 		(void)strncpy(key, arg->key.keydat_val,
519 		    arg->key.keydat_len);
520 
521 		gkey.ge_line = key;
522 		if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
523 		    &gkey)) == NULL) {
524 			/*
525 			 * canacar's trick reloaded.
526 			 */
527 			RB_INSERT(group_name_tree, env->sc_group_names, &gkey);
528 			if ((ge = RB_NEXT(group_name_tree, &env->sc_group_names,
529 			    &gkey)) == NULL) {
530 				RB_REMOVE(group_name_tree, env->sc_group_names,
531 				    &gkey);
532 				res.stat = YP_NOKEY;
533 				return (&res);
534 			}
535 			RB_REMOVE(group_name_tree, env->sc_group_names, &gkey);
536 		}
537 
538 		line = ge->ge_line + (strlen(ge->ge_line) + 1);
539 		line = line + (strlen(line) + 1);
540 		yp_make_keyval(&res, line, line);
541 		return (&res);
542 	} else {
543 		log_debug("unknown map %s", arg->map);
544 		res.stat = YP_NOMAP;
545 		return (&res);
546 	}
547 }
548 
549 ypresp_all *
550 ypproc_all_2_svc(ypreq_nokey *arg, struct svc_req *req)
551 {
552 	static struct ypresp_all	res;
553 
554 	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
555 		return (&res);
556 
557 	svcerr_auth(req->rq_xprt, AUTH_FAILED);
558 	return (NULL);
559 }
560 
561 ypresp_master *
562 ypproc_master_2_svc(ypreq_nokey *arg, struct svc_req *req)
563 {
564 	static struct ypresp_master	 res;
565 
566 	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
567 		return (&res);
568 
569 	res.stat = YP_YPERR;
570 	return (&res);
571 }
572 
573 ypresp_maplist *
574 ypproc_maplist_2_svc(domainname *arg, struct svc_req *req)
575 {
576 	size_t			 i;
577 	static struct {
578 		char		*name;
579 		int		 cond;
580 	}			 mapnames[] = {
581 		{ "passwd.byname",		YPMAP_PASSWD_BYNAME },
582 		{ "passwd.byuid",		YPMAP_PASSWD_BYUID },
583 		{ "master.passwd.byname",	YPMAP_MASTER_PASSWD_BYNAME },
584 		{ "master.passwd.byuid",	YPMAP_MASTER_PASSWD_BYUID },
585 		{ "group.byname",		YPMAP_GROUP_BYNAME },
586 		{ "group.bygid",		YPMAP_GROUP_BYGID },
587 		{ "netid.byname",		YPMAP_NETID_BYNAME },
588 	};
589 	static ypresp_maplist	 res;
590 	static struct ypmaplist	 maps[sizeof(mapnames) / sizeof(mapnames[0])];
591 
592 	if (yp_valid_domain(*arg, (struct ypresp_val *)&res) == -1)
593 		return (&res);
594 
595 	res.stat = YP_TRUE;
596 	res.maps = NULL;
597 	for (i = 0; i < sizeof(mapnames) / sizeof(mapnames[0]); i++) {
598 		if (!(env->sc_flags & mapnames[i].cond))
599 			continue;
600 		maps[i].map = mapnames[i].name;
601 		maps[i].next = res.maps;
602 		res.maps = &maps[i];
603 	}
604 
605 	return (&res);
606 }
607 
608 void
609 yp_make_val(struct ypresp_val *res, char *line, int replacecolon)
610 {
611 	static char		 buf[LINE_WIDTH];
612 
613 	bzero(buf, sizeof(buf));
614 
615 	if (replacecolon)
616 		line[strlen(line)] = ':';
617 	(void)strlcpy(buf, line, sizeof(buf));
618 	if (replacecolon)
619 		line[strcspn(line, ":")] = '\0';
620 	log_debug("sending out %s", buf);
621 
622 	res->stat = YP_TRUE;
623 	res->val.valdat_len = strlen(buf);
624 	res->val.valdat_val = buf;
625 }
626 
627 void
628 yp_make_keyval(struct ypresp_key_val *res, char *key, char *line)
629 {
630 	static char	keybuf[YPMAXRECORD+1];
631 	static char	buf[LINE_WIDTH];
632 
633 	bzero(keybuf, sizeof(keybuf));
634 	bzero(buf, sizeof(buf));
635 
636 	(void)strlcpy(keybuf, key, sizeof(keybuf));
637 	res->key.keydat_len = strlen(keybuf);
638 	res->key.keydat_val = keybuf;
639 
640 	if (*line == '\0') {
641 		res->stat = YP_NOMORE;
642 		return;
643 	}
644 	res->stat = YP_TRUE;
645 	line[strlen(line)] = ':';
646 	(void)strlcpy(buf, line, sizeof(buf));
647 	line[strcspn(line, ":")] = '\0';
648 	log_debug("sending out %s => %s", keybuf, buf);
649 
650 	res->val.valdat_len = strlen(buf);
651 	res->val.valdat_val = buf;
652 }
653