diff options
author | Pierre-Yves Ritschard <pyr@cvs.openbsd.org> | 2008-06-26 15:10:02 +0000 |
---|---|---|
committer | Pierre-Yves Ritschard <pyr@cvs.openbsd.org> | 2008-06-26 15:10:02 +0000 |
commit | db7ed9171b204082a9a59a7529c792f5fc6c4195 (patch) | |
tree | 94a1d4f857193e40e3a0cdcda403def567517c90 /usr.sbin/ypldap/yp.c | |
parent | 8fe473a53d6b9c0830712ec6bfff4f6aea72cb38 (diff) |
ypldap -- Intended to be a drop-in replacement for ypserv, gluing in a
LDAP directory and thus providing support for users and groups stored in
LDAP for the get{pw,gr}ent family of functions.
As of now it relies on external LDAP libraries, choose the one of your
liking though openldap would do fine. Not linked to the builds until
some things are sorted out, having our own LDAP client code for
instance, better support for group membership lookup as well.
Remember to sync with the latest master.passwd and group files as well
to have the _ypldap user available, which is needed.
``just get it in'' deraadt@
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; +} |