1/*	$OpenBSD: yp.c,v 1.14 2015/02/11 01:26:00 pelikan Exp $ */
2/*	$FreeBSD: stable/11/usr.sbin/ypldap/yp.c 351694 2019-09-02 10:20:57Z kib $ */
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		xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
220		xdr_result = (xdrproc_t) xdr_ypresp_all;
221		if (yp_check(req) == -1)
222			return;
223		cb = (void *)ypproc_all_2_svc;
224		break;
225	case YPPROC_MASTER:
226		log_debug("ypproc_master");
227		xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
228		xdr_result = (xdrproc_t) xdr_ypresp_master;
229		if (yp_check(req) == -1)
230			return;
231		cb = (void *)ypproc_master_2_svc;
232		break;
233	case YPPROC_ORDER:
234		log_debug("ypproc_order");
235		if (yp_check(req) == -1)
236			return;
237		svcerr_noproc(trans);
238		return;
239	case YPPROC_MAPLIST:
240		log_debug("ypproc_maplist");
241		xdr_argument = (xdrproc_t) xdr_domainname;
242		xdr_result = (xdrproc_t) xdr_ypresp_maplist;
243		if (yp_check(req) == -1)
244			return;
245		cb = (void *)ypproc_maplist_2_svc;
246		break;
247	default:
248		svcerr_noproc(trans);
249		return;
250	}
251	(void)memset(&argument, 0, sizeof(argument));
252
253	if (!svc_getargs(trans, xdr_argument, (caddr_t)&argument)) {
254		svcerr_decode(trans);
255		return;
256	}
257	result = (*cb)((char *)&argument, req);
258	if (result != NULL && !svc_sendreply(trans, xdr_result, result))
259		svcerr_systemerr(trans);
260	if (!svc_freeargs(trans, xdr_argument, (caddr_t)&argument)) {
261		/*
262		 * ypserv does it too.
263		 */
264		fatal("unable to free arguments");
265	}
266}
267
268int
269yp_check(struct svc_req *req)
270{
271	struct sockaddr_in	*caller;
272
273	caller = svc_getcaller(req->rq_xprt);
274	/*
275	 * We might want to know who we allow here.
276	 */
277	return (0);
278}
279
280int
281yp_valid_domain(char *domain, struct ypresp_val *res)
282{
283	if (domain == NULL) {
284		log_debug("NULL domain !");
285		return (-1);
286	}
287	if (strcmp(domain, env->sc_domainname) != 0) {
288		res->stat = YP_NODOM;
289		return (-1);
290	}
291	return (0);
292}
293
294bool_t *
295ypproc_domain_2_svc(domainname *arg, struct svc_req *req)
296{
297	static bool_t	res;
298
299	res = (bool_t)1;
300	if (strcmp(*arg, env->sc_domainname) != 0)
301		res = (bool_t)0;
302	return (&res);
303}
304
305bool_t *
306ypproc_domain_nonack_2_svc(domainname *arg, struct svc_req *req)
307{
308	static bool_t	res;
309
310	if (strcmp(*arg, env->sc_domainname) != 0)
311		return NULL;
312	res = (bool_t)1;
313	return (&res);
314}
315
316ypresp_val *
317ypproc_match_2_svc(ypreq_key *arg, struct svc_req *req)
318{
319	struct userent		 ukey;
320	struct userent		*ue;
321	struct groupent		 gkey;
322	struct groupent		*ge;
323	static struct ypresp_val res;
324	const char		*estr;
325	char			*bp, *cp;
326	char			 *key;
327
328	log_debug("matching '%.*s' in map %s", arg->key.keydat_len,
329	   arg->key.keydat_val, arg->map);
330
331	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
332		return (&res);
333
334	if (env->sc_user_names == NULL) {
335		/*
336		 * tree not ready.
337		 */
338		return (NULL);
339	}
340
341	if (arg->key.keydat_len > YPMAXRECORD) {
342		log_debug("argument too long");
343		return (NULL);
344	}
345	key = calloc(arg->key.keydat_len + 1, 1);
346	if (key == NULL)
347		return (NULL);
348	(void)strncpy(key, arg->key.keydat_val, arg->key.keydat_len);
349
350	if (strcmp(arg->map, "passwd.byname") == 0 ||
351	    strcmp(arg->map, "master.passwd.byname") == 0) {
352		ukey.ue_line = key;
353		if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
354		    &ukey)) == NULL) {
355			res.stat = YP_NOKEY;
356			goto out;
357		}
358
359		yp_make_val(&res, ue->ue_line, 1);
360		goto out;
361	} else if (strcmp(arg->map, "passwd.byuid") == 0 ||
362		   strcmp(arg->map, "master.passwd.byuid") == 0) {
363		ukey.ue_uid = strtonum(key, 0, UID_MAX, &estr);
364		if (estr) {
365			res.stat = YP_BADARGS;
366			goto out;
367		}
368
369		if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
370		    &ukey)) == NULL) {
371			res.stat = YP_NOKEY;
372			goto out;
373		}
374
375		yp_make_val(&res, ue->ue_line, 1);
376		return (&res);
377	} else if (strcmp(arg->map, "group.bygid") == 0) {
378		gkey.ge_gid = strtonum(key, 0, GID_MAX, &estr);
379		if (estr) {
380			res.stat = YP_BADARGS;
381			goto out;
382		}
383		if ((ge = RB_FIND(group_gid_tree, &env->sc_group_gids,
384		    &gkey)) == NULL) {
385			res.stat = YP_NOKEY;
386			goto out;
387		}
388
389		yp_make_val(&res, ge->ge_line, 1);
390		return (&res);
391	} else if (strcmp(arg->map, "group.byname") == 0) {
392		gkey.ge_line = key;
393		if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
394		    &gkey)) == NULL) {
395			res.stat = YP_NOKEY;
396			goto out;
397		}
398
399		yp_make_val(&res, ge->ge_line, 1);
400		return (&res);
401	} else if (strcmp(arg->map, "netid.byname") == 0) {
402		bp = cp = key;
403
404		if (strncmp(bp, "unix.", strlen("unix.")) != 0) {
405			res.stat = YP_BADARGS;
406			goto out;
407		}
408
409		bp += strlen("unix.");
410
411		if (*bp == '\0') {
412			res.stat = YP_BADARGS;
413			goto out;
414		}
415
416		if (!(cp = strsep(&bp, "@"))) {
417			res.stat = YP_BADARGS;
418			goto out;
419		}
420
421		if (strcmp(bp, arg->domain) != 0) {
422			res.stat = YP_BADARGS;
423			goto out;
424		}
425
426		ukey.ue_uid = strtonum(cp, 0, UID_MAX, &estr);
427		if (estr) {
428			res.stat = YP_BADARGS;
429			goto out;
430		}
431
432		if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
433		    &ukey)) == NULL) {
434			res.stat = YP_NOKEY;
435			goto out;
436		}
437
438		yp_make_val(&res, ue->ue_netid_line, 0);
439		goto out;
440
441	} else {
442		log_debug("unknown map %s", arg->map);
443		res.stat = YP_NOMAP;
444		goto out;
445	}
446out:
447	free(key);
448	return (&res);
449}
450
451ypresp_key_val *
452ypproc_first_2_svc(ypreq_nokey *arg, struct svc_req *req)
453{
454	static struct ypresp_key_val	res;
455
456	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
457		return (&res);
458
459	if (strcmp(arg->map, "passwd.byname") == 0 ||
460	    strcmp(arg->map, "master.passwd.byname") == 0) {
461		if (env->sc_user_lines == NULL)
462			return (NULL);
463
464		yp_make_keyval(&res, env->sc_user_lines, env->sc_user_lines);
465	} else if (strcmp(arg->map, "group.byname") == 0) {
466		if (env->sc_group_lines == NULL)
467			return (NULL);
468
469		yp_make_keyval(&res, env->sc_group_lines, env->sc_group_lines);
470	} else {
471		log_debug("unknown map %s", arg->map);
472		res.stat = YP_NOMAP;
473	}
474
475	return (&res);
476}
477
478ypresp_key_val *
479ypproc_next_2_svc(ypreq_key *arg, struct svc_req *req)
480{
481	struct userent			 ukey;
482	struct userent			*ue;
483	struct groupent			 gkey;
484	struct groupent			*ge;
485	char				*line;
486	static struct ypresp_key_val	 res;
487	char				 *key;
488
489	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
490		return (&res);
491
492	key = NULL;
493	if (strcmp(arg->map, "passwd.byname") == 0 ||
494	    strcmp(arg->map, "master.passwd.byname") == 0) {
495		key = calloc(arg->key.keydat_len + 1, 1);
496		if (key == NULL) {
497			res.stat = YP_YPERR;
498			return (&res);
499		}
500		(void)strncpy(key, arg->key.keydat_val,
501		    arg->key.keydat_len);
502		ukey.ue_line = key;
503		if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
504		    &ukey)) == NULL) {
505			/*
506			 * canacar's trick:
507			 * the user might have been deleted in between calls
508			 * to next since the tree may be modified by a reload.
509			 * next should still return the next user in
510			 * lexicographical order, hence insert the search key
511			 * and look up the next field, then remove it again.
512			 */
513			RB_INSERT(user_name_tree, env->sc_user_names, &ukey);
514			if ((ue = RB_NEXT(user_name_tree, &env->sc_user_names,
515			    &ukey)) == NULL) {
516				RB_REMOVE(user_name_tree, env->sc_user_names,
517				    &ukey);
518				res.stat = YP_NOKEY;
519				free(key);
520				return (&res);
521			}
522			RB_REMOVE(user_name_tree, env->sc_user_names, &ukey);
523		}
524		line = ue->ue_line + (strlen(ue->ue_line) + 1);
525		line = line + (strlen(line) + 1);
526		yp_make_keyval(&res, line, line);
527		free(key);
528		return (&res);
529
530
531	} else if (strcmp(arg->map, "group.byname") == 0) {
532		key = calloc(arg->key.keydat_len + 1, 1);
533		if (key == NULL) {
534			res.stat = YP_YPERR;
535			return (&res);
536		}
537		(void)strncpy(key, arg->key.keydat_val,
538		    arg->key.keydat_len);
539
540		gkey.ge_line = key;
541		if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
542		    &gkey)) == NULL) {
543			/*
544			 * canacar's trick reloaded.
545			 */
546			RB_INSERT(group_name_tree, env->sc_group_names, &gkey);
547			if ((ge = RB_NEXT(group_name_tree, &env->sc_group_names,
548			    &gkey)) == NULL) {
549				RB_REMOVE(group_name_tree, env->sc_group_names,
550				    &gkey);
551				res.stat = YP_NOKEY;
552				free(key);
553				return (&res);
554			}
555			RB_REMOVE(group_name_tree, env->sc_group_names, &gkey);
556		}
557
558		line = ge->ge_line + (strlen(ge->ge_line) + 1);
559		line = line + (strlen(line) + 1);
560		yp_make_keyval(&res, line, line);
561		free(key);
562		return (&res);
563	} else {
564		log_debug("unknown map %s", arg->map);
565		res.stat = YP_NOMAP;
566		return (&res);
567	}
568}
569
570ypresp_all *
571ypproc_all_2_svc(ypreq_nokey *arg, struct svc_req *req)
572{
573	static struct ypresp_all	res;
574
575	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
576		return (&res);
577
578	svcerr_auth(req->rq_xprt, AUTH_FAILED);
579	return (NULL);
580}
581
582ypresp_master *
583ypproc_master_2_svc(ypreq_nokey *arg, struct svc_req *req)
584{
585	static struct ypresp_master	 res;
586	static char master[YPMAXPEER + 1];
587
588	memset(&res, 0, sizeof(res));
589	if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
590		return (&res);
591
592	if (gethostname(master, sizeof(master)) == 0) {
593		res.peer = (peername)master;
594		res.stat = YP_TRUE;
595	} else
596		res.stat = YP_NOKEY;
597
598	return (&res);
599}
600
601ypresp_maplist *
602ypproc_maplist_2_svc(domainname *arg, struct svc_req *req)
603{
604	size_t			 i;
605	static struct {
606		char		*name;
607		int		 cond;
608	}			 mapnames[] = {
609		{ "passwd.byname",		YPMAP_PASSWD_BYNAME },
610		{ "passwd.byuid",		YPMAP_PASSWD_BYUID },
611		{ "master.passwd.byname",	YPMAP_MASTER_PASSWD_BYNAME },
612		{ "master.passwd.byuid",	YPMAP_MASTER_PASSWD_BYUID },
613		{ "group.byname",		YPMAP_GROUP_BYNAME },
614		{ "group.bygid",		YPMAP_GROUP_BYGID },
615		{ "netid.byname",		YPMAP_NETID_BYNAME },
616	};
617	static ypresp_maplist	 res;
618	static struct ypmaplist	 maps[nitems(mapnames)];
619
620	if (yp_valid_domain(*arg, (struct ypresp_val *)&res) == -1)
621		return (&res);
622
623	res.stat = YP_TRUE;
624	res.maps = NULL;
625	for (i = 0; i < nitems(mapnames); i++) {
626		if (!(env->sc_flags & mapnames[i].cond))
627			continue;
628		maps[i].map = mapnames[i].name;
629		maps[i].next = res.maps;
630		res.maps = &maps[i];
631	}
632
633	return (&res);
634}
635
636void
637yp_make_val(struct ypresp_val *res, char *line, int replacecolon)
638{
639	static char		 buf[LINE_WIDTH];
640
641	memset(buf, 0, sizeof(buf));
642
643	if (replacecolon)
644		line[strlen(line)] = ':';
645	(void)strlcpy(buf, line, sizeof(buf));
646	if (replacecolon)
647		line[strcspn(line, ":")] = '\0';
648	log_debug("sending out %s", buf);
649
650	res->stat = YP_TRUE;
651	res->val.valdat_len = strlen(buf);
652	res->val.valdat_val = buf;
653}
654
655void
656yp_make_keyval(struct ypresp_key_val *res, char *key, char *line)
657{
658	static char	keybuf[YPMAXRECORD+1];
659	static char	buf[LINE_WIDTH];
660
661	memset(keybuf, 0, sizeof(keybuf));
662	memset(buf, 0, sizeof(buf));
663
664	(void)strlcpy(keybuf, key, sizeof(keybuf));
665	res->key.keydat_len = strlen(keybuf);
666	res->key.keydat_val = keybuf;
667
668	if (*line == '\0') {
669		res->stat = YP_NOMORE;
670		return;
671	}
672	res->stat = YP_TRUE;
673	line[strlen(line)] = ':';
674	(void)strlcpy(buf, line, sizeof(buf));
675	line[strcspn(line, ":")] = '\0';
676	log_debug("sending out %s => %s", keybuf, buf);
677
678	res->val.valdat_len = strlen(buf);
679	res->val.valdat_val = buf;
680}
681