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