yp.c revision 290931
1/*	$OpenBSD: yp.c,v 1.14 2015/02/11 01:26:00 pelikan Exp $ */
2/*	$FreeBSD: head/usr.sbin/ypldap/yp.c 290931 2015-11-16 16:48:43Z rodrigc $ */
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/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	extern fd_set	*__svc_fdset;
86	extern int	 __svc_fdsetsize;
87	struct yp_event	*ye;
88
89	for (i = 0; i < __svc_fdsetsize; 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
100void
101yp_fd_event(int fd, short event, void *p)
102{
103	svc_getreq_common(fd);
104	yp_disable_events();
105	yp_enable_events();
106}
107
108void
109yp_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 */
140void
141yp_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
265int
266yp_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
277int
278yp_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
291bool_t *
292ypproc_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
302bool_t *
303ypproc_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
313ypresp_val *
314ypproc_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
443ypresp_key_val *
444ypproc_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
470ypresp_key_val *
471ypproc_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
549ypresp_all *
550ypproc_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
561ypresp_master *
562ypproc_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
573ypresp_maplist *
574ypproc_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
608void
609yp_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
627void
628yp_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