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