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
48 void yp_dispatch(struct svc_req *, SVCXPRT *);
49 void yp_disable_events(void);
50 void yp_fd_event(int, short, void *);
51 int yp_check(struct svc_req *);
52 int yp_valid_domain(char *, struct ypresp_val *);
53 void yp_make_val(struct ypresp_val *, char *, int);
54 void yp_make_keyval(struct ypresp_key_val *, char *, char *);
55
56 static struct env *env;
57
58 struct yp_event {
59 TAILQ_ENTRY(yp_event) ye_entry;
60 struct event ye_event;
61 };
62
63 struct yp_data {
64 SVCXPRT *yp_trans_udp;
65 SVCXPRT *yp_trans_tcp;
66 TAILQ_HEAD(, yp_event) yd_events;
67 };
68
69 void
yp_disable_events(void)70 yp_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
81 void
yp_enable_events(void)82 yp_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
96 void
yp_fd_event(int fd,short event,void * p)97 yp_fd_event(int fd, short event, void *p)
98 {
99 svc_getreq_common(fd);
100 yp_disable_events();
101 yp_enable_events();
102 }
103
104 void
yp_init(struct env * x_env)105 yp_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 */
136 void
yp_dispatch(struct svc_req * req,SVCXPRT * trans)137 yp_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
267 int
yp_check(struct svc_req * req)268 yp_check(struct svc_req *req)
269 {
270 /*
271 * We might want to know who we allow here.
272 */
273 return (0);
274 }
275
276 int
yp_valid_domain(char * domain,struct ypresp_val * res)277 yp_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
290 bool_t *
ypproc_domain_2_svc(domainname * arg,struct svc_req * req)291 ypproc_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
301 bool_t *
ypproc_domain_nonack_2_svc(domainname * arg,struct svc_req * req)302 ypproc_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
312 ypresp_val *
ypproc_match_2_svc(ypreq_key * arg,struct svc_req * req)313 ypproc_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 }
442 out:
443 free(key);
444 return (&res);
445 }
446
447 ypresp_key_val *
ypproc_first_2_svc(ypreq_nokey * arg,struct svc_req * req)448 ypproc_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
474 ypresp_key_val *
ypproc_next_2_svc(ypreq_key * arg,struct svc_req * req)475 ypproc_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
566 ypresp_all *
ypproc_all_2_svc(ypreq_nokey * arg,struct svc_req * req)567 ypproc_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
578 ypresp_master *
ypproc_master_2_svc(ypreq_nokey * arg,struct svc_req * req)579 ypproc_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
597 ypresp_maplist *
ypproc_maplist_2_svc(domainname * arg,struct svc_req * req)598 ypproc_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
632 void
yp_make_val(struct ypresp_val * res,char * line,int replacecolon)633 yp_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
651 void
yp_make_keyval(struct ypresp_key_val * res,char * key,char * line)652 yp_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