diff options
Diffstat (limited to 'usr.sbin/ypldap/yp.c')
-rw-r--r-- | usr.sbin/ypldap/yp.c | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/usr.sbin/ypldap/yp.c b/usr.sbin/ypldap/yp.c new file mode 100644 index 00000000000..c882f80625f --- /dev/null +++ b/usr.sbin/ypldap/yp.c @@ -0,0 +1,614 @@ +/* $OpenBSD: yp.c,v 1.1 2008/06/26 15:10:01 pyr Exp $ */ +/* + * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/tree.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <event.h> +#include <fcntl.h> +#include <unistd.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpc/pmap_clnt.h> +#include <rpc/pmap_prot.h> +#include <rpc/pmap_rmt.h> +#include <rpcsvc/yp.h> +#include <rpcsvc/ypclnt.h> + +#include "ypldap.h" + +void yp_dispatch(struct svc_req *, SVCXPRT *); +void yp_disable_events(void); +void yp_fd_event(int, short, void *); +int yp_check(struct svc_req *); +int yp_valid_domain(char *, struct ypresp_val *); +void yp_make_val(struct ypresp_val *, char *); +void yp_make_keyval(struct ypresp_key_val *, char *, char *); + +static struct env *env; + +struct yp_event { + TAILQ_ENTRY(yp_event) ye_entry; + struct event ye_event; +}; + +struct yp_data { + SVCXPRT *yp_trans_udp; + SVCXPRT *yp_trans_tcp; + TAILQ_HEAD(, yp_event) yd_events; +}; + +void +yp_disable_events(void) +{ + struct yp_event *ye; + + while ((ye = TAILQ_FIRST(&env->sc_yp->yd_events)) != NULL) { + TAILQ_REMOVE(&env->sc_yp->yd_events, ye, ye_entry); + event_del(&ye->ye_event); + free(ye); + } +} + +void +yp_enable_events(void) +{ + int i; + extern fd_set *__svc_fdset; + extern int __svc_fdsetsize; + struct yp_event *ye; + + for (i = 0; i < __svc_fdsetsize; i++) { + if (FD_ISSET(i, __svc_fdset)) { + if ((ye = calloc(1, sizeof(*ye))) == NULL) + fatal(NULL); + event_set(&ye->ye_event, i, EV_READ, yp_fd_event, NULL); + event_add(&ye->ye_event, NULL); + } + } +} + +void +yp_fd_event(int fd, short event, void *p) +{ + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + svc_getreqset(&fdset); + yp_disable_events(); + yp_enable_events(); +} + +void +yp_init(struct env *x_env) +{ + struct yp_data *yp; + + if ((yp = calloc(1, sizeof(*yp))) == NULL) + fatal(NULL); + TAILQ_INIT(&yp->yd_events); + + env = x_env; + env->sc_yp = yp; + + (void)pmap_unset(YPPROG, YPVERS); + + if ((yp->yp_trans_udp = svcudp_create(RPC_ANYSOCK)) == NULL) + fatal("cannot create udp service"); + if ((yp->yp_trans_tcp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) + fatal("cannot create tcp service"); + + if (!svc_register(yp->yp_trans_udp, YPPROG, YPVERS, + yp_dispatch, IPPROTO_UDP)) { + fatal("unable to register (YPPROG, YPVERS, udp)"); + } + if (!svc_register(yp->yp_trans_tcp, YPPROG, YPVERS, + yp_dispatch, IPPROTO_TCP)) { + fatal("unable to register (YPPROG, YPVERS, tcp)"); + } +} + +/* + * lots of inspiration from ypserv by Mats O Jansson + */ +void +yp_dispatch(struct svc_req *req, SVCXPRT *trans) +{ + xdrproc_t xdr_argument; + xdrproc_t xdr_result; + char *result; + char *(*cb)(char *, struct svc_req *); + union { + domainname ypproc_domain_2_arg; + domainname ypproc_domain_nonack_2_arg; + ypreq_key ypproc_match_2_arg; + ypreq_nokey ypproc_first_2_arg; + ypreq_key ypproc_next_2_arg; + ypreq_xfr ypproc_xfr_2_arg; + ypreq_nokey ypproc_all_2_arg; + ypreq_nokey ypproc_master_2_arg; + ypreq_nokey ypproc_order_2_arg; + domainname ypproc_maplist_2_arg; + } argument; + + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_void; + cb = NULL; + switch (req->rq_proc) { + case YPPROC_NULL: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_void; + if (yp_check(req) == -1) + return; + result = NULL; + if (!svc_sendreply(trans, (xdrproc_t) xdr_void, + (void *)&result)) + svcerr_systemerr(trans); + return; + case YPPROC_DOMAIN: + xdr_argument = (xdrproc_t) xdr_domainname; + xdr_result = (xdrproc_t) xdr_bool; + if (yp_check(req) == -1) + return; + cb = (void *)ypproc_domain_2_svc; + break; + case YPPROC_DOMAIN_NONACK: + xdr_argument = (xdrproc_t) xdr_domainname; + xdr_result = (xdrproc_t) xdr_bool; + if (yp_check(req) == -1) + return; + cb = (void *)ypproc_domain_nonack_2_svc; + break; + case YPPROC_MATCH: + xdr_argument = (xdrproc_t) xdr_ypreq_key; + xdr_result = (xdrproc_t) xdr_ypresp_val; + if (yp_check(req) == -1) + return; + cb = (void *)ypproc_match_2_svc; + break; + case YPPROC_FIRST: + xdr_argument = (xdrproc_t) xdr_ypreq_nokey; + xdr_result = (xdrproc_t) xdr_ypresp_key_val; + if (yp_check(req) == -1) + return; + cb = (void *)ypproc_first_2_svc; + break; + case YPPROC_NEXT: + xdr_argument = (xdrproc_t) xdr_ypreq_key; + xdr_result = (xdrproc_t) xdr_ypresp_key_val; + if (yp_check(req) == -1) + return; + cb = (void *)ypproc_next_2_svc; + break; + case YPPROC_XFR: + if (yp_check(req) == -1) + return; + svcerr_noproc(trans); + break; + case YPPROC_CLEAR: + log_debug("ypproc_clear"); + if (yp_check(req) == -1) + return; + svcerr_noproc(trans); + return; + case YPPROC_ALL: + log_debug("ypproc_all"); + if (yp_check(req) == -1) + return; + cb = (void *)ypproc_all_2_svc; + break;; + case YPPROC_MASTER: + log_debug("ypproc_master"); + if (yp_check(req) == -1) + return; + cb = (void *)ypproc_master_2_svc; + break; + case YPPROC_ORDER: + log_debug("ypproc_order"); + if (yp_check(req) == -1) + return; + svcerr_noproc(trans); + return; + case YPPROC_MAPLIST: + log_debug("ypproc_maplist"); + if (yp_check(req) == -1) + return; + cb = (void *)ypproc_maplist_2_svc; + break; + default: + svcerr_noproc(trans); + return; + } + (void)memset(&argument, 0, sizeof(argument)); + + if (!svc_getargs(trans, xdr_argument, (caddr_t)&argument)) { + svcerr_decode(trans); + return; + } + result = (*cb)((char *)&argument, req); + if (result != NULL && !svc_sendreply(trans, xdr_result, result)) + svcerr_systemerr(trans); + if (!svc_freeargs(trans, xdr_argument, (caddr_t)&argument)) { + /* + * ypserv does it too. + */ + fatal("unable to free arguments"); + } +} + +int +yp_check(struct svc_req *req) +{ + struct sockaddr_in *caller; + + caller = svc_getcaller(req->rq_xprt); + /* + * We might want to know who we allow here. + */ + return (0); +} + +int +yp_valid_domain(char *domain, struct ypresp_val *res) +{ + if (domain == NULL) { + log_debug("NULL domain !"); + return (-1); + } + if (strcmp(domain, env->sc_domainname) != 0) { + res->stat = YP_NODOM; + return (-1); + } + return (0); +} + +bool_t * +ypproc_domain_2_svc(domainname *arg, struct svc_req *req) +{ + static bool_t res; + + res = (bool_t)1; + if (strcmp(*arg, env->sc_domainname) != 0) + res = (bool_t)0; + return (&res); +} + +bool_t * +ypproc_domain_nonack_2_svc(domainname *arg, struct svc_req *req) +{ + static bool_t res; + + if (strcmp(*arg, env->sc_domainname) != 0) + return NULL; + res = (bool_t)1; + return (&res); +} + +ypresp_val * +ypproc_match_2_svc(ypreq_key *arg, struct svc_req *req) +{ + struct userent ukey; + struct userent *ue; + struct groupent gkey; + struct groupent *ge; + static struct ypresp_val res; + const char *estr; + char key[_PW_NAME_LEN+1]; + + if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1) + return (&res); + + if (env->sc_user_names == NULL) { + /* + * tree not ready. + */ + return (NULL); + } + if (strcmp(arg->map, "passwd.byname") == 0 || + strcmp(arg->map, "master.passwd.byname") == 0) { + bzero(key, sizeof(key)); + (void)strncpy(key, arg->key.keydat_val, + arg->key.keydat_len); + + ukey.ue_line = key; + if ((ue = RB_FIND(user_name_tree, env->sc_user_names, + &ukey)) == NULL) { + res.stat = YP_NOKEY; + return (&res); + } + + yp_make_val(&res, ue->ue_line); + return (&res); + } else if (strcmp(arg->map, "passwd.byuid") == 0) { + bzero(key, sizeof(key)); + (void)strncpy(key, arg->key.keydat_val, + arg->key.keydat_len); + ukey.ue_uid = strtonum(key, 0, UID_MAX, &estr); + if (estr) { + res.stat = YP_BADARGS; + return (&res); + } + + if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids, + &ukey)) == NULL) { + res.stat = YP_NOKEY; + return (&res); + } + + yp_make_val(&res, ue->ue_line); + return (&res); + } else if (strcmp(arg->map, "group.bygid") == 0) { + bzero(key, sizeof(key)); + (void)strncpy(key, arg->key.keydat_val, + arg->key.keydat_len); + gkey.ge_gid = strtonum(key, 0, GID_MAX, &estr); + if (estr) { + res.stat = YP_BADARGS; + return (&res); + } + if ((ge = RB_FIND(group_gid_tree, &env->sc_group_gids, + &gkey)) == NULL) { + res.stat = YP_NOKEY; + return (&res); + } + + yp_make_val(&res, ge->ge_line); + return (&res); + } else if (strcmp(arg->map, "group.byname") == 0) { + bzero(key, sizeof(key)); + (void)strncpy(key, arg->key.keydat_val, + arg->key.keydat_len); + + gkey.ge_line = key; + if ((ge = RB_FIND(group_name_tree, env->sc_group_names, + &gkey)) == NULL) { + res.stat = YP_NOKEY; + return (&res); + } + + yp_make_val(&res, ge->ge_line); + return (&res); + } else { + log_debug("unknown map %s", arg->map); + res.stat = YP_NOMAP; + return (&res); + } +} + +ypresp_key_val * +ypproc_first_2_svc(ypreq_nokey *arg, struct svc_req *req) +{ + static struct ypresp_key_val res; + + if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1) + return (&res); + + if (strcmp(arg->map, "passwd.byname") == 0 || + strcmp(arg->map, "master.passwd.byname") == 0) { + if (env->sc_user_lines == NULL) + return (NULL); + + yp_make_keyval(&res, env->sc_user_lines, env->sc_user_lines); + return (&res); + } else if (strcmp(arg->map, "group.byname") == 0) { + if (env->sc_group_lines == NULL) + return (NULL); + + yp_make_keyval(&res, env->sc_group_lines, env->sc_group_lines); + return (&res); + } else { + log_debug("unknown map %s", arg->map); + res.stat = YP_NOMAP; + return (&res); + } + return (NULL); +} + +ypresp_key_val * +ypproc_next_2_svc(ypreq_key *arg, struct svc_req *req) +{ + struct userent ukey; + struct userent *ue; + struct groupent gkey; + struct groupent *ge; + char *line; + static struct ypresp_key_val res; + char key[_PW_NAME_LEN+1]; + + if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1) + return (&res); + + if (strcmp(arg->map, "passwd.byname") == 0 || + strcmp(arg->map, "master.passwd.byname") == 0) { + bzero(key, sizeof(key)); + (void)strncpy(key, arg->key.keydat_val, + arg->key.keydat_len); + ukey.ue_line = key; + if ((ue = RB_FIND(user_name_tree, env->sc_user_names, + &ukey)) == NULL) { + /* + * canacar's trick: + * the user might have been deleted in between calls to + * to next since the tree may be modified by a reload. + * next should still return the next user in + * lexicographical order, hence insert the search key + * and look up the next field, then remove it again. + */ + RB_INSERT(user_name_tree, env->sc_user_names, &ukey); + if ((ue = RB_NEXT(user_name_tree, &env->sc_user_names, + &ukey)) == NULL) { + RB_REMOVE(user_name_tree, env->sc_user_names, + &ukey); + res.stat = YP_NOKEY; + return (&res); + } + RB_REMOVE(user_name_tree, env->sc_user_names, &ukey); + } + line = ue->ue_line + (strlen(ue->ue_line) + 1); + line = line + (strlen(line) + 1); + yp_make_keyval(&res, line, line); + return (&res); + + + } else if (strcmp(arg->map, "group.byname") == 0) { + bzero(key, sizeof(key)); + (void)strncpy(key, arg->key.keydat_val, + arg->key.keydat_len); + + gkey.ge_line = key; + if ((ge = RB_FIND(group_name_tree, env->sc_group_names, + &gkey)) == NULL) { + /* + * canacar's trick reloaded. + */ + RB_INSERT(group_name_tree, env->sc_group_names, &gkey); + if ((ge = RB_NEXT(group_name_tree, &env->sc_group_names, + &gkey)) == NULL) { + RB_REMOVE(group_name_tree, env->sc_group_names, + &gkey); + res.stat = YP_NOKEY; + return (&res); + } + RB_REMOVE(group_name_tree, env->sc_group_names, &gkey); + } + + line = ge->ge_line + (strlen(ge->ge_line) + 1); + line = line + (strlen(line) + 1); + yp_make_keyval(&res, line, line); + return (&res); + } else { + log_debug("unknown map %s", arg->map); + res.stat = YP_NOMAP; + return (&res); + } +} + +ypresp_all * +ypproc_all_2_svc(ypreq_nokey *arg, struct svc_req *req) +{ + static struct ypresp_all res; + + if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1) + return (&res); + + svcerr_auth(req->rq_xprt, AUTH_FAILED); + return (NULL); +} + +ypresp_master * +ypproc_master_2_svc(ypreq_nokey *arg, struct svc_req *req) +{ + static struct ypresp_master res; + + if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1) + return (&res); + + res.stat = YP_YPERR; + return (&res); +} + +ypresp_maplist * +ypproc_maplist_2_svc(domainname *arg, struct svc_req *req) +{ + size_t i; + static struct { + char *name; + int cond; + } mapnames[6] = { + { "passwd.byname", YPMAP_PASSWD_BYNAME }, + { "passwd.byuid", YPMAP_PASSWD_BYUID }, + { "master.passwd.byname", YPMAP_MASTER_PASSWD_BYNAME }, + { "master.passwd.byuid", YPMAP_MASTER_PASSWD_BYUID }, + { "group.byname", YPMAP_GROUP_BYNAME }, + { "group.bygid", YPMAP_GROUP_BYGID }, + }; + static ypresp_maplist res; + static struct ypmaplist maps[sizeof(mapnames) / sizeof(mapnames[0])]; + + if (yp_valid_domain(*arg, (struct ypresp_val *)&res) == -1) + return (&res); + + res.stat = YP_TRUE; + res.maps = NULL; + for (i = 0; i < sizeof(mapnames) / sizeof(mapnames[0]); i++) { + if (!(env->sc_flags & mapnames[i].cond)) + continue; + maps[i].map = mapnames[i].name; + maps[i].next = res.maps; + res.maps = &maps[i]; + } + + return (&res); +} + +void +yp_make_val(struct ypresp_val *res, char *line) +{ + static char buf[LINE_WIDTH]; + + bzero(buf, sizeof(buf)); + + line[strlen(line)] = ':'; + (void)strlcpy(buf, line, sizeof(buf)); + line[strcspn(line, ":")] = '\0'; + log_debug("sending out %s", buf); + + res->stat = YP_TRUE; + res->val.valdat_len = strlen(buf); + res->val.valdat_val = buf; +} + +void +yp_make_keyval(struct ypresp_key_val *res, char *key, char *line) +{ + static char keybuf[_PW_NAME_LEN+1]; + static char buf[LINE_WIDTH]; + + bzero(keybuf, sizeof(keybuf)); + bzero(buf, sizeof(buf)); + + (void)strlcpy(keybuf, key, sizeof(keybuf)); + res->key.keydat_len = strlen(keybuf); + res->key.keydat_val = keybuf; + + if (*line == '\0') { + res->stat = YP_NOMORE; + return; + } + res->stat = YP_TRUE; + line[strlen(line)] = ':'; + (void)strlcpy(buf, line, sizeof(buf)); + line[strcspn(line, ":")] = '\0'; + log_debug("sending out %s => %s", keybuf, buf); + + res->val.valdat_len = strlen(buf); + res->val.valdat_val = buf; +} |