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