From 98531e56318b65eb1bb6883fdfe12e771d8a1efe Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 24 Feb 2009 19:28:40 -0500 Subject: Add PAM responder Also move responders under server/responder with shared code in server/responder/common Signed-off-by: Simo Sorce --- server/Makefile.in | 24 +- server/configure.ac | 2 + server/external/ldap.m4 | 51 + server/external/pam.m4 | 5 + server/nss/nsssrv.c | 589 -------- server/nss/nsssrv.h | 122 -- server/nss/nsssrv_cmd.c | 2237 --------------------------- server/nss/nsssrv_dp.c | 432 ------ server/nss/nsssrv_packet.c | 220 --- server/providers/data_provider.c | 274 ++++ server/providers/data_provider.h | 6 + server/providers/data_provider_be.c | 138 +- server/providers/dp_backend.h | 8 + server/providers/ldap_be.c | 676 +++++++++ server/providers/proxy.c | 132 +- server/responder/common/responder_cmd.c | 78 + server/responder/common/responder_cmd.h | 71 + server/responder/common/responder_common.c | 518 +++++++ server/responder/common/responder_common.h | 24 + server/responder/common/responder_dp.c | 123 ++ server/responder/common/responder_dp.h | 4 + server/responder/common/responder_packet.c | 220 +++ server/responder/common/responder_packet.h | 39 + server/responder/nss/nsssrv.c | 590 ++++++++ server/responder/nss/nsssrv.h | 118 ++ server/responder/nss/nsssrv_cmd.c | 2238 ++++++++++++++++++++++++++++ server/responder/nss/nsssrv_dp.c | 433 ++++++ server/responder/pam/pamsrv.c | 171 +++ server/responder/pam/pamsrv.h | 33 + server/responder/pam/pamsrv_cmd.c | 196 +++ server/responder/pam/pamsrv_dp.c | 215 +++ server/responder/pam/pamsrv_util.c | 16 + server/server.mk | 44 +- 33 files changed, 6431 insertions(+), 3616 deletions(-) create mode 100644 server/external/ldap.m4 create mode 100644 server/external/pam.m4 delete mode 100644 server/nss/nsssrv.c delete mode 100644 server/nss/nsssrv.h delete mode 100644 server/nss/nsssrv_cmd.c delete mode 100644 server/nss/nsssrv_dp.c delete mode 100644 server/nss/nsssrv_packet.c create mode 100644 server/providers/ldap_be.c create mode 100644 server/responder/common/responder_cmd.c create mode 100644 server/responder/common/responder_cmd.h create mode 100644 server/responder/common/responder_common.c create mode 100644 server/responder/common/responder_common.h create mode 100644 server/responder/common/responder_dp.c create mode 100644 server/responder/common/responder_dp.h create mode 100644 server/responder/common/responder_packet.c create mode 100644 server/responder/common/responder_packet.h create mode 100644 server/responder/nss/nsssrv.c create mode 100644 server/responder/nss/nsssrv.h create mode 100644 server/responder/nss/nsssrv_cmd.c create mode 100644 server/responder/nss/nsssrv_dp.c create mode 100644 server/responder/pam/pamsrv.c create mode 100644 server/responder/pam/pamsrv.h create mode 100644 server/responder/pam/pamsrv_cmd.c create mode 100644 server/responder/pam/pamsrv_dp.c create mode 100644 server/responder/pam/pamsrv_util.c diff --git a/server/Makefile.in b/server/Makefile.in index 1cde271ef..158202ca2 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -43,6 +43,16 @@ DBUS_CFLAGS = @DBUS_CFLAGS@ CHECK_LIBS = @CHECK_LIBS@ CHECK_CFLAGS = @CHECK_CFLAGS@ +PAM_LIBS = @PAM_LIBS@ + +OPENLDAP_LIBS = @OPENLDAP_LIBS@ + +MOZLDAP_CFLAGS = @MOZLDAP_CFLAGS@ +MOZLDAP_LIBS = @MOZLDAP_LIBS@ + +LDAP_CFLAGS = $(MOZLDAP_CFLAGS) $(OPENLDAP_CFLAGS) +LDAP_LIBS = $(MOZLDAP_LIBS) $(OPENLDAP_LIBS) + LIBDL = @LIBDL@ SHLIBEXT = @SHLIBEXT@ @@ -75,6 +85,10 @@ PROXY_BE_SOBASE=libsss_proxy.$(SHLIBEXT) PROXY_BE_SONAME=$(PROXY_BE_SOBASE).0 PROXY_BE_SOLIB=$(PROXY_BE_SOBASE).$(PACKAGE_VERSION) +LDAP_BE_SOBASE=libsss_ldap.$(SHLIBEXT) +LDAP_BE_SONAME=$(LDAP_BE_SOBASE).0 +LDAP_BE_SOLIB=$(LDAP_BE_SOBASE).$(PACKAGE_VERSION) + default: all include $(srvdir)/rules.mk @@ -86,7 +100,7 @@ headers = DBUS_SYSBUS_POLICY_DIR = @sysconfdir@/dbus-1/system.d -LIBEXECBINS = sbin/sssd_nss sbin/sssd_dp sbin/sssd_be +LIBEXECBINS = sbin/sssd_nss sbin/sssd_dp sbin/sssd_be sbin/sssd_pam ifneq (x$(HAVE_INFOPIPE), x) LIBEXECBINS += sbin/sssd_info DBUS_SYSBUS_POLICIES = infopipe/org.freeipa.sssd.infopipe.conf @@ -100,9 +114,9 @@ BINS = sbin/sssd $(LIBEXECBINS) TESTS = tests/sysdb-tests tests/infopipe-tests TESTS_DATA = tests/tests.ldb tests/tests_conf.ldb tests/introspect.ref -SOLIBS = lib/$(MEMBEROF_SOLIB) lib/$(PROXY_BE_SOLIB) -SONAMELIBS = lib/$(PROXY_BE_SONAME) lib/$(MEMBEROF_SONAME) -SOBASELIBS = lib/$(PROXY_BE_SOBASE) lib/$(MEMBEROF_SOBASE) +SOLIBS = lib/$(MEMBEROF_SOLIB) lib/$(PROXY_BE_SOLIB) lib/$(LDAP_BE_SOLIB) +SONAMELIBS = lib/$(PROXY_BE_SONAME) lib/$(MEMBEROF_SONAME) lib/$(LDAP_BE_SONAME) +SOBASELIBS = lib/$(PROXY_BE_SOBASE) lib/$(MEMBEROF_SOBASE) lib/$(LDAP_BE_SOBASE) DIRS = sbin lib @@ -158,6 +172,8 @@ endif ${INSTALLCMD} -m 755 $(SOLIBS) $(DESTDIR)$(SSSD_LIBDIR) ln -fs $(PROXY_BE_SOLIB) $(DESTDIR)$(SSSD_LIBDIR)/$(PROXY_BE_SONAME) ln -fs $(PROXY_BE_SOLIB) $(DESTDIR)$(SSSD_LIBDIR)/$(PROXY_BE_SOBASE) + ln -fs $(LDAP_BE_SOLIB) $(DESTDIR)$(SSSD_LIBDIR)/$(LDAP_BE_SONAME) + ln -fs $(LDAP_BE_SOLIB) $(DESTDIR)$(SSSD_LIBDIR)/$(LDAP_BE_SOBASE) ln -fs $(MEMBEROF_SOLIB) $(DESTDIR)$(SSSD_LIBDIR)/$(MEMBEROF_SONAME) ln -fs $(MEMBEROF_SOLIB) $(DESTDIR)$(SSSD_LIBDIR)/$(MEMBEROF_SOBASE) diff --git a/server/configure.ac b/server/configure.ac index b2f73b2e1..a06dfdab9 100644 --- a/server/configure.ac +++ b/server/configure.ac @@ -50,6 +50,8 @@ m4_include(libtalloc.m4) m4_include(libtdb.m4) m4_include(libevents.m4) m4_include(libldb.m4) +m4_include(pam.m4) +m4_include(ldap.m4) m4_include(util/signal.m4) PKG_CHECK_MODULES([DBUS],[dbus-1]) diff --git a/server/external/ldap.m4 b/server/external/ldap.m4 new file mode 100644 index 000000000..535c28908 --- /dev/null +++ b/server/external/ldap.m4 @@ -0,0 +1,51 @@ +dnl AC_SUBST(LDAP_LIBS) +dnl +dnl AC_CHECK_HEADERS(lber.h ldap.h, , AC_MSG_ERROR("could not locate ldap header files please install devel package")) +dnl +dnl AC_CHECK_LIB(lber, main, LDAP_LIBS="-llber $LDAP_LIBS") +dnl AC_CHECK_LIB(ldap, main, LDAP_LIBS="-lldap $LDAP_LIBS") +dnl +dnl --------------------------------------------------------------------------- +dnl - Check for Mozilla LDAP or OpenLDAP SDK +dnl --------------------------------------------------------------------------- + +AC_ARG_WITH(openldap, [ --with-openldap Use OpenLDAP]) + +if test x$with_openldap = xyes; then + AC_CHECK_LIB(ldap, ldap_search, with_ldap=yes) + dnl Check for other libraries we need to link with to get the main routines. + test "$with_ldap" != "yes" && { AC_CHECK_LIB(ldap, ldap_open, [with_ldap=yes with_ldap_lber=yes], , -llber) } + test "$with_ldap" != "yes" && { AC_CHECK_LIB(ldap, ldap_open, [with_ldap=yes with_ldap_lber=yes with_ldap_krb=yes], , -llber -lkrb) } + test "$with_ldap" != "yes" && { AC_CHECK_LIB(ldap, ldap_open, [with_ldap=yes with_ldap_lber=yes with_ldap_krb=yes with_ldap_des=yes], , -llber -lkrb -ldes) } + dnl Recently, we need -lber even though the main routines are elsewhere, + dnl because otherwise be get link errors w.r.t. ber_pvt_opt_on. So just + dnl check for that (it's a variable not a fun but that doesn't seem to + dnl matter in these checks) and stick in -lber if so. Can't hurt (even to + dnl stick it in always shouldn't hurt, I don't think) ... #### Someone who + dnl #### understands LDAP needs to fix this properly. + test "$with_ldap_lber" != "yes" && { AC_CHECK_LIB(lber, ber_pvt_opt_on, with_ldap_lber=yes) } + + if test "$with_ldap" = "yes"; then + if test "$with_ldap_des" = "yes" ; then + OPENLDAP_LIBS="${OPENLDAP_LIBS} -ldes" + fi + if test "$with_ldap_krb" = "yes" ; then + OPENLDAP_LIBS="${OPENLDAP_LIBS} -lkrb" + fi + if test "$with_ldap_lber" = "yes" ; then + OPENLDAP_LIBS="${OPENLDAP_LIBS} -llber" + fi + OPENLDAP_LIBS="${OPENLDAP_LIBS} -lldap" + else + AC_MSG_ERROR([OpenLDAP not found]) + fi + + AC_SUBST(OPENLDAP_LIBS) +else + PKG_CHECK_MODULES(MOZLDAP, mozldap > 6) + MOZLDAP_CFLAGS="${MOZLDAP_CFLAGS} -DWITH_MOZLDAP" + AC_SUBST(MOZLDAP_CFLAGS) + AC_SUBST(MOZLDAP_LIBS) +fi + + diff --git a/server/external/pam.m4 b/server/external/pam.m4 new file mode 100644 index 000000000..f99a2e69b --- /dev/null +++ b/server/external/pam.m4 @@ -0,0 +1,5 @@ +AC_SUBST(PAM_LIBS) + +AC_CHECK_HEADERS(security/pam_appl.h security/pam_misc.h security/pam_modules.h) + +AC_CHECK_LIB(pam, pam_get_item, [ PAM_LIBS="-lpam" ]) diff --git a/server/nss/nsssrv.c b/server/nss/nsssrv.c deleted file mode 100644 index c48aed4a3..000000000 --- a/server/nss/nsssrv.c +++ /dev/null @@ -1,589 +0,0 @@ -/* - SSSD - - NSS Responder - - Copyright (C) Simo Sorce 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "popt.h" -#include "util/util.h" -#include "nss/nsssrv.h" -#include "db/sysdb.h" -#include "confdb/confdb.h" -#include "dbus/dbus.h" -#include "sbus/sssd_dbus.h" -#include "util/btreemap.h" -#include "providers/data_provider.h" -#include "monitor/monitor_sbus.h" -#include "monitor/monitor_interfaces.h" -#include "sbus/sbus_client.h" - -#define SSS_NSS_PIPE_NAME "nss" - -static int service_identity(DBusMessage *message, struct sbus_conn_ctx *sconn); -static int service_pong(DBusMessage *message, struct sbus_conn_ctx *sconn); -static int service_reload(DBusMessage *message, struct sbus_conn_ctx *sconn); -static int nss_init_domains(struct nss_ctx *nctx); -static int _domain_comparator(const void *key1, const void *key2); - -struct sbus_method nss_sbus_methods[] = { - {SERVICE_METHOD_IDENTITY, service_identity}, - {SERVICE_METHOD_PING, service_pong}, - {SERVICE_METHOD_RELOAD, service_reload}, - {NULL, NULL} -}; - -static void set_nonblocking(int fd) -{ - unsigned v; - v = fcntl(fd, F_GETFL, 0); - fcntl(fd, F_SETFL, v | O_NONBLOCK); -} - -static void set_close_on_exec(int fd) -{ - unsigned v; - v = fcntl(fd, F_GETFD, 0); - fcntl(fd, F_SETFD, v | FD_CLOEXEC); -} - -static int client_destructor(struct cli_ctx *ctx) -{ - if (ctx->cfd > 0) close(ctx->cfd); - return 0; -} - -static void client_send(struct event_context *ev, struct cli_ctx *cctx) -{ - int ret; - - ret = nss_packet_send(cctx->creq->out, cctx->cfd); - if (ret == EAGAIN) { - /* not all data was sent, loop again */ - return; - } - if (ret != EOK) { - DEBUG(0, ("Failed to read request, aborting client!\n")); - talloc_free(cctx); - return; - } - - /* ok all sent */ - EVENT_FD_NOT_WRITEABLE(cctx->cfde); - EVENT_FD_READABLE(cctx->cfde); - talloc_free(cctx->creq); - cctx->creq = NULL; - return; -} - -static void client_recv(struct event_context *ev, struct cli_ctx *cctx) -{ - int ret; - - if (!cctx->creq) { - cctx->creq = talloc_zero(cctx, struct cli_request); - if (!cctx->creq) { - DEBUG(0, ("Failed to alloc request, aborting client!\n")); - talloc_free(cctx); - return; - } - } - - if (!cctx->creq->in) { - ret = nss_packet_new(cctx->creq, NSS_PACKET_MAX_RECV_SIZE, - 0, &cctx->creq->in); - if (ret != EOK) { - DEBUG(0, ("Failed to alloc request, aborting client!\n")); - talloc_free(cctx); - return; - } - } - - ret = nss_packet_recv(cctx->creq->in, cctx->cfd); - switch (ret) { - case EOK: - /* do not read anymore */ - EVENT_FD_NOT_READABLE(cctx->cfde); - /* execute command */ - ret = nss_cmd_execute(cctx); - if (ret != EOK) { - DEBUG(0, ("Failed to execute request, aborting client!\n")); - talloc_free(cctx); - } - /* past this point cctx can be freed at any time by callbacks - * in case of error, do not use it */ - return; - - case EAGAIN: - /* need to read still some data, loop again */ - break; - - case EINVAL: - DEBUG(6, ("Invalid data from client, closing connection!\n")); - talloc_free(cctx); - break; - - case ENODATA: - DEBUG(5, ("Client disconnected!\n")); - talloc_free(cctx); - break; - - default: - DEBUG(6, ("Failed to read request, aborting client!\n")); - talloc_free(cctx); - } - - return; -} - -static void client_fd_handler(struct event_context *ev, - struct fd_event *fde, - uint16_t flags, void *ptr) -{ - struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx); - - if (flags & EVENT_FD_READ) { - client_recv(ev, cctx); - return; - } - if (flags & EVENT_FD_WRITE) { - client_send(ev, cctx); - return; - } -} - -static void accept_fd_handler(struct event_context *ev, - struct fd_event *fde, - uint16_t flags, void *ptr) -{ - /* accept and attach new event handler */ - struct nss_ctx *nctx = talloc_get_type(ptr, struct nss_ctx); - struct cli_ctx *cctx; - socklen_t len; - - cctx = talloc_zero(nctx, struct cli_ctx); - if (!cctx) { - struct sockaddr_un addr; - int fd; - DEBUG(0, ("Out of memory trying to setup client context!\n")); - /* accept and close to signal the client we have a problem */ - memset(&addr, 0, sizeof(addr)); - len = sizeof(addr); - fd = accept(nctx->lfd, (struct sockaddr *)&addr, &len); - if (fd == -1) { - return; - } - close(fd); - return; - } - - len = sizeof(cctx->addr); - cctx->cfd = accept(nctx->lfd, (struct sockaddr *)&cctx->addr, &len); - if (cctx->cfd == -1) { - DEBUG(1, ("Accept failed [%s]", strerror(errno))); - talloc_free(cctx); - return; - } - - cctx->cfde = event_add_fd(ev, cctx, cctx->cfd, - EVENT_FD_READ, client_fd_handler, cctx); - if (!cctx->cfde) { - close(cctx->cfd); - talloc_free(cctx); - DEBUG(2, ("Failed to queue client handler\n")); - } - - cctx->ev = ev; - cctx->nctx = nctx; - - talloc_set_destructor(cctx, client_destructor); - - DEBUG(4, ("Client connected!\n")); - - return; -} - -static int service_identity(DBusMessage *message, struct sbus_conn_ctx *sconn) -{ - dbus_uint16_t version = NSS_SBUS_SERVICE_VERSION; - const char *name = NSS_SBUS_SERVICE_NAME; - DBusMessage *reply; - dbus_bool_t ret; - - DEBUG(4,("Sending ID reply: (%s,%d)\n", - name, version)); - - reply = dbus_message_new_method_return(message); - if (!reply) return ENOMEM; - - ret = dbus_message_append_args(reply, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_UINT16, &version, - DBUS_TYPE_INVALID); - if (!ret) { - dbus_message_unref(reply); - return EIO; - } - - /* send reply back */ - sbus_conn_send_reply(sconn, reply); - dbus_message_unref(reply); - - return EOK; -} - -static int service_pong(DBusMessage *message, struct sbus_conn_ctx *sconn) -{ - DBusMessage *reply; - dbus_bool_t ret; - - reply = dbus_message_new_method_return(message); - if (!reply) return ENOMEM; - - ret = dbus_message_append_args(reply, DBUS_TYPE_INVALID); - if (!ret) { - dbus_message_unref(reply); - return EIO; - } - - /* send reply back */ - sbus_conn_send_reply(sconn, reply); - dbus_message_unref(reply); - - return EOK; -} - -static int service_reload(DBusMessage *message, struct sbus_conn_ctx *sconn) -{ - /* Monitor calls this function when we need to reload - * our configuration information. Perform whatever steps - * are needed to update the configuration objects. - */ - - /* Send an empty reply to acknowledge receipt */ - return service_pong(message, sconn); -} - -static int nss_sbus_init(struct nss_ctx *nctx) -{ - int ret; - char *sbus_address; - struct service_sbus_ctx *ss_ctx; - struct sbus_method_ctx *sm_ctx; - - /* Set up SBUS connection to the monitor */ - ret = monitor_get_sbus_address(nctx, nctx->cdb, &sbus_address); - if (ret != EOK) { - DEBUG(0, ("Could not locate monitor address.\n")); - return ret; - } - - ret = monitor_init_sbus_methods(nctx, nss_sbus_methods, &sm_ctx); - if (ret != EOK) { - DEBUG(0, ("Could not initialize SBUS methods.\n")); - return ret; - } - - ret = sbus_client_init(nctx, nctx->ev, - sbus_address, sm_ctx, - NULL /* Private Data */, - NULL /* Destructor */, - &ss_ctx); - if (ret != EOK) { - DEBUG(0, ("Failed to connect to monitor services.\n")); - return ret; - } - - /* Set up NSS-specific listeners */ - /* None currently used */ - - nctx->ss_ctx = ss_ctx; - - return EOK; -} - -/* create a unix socket and listen to it */ -static int set_unix_socket(struct nss_ctx *nctx) -{ - struct sockaddr_un addr; - char *default_pipe; - int ret; - - default_pipe = talloc_asprintf(nctx, "%s/%s", PIPE_PATH, SSS_NSS_PIPE_NAME); - if (!default_pipe) { - return ENOMEM; - } - - ret = confdb_get_string(nctx->cdb, nctx, - "config/services/nss", "unixSocket", - default_pipe, &nctx->sock_name); - if (ret != EOK) { - talloc_free(default_pipe); - return ret; - } - talloc_free(default_pipe); - - nctx->lfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (nctx->lfd == -1) { - return EIO; - } - - /* Set the umask so that permissions are set right on the socket. - * It must be readable and writable by anybody on the system. */ - umask(0111); - - set_nonblocking(nctx->lfd); - set_close_on_exec(nctx->lfd); - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, nctx->sock_name, sizeof(addr.sun_path)); - - /* make sure we have no old sockets around */ - unlink(nctx->sock_name); - - if (bind(nctx->lfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - DEBUG(0,("Unable to bind on socket '%s'\n", nctx->sock_name)); - goto failed; - } - if (listen(nctx->lfd, 10) != 0) { - DEBUG(0,("Unable to listen on socket '%s'\n", nctx->sock_name)); - goto failed; - } - - nctx->lfde = event_add_fd(nctx->ev, nctx, nctx->lfd, - EVENT_FD_READ, accept_fd_handler, nctx); - - /* we want default permissions on created files to be very strict, - so set our umask to 0177 */ - umask(0177); - return EOK; - -failed: - /* we want default permissions on created files to be very strict, - so set our umask to 0177 */ - umask(0177); - close(nctx->lfd); - return EIO; -} - -/* domain names are case insensitive for now - * NOTE: this function is not utf-8 safe, - * only ASCII names for now */ -static int _domain_comparator(const void *key1, const void *key2) -{ - return strcasecmp((const char *)key1, (const char *)key2); -} - -static int nss_init_domains(struct nss_ctx *nctx) -{ - char *path; - char **domains; - char *provider; - TALLOC_CTX *tmp_ctx; - struct nss_domain_info *info; - int ret, i, c; - int retval; - - tmp_ctx = talloc_new(nctx); - ret = confdb_get_domains(nctx->cdb, tmp_ctx, &domains); - if (ret != EOK) { - retval = ret; - goto done; - } - - i = 0; - c = 0; - while (domains[i] != NULL) { - DEBUG(3, ("Adding domain %s to the map\n", domains[i])); - - path = talloc_asprintf(tmp_ctx, "config/domains/%s", domains[i]); - if (!path) { - retval = ENOMEM; - goto done; - } - - /* alloc on tmp_ctx, it will be stolen by btreemap_set_value */ - info = talloc_zero(tmp_ctx, struct nss_domain_info); - if (!info) { - retval = ENOMEM; - goto done; - } - - /* Build the basedn for this domain */ - info->basedn = talloc_asprintf(info, SYSDB_DOM_BASE, domains[i]); - DEBUG(3, ("BaseDN: %s\n", info->basedn)); - - ret = confdb_get_int(nctx->cdb, tmp_ctx, path, - "enumerate", false, &(info->enumerate)); - if (ret != EOK) { - DEBUG(0, ("Failed to fetch enumerate for [%s]!\n", domains[i])); - } - - ret = confdb_get_bool(nctx->cdb, tmp_ctx, path, - "legacy", false, &(info->legacy)); - if (ret != EOK) { - DEBUG(0, ("Failed to fetch legacy for [%s]!\n", domains[i])); - } - - ret = confdb_get_string(nctx->cdb, tmp_ctx, path, "provider", - NULL, &provider); - if (ret != EOK) { - DEBUG(0, ("Failed to fetch provider for [%s]!\n", domains[i])); - } - if (provider) info->has_provider = true; - - ret = btreemap_set_value(nctx, &nctx->domain_map, - domains[i], info, - _domain_comparator); - if (ret != EOK) { - DEBUG(1, ("Failed to store domain info, aborting!\n")); - retval = ret; - goto done; - } - - i++; - c++; - } - if (c == 0) { - /* No domains configured! - * Note: this should never happen, since LOCAL should - * always be configured */ - DEBUG(0, ("No domains configured on this client!\n")); - retval = EINVAL; - goto done; - } - - ret = confdb_get_string(nctx->cdb, nctx, - "config/domains", "default", - NULL, &nctx->default_domain); - if (ret != EOK) { - retval = ret; - goto done; - } - - retval = EOK; - -done: - talloc_free(tmp_ctx); - return retval; -} - -int nss_process_init(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct confdb_ctx *cdb) -{ - struct nss_ctx *nctx; - int ret; - - nctx = talloc_zero(mem_ctx, struct nss_ctx); - if (!nctx) { - DEBUG(0, ("fatal error initializing nss_ctx\n")); - return ENOMEM; - } - nctx->ev = ev; - nctx->cdb = cdb; - - ret = nss_init_domains(nctx); - if (ret != EOK) { - DEBUG(0, ("fatal error setting up domain map\n")); - return ret; - } - - ret = nss_sbus_init(nctx); - if (ret != EOK) { - DEBUG(0, ("fatal error setting up message bus\n")); - return ret; - } - - ret = nss_dp_init(nctx); - if (ret != EOK) { - DEBUG(0, ("fatal error setting up backend connector\n")); - return ret; - } - - ret = sysdb_init(nctx, ev, cdb, NULL, &nctx->sysdb); - if (ret != EOK) { - DEBUG(0, ("fatal error initializing nss_ctx\n")); - return ret; - } - - /* after all initializations we are ready to listen on our socket */ - ret = set_unix_socket(nctx); - if (ret != EOK) { - DEBUG(0, ("fatal error initializing socket\n")); - return ret; - } - - nctx->cache_timeout = 600; /* FIXME: read from conf */ - - DEBUG(1, ("NSS Initialization complete\n")); - - return EOK; -} - -int main(int argc, const char *argv[]) -{ - int opt; - poptContext pc; - struct main_context *main_ctx; - int ret; - - struct poptOption long_options[] = { - POPT_AUTOHELP - SSSD_MAIN_OPTS - { NULL } - }; - - pc = poptGetContext(argv[0], argc, argv, long_options, 0); - while((opt = poptGetNextOpt(pc)) != -1) { - switch(opt) { - default: - fprintf(stderr, "\nInvalid option %s: %s\n\n", - poptBadOption(pc, 0), poptStrerror(opt)); - poptPrintUsage(pc, stderr, 0); - return 1; - } - } - - poptFreeContext(pc); - - /* set up things like debug , signals, daemonization, etc... */ - ret = server_setup("sssd[nss]", 0, &main_ctx); - if (ret != EOK) return 2; - - ret = nss_process_init(main_ctx, - main_ctx->event_ctx, - main_ctx->confdb_ctx); - if (ret != EOK) return 3; - - /* loop on main */ - server_loop(main_ctx); - - return 0; -} - diff --git a/server/nss/nsssrv.h b/server/nss/nsssrv.h deleted file mode 100644 index 2352a5054..000000000 --- a/server/nss/nsssrv.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - SSSD - - NSS Responder, header file - - Copyright (C) Simo Sorce 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef __NSSSRV_H__ -#define __NSSSRV_H__ - -#include -#include -#include "talloc.h" -#include "tevent.h" -#include "ldb.h" -#include "../nss_client/sss_nss.h" -#include "dbus/dbus.h" - -#define NSS_SBUS_SERVICE_VERSION 0x0001 -#define NSS_SBUS_SERVICE_NAME "nss" - -#define NSS_PACKET_MAX_RECV_SIZE 1024 - -/* NSS_DOMAIN_DELIM can be specified in config.h */ -#include "config.h" -#ifndef NSS_DOMAIN_DELIM -#define NSS_DOMAIN_DELIM '@' -#endif - -#define NSS_ENUM_USERS 0x01 -#define NSS_ENUM_GROUPS 0x02 -#define NSS_ENUM_ALL 0x03 - -struct sysdb_ctx; -struct getent_ctx; - -struct nss_ctx { - struct event_context *ev; - struct fd_event *lfde; - int lfd; - struct sysdb_ctx *sysdb; - struct confdb_ctx *cdb; - char *sock_name; - struct service_sbus_ctx *ss_ctx; - struct service_sbus_ctx *dp_ctx; - struct btreemap *domain_map; - char *default_domain; - - int cache_timeout; -}; - -struct cli_ctx { - struct event_context *ev; - struct nss_ctx *nctx; - int cfd; - struct fd_event *cfde; - struct sockaddr_un addr; - struct cli_request *creq; - struct getent_ctx *gctx; -}; - -struct nss_domain_info { - char *basedn; - int enumerate; - bool has_provider; - bool legacy; -}; - -struct nss_packet; - -struct cli_request { - - /* original request from the wire */ - struct nss_packet *in; - - /* reply data */ - struct nss_packet *out; -}; - -/* from nsssrv_packet.c */ -int nss_packet_new(TALLOC_CTX *mem_ctx, size_t size, - enum sss_nss_command cmd, - struct nss_packet **rpacket); -int nss_packet_grow(struct nss_packet *packet, size_t size); -int nss_packet_recv(struct nss_packet *packet, int fd); -int nss_packet_send(struct nss_packet *packet, int fd); -enum sss_nss_command nss_packet_get_cmd(struct nss_packet *packet); -void nss_packet_get_body(struct nss_packet *packet, uint8_t **body, size_t *blen); -void nss_packet_set_error(struct nss_packet *packet, int error); - -/* from nsssrv_cmd.c */ -int nss_cmd_execute(struct cli_ctx *cctx); - -/* from nsssrv_dp.c */ -#define NSS_DP_USER 1 -#define NSS_DP_GROUP 2 -#define NSS_DP_INITGROUPS 3 - -typedef void (*nss_dp_callback_t)(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr); - -int nss_dp_send_acct_req(struct nss_ctx *nctx, TALLOC_CTX *memctx, - nss_dp_callback_t callback, void *callback_ctx, - int timeout, const char *domain, int type, - const char *opt_name, uint32_t opt_id); -int nss_dp_init(struct nss_ctx *nctx); - -#endif /* __NSSSRV_H__ */ diff --git a/server/nss/nsssrv_cmd.c b/server/nss/nsssrv_cmd.c deleted file mode 100644 index b16d27c22..000000000 --- a/server/nss/nsssrv_cmd.c +++ /dev/null @@ -1,2237 +0,0 @@ -/* - SSSD - - NSS Responder - - Copyright (C) Simo Sorce 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include "util/util.h" -#include "util/btreemap.h" -#include "nss/nsssrv.h" -#include "db/sysdb.h" -#include - -struct nss_cmd_ctx { - struct cli_ctx *cctx; - const char *name; - uid_t id; - - bool immediate; - bool done; - int nr; -}; - -struct getent_ctx { - struct ldb_result *pwds; - struct ldb_result *grps; - int pwd_cur; - int grp_cur; -}; - -struct nss_dom_ctx { - struct nss_cmd_ctx *cmdctx; - const char *domain; - bool check_provider; - bool legacy; -}; - -struct nss_cmd_table { - enum sss_nss_command cmd; - int (*fn)(struct cli_ctx *cctx); -}; - -static void nss_cmd_done(struct nss_cmd_ctx *cmdctx) -{ - /* now that the packet is in place, unlock queue - * making the event writable */ - EVENT_FD_WRITEABLE(cmdctx->cctx->cfde); - - /* free all request related data through the talloc hierarchy */ - talloc_free(cmdctx); -} - -static int nss_cmd_send_error(struct nss_cmd_ctx *cmdctx, int err) -{ - struct cli_ctx *cctx = cmdctx->cctx; - int ret; - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - return ret; - } - - nss_packet_set_error(cctx->creq->out, err); - return EOK; -} - -#define NSS_CMD_FATAL_ERROR(cctx) do { \ - DEBUG(1,("Fatal error, killing connection!")); \ - talloc_free(cctx); \ - return; \ -} while(0) - -static int nss_parse_name(struct nss_dom_ctx *dctx, const char *fullname) -{ - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct nss_ctx *nctx = cmdctx->cctx->nctx; - struct nss_domain_info *info; - struct btreemap *domain_map; - char *delim; - char *domain; - - domain_map = nctx->domain_map; - - if ((delim = strchr(fullname, NSS_DOMAIN_DELIM)) != NULL) { - domain = delim+1; - } else { - domain = nctx->default_domain; - } - - /* Check for registered domain */ - info = btreemap_get_value(domain_map, (void *)domain); - if (!info) { - /* No such domain was registered. Return EINVAL. - * TODO: alternative approach? - * Alternatively, we could simply fail down to - * below, treating the entire construct as the - * full name if the domain is unspecified. - */ - return EINVAL; - } - - dctx->check_provider = info->has_provider; - dctx->legacy = info->legacy; - - dctx->domain = talloc_strdup(dctx, domain); - if (!dctx->domain) return ENOMEM; - - if (delim) { - cmdctx->name = talloc_strndup(cmdctx, fullname, delim-fullname); - } else { - cmdctx->name = talloc_strdup(cmdctx, fullname); - } - if (!cmdctx->name) return ENOMEM; - - return EOK; -} - -static int nss_cmd_get_version(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - uint8_t *body; - size_t blen; - int ret; - - cmdctx = talloc(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - /* create response packet */ - ret = nss_packet_new(cctx->creq, sizeof(uint32_t), - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - return ret; - } - nss_packet_get_body(cctx->creq->out, &body, &blen); - ((uint32_t *)body)[0] = SSS_NSS_VERSION; - - nss_cmd_done(cmdctx); - return EOK; -} - -/**************************************************************************** - * PASSWD db related functions - ***************************************************************************/ - -static int fill_pwent(struct nss_packet *packet, - struct ldb_message **msgs, - int count) -{ - struct ldb_message *msg; - uint8_t *body; - const char *name; - const char *fullname; - const char *homedir; - const char *shell; - uint64_t uid; - uint64_t gid; - size_t rsize, rp, blen; - size_t s1, s2, s3, s4; - int i, ret, num; - - /* first 2 fields (len and reserved), filled up later */ - ret = nss_packet_grow(packet, 2*sizeof(uint32_t)); - rp = 2*sizeof(uint32_t); - - num = 0; - for (i = 0; i < count; i++) { - msg = msgs[i]; - - name = ldb_msg_find_attr_as_string(msg, SYSDB_PW_NAME, NULL); - fullname = ldb_msg_find_attr_as_string(msg, SYSDB_PW_FULLNAME, NULL); - homedir = ldb_msg_find_attr_as_string(msg, SYSDB_PW_HOMEDIR, NULL); - shell = ldb_msg_find_attr_as_string(msg, SYSDB_PW_SHELL, NULL); - uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_PW_UIDNUM, 0); - gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_PW_GIDNUM, 0); - - if (!name || !fullname || !homedir || !shell || !uid || !gid) { - DEBUG(1, ("Incomplete user object for %s[%llu]! Skipping\n", - name?name:"", (unsigned long long int)uid)); - continue; - } - - s1 = strlen(name) + 1; - s2 = strlen(fullname) + 1; - s3 = strlen(homedir) + 1; - s4 = strlen(shell) + 1; - rsize = 2*sizeof(uint64_t) +s1 + 2 + s2 + s3 +s4; - - ret = nss_packet_grow(packet, rsize); - if (ret != EOK) { - num = 0; - goto done; - } - nss_packet_get_body(packet, &body, &blen); - - ((uint64_t *)(&body[rp]))[0] = uid; - ((uint64_t *)(&body[rp]))[1] = gid; - rp += 2*sizeof(uint64_t); - memcpy(&body[rp], name, s1); - rp += s1; - memcpy(&body[rp], "x", 2); - rp += 2; - memcpy(&body[rp], fullname, s2); - rp += s2; - memcpy(&body[rp], homedir, s3); - rp += s3; - memcpy(&body[rp], shell, s4); - rp += s4; - - num++; - } - -done: - nss_packet_get_body(packet, &body, &blen); - ((uint32_t *)body)[0] = num; /* num results */ - ((uint32_t *)body)[1] = 0; /* reserved */ - - return EOK; -} - -static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr); - -static void nss_cmd_getpwnam_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int timeout; - uint64_t lastUpdate; - uint8_t *body; - size_t blen; - bool call_provider = false; - int ret; - - if (status != LDB_SUCCESS) { - ret = nss_cmd_send_error(cmdctx, status); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - - if (dctx->check_provider) { - switch (res->count) { - case 0: - call_provider = true; - break; - - case 1: - timeout = cmdctx->cctx->nctx->cache_timeout; - - lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], - SYSDB_LAST_UPDATE, 0); - if (lastUpdate + timeout < time(NULL)) { - call_provider = true; - } - break; - - default: - DEBUG(1, ("getpwnam call returned more than one result !?!\n")); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - } - - if (call_provider) { - - /* dont loop forever :-) */ - dctx->check_provider = false; - timeout = SSS_NSS_SOCKET_TIMEOUT/2; - - ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, - nss_cmd_getpwnam_dp_callback, dctx, - timeout, dctx->domain, NSS_DP_USER, - cmdctx->name, 0); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)\n", - ret, strerror(ret))); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - return; - } - - switch (res->count) { - case 0: - - DEBUG(2, ("No results for getpwnam call\n")); - - ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_packet_get_body(cctx->creq->out, &body, &blen); - ((uint32_t *)body)[0] = 0; /* 0 results */ - ((uint32_t *)body)[1] = 0; /* reserved */ - break; - - case 1: - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - - ret = fill_pwent(cctx->creq->out, res->msgs, res->count); - nss_packet_set_error(cctx->creq->out, ret); - - break; - - default: - DEBUG(1, ("getpwnam call returned more than one result !?!\n")); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - } - -done: - nss_cmd_done(cmdctx); -} - -static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int ret; - - if (err_maj) { - DEBUG(2, ("Unable to get information from Data Provider\n" - "Error: %u, %u, %s\n" - "Will try to return what we have in cache\n", - (unsigned int)err_maj, (unsigned int)err_min, err_msg)); - } - - ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->name, - dctx->legacy, - nss_cmd_getpwnam_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_cmd_done(cmdctx); - } -} - -static int nss_cmd_getpwnam(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - struct nss_dom_ctx *dctx; - uint8_t *body; - size_t blen; - int ret; - - cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - dctx->cmdctx = cmdctx; - - /* get user name to query */ - nss_packet_get_body(cctx->creq->in, &body, &blen); - /* if not terminated fail */ - if (body[blen -1] != '\0') { - talloc_free(cmdctx); - return EINVAL; - } - - ret = nss_parse_name(dctx, (const char *)body); - if (ret != EOK) { - DEBUG(1, ("Invalid name received\n")); - talloc_free(cmdctx); - return ret; - } - DEBUG(4, ("Requesting info for [%s] from [%s]\n", - cmdctx->name, dctx->domain)); - - ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->name, - dctx->legacy, - nss_cmd_getpwnam_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret == EOK) { - nss_cmd_done(cmdctx); - } - return ret; - } - - return EOK; -} - -static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr); - -static void nss_cmd_getpwuid_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int timeout; - uint64_t lastUpdate; - uint8_t *body; - size_t blen; - bool call_provider = false; - int ret; - - /* one less to go */ - cmdctx->nr--; - - /* check if another callback already replied */ - if (cmdctx->done) { - /* now check if this is the last callback */ - if (cmdctx->nr == 0) { - /* ok we are really done with this request */ - goto done; - } - } - - if (status != LDB_SUCCESS) { - ret = nss_cmd_send_error(cmdctx, status); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - - if (dctx->check_provider) { - switch (res->count) { - case 0: - call_provider = true; - break; - - case 1: - timeout = cmdctx->cctx->nctx->cache_timeout; - - lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], - SYSDB_LAST_UPDATE, 0); - if (lastUpdate + timeout < time(NULL)) { - call_provider = true; - } - break; - - default: - DEBUG(1, ("getpwuid call returned more than one result !?!\n")); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - } - - if (call_provider) { - - /* yet one more call to go */ - cmdctx->nr++; - - /* dont loop forever :-) */ - dctx->check_provider = false; - timeout = SSS_NSS_SOCKET_TIMEOUT/2; - - ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, - nss_cmd_getpwuid_dp_callback, dctx, - timeout, dctx->domain, NSS_DP_USER, - NULL, cmdctx->id); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)\n", - ret, strerror(ret))); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - return; - } - - switch (res->count) { - case 0: - if (cmdctx->nr != 0) { - /* nothing to do */ - return; - } - - DEBUG(2, ("No results for getpwuid call\n")); - - ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_packet_get_body(cctx->creq->out, &body, &blen); - ((uint32_t *)body)[0] = 0; /* 0 results */ - ((uint32_t *)body)[1] = 0; /* reserved */ - break; - - case 1: - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - - ret = fill_pwent(cctx->creq->out, res->msgs, res->count); - nss_packet_set_error(cctx->creq->out, ret); - - break; - - default: - DEBUG(1, ("getpwnam call returned more than one result !?!\n")); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - } - -done: - if (cmdctx->nr != 0) { - cmdctx->done = true; /* signal that we are done */ - return; - } - nss_cmd_done(cmdctx); -} - -static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int ret; - - if (err_maj) { - DEBUG(2, ("Unable to get information from Data Provider\n" - "Error: %u, %u, %s\n" - "Will try to return what we have in cache\n", - (unsigned int)err_maj, (unsigned int)err_min, err_msg)); - } - - ret = sysdb_getpwuid(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->id, - dctx->legacy, - nss_cmd_getpwuid_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - if (cmdctx->nr != 0) { - cmdctx->done = true; /* signal that we are done */ - return; - } - nss_cmd_done(cmdctx); - } -} - -static int nss_cmd_getpwuid(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - struct nss_dom_ctx *dctx; - struct nss_domain_info *info; - const char **domains; - uint8_t *body; - size_t blen; - int i, num, ret; - - cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - /* get uid to query */ - nss_packet_get_body(cctx->creq->in, &body, &blen); - - if (blen != sizeof(uint64_t)) { - return EINVAL; - } - - cmdctx->id = (uid_t)*((uint64_t *)body); - - /* FIXME: Just ask all backends for now, until we check for ranges */ - dctx = NULL; - domains = NULL; - num = 0; - /* get domains list */ - btreemap_get_keys(cmdctx, cctx->nctx->domain_map, - (const void ***)&domains, &num); - - cmdctx->nr = num; - - for (i = 0; i < num; i++) { - info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); - - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - - dctx->cmdctx = cmdctx; - dctx->domain = talloc_strdup(dctx, domains[i]); - if (!dctx->domain) return ENOMEM; - dctx->check_provider = info->has_provider; - dctx->legacy = info->legacy; - - - DEBUG(4, ("Requesting info for [%lu@%s]\n", - cmdctx->id, dctx->domain)); - - ret = sysdb_getpwuid(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->id, - dctx->legacy, - nss_cmd_getpwuid_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - /* shutdown ? */ - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret == EOK) { - nss_cmd_done(cmdctx); - } - return ret; - } - } - - return EOK; -} - -/* to keep it simple at this stage we are retrieving the - * full enumeration again for each request for each process - * and we also block on setpwent() for the full time needed - * to retrieve the data. And endpwent() frees all the data. - * Next steps are: - * - use an nsssrv wide cache with data already structured - * so that it can be immediately returned (see nscd way) - * - use mutexes so that setpwent() can return immediately - * even if the data is still being fetched - * - make getpwent() wait on the mutex - */ -static void nss_cmd_getpwent_callback(void *ptr, int status, - struct ldb_result *res); - -static void nss_cmd_setpwent_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = cmdctx->cctx; - struct getent_ctx *gctx = cctx->gctx; - struct ldb_result *store = gctx->pwds; - int i, j, c, ret; - - cmdctx->nr--; - - if (cmdctx->done) { - /* do not reply until all domain searches are done */ - if (cmdctx->nr != 0) return; - else goto done; - } - - if (status != LDB_SUCCESS) { - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_packet_set_error(cctx->creq->out, status); - cmdctx->done = true; - return; - } - - if (store) { - c = store->count + res->count; - store->msgs = talloc_realloc(store, store->msgs, - struct ldb_message *, c); - if (!store->msgs) NSS_CMD_FATAL_ERROR(cctx); - - for (i = store->count, j = 0; i < c; i++, j++) { - store->msgs[i] = talloc_steal(store->msgs, res->msgs[j]); - if (!store->msgs[i]) NSS_CMD_FATAL_ERROR(cctx); - } - store->count = c; - talloc_free(res); - } else { - gctx->pwds = talloc_steal(gctx, res); - } - - /* do not reply until all domain searches are done */ - if (cmdctx->nr) return; - - if (cmdctx->immediate) { - /* this was a getpwent call w/o setpwent, - * return immediately one result */ - nss_cmd_getpwent_callback(ptr, status, res); - - return; - } - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - -done: - nss_cmd_done(cmdctx); -} - -static void nss_cmd_setpw_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int ret; - - if (err_maj) { - DEBUG(2, ("Unable to get information from Data Provider\n" - "Error: %u, %u, %s\n" - "Will try to return what we have in cache\n", - (unsigned int)err_maj, (unsigned int)err_min, err_msg)); - } - - ret = sysdb_enumpwent(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, dctx->legacy, - nss_cmd_setpwent_callback, cmdctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_cmd_done(cmdctx); - } -} - -static int nss_cmd_setpwent_ext(struct cli_ctx *cctx, bool immediate) -{ - struct nss_domain_info *info; - struct nss_cmd_ctx *cmdctx; - struct nss_dom_ctx *dctx; - struct getent_ctx *gctx; - const char **domains; - int timeout; - int i, ret, num; - - DEBUG(4, ("Requesting info for all users\n")); - - cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - if (cctx->gctx == NULL) { - gctx = talloc_zero(cctx, struct getent_ctx); - if (!gctx) { - talloc_free(cmdctx); - return ENOMEM; - } - cctx->gctx = gctx; - } - if (cctx->gctx->pwds) { - talloc_free(cctx->gctx->pwds); - cctx->gctx->pwds = NULL; - cctx->gctx->pwd_cur = 0; - } - - cmdctx->immediate = immediate; - - domains = NULL; - num = 0; - /* get domains list */ - btreemap_get_keys(cmdctx, cctx->nctx->domain_map, - (const void ***)&domains, &num); - - /* check if enumeration is enabled in any domain */ - for (i = 0; i < num; i++) { - info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); - - if ((info->enumerate & NSS_ENUM_USERS) == 0) { - continue; - } - - /* TODO: enabled, check if we have a recent cached enumeration */ - - /* ok no cache, go and ask the backend to enumerate */ - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - - dctx->cmdctx = cmdctx; - dctx->domain = talloc_strdup(dctx, domains[i]); - if (!dctx->domain) return ENOMEM; - dctx->check_provider = info->has_provider; - dctx->legacy = info->legacy; - - if (dctx->check_provider) { - timeout = SSS_NSS_SOCKET_TIMEOUT/(i+2); - ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, - nss_cmd_setpw_dp_callback, dctx, - timeout, domains[i], NSS_DP_USER, - NULL, 0); - } else { - ret = sysdb_enumpwent(dctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, dctx->legacy, - nss_cmd_setpwent_callback, cmdctx); - } - if (ret != EOK) { - /* FIXME: shutdown ? */ - DEBUG(1, ("Failed to send enumeration request for domain [%s]!\n", - domains[i])); - continue; - } - - /* number of replies to wait for before setpwent is done */ - cmdctx->nr++; - } - - if (cmdctx->nr == 0) { - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - return ret; - } - - nss_packet_set_error(cctx->creq->out, ret); - nss_cmd_done(cmdctx); - return EOK; - } - - return ret; -} - -static int nss_cmd_setpwent(struct cli_ctx *cctx) -{ - return nss_cmd_setpwent_ext(cctx, false); -} - - -static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) -{ - struct getent_ctx *gctx = cctx->gctx; - int n, ret; - - n = gctx->pwds->count - gctx->pwd_cur; - if (n > num) n = num; - - ret = fill_pwent(cctx->creq->out, - &(gctx->pwds->msgs[gctx->pwd_cur]), n); - gctx->pwd_cur += n; - - return ret; -} - -/* used only if a process calls getpwent() without first calling setpwent() - */ -static void nss_cmd_getpwent_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = cmdctx->cctx; - struct getent_ctx *gctx = cctx->gctx; - uint8_t *body; - size_t blen; - uint32_t num; - int ret; - - /* get max num of entries to return in one call */ - nss_packet_get_body(cctx->creq->in, &body, &blen); - if (blen != sizeof(uint32_t)) { - NSS_CMD_FATAL_ERROR(cctx); - } - num = *((uint32_t *)body); - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - - if (status != LDB_SUCCESS) { - nss_packet_set_error(cctx->creq->out, status); - goto done; - } - - gctx->pwds = talloc_steal(gctx, res); - - ret = nss_cmd_retpwent(cctx, num); - nss_packet_set_error(cctx->creq->out, ret); - -done: - nss_cmd_done(cmdctx); -} - -static int nss_cmd_getpwent(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - struct getent_ctx *gctx; - uint8_t *body; - size_t blen; - uint32_t num; - int ret; - - DEBUG(4, ("Requesting info for all accounts\n")); - - /* get max num of entries to return in one call */ - nss_packet_get_body(cctx->creq->in, &body, &blen); - if (blen != sizeof(uint32_t)) { - return EINVAL; - } - num = *((uint32_t *)body); - - /* see if we need to trigger an implicit setpwent() */ - if (cctx->gctx == NULL || cctx->gctx->pwds == NULL) { - if (cctx->gctx == NULL) { - gctx = talloc_zero(cctx, struct getent_ctx); - if (!gctx) { - return ENOMEM; - } - cctx->gctx = gctx; - } - if (cctx->gctx->pwds == NULL) { - ret = nss_cmd_setpwent_ext(cctx, true); - return ret; - } - } - - cmdctx = talloc(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - return ret; - } - - ret = nss_cmd_retpwent(cctx, num); - nss_packet_set_error(cctx->creq->out, ret); - nss_cmd_done(cmdctx); - return EOK; -} - -static int nss_cmd_endpwent(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - int ret; - - DEBUG(4, ("Terminating request info for all accounts\n")); - - cmdctx = talloc(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - - if (cctx->gctx == NULL) goto done; - if (cctx->gctx->pwds == NULL) goto done; - - /* free results and reset */ - talloc_free(cctx->gctx->pwds); - cctx->gctx->pwds = NULL; - cctx->gctx->pwd_cur = 0; - -done: - nss_cmd_done(cmdctx); - return EOK; -} - -/**************************************************************************** - * GROUP db related functions - ***************************************************************************/ - -static int fill_grent(struct nss_packet *packet, - struct ldb_message **msgs, - int count) -{ - struct ldb_message_element *el; - struct ldb_message *msg; - uint8_t *body; - const char *name; - uint64_t gid; - size_t rsize, rp, blen, mnump; - int i, j, ret, num, memnum; - bool get_group = true; - bool memnum_set = false; - - /* first 2 fields (len and reserved), filled up later */ - ret = nss_packet_grow(packet, 2*sizeof(uint32_t)); - rp = 2*sizeof(uint32_t); - - num = 0; - mnump = 0; - for (i = 0; i < count; i++) { - msg = msgs[i]; - - if (get_group) { - /* find group name/gid */ - name = ldb_msg_find_attr_as_string(msg, SYSDB_GR_NAME, NULL); - gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GR_GIDNUM, 0); - if (!name || !gid) { - DEBUG(1, ("Incomplete group object for %s[%llu]! Aborting\n", - name?name:"", (unsigned long long int)gid)); - num = 0; - goto done; - } - - /* fill in gid and name and set pointer for number of members */ - rsize = sizeof(uint64_t) + sizeof(uint32_t) + strlen(name)+1 +2; - ret = nss_packet_grow(packet, rsize); - nss_packet_get_body(packet, &body, &blen); - rp = blen - rsize; - ((uint64_t *)(&body[rp]))[0] = gid; - rp += sizeof(uint64_t); - ((uint32_t *)(&body[rp]))[0] = 0; /* init members num to 0 */ - mnump = rp; /* keep around members num pointer to set later */ - rp += sizeof(uint32_t); - memcpy(&body[rp], name, strlen(name)+1); - body[blen-2] = 'x'; /* group passwd field */ - body[blen-1] = '\0'; - - memnum_set = false; - memnum = 0; - num++; - - /* legacy style group, members are in SYSDB_LEGACY_MEMBER */ - el = ldb_msg_find_element(msg, SYSDB_LEGACY_MEMBER); - if (el) { - /* legacy */ - memnum = el->num_values; - - for (j = 0; j < memnum; j++) { - rsize = el->values[j].length + 1; - ret = nss_packet_grow(packet, rsize); - if (ret != EOK) { - num = 0; - goto done; - } - - nss_packet_get_body(packet, &body, &blen); - rp = blen - rsize; - memcpy(&body[rp], el->values[j].data, el->values[j].length); - body[blen-1] = '\0'; - } - - nss_packet_get_body(packet, &body, &blen); - ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ - memnum_set = true; - - } else { - get_group = false; - } - - continue; - } - - name = ldb_msg_find_attr_as_string(msg, SYSDB_PW_NAME, NULL); - - if (!name) { - /* last member of previous group found, or error. - * set next element to be a group, and eventually - * fail there if here start bogus entries */ - get_group = true; - i--; - nss_packet_get_body(packet, &body, &blen); - ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ - memnum_set = true; - continue; - } - - rsize = strlen(name) + 1; - - ret = nss_packet_grow(packet, rsize); - if (ret != EOK) { - num = 0; - goto done; - } - nss_packet_get_body(packet, &body, &blen); - rp = blen - rsize; - memcpy(&body[rp], name, rsize); - - memnum++; - } - - if (!memnum_set) { - /* fill in the last group member count */ - if (mnump != 0) { - nss_packet_get_body(packet, &body, &blen); - ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ - } - } - -done: - nss_packet_get_body(packet, &body, &blen); - ((uint32_t *)body)[0] = num; /* num results */ - ((uint32_t *)body)[1] = 0; /* reserved */ - - return EOK; -} - -static void nss_cmd_getgrnam_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr); - -static void nss_cmd_getgrnam_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int timeout; - uint64_t lastUpdate; - uint8_t *body; - size_t blen; - bool call_provider = false; - int ret; - - if (status != LDB_SUCCESS) { - ret = nss_cmd_send_error(cmdctx, status); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_cmd_done(cmdctx); - return; - } - - if (dctx->check_provider) { - switch (res->count) { - case 0: - call_provider = true; - break; - - default: - timeout = cmdctx->cctx->nctx->cache_timeout; - - lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], - SYSDB_LAST_UPDATE, 0); - if (lastUpdate + timeout < time(NULL)) { - call_provider = true; - } - } - } - - if (call_provider) { - - /* dont loop forever :-) */ - dctx->check_provider = false; - timeout = SSS_NSS_SOCKET_TIMEOUT/2; - - ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, - nss_cmd_getgrnam_dp_callback, dctx, - timeout, dctx->domain, NSS_DP_GROUP, - cmdctx->name, 0); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)\n", - ret, strerror(ret))); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - - return; - } - - switch (res->count) { - case 0: - - DEBUG(2, ("No results for getgrnam call\n")); - - ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_packet_get_body(cctx->creq->out, &body, &blen); - ((uint32_t *)body)[0] = 0; /* 0 results */ - ((uint32_t *)body)[1] = 0; /* reserved */ - break; - - default: - - DEBUG(6, ("Returning info for group [%s]\n", cmdctx->name)); - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - - ret = fill_grent(cctx->creq->out, res->msgs, res->count); - nss_packet_set_error(cctx->creq->out, ret); - } - -done: - nss_cmd_done(cmdctx); -} - -static void nss_cmd_getgrnam_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int ret; - - if (err_maj) { - DEBUG(2, ("Unable to get information from Data Provider\n" - "Error: %u, %u, %s\n" - "Will try to return what we have in cache\n", - (unsigned int)err_maj, (unsigned int)err_min, err_msg)); - } - - ret = sysdb_getgrnam(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->name, - dctx->legacy, - nss_cmd_getgrnam_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_cmd_done(cmdctx); - } -} - -static int nss_cmd_getgrnam(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - struct nss_dom_ctx *dctx; - uint8_t *body; - size_t blen; - int ret; - - cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - dctx->cmdctx = cmdctx; - - /* get user name to query */ - nss_packet_get_body(cctx->creq->in, &body, &blen); - /* if not terminated fail */ - if (body[blen -1] != '\0') { - talloc_free(cmdctx); - return EINVAL; - } - - ret = nss_parse_name(dctx, (const char *)body); - if (ret != EOK) { - DEBUG(1, ("Invalid name received\n")); - talloc_free(cmdctx); - return ret; - } - DEBUG(4, ("Requesting info for [%s] from [%s]\n", - cmdctx->name, dctx->domain)); - - ret = sysdb_getgrnam(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->name, - dctx->legacy, - nss_cmd_getgrnam_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret == EOK) { - nss_cmd_done(cmdctx); - } - return ret; - } - - return EOK; -} - -static void nss_cmd_getgrgid_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr); - -static void nss_cmd_getgrgid_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int timeout; - uint64_t lastUpdate; - uint8_t *body; - size_t blen; - bool call_provider = false; - int ret; - - /* one less to go */ - cmdctx->nr--; - - /* check if another callback already replied */ - if (cmdctx->done) { - /* now check if this is the last callback */ - if (cmdctx->nr == 0) { - /* ok we are really done with this request */ - goto done; - } - } - - if (status != LDB_SUCCESS) { - ret = nss_cmd_send_error(cmdctx, status); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - - if (dctx->check_provider) { - switch (res->count) { - case 0: - call_provider = true; - break; - - default: - timeout = cmdctx->cctx->nctx->cache_timeout; - - lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], - SYSDB_LAST_UPDATE, 0); - if (lastUpdate + timeout < time(NULL)) { - call_provider = true; - } - } - } - - if (call_provider) { - - /* yet one more call to go */ - cmdctx->nr++; - - /* dont loop forever :-) */ - dctx->check_provider = false; - timeout = SSS_NSS_SOCKET_TIMEOUT/2; - - ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, - nss_cmd_getgrgid_dp_callback, dctx, - timeout, dctx->domain, NSS_DP_GROUP, - NULL, cmdctx->id); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)\n", - ret, strerror(ret))); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - return; - } - - switch (res->count) { - case 0: - if (cmdctx->nr != 0) { - /* nothing to do */ - return; - } - - DEBUG(2, ("No results for getgrgid call\n")); - - ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_packet_get_body(cctx->creq->out, &body, &blen); - ((uint32_t *)body)[0] = 0; /* 0 results */ - ((uint32_t *)body)[1] = 0; /* reserved */ - break; - - default: - - DEBUG(6, ("Returning info for group [%u]\n", (unsigned)cmdctx->id)); - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - - ret = fill_grent(cctx->creq->out, res->msgs, res->count); - nss_packet_set_error(cctx->creq->out, ret); - } - -done: - if (cmdctx->nr != 0) { - cmdctx->done = true; /* signal that we are done */ - return; - } - nss_cmd_done(cmdctx); -} - -static void nss_cmd_getgrgid_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int ret; - - if (err_maj) { - DEBUG(2, ("Unable to get information from Data Provider\n" - "Error: %u, %u, %s\n" - "Will try to return what we have in cache\n", - (unsigned int)err_maj, (unsigned int)err_min, err_msg)); - } - - ret = sysdb_getgrgid(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->id, - dctx->legacy, - nss_cmd_getgrgid_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_cmd_done(cmdctx); - } -} - -static int nss_cmd_getgrgid(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - struct nss_dom_ctx *dctx; - struct nss_domain_info *info; - const char **domains; - uint8_t *body; - size_t blen; - int i, num, ret; - - cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - /* get uid to query */ - nss_packet_get_body(cctx->creq->in, &body, &blen); - - if (blen != sizeof(uint64_t)) { - return EINVAL; - } - - cmdctx->id = (gid_t)*((uint64_t *)body); - - /* FIXME: Just ask all backends for now, until we check for ranges */ - dctx = NULL; - domains = NULL; - num = 0; - /* get domains list */ - btreemap_get_keys(cmdctx, cctx->nctx->domain_map, - (const void ***)&domains, &num); - - cmdctx->nr = num; - - for (i = 0; i < num; i++) { - info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); - - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - - dctx->cmdctx = cmdctx; - dctx->domain = talloc_strdup(dctx, domains[i]); - if (!dctx->domain) return ENOMEM; - dctx->check_provider = info->has_provider; - dctx->legacy = info->legacy; - - DEBUG(4, ("Requesting info for [%lu@%s]\n", - cmdctx->id, dctx->domain)); - - ret = sysdb_getgrgid(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->id, - dctx->legacy, - nss_cmd_getgrgid_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - /* shutdown ? */ - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret == EOK) { - nss_cmd_done(cmdctx); - } - return ret; - } - } - - return EOK; -} - -/* to keep it simple at this stage we are retrieving the - * full enumeration again for each request for each process - * and we also block on setgrent() for the full time needed - * to retrieve the data. And endgrent() frees all the data. - * Next steps are: - * - use and nsssrv wide cache with data already structured - * so that it can be immediately returned (see nscd way) - * - use mutexes so that setgrent() can return immediately - * even if the data is still being fetched - * - make getgrent() wait on the mutex - */ -static void nss_cmd_getgrent_callback(void *ptr, int status, - struct ldb_result *res); - -static void nss_cmd_setgrent_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = cmdctx->cctx; - struct getent_ctx *gctx = cctx->gctx; - struct ldb_result *store = gctx->grps; - int i, j, c, ret; - - cmdctx->nr--; - - if (cmdctx->done) { - /* do not reply until all domain searches are done */ - if (cmdctx->nr != 0) return; - else goto done; - } - - if (status != LDB_SUCCESS) { - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_packet_set_error(cctx->creq->out, status); - cmdctx->done = true; - return; - } - - if (store) { - c = store->count + res->count; - store->msgs = talloc_realloc(store, store->msgs, - struct ldb_message *, c); - if (!store->msgs) NSS_CMD_FATAL_ERROR(cctx); - - for (i = store->count, j = 0; i < c; i++, j++) { - store->msgs[i] = talloc_steal(store->msgs, res->msgs[j]); - if (!store->msgs[i]) NSS_CMD_FATAL_ERROR(cctx); - } - store->count = c; - talloc_free(res); - } else { - gctx->grps = talloc_steal(gctx, res); - } - - /* do not reply until all domain searches are done */ - if (cmdctx->nr) return; - - if (cmdctx->immediate) { - /* this was a getgrent call w/o setgrent, - * return immediately one result */ - nss_cmd_getgrent_callback(ptr, status, res); - return; - } - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - -done: - nss_cmd_done(cmdctx); -} - -static void nss_cmd_setgr_dp_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int ret; - - if (err_maj) { - DEBUG(2, ("Unable to get information from Data Provider\n" - "Error: %u, %u, %s\n" - "Will try to return what we have in cache\n", - (unsigned int)err_maj, (unsigned int)err_min, err_msg)); - } - - ret = sysdb_enumgrent(dctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, dctx->legacy, - nss_cmd_setgrent_callback, cmdctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_cmd_done(cmdctx); - } -} - -static int nss_cmd_setgrent_ext(struct cli_ctx *cctx, bool immediate) -{ - struct nss_domain_info *info; - struct nss_cmd_ctx *cmdctx; - struct nss_dom_ctx *dctx; - struct getent_ctx *gctx; - const char **domains; - int timeout; - int i, ret, num; - - DEBUG(4, ("Requesting info for all groups\n")); - - cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - if (cctx->gctx == NULL) { - gctx = talloc_zero(cctx, struct getent_ctx); - if (!gctx) { - talloc_free(cmdctx); - return ENOMEM; - } - cctx->gctx = gctx; - } - if (cctx->gctx->grps) { - talloc_free(cctx->gctx->grps); - cctx->gctx->grps = NULL; - cctx->gctx->grp_cur = 0; - } - - cmdctx->immediate = immediate; - - domains = NULL; - num = 0; - /* get domains list */ - btreemap_get_keys(cmdctx, cctx->nctx->domain_map, - (const void ***)&domains, &num); - - /* check if enumeration is enabled in any domain */ - for (i = 0; i < num; i++) { - info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); - - if ((info->enumerate & NSS_ENUM_GROUPS) == 0) { - continue; - } - - /* TODO: enabled, check if we have a recent cached enumeration */ - - /* ok no cache, go and ask the backend to enumerate */ - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - - dctx->cmdctx = cmdctx; - dctx->domain = talloc_strdup(dctx, domains[i]); - if (!dctx->domain) return ENOMEM; - dctx->check_provider = info->has_provider; - dctx->legacy = info->legacy; - - if (dctx->check_provider) { - timeout = SSS_NSS_SOCKET_TIMEOUT/(i+2); - ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, - nss_cmd_setgr_dp_callback, dctx, - timeout, domains[i], NSS_DP_GROUP, - NULL, 0); - } else { - ret = sysdb_enumgrent(dctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, dctx->legacy, - nss_cmd_setgrent_callback, cmdctx); - } - if (ret != EOK) { - /* FIXME: shutdown ? */ - DEBUG(1, ("Failed to send enumeration request for domain [%s]!\n", - domains[i])); - continue; - } - - cmdctx->nr++; - } - - if (cmdctx->nr == 0) { - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - return ret; - } - - nss_packet_set_error(cctx->creq->out, ret); - nss_cmd_done(cmdctx); - return EOK; - } - - return ret; -} - -static int nss_cmd_setgrent(struct cli_ctx *cctx) -{ - return nss_cmd_setgrent_ext(cctx, false); -} - -static int nss_cmd_retgrent(struct cli_ctx *cctx, int num) -{ - struct getent_ctx *gctx = cctx->gctx; - int n, ret; - - n = gctx->grps->count - gctx->grp_cur; - if (n > num) n = num; - - ret = fill_grent(cctx->creq->out, - &(gctx->grps->msgs[gctx->grp_cur]), n); - gctx->grp_cur += n; - - return ret; -} - -/* used only if a process calls getpwent() without first calling setpwent() - * in this case we basically trigger an implicit setpwent() */ -static void nss_cmd_getgrent_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = cmdctx->cctx; - struct getent_ctx *gctx = cctx->gctx; - uint8_t *body; - size_t blen; - uint32_t num; - int ret; - - /* get max num of entries to return in one call */ - nss_packet_get_body(cctx->creq->in, &body, &blen); - if (blen != sizeof(uint32_t)) { - ret = nss_cmd_send_error(cmdctx, EIO); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_cmd_done(cmdctx); - } - num = *((uint32_t *)body); - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - - if (status != LDB_SUCCESS) { - nss_packet_set_error(cctx->creq->out, status); - goto done; - } - - gctx->grps = talloc_steal(gctx, res); - - ret = nss_cmd_retgrent(cctx, num); - nss_packet_set_error(cctx->creq->out, ret); - -done: - nss_cmd_done(cmdctx); -} - -static int nss_cmd_getgrent(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - struct getent_ctx *gctx; - uint8_t *body; - size_t blen; - uint32_t num; - int ret; - - DEBUG(4, ("Requesting info for all groups\n")); - - /* get max num of entries to return in one call */ - nss_packet_get_body(cctx->creq->in, &body, &blen); - if (blen != sizeof(uint32_t)) { - return EINVAL; - } - num = *((uint32_t *)body); - - /* see if we need to trigger an implicit setpwent() */ - if (cctx->gctx == NULL || cctx->gctx->grps == NULL) { - if (cctx->gctx == NULL) { - gctx = talloc_zero(cctx, struct getent_ctx); - if (!gctx) { - return ENOMEM; - } - cctx->gctx = gctx; - } - if (cctx->gctx->grps == NULL) { - ret = nss_cmd_setgrent_ext(cctx, true); - return ret; - } - } - - cmdctx = talloc(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - return ret; - } - - ret = nss_cmd_retgrent(cctx, num); - nss_packet_set_error(cctx->creq->out, ret); - nss_cmd_done(cmdctx); - return EOK; -} - -static int nss_cmd_endgrent(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - int ret; - - DEBUG(4, ("Terminating request info for all groups\n")); - - cmdctx = talloc(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - - if (cctx->gctx == NULL) goto done; - if (cctx->gctx->grps == NULL) goto done; - - /* free results and reset */ - talloc_free(cctx->gctx->grps); - cctx->gctx->grps = NULL; - cctx->gctx->grp_cur = 0; - -done: - nss_cmd_done(cmdctx); - return EOK; -} - -static void nss_cmd_initgr_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = cmdctx->cctx; - uint8_t *body; - size_t blen; - uint64_t gid; - uint32_t num; - int ret, i; - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - - if (status != LDB_SUCCESS) { - nss_packet_set_error(cctx->creq->out, status); - goto done; - } - - num = res->count; - /* the first 64 bit uint is really 2 32 units used to hold the number of - * results */ - ret = nss_packet_grow(cctx->creq->out, (1 + num) * sizeof(uint64_t)); - if (ret != EOK) { - nss_packet_set_error(cctx->creq->out, ret); - goto done; - } - nss_packet_get_body(cctx->creq->out, &body, &blen); - - for (i = 0; i < num; i++) { - gid = ldb_msg_find_attr_as_uint64(res->msgs[i], SYSDB_GR_GIDNUM, 0); - if (!gid) { - DEBUG(1, ("Incomplete group object for initgroups! Aborting\n")); - nss_packet_set_error(cctx->creq->out, EIO); - num = 0; - goto done; - } - ((uint64_t *)body)[i+1] = gid; - } - - ((uint32_t *)body)[0] = num; /* num results */ - ((uint32_t *)body)[1] = 0; /* reserved */ - -done: - nss_cmd_done(cmdctx); -} - -static void nss_cmd_getinitgr_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int ret; - - if (err_maj) { - DEBUG(2, ("Unable to get information from Data Provider\n" - "Error: %u, %u, %s\n" - "Will try to return what we have in cache\n", - (unsigned int)err_maj, (unsigned int)err_min, err_msg)); - } - - ret = sysdb_initgroups(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->name, - dctx->legacy, - nss_cmd_initgr_callback, cmdctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_cmd_done(cmdctx); - } -} - -static void nss_cmd_getinit_callback(void *ptr, int status, - struct ldb_result *res); - -static void nss_cmd_getinitnam_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int ret; - - if (err_maj) { - DEBUG(2, ("Unable to get information from Data Provider\n" - "Error: %u, %u, %s\n" - "Will try to return what we have in cache\n", - (unsigned int)err_maj, (unsigned int)err_min, err_msg)); - } - - ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->name, - dctx->legacy, - nss_cmd_getinit_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_cmd_done(cmdctx); - } -} - -static void nss_cmd_getinit_callback(void *ptr, int status, - struct ldb_result *res) -{ - struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct cli_ctx *cctx = cmdctx->cctx; - int timeout; - uint64_t lastUpdate; - uint8_t *body; - size_t blen; - bool call_provider = false; - int ret; - - if (status != LDB_SUCCESS) { - ret = nss_cmd_send_error(cmdctx, status); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - - if (dctx->check_provider) { - switch (res->count) { - case 0: - call_provider = true; - break; - - default: - timeout = cmdctx->cctx->nctx->cache_timeout; - - lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], - SYSDB_LAST_UPDATE, 0); - if (lastUpdate + timeout < time(NULL)) { - call_provider = true; - } - } - } - - if (call_provider) { - - /* dont loop forever :-) */ - dctx->check_provider = false; - timeout = SSS_NSS_SOCKET_TIMEOUT/2; - - ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, - nss_cmd_getinitnam_callback, dctx, - timeout, dctx->domain, NSS_DP_USER, - cmdctx->name, 0); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)\n", - ret, strerror(ret))); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - - return; - } - - switch (res->count) { - case 0: - - DEBUG(2, ("No results for initgroups call\n")); - - ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - nss_packet_get_body(cctx->creq->out, &body, &blen); - ((uint32_t *)body)[0] = 0; /* 0 results */ - ((uint32_t *)body)[1] = 0; /* reserved */ - break; - - case 1: - - timeout = SSS_NSS_SOCKET_TIMEOUT/2; - ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, - nss_cmd_getinitgr_callback, dctx, - timeout, dctx->domain, NSS_DP_INITGROUPS, - cmdctx->name, 0); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)\n", - ret, strerror(ret))); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - goto done; - } - - return; - - default: - DEBUG(1, ("getpwnam call returned more than one result !?!\n")); - ret = nss_cmd_send_error(cmdctx, ret); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - } - -done: - nss_cmd_done(cmdctx); -} - -/* for now, if we are online, try to always query the backend */ -static int nss_cmd_initgroups(struct cli_ctx *cctx) -{ - struct nss_cmd_ctx *cmdctx; - struct nss_dom_ctx *dctx; - uint8_t *body; - size_t blen; - int ret; - - cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!cmdctx) { - return ENOMEM; - } - cmdctx->cctx = cctx; - - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - dctx->cmdctx = cmdctx; - - /* get user name to query */ - nss_packet_get_body(cctx->creq->in, &body, &blen); - cmdctx->name = (const char *)body; - /* if not terminated fail */ - if (cmdctx->name[blen -1] != '\0') { - return EINVAL; - } - - ret = nss_parse_name(dctx, (const char *)body); - if (ret != EOK) { - DEBUG(1, ("Invalid name received\n")); - talloc_free(cmdctx); - return ret; - } - DEBUG(4, ("Requesting info for [%s] from [%s]\n", - cmdctx->name, dctx->domain)); - - ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, - dctx->domain, cmdctx->name, - dctx->legacy, - nss_cmd_getinit_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); - - ret = nss_cmd_send_error(cmdctx, ret); - if (ret == EOK) { - nss_cmd_done(cmdctx); - } - return ret; - } - - return EOK; -} - -struct nss_cmd_table nss_cmds[] = { - {SSS_NSS_GET_VERSION, nss_cmd_get_version}, - {SSS_NSS_GETPWNAM, nss_cmd_getpwnam}, - {SSS_NSS_GETPWUID, nss_cmd_getpwuid}, - {SSS_NSS_SETPWENT, nss_cmd_setpwent}, - {SSS_NSS_GETPWENT, nss_cmd_getpwent}, - {SSS_NSS_ENDPWENT, nss_cmd_endpwent}, - {SSS_NSS_GETGRNAM, nss_cmd_getgrnam}, - {SSS_NSS_GETGRGID, nss_cmd_getgrgid}, - {SSS_NSS_SETGRENT, nss_cmd_setgrent}, - {SSS_NSS_GETGRENT, nss_cmd_getgrent}, - {SSS_NSS_ENDGRENT, nss_cmd_endgrent}, - {SSS_NSS_INITGR, nss_cmd_initgroups}, - {SSS_NSS_NULL, NULL} -}; - -int nss_cmd_execute(struct cli_ctx *cctx) -{ - enum sss_nss_command cmd; - int i; - - cmd = nss_packet_get_cmd(cctx->creq->in); - - for (i = 0; nss_cmds[i].cmd != SSS_NSS_NULL; i++) { - if (cmd == nss_cmds[i].cmd) { - return nss_cmds[i].fn(cctx); - } - } - - return EINVAL; -} - diff --git a/server/nss/nsssrv_dp.c b/server/nss/nsssrv_dp.c deleted file mode 100644 index ec8aea8b1..000000000 --- a/server/nss/nsssrv_dp.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - SSSD - - NSS Responder - Data Provider Interfaces - - Copyright (C) Simo Sorce 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include "util/util.h" -#include "nss/nsssrv.h" -#include "providers/data_provider.h" -#include "sbus/sbus_client.h" -#include "providers/dp_sbus.h" - -struct nss_dp_req { - nss_dp_callback_t callback; - void *callback_ctx; - struct timed_event *te; - DBusPendingCall *pending_reply; -}; - -static int nss_dp_req_destructor(void *ptr) -{ - struct nss_dp_req *req = talloc_get_type(ptr, struct nss_dp_req); - - if (req->pending_reply) { - dbus_pending_call_cancel(req->pending_reply); - } - - return 0; -} - -static void nss_dp_send_acct_timeout(struct event_context *ev, - struct timed_event *te, - struct timeval t, void *data) -{ - struct nss_dp_req *ndp_req; - dbus_uint16_t err_maj = DP_ERR_TIMEOUT; - dbus_uint32_t err_min = EIO; - const char *err_msg = "Request timed out"; - - ndp_req = talloc_get_type(data, struct nss_dp_req); - - ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx); - - talloc_free(ndp_req); -} - -static int nss_dp_get_reply(DBusPendingCall *pending, - dbus_uint16_t *err_maj, - dbus_uint32_t *err_min, - const char **err_msg); - -static void nss_dp_send_acct_callback(DBusPendingCall *pending, void *ptr) -{ - struct nss_dp_req *ndp_req; - dbus_uint16_t err_maj; - dbus_uint32_t err_min; - const char *err_msg; - int ret; - - ndp_req = talloc_get_type(ptr, struct nss_dp_req); - - /* free timeout event and remove request destructor */ - talloc_free(ndp_req->te); - talloc_set_destructor(ndp_req, NULL); - - ret = nss_dp_get_reply(pending, &err_maj, &err_min, &err_msg); - if (ret != EOK) { - err_maj = DP_ERR_FATAL; - err_min = ret; - err_msg = "Failed to get reply from Data Provider"; - } - - ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx); - - talloc_free(ndp_req); -} - -int nss_dp_send_acct_req(struct nss_ctx *nctx, TALLOC_CTX *memctx, - nss_dp_callback_t callback, void *callback_ctx, - int timeout, const char *domain, int type, - const char *opt_name, uint32_t opt_id) -{ - struct nss_dp_req *ndp_req; - DBusMessage *msg; - DBusPendingCall *pending_reply; - DBusConnection *conn; - dbus_bool_t ret; - uint32_t be_type; - const char *attrs = "core"; - char *filter; - struct timeval tv; - - /* either, or, not both */ - if (opt_name && opt_id) { - return EINVAL; - } - - if (!domain) { - return EINVAL; - } - - switch (type) { - case NSS_DP_USER: - be_type = BE_REQ_USER; - break; - case NSS_DP_GROUP: - be_type = BE_REQ_GROUP; - break; - case NSS_DP_INITGROUPS: - be_type = BE_REQ_INITGROUPS; - break; - default: - return EINVAL; - } - - if (opt_name) { - filter = talloc_asprintf(memctx, "name=%s", opt_name); - } else if (opt_id) { - filter = talloc_asprintf(memctx, "idnumber=%u", opt_id); - } else { - filter = talloc_strdup(memctx, "name=*"); - } - if (!filter) { - talloc_free(nctx); - return ENOMEM; - } - - conn = sbus_get_connection(nctx->dp_ctx->scon_ctx); - - /* create the message */ - msg = dbus_message_new_method_call(NULL, - DP_CLI_PATH, - DP_CLI_INTERFACE, - DP_SRV_METHOD_GETACCTINFO); - if (msg == NULL) { - DEBUG(0,("Out of memory?!\n")); - return ENOMEM; - } - - DEBUG(4, ("Sending request for [%s][%u][%s][%s]\n", - domain, be_type, attrs, filter)); - - ret = dbus_message_append_args(msg, - DBUS_TYPE_STRING, &domain, - DBUS_TYPE_UINT32, &be_type, - DBUS_TYPE_STRING, &attrs, - DBUS_TYPE_STRING, &filter, - DBUS_TYPE_INVALID); - if (!ret) { - DEBUG(1,("Failed to build message\n")); - return EIO; - } - - ret = dbus_connection_send_with_reply(conn, msg, &pending_reply, - 600000 /* TODO: set timeout */); - if (!ret) { - /* - * Critical Failure - * We can't communicate on this connection - * We'll drop it using the default destructor. - */ - DEBUG(0, ("D-BUS send failed.\n")); - dbus_message_unref(msg); - return EIO; - } - - ndp_req = talloc_zero(memctx, struct nss_dp_req); - if (!ndp_req) { - dbus_message_unref(msg); - return ENOMEM; - } - ndp_req->callback = callback; - ndp_req->callback_ctx = callback_ctx; - - /* set up destructor */ - ndp_req->pending_reply = pending_reply; - talloc_set_destructor((TALLOC_CTX *)ndp_req, nss_dp_req_destructor); - - /* setup the timeout handler */ - gettimeofday(&tv, NULL); - tv.tv_sec += timeout/1000; - tv.tv_usec += (timeout%1000) * 1000; - ndp_req->te = event_add_timed(nctx->ev, memctx, tv, - nss_dp_send_acct_timeout, ndp_req); - - /* Set up the reply handler */ - dbus_pending_call_set_notify(pending_reply, - nss_dp_send_acct_callback, - ndp_req, NULL); - dbus_message_unref(msg); - - return EOK; -} - -static int nss_dp_get_reply(DBusPendingCall *pending, - dbus_uint16_t *err_maj, - dbus_uint32_t *err_min, - const char **err_msg) -{ - DBusMessage *reply; - DBusError dbus_error; - dbus_bool_t ret; - int type; - int err = EOK; - - dbus_error_init(&dbus_error); - - reply = dbus_pending_call_steal_reply(pending); - if (!reply) { - /* reply should never be null. This function shouldn't be called - * until reply is valid or timeout has occurred. If reply is NULL - * here, something is seriously wrong and we should bail out. - */ - DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n")); - - /* FIXME: Destroy this connection ? */ - err = EIO; - goto done; - } - - type = dbus_message_get_type(reply); - switch (type) { - case DBUS_MESSAGE_TYPE_METHOD_RETURN: - ret = dbus_message_get_args(reply, &dbus_error, - DBUS_TYPE_UINT16, err_maj, - DBUS_TYPE_UINT32, err_min, - DBUS_TYPE_STRING, err_msg, - DBUS_TYPE_INVALID); - if (!ret) { - DEBUG(1,("Filed to parse message\n")); - /* FIXME: Destroy this connection ? */ - if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); - err = EIO; - goto done; - } - - DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n", - (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg)); - - break; - - case DBUS_MESSAGE_TYPE_ERROR: - DEBUG(0,("The Data Provider returned an error [%s]\n", - dbus_message_get_error_name(reply))); - /* Falling through to default intentionally*/ - default: - /* - * Timeout or other error occurred or something - * unexpected happened. - * It doesn't matter which, because either way we - * know that this connection isn't trustworthy. - * We'll destroy it now. - */ - - /* FIXME: Destroy this connection ? */ - err = EIO; - } - -done: - dbus_pending_call_unref(pending); - dbus_message_unref(reply); - - return err; -} - -static int nss_dp_identity(DBusMessage *message, struct sbus_conn_ctx *sconn) -{ - dbus_uint16_t version = DATA_PROVIDER_VERSION; - dbus_uint16_t clitype = DP_CLI_FRONTEND; - const char *cliname = "NSS"; - const char *nullname = ""; - DBusMessage *reply; - dbus_bool_t ret; - - DEBUG(4,("Sending ID reply: (%d,%d,%s)\n", - clitype, version, cliname)); - - reply = dbus_message_new_method_return(message); - if (!reply) return ENOMEM; - - ret = dbus_message_append_args(reply, - DBUS_TYPE_UINT16, &clitype, - DBUS_TYPE_UINT16, &version, - DBUS_TYPE_STRING, &cliname, - DBUS_TYPE_STRING, &nullname, - DBUS_TYPE_INVALID); - if (!ret) { - dbus_message_unref(reply); - return EIO; - } - - /* send reply back */ - sbus_conn_send_reply(sconn, reply); - dbus_message_unref(reply); - - return EOK; -} - -struct sbus_method nss_dp_methods[] = { - { DP_CLI_METHOD_IDENTITY, nss_dp_identity }, - { NULL, NULL } -}; - -struct nss_dp_pvt_ctx { - struct nss_ctx *nctx; - struct sbus_method *methods; - time_t last_retry; - int retries; -}; - -static int nss_dp_conn_destructor(void *data); -static void nss_dp_reconnect(struct event_context *ev, - struct timed_event *te, - struct timeval tv, void *data); - -static void nss_dp_conn_reconnect(struct nss_dp_pvt_ctx *pvt) -{ - struct nss_ctx *nctx; - struct timed_event *te; - struct timeval tv; - struct sbus_method_ctx *sm_ctx; - char *sbus_address; - time_t now; - int ret; - - now = time(NULL); - - /* reset retry if last reconnect was > 60 sec. ago */ - if (pvt->last_retry + 60 < now) pvt->retries = 0; - if (pvt->retries >= 3) { - DEBUG(4, ("Too many reconnect retries! Giving up\n")); - return; - } - - pvt->last_retry = now; - pvt->retries++; - - nctx = pvt->nctx; - - ret = dp_get_sbus_address(nctx, nctx->cdb, &sbus_address); - if (ret != EOK) { - DEBUG(0, ("Could not locate data provider address.\n")); - return; - } - - ret = dp_init_sbus_methods(nctx, pvt->methods, &sm_ctx); - if (ret != EOK) { - DEBUG(0, ("Could not initialize SBUS methods.\n")); - return; - } - - ret = sbus_client_init(nctx, nctx->ev, - sbus_address, sm_ctx, - pvt, nss_dp_conn_destructor, - &nctx->dp_ctx); - if (ret != EOK) { - DEBUG(4, ("Failed to reconnect [%d(%s)]!\n", ret, strerror(ret))); - - tv.tv_sec = now +5; - tv.tv_usec = 0; - te = event_add_timed(nctx->ev, nctx, tv, nss_dp_reconnect, pvt); - if (te == NULL) { - DEBUG(4, ("Failed to add timed event! Giving up\n")); - } else { - DEBUG(4, ("Retrying in 5 seconds\n")); - } - } -} - -static void nss_dp_reconnect(struct event_context *ev, - struct timed_event *te, - struct timeval tv, void *data) -{ - struct nss_dp_pvt_ctx *pvt; - - pvt = talloc_get_type(data, struct nss_dp_pvt_ctx); - - nss_dp_conn_reconnect(pvt); -} - -int nss_dp_conn_destructor(void *data) -{ - struct nss_dp_pvt_ctx *pvt; - struct sbus_conn_ctx *scon; - - scon = talloc_get_type(data, struct sbus_conn_ctx); - if (!scon) return 0; - - /* if this is a regular disconnect just quit */ - if (sbus_conn_disconnecting(scon)) return 0; - - pvt = talloc_get_type(sbus_conn_get_private_data(scon), - struct nss_dp_pvt_ctx); - if (pvt) return 0; - - nss_dp_conn_reconnect(pvt); - - return 0; -} - -int nss_dp_init(struct nss_ctx *nctx) -{ - struct nss_dp_pvt_ctx *pvt; - - pvt = talloc_zero(nctx, struct nss_dp_pvt_ctx); - if (!pvt) return ENOMEM; - - pvt->nctx = nctx; - pvt->methods = nss_dp_methods; - - nss_dp_conn_reconnect(pvt); - - return EOK; -} - diff --git a/server/nss/nsssrv_packet.c b/server/nss/nsssrv_packet.c deleted file mode 100644 index f79087d4e..000000000 --- a/server/nss/nsssrv_packet.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - SSSD - - NSS Responder, command parser - - Copyright (C) Simo Sorce 2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include "talloc.h" -#include "util/util.h" -#include "nss/nsssrv.h" - -#define NSSSRV_PACKET_MEM_SIZE 512 - -struct nss_packet { - size_t memsize; - uint8_t *buffer; - - /* header */ - uint32_t *len; - uint32_t *cmd; - uint32_t *status; - uint32_t *reserved; - - uint8_t *body; - - /* io pointer */ - size_t iop; -}; - -/* - * Allocate a new packet structure - * - * - if size is defined use it otherwise the default packet will be - * NSSSRV_PACKET_MEM_SIZE bytes. - */ -int nss_packet_new(TALLOC_CTX *mem_ctx, size_t size, - enum sss_nss_command cmd, - struct nss_packet **rpacket) -{ - struct nss_packet *packet; - - packet = talloc(mem_ctx, struct nss_packet); - if (!packet) return ENOMEM; - - if (size) { - int n = (size + SSS_NSS_HEADER_SIZE) % NSSSRV_PACKET_MEM_SIZE; - packet->memsize = (n + 1) * NSSSRV_PACKET_MEM_SIZE; - } else { - packet->memsize = NSSSRV_PACKET_MEM_SIZE; - } - - packet->buffer = talloc_size(packet, packet->memsize); - if (!packet->buffer) { - talloc_free(packet); - return ENOMEM; - } - memset(packet->buffer, 0, SSS_NSS_HEADER_SIZE); - - packet->len = &((uint32_t *)packet->buffer)[0]; - packet->cmd = &((uint32_t *)packet->buffer)[1]; - packet->status = &((uint32_t *)packet->buffer)[2]; - packet->reserved = &((uint32_t *)packet->buffer)[3]; - packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4]; - - *(packet->len) = size + SSS_NSS_HEADER_SIZE; - *(packet->cmd) = cmd; - - packet->iop = 0; - - *rpacket = packet; - - return EOK; -} - -/* grows a packet size only in NSSSRV_PACKET_MEM_SIZE chunks */ -int nss_packet_grow(struct nss_packet *packet, size_t size) -{ - size_t totlen, len; - uint8_t *newmem; - - if (size == 0) { - return EOK; - } - - totlen = packet->memsize; - len = *packet->len + size; - - /* make sure we do not overflow */ - if (totlen < len) { - int n = len % NSSSRV_PACKET_MEM_SIZE + 1; - totlen += n * NSSSRV_PACKET_MEM_SIZE; - if (totlen < len) { - return EINVAL; - } - } - - if (totlen > packet->memsize) { - newmem = talloc_realloc_size(packet, packet->buffer, totlen); - if (!newmem) { - return ENOMEM; - } - - packet->memsize = totlen; - packet->buffer = newmem; - packet->len = &((uint32_t *)packet->buffer)[0]; - packet->cmd = &((uint32_t *)packet->buffer)[1]; - packet->status = &((uint32_t *)packet->buffer)[2]; - packet->reserved = &((uint32_t *)packet->buffer)[3]; - packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4]; - } - - *(packet->len) += size; - - return 0; -} - -int nss_packet_recv(struct nss_packet *packet, int fd) -{ - size_t rb; - size_t len; - void *buf; - - buf = packet->buffer + packet->iop; - if (packet->iop > 4) len = *packet->len - packet->iop; - else len = packet->memsize - packet->iop; - - /* check for wrapping */ - if (len > packet->memsize) { - return EINVAL; - } - - errno = 0; - rb = recv(fd, buf, len, 0); - - if (rb == -1 && errno == EAGAIN) { - return EAGAIN; - } - - if (rb == 0) { - return ENODATA; - } - - if (*packet->len > packet->memsize) { - return EINVAL; - } - - packet->iop += rb; - if (packet->iop < 4) { - return EAGAIN; - } - - if (packet->iop < *packet->len) { - return EAGAIN; - } - - return EOK; -} - -int nss_packet_send(struct nss_packet *packet, int fd) -{ - size_t rb; - size_t len; - void *buf; - - buf = packet->buffer + packet->iop; - len = *packet->len - packet->iop; - - errno = 0; - rb = send(fd, buf, len, 0); - - if (rb == -1 && errno == EAGAIN) { - return EAGAIN; - } - - if (rb == 0) { - return EIO; - } - - packet->iop += rb; - - if (packet->iop < *packet->len) { - return EAGAIN; - } - - return EOK; -} - -enum sss_nss_command nss_packet_get_cmd(struct nss_packet *packet) -{ - return (enum sss_nss_command)(*packet->cmd); -} - -void nss_packet_get_body(struct nss_packet *packet, uint8_t **body, size_t *blen) -{ - *body = packet->body; - *blen = *packet->len - SSS_NSS_HEADER_SIZE; -} - -void nss_packet_set_error(struct nss_packet *packet, int error) -{ - *(packet->status) = error; -} diff --git a/server/providers/data_provider.c b/server/providers/data_provider.c index c6dc8d124..a0e3b08d2 100644 --- a/server/providers/data_provider.c +++ b/server/providers/data_provider.c @@ -29,6 +29,8 @@ #include #include #include +#include + #include "popt.h" #include "util/util.h" #include "confdb/confdb.h" @@ -39,6 +41,7 @@ #include "dp_interfaces.h" #include "monitor/monitor_sbus.h" #include "monitor/monitor_interfaces.h" +#include "responder/pam/pamsrv.h" struct dp_backend; struct dp_frontend; @@ -88,9 +91,11 @@ struct sbus_method mon_sbus_methods[] = { }; static int dp_get_account_info(DBusMessage *message, struct sbus_conn_ctx *sconn); +static int dp_pamhandler(DBusMessage *message, struct sbus_conn_ctx *sconn); struct sbus_method dp_sbus_methods[] = { { DP_SRV_METHOD_GETACCTINFO, dp_get_account_info }, + { DP_SRV_METHOD_PAMHANDLER, dp_pamhandler }, { NULL, NULL } }; @@ -721,6 +726,275 @@ respond: return EOK; } +static void be_got_pam_reply(DBusPendingCall *pending, void *data) +{ + struct dp_be_request *bereq; + DBusMessage *reply; + DBusConnection *conn; + DBusError dbus_error; + dbus_bool_t ret; + uint32_t pam_status; + char *domain; + int type; + + bereq = talloc_get_type(data, struct dp_be_request); + dbus_error_init(&dbus_error); + + reply = dbus_pending_call_steal_reply(pending); + if (!reply) { + /* reply should never be null. This function shouldn't be called + * until reply is valid or timeout has occurred. If reply is NULL + * here, something is seriously wrong and we should bail out. + */ + DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n")); + + /* Destroy this connection */ + sbus_disconnect(bereq->be->dpcli->conn_ctx); + goto done; + } + + type = dbus_message_get_type(reply); + switch (type) { + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + ret = dbus_message_get_args(reply, &dbus_error, + DBUS_TYPE_UINT32, &pam_status, + DBUS_TYPE_STRING, &domain, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed to parse message, killing connection\n")); + if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); + sbus_disconnect(bereq->be->dpcli->conn_ctx); + pam_status = PAM_SYSTEM_ERR; + domain = ""; + goto done; + } + + DEBUG(4, ("Got reply (%d, %s) from %s(%s)\n", pam_status, domain, + bereq->be->name, bereq->be->domain)); + + break; + + case DBUS_MESSAGE_TYPE_ERROR: + DEBUG(0,("The Data Provider returned an error [%s], closing connection.\n", + dbus_message_get_error_name(reply))); + /* Falling through to default intentionally*/ + default: + /* + * Timeout or other error occurred or something + * unexpected happened. + * It doesn't matter which, because either way we + * know that this connection isn't trustworthy. + * We'll destroy it now. + */ + DEBUG(1,("Maybe timeout?\n")); + sbus_disconnect(bereq->be->dpcli->conn_ctx); + goto done; + } + + conn = sbus_get_connection(bereq->req->src_cli->conn_ctx); + ret = dbus_message_append_args(bereq->req->reply, + DBUS_TYPE_UINT32, &pam_status, + DBUS_TYPE_STRING, &domain, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1, ("Failed to build reply ... frontend will wait for timeout ...\n")); + talloc_free(bereq->req); + goto done; + } + + /* finally send it */ + dbus_connection_send(conn, bereq->req->reply, NULL); + dbus_message_unref(bereq->req->reply); + talloc_free(bereq->req); + +done: + dbus_pending_call_unref(pending); + dbus_message_unref(reply); +} + +static int dp_call_pamhandler(struct dp_be_request *bereq, struct pam_data *pd) +{ + DBusMessage *msg; + DBusPendingCall *pending_reply; + DBusConnection *conn; + dbus_bool_t ret; + + conn = sbus_get_connection(bereq->be->dpcli->conn_ctx); + + /* create the message */ + msg = dbus_message_new_method_call(NULL, + DP_CLI_PATH, + DP_CLI_INTERFACE, + DP_CLI_METHOD_PAMHANDLER); + if (msg == NULL) { + DEBUG(0,("Out of memory?!\n")); + return ENOMEM; + } + + DEBUG(4, ("Sending request with to following data\n")); + DEBUG_PAM_DATA(4, pd); + + ret = dbus_message_append_args(msg, + DBUS_TYPE_INT32, &(pd->cmd), + DBUS_TYPE_STRING, &(pd->domain), + DBUS_TYPE_STRING, &(pd->user), + DBUS_TYPE_STRING, &(pd->service), + DBUS_TYPE_STRING, &(pd->tty), + DBUS_TYPE_STRING, &(pd->ruser), + DBUS_TYPE_STRING, &(pd->rhost), + DBUS_TYPE_INT32, &(pd->authtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->authtok), + (pd->authtok_size), + DBUS_TYPE_INT32, &(pd->newauthtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->newauthtok), + pd->newauthtok_size, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed to build message\n")); + return EIO; + } + + ret = dbus_connection_send_with_reply(conn, msg, &pending_reply, + 600000 /* TODO: set timeout */); + if (!ret) { + /* + * Critical Failure + * We can't communicate on this connection + * We'll drop it using the default destructor. + */ + DEBUG(0, ("D-BUS send failed.\n")); + dbus_message_unref(msg); + return EIO; + } + + /* Set up the reply handler */ + dbus_pending_call_set_notify(pending_reply, be_got_pam_reply, + bereq, NULL); + dbus_message_unref(msg); + + return EOK; +} + +static int dp_pamhandler(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + DBusMessage *reply; + DBusError dbus_error; + struct dp_client *dpcli; + struct dp_backend *dpbe; + struct dp_be_request *bereq; + struct dp_request *dpreq = NULL; + dbus_bool_t dbret; + void *user_data; + int ret; + struct pam_data *pd; + int pam_status=PAM_SUCCESS; + int domain_found=0; + + user_data = sbus_conn_get_private_data(sconn); + if (!user_data) return EINVAL; + dpcli = talloc_get_type(user_data, struct dp_client); + if (!dpcli) return EINVAL; + +/* FIXME: free arrays returned by dbus_message_get_args() */ + pd = talloc(NULL, struct pam_data); + if (!pd) return ENOMEM; + + dbus_error_init(&dbus_error); + + ret = dbus_message_get_args(message, &dbus_error, + DBUS_TYPE_INT32, &(pd->cmd), + DBUS_TYPE_STRING, &(pd->domain), + DBUS_TYPE_STRING, &(pd->user), + DBUS_TYPE_STRING, &(pd->service), + DBUS_TYPE_STRING, &(pd->tty), + DBUS_TYPE_STRING, &(pd->ruser), + DBUS_TYPE_STRING, &(pd->rhost), + DBUS_TYPE_INT32, &(pd->authtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->authtok), + &(pd->authtok_size), + DBUS_TYPE_INT32, &(pd->newauthtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->newauthtok), + &(pd->newauthtok_size), + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(0,("Failed, to parse message!\n")); + if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); + talloc_free(pd); + return EIO; + } + + DEBUG(4, ("Got the following data:\n")); + DEBUG_PAM_DATA(4, pd); + + reply = dbus_message_new_method_return(message); + if (!reply) { + DEBUG(0,("Out of memory?!\n")); + talloc_free(pd); + return ENOMEM; + } + + dpreq = talloc(dpcli->dpctx, struct dp_request); + if (!dpreq) { + ret = ENOMEM; + pam_status = PAM_ABORT; + goto respond; + } + + dpreq->reply = reply; + dpreq->src_cli = dpcli; + dpreq->pending_replies = 0; + /* FIXME: add handling of default domain */ + dpbe = dpcli->dpctx->be_list; + while (dpbe) { + DEBUG(4, ("Checking [%s][%s]\n", pd->domain, dpbe->domain)); + if (strcasecmp(dpbe->domain, pd->domain) == 0 ) { + domain_found=1; + bereq = talloc(dpreq, struct dp_be_request); + if (!bereq) { + DEBUG(1, ("Out of memory while sending requests\n")); + dpbe = dpbe->next; + continue; + } + bereq->req = dpreq; + bereq->be = dpbe; + DEBUG(4, ("Sending wildcard request to [%s]\n", dpbe->domain)); + ret = dp_call_pamhandler(bereq, pd); + if (ret != EOK) { + DEBUG(2,("Failed to dispatch request to %s\n", dpbe->domain)); + dpbe = dpbe->next; + continue; + } + dpreq->pending_replies++; + } + dpbe = dpbe->next; + } + + if (domain_found) { + talloc_free(pd); + return EOK; + } + + pam_status = PAM_MODULE_UNKNOWN; + +respond: + dbret = dbus_message_append_args(reply, + DBUS_TYPE_UINT32, &pam_status, + DBUS_TYPE_STRING, &(pd->domain), + DBUS_TYPE_INVALID); + if (!dbret) return EIO; + + /* send reply back immediately */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + talloc_free(pd); + return EOK; +} + static int dp_backend_destructor(void *ctx) { struct dp_backend *dpbe = talloc_get_type(ctx, struct dp_backend); diff --git a/server/providers/data_provider.h b/server/providers/data_provider.h index 5dc402efa..2d0e37e17 100644 --- a/server/providers/data_provider.h +++ b/server/providers/data_provider.h @@ -52,11 +52,17 @@ #define DP_CLI_FRONTEND 0x0002 #define DP_CLI_TYPE_MASK 0x0003 +#define DP_CLI_PROVIDE_ACC_INFO (1<<8) +#define DP_CLI_PROVIDE_PAM (1<<9) +#define DP_CLI_PROVIDE_POLICY (1<<10) + #define DP_CLI_METHOD_IDENTITY "getIdentity" #define DP_CLI_METHOD_ONLINE "getOnline" #define DP_CLI_METHOD_GETACCTINFO "getAccountInfo" +#define DP_CLI_METHOD_PAMHANDLER "pamHandler" #define DP_SRV_METHOD_GETACCTINFO "getAccountInfo" +#define DP_SRV_METHOD_PAMHANDLER "pamHandler" #define DP_ERR_OK 0 #define DP_ERR_OFFLINE 1 diff --git a/server/providers/data_provider_be.c b/server/providers/data_provider_be.c index 26a78efb2..9ed3d2ce6 100644 --- a/server/providers/data_provider_be.c +++ b/server/providers/data_provider_be.c @@ -29,6 +29,10 @@ #include #include #include + +#include +#include + #include "popt.h" #include "util/util.h" #include "confdb/confdb.h" @@ -40,7 +44,7 @@ #include "providers/dp_sbus.h" #include "monitor/monitor_sbus.h" #include "monitor/monitor_interfaces.h" - +#include "../sss_client/sss_cli.h" typedef int (*be_init_fn_t)(TALLOC_CTX *, struct be_mod_ops **, void **); @@ -56,11 +60,13 @@ struct sbus_method mon_sbus_methods[] = { static int be_identity(DBusMessage *message, struct sbus_conn_ctx *sconn); static int be_check_online(DBusMessage *message, struct sbus_conn_ctx *sconn); static int be_get_account_info(DBusMessage *message, struct sbus_conn_ctx *sconn); +static int be_pam_handler(DBusMessage *message, struct sbus_conn_ctx *sconn); struct sbus_method be_methods[] = { { DP_CLI_METHOD_IDENTITY, be_identity }, { DP_CLI_METHOD_ONLINE, be_check_online }, { DP_CLI_METHOD_GETACCTINFO, be_get_account_info }, + { DP_CLI_METHOD_PAMHANDLER, be_pam_handler }, { NULL, NULL } }; @@ -503,8 +509,138 @@ done: return EOK; } +static void be_pam_handler_callback(struct be_req *req, int status, + const char *errstr) { + struct be_pam_handler *ph; + DBusMessage *reply; + DBusConnection *conn; + dbus_bool_t dbret; + + ph = talloc_get_type(req->req_data, struct be_pam_handler); + + DEBUG(4, ("Sending result [%d][%s]\n", ph->pam_status, ph->domain)); + reply = (DBusMessage *)req->pvt; + dbret = dbus_message_append_args(reply, + DBUS_TYPE_UINT32, &(ph->pam_status), + DBUS_TYPE_STRING, &(ph->domain), + DBUS_TYPE_INVALID); + if (!dbret) { + DEBUG(1, ("Failed to generate dbus reply\n")); + return; + } + + conn = sbus_get_connection(req->be_ctx->dp_ctx->scon_ctx); + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + + DEBUG(4, ("Sent result [%d][%s]\n", ph->pam_status, ph->domain)); + + talloc_free(req); +} + +static int be_pam_handler(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + DBusError dbus_error; + DBusMessage *reply; + struct be_ctx *ctx; + struct be_pam_handler *req; + struct be_req *be_req; + dbus_bool_t ret; + void *user_data; + struct pam_data *pd; + uint32_t pam_status=99; + + user_data = sbus_conn_get_private_data(sconn); + if (!user_data) return EINVAL; + ctx = talloc_get_type(user_data, struct be_ctx); + if (!ctx) return EINVAL; + + pd = talloc(NULL, struct pam_data); + if (!pd) return ENOMEM; + + dbus_error_init(&dbus_error); + + reply = dbus_message_new_method_return(message); + if (!reply) { + DEBUG(1, ("dbus_message_new_method_return failed, cannot send reply.\n")); + talloc_free(pd); + return ENOMEM; + } + ret = dbus_message_get_args(message, &dbus_error, + DBUS_TYPE_INT32, &(pd->cmd), + DBUS_TYPE_STRING, &(pd->domain), + DBUS_TYPE_STRING, &(pd->user), + DBUS_TYPE_STRING, &(pd->service), + DBUS_TYPE_STRING, &(pd->tty), + DBUS_TYPE_STRING, &(pd->ruser), + DBUS_TYPE_STRING, &(pd->rhost), + DBUS_TYPE_INT32, &(pd->authtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->authtok), + &(pd->authtok_size), + DBUS_TYPE_INT32, &(pd->newauthtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->newauthtok), + &(pd->newauthtok_size), + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed, to parse message!\n")); + talloc_free(pd); + return EIO; + } + + DEBUG(4, ("Got request with the following data\n")); + DEBUG_PAM_DATA(4, pd); + + be_req = talloc(ctx, struct be_req); + if (!be_req) { + pam_status = PAM_SYSTEM_ERR; + goto done; + } + be_req->be_ctx = ctx; + be_req->fn = be_pam_handler_callback; + be_req->pvt = reply; + + req = talloc(be_req, struct be_pam_handler); + if (!req) { + pam_status = PAM_SYSTEM_ERR; + goto done; + } + req->domain = ctx->domain; + req->pd = pd; + + be_req->req_data = req; + + ret = be_file_request(ctx, ctx->ops->pam_handler, be_req); + if (ret != EOK) { + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + return EOK; + +done: + if (be_req) { + talloc_free(be_req); + } + + DEBUG(4, ("Sending result [%d][%s]\n", pam_status, ctx->domain)); + ret = dbus_message_append_args(reply, + DBUS_TYPE_UINT32, &pam_status, + DBUS_TYPE_STRING, &ctx->domain, + DBUS_TYPE_INVALID); + if (!ret) return EIO; + + /* send reply back immediately */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + talloc_free(pd); + return EOK; +} + /* mon_cli_init * sbus channel to the monitor daemon */ static int mon_cli_init(struct be_ctx *ctx) diff --git a/server/providers/dp_backend.h b/server/providers/dp_backend.h index 765c16ee7..cdd2c51b7 100644 --- a/server/providers/dp_backend.h +++ b/server/providers/dp_backend.h @@ -24,6 +24,7 @@ #include "providers/data_provider.h" #include "db/sysdb.h" +#include "responder/pam/pamsrv.h" struct be_mod_ops; @@ -64,11 +65,18 @@ struct be_online_req { int online; }; +struct be_pam_handler { + int pam_status; + const char *domain; + struct pam_data *pd; +}; + typedef void (*be_req_fn_t)(struct be_req *); struct be_mod_ops { be_req_fn_t check_online; be_req_fn_t get_account_info; + be_req_fn_t pam_handler; }; #endif /* __DP_BACKEND_H___ */ diff --git a/server/providers/ldap_be.c b/server/providers/ldap_be.c new file mode 100644 index 000000000..6c573b7d6 --- /dev/null +++ b/server/providers/ldap_be.c @@ -0,0 +1,676 @@ +/* + SSSD + + LDAP Backend Module + + Authors: + Sumit Bose + + Copyright (C) 2008 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include + +#include + +#include "util/util.h" +#include "providers/dp_backend.h" +#include "db/sysdb.h" +#include "../sss_client/sss_cli.h" + +struct ldap_ctx { + char *ldap_uri; + char *default_bind_dn; + char *default_authtok_type; + uint32_t default_authtok_size; + char *default_authtok; +}; + +struct ldap_ops; +struct ldap_req; + +struct ldap_ops { + void (*op)(struct ldap_req *); + struct ldap_ops *next; +}; + +enum ldap_be_ops { + LDAP_NOOP = 0x0000, + LDAP_OP_INIT = 0x0001, + LDAP_CHECK_INIT_RESULT, + LDAP_CHECK_STD_BIND, + LDAP_CHECK_SEARCH_DN_RESULT, + LDAP_CHECK_USER_BIND +}; + +struct ldap_req { + struct be_req *req; + struct pam_data *pd; + struct ldap_ctx *ldap_ctx; + LDAP *ldap; + struct ldap_ops *ops; + char *user_dn; + /*event_timed_handler_t next_task;*/ + event_fd_handler_t next_task; + enum ldap_be_ops next_op; + int msgid; +}; + +static int schedule_next_task(struct ldap_req *lr, struct timeval tv, + event_timed_handler_t task) { + int ret; + struct timed_event *te; + struct timeval timeout; + + ret = gettimeofday(&timeout, NULL); + if (ret == -1) { + DEBUG(1, ("gettimeofday failed [%d][%s].\n", errno, strerror(errno))); + return ret; + } + timeout.tv_sec += tv.tv_sec; + timeout.tv_usec += tv.tv_usec; + + + te = event_add_timed(lr->req->be_ctx->ev, lr, timeout, task, lr); + if ( te == NULL ) { + return EIO; + } + + return EOK; +} + +static int wait_for_fd(struct ldap_req *lr) { + int ret; + int fd; + struct fd_event *fde; + + ret = ldap_get_option(lr->ldap, LDAP_OPT_DESC, &fd); + if ( ret != LDAP_OPT_SUCCESS ) { + DEBUG(1, ("ldap_get_option failed.\n")); + return ret; + } + + fde = event_add_fd(lr->req->be_ctx->ev, lr, fd, EVENT_FD_READ, lr->next_task, lr); + if ( fde == NULL ) { + return EIO; + } + + return EOK; +} + +static int ldap_pam_chauthtok(struct ldap_req *lr) { + BerElement *ber=NULL; + int ret; + int pam_status=PAM_SUCCESS; + struct berval *bv; + int msgid; + LDAPMessage *result=NULL; + int ldap_ret; + + ber = ber_alloc_t( LBER_USE_DER ); + if ( ber == NULL ) { + DEBUG(1, ("ber_alloc_t failed.\n")); + return PAM_SYSTEM_ERR; + } + + ret = ber_printf( ber, "{tststs}", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, + lr->user_dn, + LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, lr->pd->authtok, + LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, lr->pd->newauthtok); + if ( ret == -1 ) { + DEBUG(1, ("ber_printf failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + + ret = ber_flatten(ber, &bv); + if ( ret == -1 ) { + DEBUG(1, ("ber_flatten failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + + ret = ldap_extended_operation(lr->ldap, LDAP_EXOP_MODIFY_PASSWD, bv, + NULL, NULL, &msgid); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_extended_operation failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + + ret = ldap_result(lr->ldap, msgid, FALSE, NULL, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, NULL, NULL, + NULL, 0); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_parse_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto cleanup; + } + DEBUG(3, ("LDAP_EXOP_MODIFY_PASSWD result: [%d][%s]\n", ldap_ret, + ldap_err2string(ldap_ret))); + + ldap_msgfree(result); + + if ( ldap_ret != LDAP_SUCCESS ) pam_status = PAM_SYSTEM_ERR; + +cleanup: + ber_bvfree(bv); + ber_free(ber, 1); + return pam_status; +} + +static int ldap_be_init(struct ldap_req *lr) { + int ret; + int status=EOK; + int ldap_vers = LDAP_VERSION3; + int msgid; + + ret = ldap_initialize(&(lr->ldap), lr->ldap_ctx->ldap_uri); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_initialize failed: %s\n", strerror(errno))); + return EIO; + } + + /* LDAPv3 is needed for TLS */ + ret = ldap_set_option(lr->ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_vers); + if ( ret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("ldap_set_option failed: %s\n", ldap_err2string(ret))); + status = EIO; + goto cleanup; + } + + /* For now TLS is forced. Maybe it would be necessary to make this + * configurable to allow people to expose their passwords over the + * network. */ + ret = ldap_start_tls(lr->ldap, NULL, NULL, &msgid); + if ( ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_start_tls failed: %s\n", ldap_err2string(ret))); + status = EIO; + goto cleanup; + } + + lr->msgid = msgid; + + return EOK; + +cleanup: + ldap_unbind_ext(lr->ldap, NULL, NULL); + lr->ldap = NULL; + return status; +} + +static int ldap_be_bind(struct ldap_req *lr) { + int ret; + int msgid; + char *dn=NULL; + struct berval pw; + + pw.bv_len = 0; + pw.bv_val = NULL; + + if ( lr->user_dn != NULL ) { + dn = lr->user_dn; + pw.bv_len = lr->pd->authtok_size; + pw.bv_val = (char *) lr->pd->authtok; + } + if ( lr->user_dn == NULL && lr->ldap_ctx->default_bind_dn != NULL ) { + dn = lr->ldap_ctx->default_bind_dn; + pw.bv_len = lr->ldap_ctx->default_authtok_size; + pw.bv_val = lr->ldap_ctx->default_authtok; + } + + DEBUG(3, ("Trying to bind as [%s][%*s]\n", dn, pw.bv_len, pw.bv_val)); + ret = ldap_sasl_bind(lr->ldap, dn, LDAP_SASL_SIMPLE, &pw, NULL, NULL, + &msgid); + if ( ret == -1 || msgid == -1 ) { + DEBUG(1, ("ldap_bind failed\n")); + return LDAP_OTHER; + } + lr->msgid = msgid; + return LDAP_SUCCESS; +} + +static void ldap_be_loop(struct event_context *ev, struct fd_event *te, + uint16_t fd, void *pvt) { + int ret; + int pam_status=PAM_SUCCESS; + int ldap_ret; + struct ldap_req *lr; + struct be_pam_handler *ph; + struct be_req *req; + LDAPMessage *result=NULL; + LDAPMessage *msg=NULL; + struct timeval no_timeout={0, 0}; + char *errmsgp = NULL; +/* FIXME: user timeout form config */ + char *filter=NULL; + char *attrs[] = { LDAP_NO_ATTRS, NULL }; + + lr = talloc_get_type(pvt, struct ldap_req); + + switch ( lr->next_op ) { + case LDAP_OP_INIT: + ret = ldap_be_init(lr); + if (ret != EOK) { + DEBUG(1, ("ldap_be_init failed.\n")); + lr->ldap = NULL; + pam_status = PAM_SYSTEM_ERR; + goto done; + } + case LDAP_CHECK_INIT_RESULT: + ret = ldap_result(lr->ldap, lr->msgid, FALSE, &no_timeout, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + if ( ret == 0 ) { + DEBUG(1, ("ldap_result not ready yet, waiting.\n")); + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_INIT_RESULT; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + } + + ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, NULL, NULL, NULL, 0); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_parse_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + DEBUG(3, ("ldap_start_tls result: [%d][%s]\n", ldap_ret, ldap_err2string(ldap_ret))); + + if ( ldap_ret != LDAP_SUCCESS ) { + DEBUG(1, ("setting up TLS failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + +/* FIXME: take care that ldap_install_tls might block */ + ret = ldap_install_tls(lr->ldap); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_install_tls failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + ret = ldap_be_bind(lr); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_be_bind failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + case LDAP_CHECK_STD_BIND: + ret = ldap_result(lr->ldap, lr->msgid, FALSE, &no_timeout, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + if ( ret == 0 ) { + DEBUG(1, ("ldap_result not ready yet, waiting.\n")); + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_STD_BIND; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + } + + ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, &errmsgp, + NULL, NULL, 0); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_parse_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + DEBUG(3, ("Bind result: [%d][%s][%s]\n", ldap_ret, + ldap_err2string(ldap_ret), errmsgp)); + if ( ldap_ret != LDAP_SUCCESS ) { + DEBUG(1, ("bind failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + filter = talloc_asprintf(lr->ldap_ctx, + "(&(uid=%s)(objectclass=posixAccount))", + lr->pd->user); + + DEBUG(4, ("calling ldap_search_ext with [%s].\n", filter)); + ret = ldap_search_ext(lr->ldap, + "ou=user,dc=my-domain,dc=com", + LDAP_SCOPE_SUBTREE, + filter, + attrs, + TRUE, + NULL, + NULL, + NULL, + 0, + &(lr->msgid)); + if ( ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_search_ext failed [%d][%s].\n", ret, ldap_err2string(ret))); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + case LDAP_CHECK_SEARCH_DN_RESULT: + ret = ldap_result(lr->ldap, lr->msgid, TRUE, &no_timeout, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + if ( ret == 0 ) { + DEBUG(1, ("ldap_result not ready yet, waiting.\n")); + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_SEARCH_DN_RESULT; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + } + + msg = ldap_first_message(lr->ldap, result); + if ( msg == NULL ) { + DEBUG(1, ("ldap_first_message failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + do { + switch ( ldap_msgtype(msg) ) { + case LDAP_RES_SEARCH_ENTRY: + if ( lr->user_dn != NULL ) { + DEBUG(1, ("Found more than one object with filter [%s].\n", + filter)); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + lr->user_dn = ldap_get_dn(lr->ldap, msg); + if ( lr->user_dn == NULL ) { + DEBUG(1, ("ldap_get_dn failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + if ( *(lr->user_dn) == '\0' ) { + DEBUG(1, ("No user found.\n")); + pam_status = PAM_USER_UNKNOWN; + goto done; + } + DEBUG(3, ("Found dn: %s\n",lr->user_dn)); + + ldap_msgfree(result); + result = NULL; + break; + default: + DEBUG(3, ("ignoring message with type %d.\n", ldap_msgtype(msg))); + } + } while( (msg=ldap_next_message(lr->ldap, msg)) != NULL ); + + ret = ldap_be_bind(lr); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_be_bind failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + case LDAP_CHECK_USER_BIND: + ret = ldap_result(lr->ldap, lr->msgid, FALSE, &no_timeout, &result); + if ( ret == -1 ) { + DEBUG(1, ("ldap_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + if ( ret == 0 ) { + DEBUG(1, ("ldap_result not ready yet, waiting.\n")); + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_USER_BIND; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + } + + ret = ldap_parse_result(lr->ldap, result, &ldap_ret, NULL, &errmsgp, + NULL, NULL, 0); + if ( ret != LDAP_SUCCESS ) { + DEBUG(1, ("ldap_parse_result failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + DEBUG(3, ("Bind result: [%d][%s][%s]\n", ldap_ret, + ldap_err2string(ldap_ret), errmsgp)); + switch (ldap_ret) { + case LDAP_SUCCESS: + pam_status = PAM_SUCCESS; + break; + case LDAP_INVALID_CREDENTIALS: + pam_status = PAM_CRED_INSUFFICIENT; + goto done; + break; + default: + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + + + +/* + ret = pam_setup_ldap_connection(lr); + if ( ret != PAM_SUCCESS ) { + DEBUG(1, ("pam_setup_ldap_connection failed.\n")); + pam_status = ret; + goto done; + } + DEBUG(3, ("Successfully connected as %s.\n", lr->user_dn)); +*/ + + switch (lr->pd->cmd) { + case SSS_PAM_AUTHENTICATE: + pam_status = PAM_SUCCESS; + break; + case SSS_PAM_CHAUTHTOK: + pam_status = ldap_pam_chauthtok(lr); + break; + case SSS_PAM_ACCT_MGMT: + case SSS_PAM_SETCRED: + case SSS_PAM_OPEN_SESSION: + case SSS_PAM_CLOSE_SESSION: + pam_status = PAM_SUCCESS; + break; + default: + DEBUG(1, ("Unknown pam command %d.\n", lr->pd->cmd)); + pam_status = PAM_SYSTEM_ERR; + } + break; + default: + DEBUG(1, ("Unknown ldap backend operation %d.\n", lr->next_op)); + pam_status = PAM_SYSTEM_ERR; + } + +done: + ldap_memfree(errmsgp); + ldap_msgfree(result); + talloc_free(filter); + if (lr->ldap != NULL ) ldap_unbind_ext(lr->ldap, NULL, NULL); + req = lr->req; + ph = talloc_get_type(lr->req->req_data, struct be_pam_handler); + ph->pam_status = pam_status; + + talloc_free(lr); + + req->fn(req, pam_status, NULL); +} + +static void ldap_start(struct event_context *ev, struct timed_event *te, + struct timeval tv, void *pvt) { + int ret; + int pam_status; + struct ldap_req *lr; + struct be_req *req; + struct be_pam_handler *ph; + + lr = talloc_get_type(pvt, struct ldap_req); + + ret = ldap_be_init(lr); + if (ret != EOK) { + DEBUG(1, ("ldap_be_init failed.\n")); + lr->ldap = NULL; + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + lr->next_task = ldap_be_loop; + lr->next_op = LDAP_CHECK_INIT_RESULT; + ret = wait_for_fd(lr); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + return; + +done: + if (lr->ldap != NULL ) ldap_unbind_ext(lr->ldap, NULL, NULL); + req = lr->req; + ph = talloc_get_type(lr->req->req_data, struct be_pam_handler); + ph->pam_status = pam_status; + + talloc_free(lr); + + req->fn(req, pam_status, NULL); +} + +static void ldap_pam_handler(struct be_req *req) { + int ret; + int pam_status=PAM_SUCCESS; + struct ldap_req *lr; + struct ldap_ctx *ldap_ctx; + struct be_pam_handler *ph; + struct pam_data *pd; + struct timeval timeout; + + ph = talloc_get_type(req->req_data, struct be_pam_handler); + pd = ph->pd; + + ldap_ctx = talloc_get_type(req->be_ctx->pvt_data, struct ldap_ctx); + + lr = talloc(req, struct ldap_req); + + lr->ldap = NULL; + lr->req = req; + lr->pd = pd; + lr->ldap_ctx = ldap_ctx; + lr->user_dn = NULL; + lr->next_task = NULL; + lr->next_op = LDAP_NOOP; + + timeout.tv_sec=0; + timeout.tv_usec=0; + ret = schedule_next_task(lr, timeout, ldap_start); + if ( ret != EOK ) { + DEBUG(1, ("schedule_next_task failed.\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + return; + +done: + talloc_free(lr); + + ph->pam_status = pam_status; + req->fn(req, pam_status, NULL); +} + +struct be_mod_ops ldap_mod_ops = { + .check_online = NULL, + .get_account_info = NULL, + .pam_handler = ldap_pam_handler +}; + + +int sssm_ldap_init(struct be_ctx *bectx, struct be_mod_ops **ops, void **pvt_data) +{ + struct ldap_ctx *ctx; + char *ldap_uri; + char *default_bind_dn; + char *default_authtok_type; + char *default_authtok; + int ret; + + ctx = talloc(bectx, struct ldap_ctx); + if (!ctx) { + return ENOMEM; + } + + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "ldapUri", "ldap://localhost", &ldap_uri); + if (ret != EOK) goto done; + ctx->ldap_uri = ldap_uri; + + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "defaultBindDn", NULL, &default_bind_dn); + if (ret != EOK) goto done; + ctx->default_bind_dn = default_bind_dn; + + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "defaultAuthtokType", NULL, &default_authtok_type); + if (ret != EOK) goto done; + ctx->default_authtok_type = default_authtok_type; + + +/* TODO: better to have a blob object than a string here */ + ret = confdb_get_string(bectx->cdb, ctx, bectx->conf_path, + "defaultAuthtok", NULL, &default_authtok); + if (ret != EOK) goto done; + ctx->default_authtok = default_authtok; + ctx->default_authtok_size = (default_authtok==NULL?0:strlen(default_authtok)); + + + + *ops = &ldap_mod_ops; + *pvt_data = ctx; + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(ctx); + } + return ret; +} diff --git a/server/providers/proxy.c b/server/providers/proxy.c index 0ec6e5302..e072ec3c7 100644 --- a/server/providers/proxy.c +++ b/server/providers/proxy.c @@ -23,9 +23,14 @@ #include #include #include + +#include +#include + #include "util/util.h" #include "providers/dp_backend.h" #include "db/sysdb.h" +#include "../sss_client/sss_cli.h" struct proxy_nss_ops { enum nss_status (*getpwnam_r)(const char *name, struct passwd *result, @@ -154,6 +159,130 @@ static void get_pw_uid(struct be_req *req, uid_t uid) return proxy_reply(req, EOK, NULL); } +struct authtok_conv { + char *authtok; + char *oldauthtok; +}; + +static int proxy_internal_conv(int num_msg, const struct pam_message **msgm, + struct pam_response **response, + void *appdata_ptr) { + int i; + struct pam_response *reply; + struct authtok_conv *auth_data; + + auth_data = talloc_get_type(appdata_ptr, struct authtok_conv); + + if (num_msg <= 0) return PAM_CONV_ERR; + + reply = (struct pam_response *) calloc(num_msg, + sizeof(struct pam_response)); + if (reply == NULL) return PAM_CONV_ERR; + + for (i=0; i < num_msg; i++) { + switch( msgm[i]->msg_style ) { + case PAM_PROMPT_ECHO_OFF: + DEBUG(4, ("Conversation message: %s.\n", msgm[i]->msg)); + reply[i].resp_retcode = 0; + reply[i].resp = strdup(auth_data->authtok); + break; + default: + DEBUG(1, ("Conversation style %d not supported.\n", + msgm[i]->msg_style)); + goto failed; + } + } + + *response = reply; + reply = NULL; + + return PAM_SUCCESS; + +failed: + free(reply); + return PAM_CONV_ERR; +} + +static void proxy_pam_handler(struct be_req *req) { + int ret; + int pam_status; + pam_handle_t *pamh=NULL; + struct authtok_conv *auth_data; + struct pam_conv conv; + struct be_pam_handler *ph; + struct pam_data *pd; + + ph = talloc_get_type(req->req_data, struct be_pam_handler); + pd = ph->pd; + + conv.conv=proxy_internal_conv; + auth_data = talloc_zero(req->be_ctx, struct authtok_conv); + conv.appdata_ptr=auth_data; + + ret = pam_start("sssd_be_test", pd->user, &conv, &pamh); + if (ret == PAM_SUCCESS) { + DEBUG(1, ("Pam transaction started.\n")); + pam_set_item(pamh, PAM_TTY, pd->tty); + if (ret != PAM_SUCCESS) { + DEBUG(1, ("Setting PAM_TTY failed: %s.\n", pam_strerror(pamh, ret))); + } + pam_set_item(pamh, PAM_RUSER, pd->ruser); + if (ret != PAM_SUCCESS) { + DEBUG(1, ("Setting PAM_RUSER failed: %s.\n", pam_strerror(pamh, ret))); + } + pam_set_item(pamh, PAM_RHOST, pd->rhost); + if (ret != PAM_SUCCESS) { + DEBUG(1, ("Setting PAM_RHOST failed: %s.\n", pam_strerror(pamh, ret))); + } + switch (pd->cmd) { + case SSS_PAM_AUTHENTICATE: +/* FIXME: \0 missing at the end */ + auth_data->authtok=(char *) pd->authtok; + auth_data->oldauthtok=NULL; + pam_status=pam_authenticate(pamh, 0); + break; + case SSS_PAM_SETCRED: + pam_status=pam_setcred(pamh, 0); + break; + case SSS_PAM_ACCT_MGMT: + pam_status=pam_acct_mgmt(pamh, 0); + break; + case SSS_PAM_OPEN_SESSION: + pam_status=pam_open_session(pamh, 0); + break; + case SSS_PAM_CLOSE_SESSION: + pam_status=pam_close_session(pamh, 0); + break; + case SSS_PAM_CHAUTHTOK: +/* FIXME: \0 missing at the end */ + auth_data->authtok=(char *) pd->newauthtok; + auth_data->oldauthtok=(char *) pd->authtok; + pam_status=pam_chauthtok(pamh, 0); + break; + default: + DEBUG(1, ("unknown PAM call")); + pam_status=PAM_ABORT; + } + + DEBUG(4, ("Pam result: [%d][%s]\n", pam_status, pam_strerror(pamh, pam_status))); + + ret = pam_end(pamh, pam_status); + if (ret != PAM_SUCCESS) { + pamh=NULL; + DEBUG(1, ("Cannot terminate pam transaction.\n")); + } + + } else { + DEBUG(1, ("Failed to initialize pam transaction.\n")); + pam_status = PAM_SYSTEM_ERR; + } + + talloc_free(auth_data); + + ph->pam_status = pam_status; + req->fn(req, EOK, NULL); +} + #define MAX_BUF_SIZE 1024*1024 /* max 1MiB */ static void enum_users(struct be_req *req) @@ -664,7 +793,8 @@ static void proxy_get_account_info(struct be_req *req) struct be_mod_ops proxy_mod_ops = { .check_online = proxy_check_online, - .get_account_info = proxy_get_account_info + .get_account_info = proxy_get_account_info, + .pam_handler = proxy_pam_handler }; static void *proxy_dlsym(void *handle, const char *functemp, char *libname) diff --git a/server/responder/common/responder_cmd.c b/server/responder/common/responder_cmd.c new file mode 100644 index 000000000..83d55e651 --- /dev/null +++ b/server/responder/common/responder_cmd.c @@ -0,0 +1,78 @@ +/* + SSSD + + SSS Client Responder, command parser + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include +#include "util/util.h" +#include "responder/common/responder_cmd.h" +#include "responder/common/responder_packet.h" + + +void sss_cmd_done(struct sss_cmd_ctx *nctx) +{ + /* now that the packet is in place, unlock queue + * making the event writable */ + EVENT_FD_WRITEABLE(nctx->cctx->cfde); + + /* free all request related data through the talloc hierarchy */ + talloc_free(nctx); +} + +int sss_cmd_get_version(struct cli_ctx *cctx) +{ + struct sss_cmd_ctx *nctx; + uint8_t *body; + size_t blen; + int ret; + + nctx = talloc(cctx, struct sss_cmd_ctx); + if (!nctx) { + return ENOMEM; + } + nctx->cctx = cctx; + + /* create response packet */ + ret = sss_packet_new(cctx->creq, sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = SSS_PROTOCOL_VERSION; + + sss_cmd_done(nctx); + return EOK; +} + +int sss_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds) +{ + enum sss_cli_command cmd; + int i; + + cmd = sss_packet_get_cmd(cctx->creq->in); + + for (i = 0; sss_cmds[i].cmd != SSS_CLI_NULL; i++) { + if (cmd == sss_cmds[i].cmd) { + return sss_cmds[i].fn(cctx); + } + } + + return EINVAL; +} diff --git a/server/responder/common/responder_cmd.h b/server/responder/common/responder_cmd.h new file mode 100644 index 000000000..ee094ad3b --- /dev/null +++ b/server/responder/common/responder_cmd.h @@ -0,0 +1,71 @@ +/* + SSSD + + SSS Client Responder, header file + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __SSSSRV_CMD_H__ +#define __SSSSRV_CMD_H__ + +#include +#include +#include "talloc.h" +#include "tevent.h" +#include "ldb.h" +#include "../sss_client/sss_cli.h" + +/* needed until nsssrv.h is updated */ +#ifndef __NSSSRV_H__ +struct cli_request { + + /* original request from the wire */ + struct sss_packet *in; + + /* reply data */ + struct sss_packet *out; +}; + +struct cli_ctx { + struct event_context *ev; + struct nss_ctx *nctx; + int cfd; + struct fd_event *cfde; + struct sockaddr_un addr; + struct cli_request *creq; + struct getent_ctx *gctx; +}; +#endif + +struct sss_cmd_ctx { + struct cli_ctx *cctx; + const char *domain; + const char *name; + uid_t id; + bool check_expiration; +}; + +struct sss_cmd_table { + enum sss_cli_command cmd; + int (*fn)(struct cli_ctx *cctx); +}; + +int sss_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds); +void sss_cmd_done(struct sss_cmd_ctx *nctx); +int sss_cmd_get_version(struct cli_ctx *cctx); + +#endif /* __SSSSRV_CMD_H__ */ diff --git a/server/responder/common/responder_common.c b/server/responder/common/responder_common.c new file mode 100644 index 000000000..b5db7d1e2 --- /dev/null +++ b/server/responder/common/responder_common.c @@ -0,0 +1,518 @@ +/* + SSSD + + Common Responder methods + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "popt.h" +#include "util/util.h" +#include "responder/nss/nsssrv.h" +#include "db/sysdb.h" +#include "confdb/confdb.h" +#include "dbus/dbus.h" +#include "sbus/sssd_dbus.h" +#include "util/btreemap.h" +#include "responder/common/responder_packet.h" +#include "responder/common/responder_cmd.h" +#include "responder/common/responder_dp.h" +#include "providers/data_provider.h" +#include "monitor/monitor_sbus.h" +#include "monitor/monitor_interfaces.h" +#include "sbus/sbus_client.h" + +static void set_nonblocking(int fd) +{ + unsigned v; + v = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, v | O_NONBLOCK); +} + +static void set_close_on_exec(int fd) +{ + unsigned v; + v = fcntl(fd, F_GETFD, 0); + fcntl(fd, F_SETFD, v | FD_CLOEXEC); +} + +static int client_destructor(struct cli_ctx *ctx) +{ + if (ctx->cfd > 0) close(ctx->cfd); + return 0; +} + +static void client_send(struct event_context *ev, struct cli_ctx *cctx) +{ + int ret; + + ret = sss_packet_send(cctx->creq->out, cctx->cfd); + if (ret == EAGAIN) { + /* not all data was sent, loop again */ + return; + } + if (ret != EOK) { + DEBUG(0, ("Failed to read request, aborting client!\n")); + talloc_free(cctx); + return; + } + + /* ok all sent */ + EVENT_FD_NOT_WRITEABLE(cctx->cfde); + EVENT_FD_READABLE(cctx->cfde); + talloc_free(cctx->creq); + cctx->creq = NULL; + return; +} + +static void client_recv(struct event_context *ev, struct cli_ctx *cctx) +{ + int ret; + + if (!cctx->creq) { + cctx->creq = talloc_zero(cctx, struct cli_request); + if (!cctx->creq) { + DEBUG(0, ("Failed to alloc request, aborting client!\n")); + talloc_free(cctx); + return; + } + } + + if (!cctx->creq->in) { + ret = sss_packet_new(cctx->creq, NSS_PACKET_MAX_RECV_SIZE, + 0, &cctx->creq->in); + if (ret != EOK) { + DEBUG(0, ("Failed to alloc request, aborting client!\n")); + talloc_free(cctx); + return; + } + } + + ret = sss_packet_recv(cctx->creq->in, cctx->cfd); + switch (ret) { + case EOK: + /* do not read anymore */ + EVENT_FD_NOT_READABLE(cctx->cfde); + /* execute command */ + ret = sss_cmd_execute(cctx, cctx->nctx->sss_cmds); + if (ret != EOK) { + DEBUG(0, ("Failed to execute request, aborting client!\n")); + talloc_free(cctx); + } + /* past this point cctx can be freed at any time by callbacks + * in case of error, do not use it */ + return; + + case EAGAIN: + /* need to read still some data, loop again */ + break; + + case EINVAL: + DEBUG(6, ("Invalid data from client, closing connection!\n")); + talloc_free(cctx); + break; + + case ENODATA: + DEBUG(5, ("Client disconnected!\n")); + talloc_free(cctx); + break; + + default: + DEBUG(6, ("Failed to read request, aborting client!\n")); + talloc_free(cctx); + } + + return; +} + +static void client_fd_handler(struct event_context *ev, + struct fd_event *fde, + uint16_t flags, void *ptr) +{ + struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx); + + if (flags & EVENT_FD_READ) { + client_recv(ev, cctx); + return; + } + if (flags & EVENT_FD_WRITE) { + client_send(ev, cctx); + return; + } +} + +static void accept_fd_handler(struct event_context *ev, + struct fd_event *fde, + uint16_t flags, void *ptr) +{ + /* accept and attach new event handler */ + struct nss_ctx *nctx = talloc_get_type(ptr, struct nss_ctx); + struct cli_ctx *cctx; + socklen_t len; + + cctx = talloc_zero(nctx, struct cli_ctx); + if (!cctx) { + struct sockaddr_un addr; + int fd; + DEBUG(0, ("Out of memory trying to setup client context!\n")); + /* accept and close to signal the client we have a problem */ + memset(&addr, 0, sizeof(addr)); + len = sizeof(addr); + fd = accept(nctx->lfd, (struct sockaddr *)&addr, &len); + if (fd == -1) { + return; + } + close(fd); + return; + } + + len = sizeof(cctx->addr); + cctx->cfd = accept(nctx->lfd, (struct sockaddr *)&cctx->addr, &len); + if (cctx->cfd == -1) { + DEBUG(1, ("Accept failed [%s]", strerror(errno))); + talloc_free(cctx); + return; + } + + cctx->cfde = event_add_fd(ev, cctx, cctx->cfd, + EVENT_FD_READ, client_fd_handler, cctx); + if (!cctx->cfde) { + close(cctx->cfd); + talloc_free(cctx); + DEBUG(2, ("Failed to queue client handler\n")); + } + + cctx->ev = ev; + cctx->nctx = nctx; + + talloc_set_destructor(cctx, client_destructor); + + DEBUG(4, ("Client connected!\n")); + + return; +} + +static int sss_sbus_init(struct nss_ctx *nctx) +{ + int ret; + char *sbus_address; + struct service_sbus_ctx *ss_ctx; + struct sbus_method_ctx *sm_ctx; + + /* Set up SBUS connection to the monitor */ + ret = monitor_get_sbus_address(nctx, nctx->cdb, &sbus_address); + if (ret != EOK) { + DEBUG(0, ("Could not locate monitor address.\n")); + return ret; + } + + ret = monitor_init_sbus_methods(nctx, nctx->sss_sbus_methods, &sm_ctx); + if (ret != EOK) { + DEBUG(0, ("Could not initialize SBUS methods.\n")); + return ret; + } + + ret = sbus_client_init(nctx, nctx->ev, + sbus_address, sm_ctx, + NULL /* Private Data */, + NULL /* Destructor */, + &ss_ctx); + if (ret != EOK) { + DEBUG(0, ("Failed to connect to monitor services.\n")); + return ret; + } + + /* Set up NSS-specific listeners */ + /* None currently used */ + + nctx->ss_ctx = ss_ctx; + + return EOK; +} + +/* create a unix socket and listen to it */ +static int set_unix_socket(struct nss_ctx *nctx) +{ + struct sockaddr_un addr; + char *default_pipe; + int ret; + + default_pipe = talloc_asprintf(nctx, "%s/%s", PIPE_PATH, + nctx->sss_pipe_name); + if (!default_pipe) { + return ENOMEM; + } + + ret = confdb_get_string(nctx->cdb, nctx, + nctx->confdb_socket_path, "unixSocket", + default_pipe, &nctx->sock_name); + if (ret != EOK) { + talloc_free(default_pipe); + return ret; + } + talloc_free(default_pipe); + + nctx->lfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (nctx->lfd == -1) { + return EIO; + } + + /* Set the umask so that permissions are set right on the socket. + * It must be readable and writable by anybody on the system. */ + umask(0111); + + set_nonblocking(nctx->lfd); + set_close_on_exec(nctx->lfd); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, nctx->sock_name, sizeof(addr.sun_path)); + + /* make sure we have no old sockets around */ + unlink(nctx->sock_name); + + if (bind(nctx->lfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + DEBUG(0,("Unable to bind on socket '%s'\n", nctx->sock_name)); + goto failed; + } + if (listen(nctx->lfd, 10) != 0) { + DEBUG(0,("Unable to listen on socket '%s'\n", nctx->sock_name)); + goto failed; + } + + nctx->lfde = event_add_fd(nctx->ev, nctx, nctx->lfd, + EVENT_FD_READ, accept_fd_handler, nctx); + + /* we want default permissions on created files to be very strict, + so set our umask to 0177 */ + umask(0177); + return EOK; + +failed: + /* we want default permissions on created files to be very strict, + so set our umask to 0177 */ + umask(0177); + close(nctx->lfd); + return EIO; +} + +/* domain names are case insensitive for now + * NOTE: this function is not utf-8 safe, + * only ASCII names for now */ +static int _domain_comparator(const void *key1, const void *key2) +{ + return strcasecmp((const char *)key1, (const char *)key2); +} + +static int sss_init_domains(struct nss_ctx *nctx) +{ + char *path; + char **domains; + char *provider; + TALLOC_CTX *tmp_ctx; + struct nss_domain_info *info; + int ret, i, c; + int retval; + + tmp_ctx = talloc_new(nctx); + ret = confdb_get_domains(nctx->cdb, tmp_ctx, &domains); + if (ret != EOK) { + retval = ret; + goto done; + } + + i = 0; + c = 0; + while (domains[i] != NULL) { + DEBUG(3, ("Adding domain %s to the map\n", domains[i])); + + path = talloc_asprintf(tmp_ctx, "config/domains/%s", domains[i]); + if (!path) { + retval = ENOMEM; + goto done; + } + + /* alloc on tmp_ctx, it will be stolen by btreemap_set_value */ + info = talloc_zero(tmp_ctx, struct nss_domain_info); + if (!info) { + retval = ENOMEM; + goto done; + } + + /* Build the basedn for this domain */ + info->basedn = talloc_asprintf(info, SYSDB_DOM_BASE, domains[i]); + DEBUG(3, ("BaseDN: %s\n", info->basedn)); + + ret = confdb_get_int(nctx->cdb, tmp_ctx, path, + "enumerate", false, &(info->enumerate)); + if (ret != EOK) { + DEBUG(0, ("Failed to fetch enumerate for [%s]!\n", domains[i])); + } + + ret = confdb_get_bool(nctx->cdb, tmp_ctx, path, + "legacy", false, &(info->legacy)); + if (ret != EOK) { + DEBUG(0, ("Failed to fetch legacy for [%s]!\n", domains[i])); + } + + ret = confdb_get_string(nctx->cdb, tmp_ctx, path, "provider", + NULL, &provider); + if (ret != EOK) { + DEBUG(0, ("Failed to fetch provider for [%s]!\n", domains[i])); + } + if (provider) info->has_provider = true; + + ret = btreemap_set_value(nctx, &nctx->domain_map, + domains[i], info, + _domain_comparator); + if (ret != EOK) { + DEBUG(1, ("Failed to store domain info, aborting!\n")); + retval = ret; + goto done; + } + + i++; + c++; + } + if (c == 0) { + /* No domains configured! + * Note: this should never happen, since LOCAL should + * always be configured */ + DEBUG(0, ("No domains configured on this client!\n")); + retval = EINVAL; + goto done; + } + + ret = confdb_get_string(nctx->cdb, nctx, + "config/domains", "default", + NULL, &nctx->default_domain); + if (ret != EOK) { + retval = ret; + goto done; + } + + retval = EOK; + +done: + talloc_free(tmp_ctx); + return retval; +} + +int sss_process_init(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct confdb_ctx *cdb, + struct sbus_method sss_sbus_methods[], + struct sss_cmd_table sss_cmds[], + const char *sss_pipe_name, + const char *confdb_socket_path, + struct sbus_method dp_methods[]) +{ + struct nss_ctx *nctx; + int ret; + + nctx = talloc_zero(mem_ctx, struct nss_ctx); + if (!nctx) { + DEBUG(0, ("fatal error initializing nss_ctx\n")); + return ENOMEM; + } + nctx->ev = ev; + nctx->cdb = cdb; + nctx->sss_sbus_methods = sss_sbus_methods; + nctx->sss_cmds = sss_cmds; + nctx->sss_pipe_name = sss_pipe_name; + nctx->confdb_socket_path = confdb_socket_path; + nctx->dp_methods = dp_methods; + + ret = sss_init_domains(nctx); + if (ret != EOK) { + DEBUG(0, ("fatal error setting up domain map\n")); + return ret; + } + + ret = sss_sbus_init(nctx); + if (ret != EOK) { + DEBUG(0, ("fatal error setting up message bus\n")); + return ret; + } + + ret = sss_dp_init(nctx, nctx->dp_methods); + if (ret != EOK) { + DEBUG(0, ("fatal error setting up backend connector\n")); + return ret; + } + + ret = sysdb_init(nctx, ev, cdb, NULL, &nctx->sysdb); + if (ret != EOK) { + DEBUG(0, ("fatal error initializing nss_ctx\n")); + return ret; + } + + /* after all initializations we are ready to listen on our socket */ + ret = set_unix_socket(nctx); + if (ret != EOK) { + DEBUG(0, ("fatal error initializing socket\n")); + return ret; + } + + nctx->cache_timeout = 600; /* FIXME: read from conf */ + + DEBUG(1, ("NSS Initialization complete\n")); + + return EOK; +} + +int sss_parse_name(TALLOC_CTX *memctx, + const char *fullname, + struct btreemap *domain_map, + const char **domain, const char **name) { + char *delim; + struct btreemap *node; + int ret; + + if ((delim = strchr(fullname, NSS_DOMAIN_DELIM)) != NULL) { + + /* Check for registered domain */ + ret = btreemap_search_key(domain_map, (void *)(delim+1), &node); + if (ret != BTREEMAP_FOUND) { + /* No such domain was registered. Return EINVAL. + * TODO: alternative approach? + * Alternatively, we could simply fail down to + * below, treating the entire construct as the + * full name if the domain is unspecified. + */ + return EINVAL; + } + + *name = talloc_strndup(memctx, fullname, delim-fullname); + *domain = talloc_strdup(memctx, delim+1); + } + else { + *name = talloc_strdup(memctx, fullname); + *domain = NULL; + } + + return EOK; +} diff --git a/server/responder/common/responder_common.h b/server/responder/common/responder_common.h new file mode 100644 index 000000000..433b242e2 --- /dev/null +++ b/server/responder/common/responder_common.h @@ -0,0 +1,24 @@ +#include "sbus/sssd_dbus.h" +#include "responder/common/responder_cmd.h" +#include "util/btreemap.h" + +/* SSS_DOMAIN_DELIM can be specified in config.h */ +#include "config.h" +#ifndef SSS_DOMAIN_DELIM +#define SSS_DOMAIN_DELIM '@' +#endif + + +int sss_process_init(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct confdb_ctx *cdb, + struct sbus_method sss_sbus_methods[], + struct sss_cmd_table sss_cmds[], + const char *sss_pipe_name, + const char *confdb_socket_path, + struct sbus_method dp_methods[]); + +int sss_parse_name(TALLOC_CTX *memctx, + const char *fullname, + struct btreemap *domain_map, + const char **domain, const char **name); diff --git a/server/responder/common/responder_dp.c b/server/responder/common/responder_dp.c new file mode 100644 index 000000000..a024674b3 --- /dev/null +++ b/server/responder/common/responder_dp.c @@ -0,0 +1,123 @@ + +#include +#include +#include "responder/nss/nsssrv.h" +#include "util/util.h" +#include "responder/common/responder_packet.h" +#include "responder/common/responder_common.h" +#include "providers/data_provider.h" +#include "sbus/sbus_client.h" +#include "providers/dp_sbus.h" + +struct sss_dp_pvt_ctx { + struct nss_ctx *nctx; + struct sbus_method *methods; + time_t last_retry; + int retries; +}; + +static int sss_dp_conn_destructor(void *data); +static void sss_dp_reconnect(struct event_context *ev, + struct timed_event *te, + struct timeval tv, void *data); + +static void sss_dp_conn_reconnect(struct sss_dp_pvt_ctx *pvt) +{ + struct nss_ctx *nctx; + struct timed_event *te; + struct timeval tv; + struct sbus_method_ctx *sm_ctx; + char *sbus_address; + time_t now; + int ret; + + now = time(NULL); + + /* reset retry if last reconnect was > 60 sec. ago */ + if (pvt->last_retry + 60 < now) pvt->retries = 0; + if (pvt->retries >= 3) { + DEBUG(4, ("Too many reconnect retries! Giving up\n")); + return; + } + + pvt->last_retry = now; + pvt->retries++; + + nctx = pvt->nctx; + + ret = dp_get_sbus_address(nctx, nctx->cdb, &sbus_address); + if (ret != EOK) { + DEBUG(0, ("Could not locate data provider address.\n")); + return; + } + + ret = dp_init_sbus_methods(nctx, pvt->methods, &sm_ctx); + if (ret != EOK) { + DEBUG(0, ("Could not initialize SBUS methods.\n")); + return; + } + + ret = sbus_client_init(nctx, nctx->ev, + sbus_address, sm_ctx, + pvt, sss_dp_conn_destructor, + &nctx->dp_ctx); + if (ret != EOK) { + DEBUG(4, ("Failed to reconnect [%d(%s)]!\n", ret, strerror(ret))); + + tv.tv_sec = now +5; + tv.tv_usec = 0; + te = event_add_timed(nctx->ev, nctx, tv, sss_dp_reconnect, pvt); + if (te == NULL) { + DEBUG(4, ("Failed to add timed event! Giving up\n")); + } else { + DEBUG(4, ("Retrying in 5 seconds\n")); + } + } +} + +static void sss_dp_reconnect(struct event_context *ev, + struct timed_event *te, + struct timeval tv, void *data) +{ + struct sss_dp_pvt_ctx *pvt; + + pvt = talloc_get_type(data, struct sss_dp_pvt_ctx); + + sss_dp_conn_reconnect(pvt); +} + +int sss_dp_conn_destructor(void *data) +{ + struct sss_dp_pvt_ctx *pvt; + struct sbus_conn_ctx *scon; + + scon = talloc_get_type(data, struct sbus_conn_ctx); + if (!scon) return 0; + + /* if this is a regular disconnect just quit */ + if (sbus_conn_disconnecting(scon)) return 0; + + pvt = talloc_get_type(sbus_conn_get_private_data(scon), + struct sss_dp_pvt_ctx); + if (pvt) return 0; + + sss_dp_conn_reconnect(pvt); + + return 0; +} + +int sss_dp_init(struct nss_ctx *nctx, struct sbus_method *dp_methods) +{ + struct sss_dp_pvt_ctx *pvt; + + pvt = talloc_zero(nctx, struct sss_dp_pvt_ctx); + if (!pvt) return ENOMEM; + + pvt->nctx = nctx; + pvt->methods = dp_methods; + + sss_dp_conn_reconnect(pvt); + + return EOK; +} + diff --git a/server/responder/common/responder_dp.h b/server/responder/common/responder_dp.h new file mode 100644 index 000000000..2eeddf5b0 --- /dev/null +++ b/server/responder/common/responder_dp.h @@ -0,0 +1,4 @@ +#include "responder/nss/nsssrv.h" +#include "sbus/sssd_dbus.h" + +int sss_dp_init(struct nss_ctx *nctx, struct sbus_method dp_methods[]); diff --git a/server/responder/common/responder_packet.c b/server/responder/common/responder_packet.c new file mode 100644 index 000000000..247629342 --- /dev/null +++ b/server/responder/common/responder_packet.c @@ -0,0 +1,220 @@ +/* + SSSD + + SSS Client Responder, command parser + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include "talloc.h" +#include "util/util.h" +#include "responder/common/responder_packet.h" + +#define SSSSRV_PACKET_MEM_SIZE 512 + +struct sss_packet { + size_t memsize; + uint8_t *buffer; + + /* header */ + uint32_t *len; + uint32_t *cmd; + uint32_t *status; + uint32_t *reserved; + + uint8_t *body; + + /* io pointer */ + size_t iop; +}; + +/* + * Allocate a new packet structure + * + * - if size is defined use it otherwise the default packet will be + * SSSSRV_PACKET_MEM_SIZE bytes. + */ +int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size, + enum sss_cli_command cmd, + struct sss_packet **rpacket) +{ + struct sss_packet *packet; + + packet = talloc(mem_ctx, struct sss_packet); + if (!packet) return ENOMEM; + + if (size) { + int n = (size + SSS_NSS_HEADER_SIZE) % SSSSRV_PACKET_MEM_SIZE; + packet->memsize = (n + 1) * SSSSRV_PACKET_MEM_SIZE; + } else { + packet->memsize = SSSSRV_PACKET_MEM_SIZE; + } + + packet->buffer = talloc_size(packet, packet->memsize); + if (!packet->buffer) { + talloc_free(packet); + return ENOMEM; + } + memset(packet->buffer, 0, SSS_NSS_HEADER_SIZE); + + packet->len = &((uint32_t *)packet->buffer)[0]; + packet->cmd = &((uint32_t *)packet->buffer)[1]; + packet->status = &((uint32_t *)packet->buffer)[2]; + packet->reserved = &((uint32_t *)packet->buffer)[3]; + packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4]; + + *(packet->len) = size + SSS_NSS_HEADER_SIZE; + *(packet->cmd) = cmd; + + packet->iop = 0; + + *rpacket = packet; + + return EOK; +} + +/* grows a packet size only in SSSSRV_PACKET_MEM_SIZE chunks */ +int sss_packet_grow(struct sss_packet *packet, size_t size) +{ + size_t totlen, len; + uint8_t *newmem; + + if (size == 0) { + return EOK; + } + + totlen = packet->memsize; + len = *packet->len + size; + + /* make sure we do not overflow */ + if (totlen < len) { + int n = len % SSSSRV_PACKET_MEM_SIZE + 1; + totlen += n * SSSSRV_PACKET_MEM_SIZE; + if (totlen < len) { + return EINVAL; + } + } + + if (totlen > packet->memsize) { + newmem = talloc_realloc_size(packet, packet->buffer, totlen); + if (!newmem) { + return ENOMEM; + } + + packet->memsize = totlen; + packet->buffer = newmem; + packet->len = &((uint32_t *)packet->buffer)[0]; + packet->cmd = &((uint32_t *)packet->buffer)[1]; + packet->status = &((uint32_t *)packet->buffer)[2]; + packet->reserved = &((uint32_t *)packet->buffer)[3]; + packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4]; + } + + *(packet->len) += size; + + return 0; +} + +int sss_packet_recv(struct sss_packet *packet, int fd) +{ + size_t rb; + size_t len; + void *buf; + + buf = packet->buffer + packet->iop; + if (packet->iop > 4) len = *packet->len - packet->iop; + else len = packet->memsize - packet->iop; + + /* check for wrapping */ + if (len > packet->memsize) { + return EINVAL; + } + + errno = 0; + rb = recv(fd, buf, len, 0); + + if (rb == -1 && errno == EAGAIN) { + return EAGAIN; + } + + if (rb == 0) { + return ENODATA; + } + + if (*packet->len > packet->memsize) { + return EINVAL; + } + + packet->iop += rb; + if (packet->iop < 4) { + return EAGAIN; + } + + if (packet->iop < *packet->len) { + return EAGAIN; + } + + return EOK; +} + +int sss_packet_send(struct sss_packet *packet, int fd) +{ + size_t rb; + size_t len; + void *buf; + + buf = packet->buffer + packet->iop; + len = *packet->len - packet->iop; + + errno = 0; + rb = send(fd, buf, len, 0); + + if (rb == -1 && errno == EAGAIN) { + return EAGAIN; + } + + if (rb == 0) { + return EIO; + } + + packet->iop += rb; + + if (packet->iop < *packet->len) { + return EAGAIN; + } + + return EOK; +} + +enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet) +{ + return (enum sss_cli_command)(*packet->cmd); +} + +void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen) +{ + *body = packet->body; + *blen = *packet->len - SSS_NSS_HEADER_SIZE; +} + +void sss_packet_set_error(struct sss_packet *packet, int error) +{ + *(packet->status) = error; +} diff --git a/server/responder/common/responder_packet.h b/server/responder/common/responder_packet.h new file mode 100644 index 000000000..1a8529466 --- /dev/null +++ b/server/responder/common/responder_packet.h @@ -0,0 +1,39 @@ +/* + SSSD + + SSS Client Responder, header file + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __SSSSRV_PACKET_H__ +#define __SSSSRV_PACKET_H__ + +#include "../sss_client/sss_cli.h" + +struct sss_packet; + +int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size, + enum sss_cli_command cmd, + struct sss_packet **rpacket); +int sss_packet_grow(struct sss_packet *packet, size_t size); +int sss_packet_recv(struct sss_packet *packet, int fd); +int sss_packet_send(struct sss_packet *packet, int fd); +enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet); +void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen); +void sss_packet_set_error(struct sss_packet *packet, int error); + +#endif /* __SSSSRV_PACKET_H__ */ diff --git a/server/responder/nss/nsssrv.c b/server/responder/nss/nsssrv.c new file mode 100644 index 000000000..93490c46d --- /dev/null +++ b/server/responder/nss/nsssrv.c @@ -0,0 +1,590 @@ +/* + SSSD + + NSS Responder + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "popt.h" +#include "util/util.h" +#include "responder/nss/nsssrv.h" +#include "db/sysdb.h" +#include "confdb/confdb.h" +#include "dbus/dbus.h" +#include "sbus/sssd_dbus.h" +#include "util/btreemap.h" +#include "responder/common/responder_packet.h" +#include "providers/data_provider.h" +#include "monitor/monitor_sbus.h" +#include "monitor/monitor_interfaces.h" +#include "sbus/sbus_client.h" + +#define SSS_NSS_PIPE_NAME "nss" + +static int service_identity(DBusMessage *message, struct sbus_conn_ctx *sconn); +static int service_pong(DBusMessage *message, struct sbus_conn_ctx *sconn); +static int service_reload(DBusMessage *message, struct sbus_conn_ctx *sconn); +static int nss_init_domains(struct nss_ctx *nctx); +static int _domain_comparator(const void *key1, const void *key2); + +struct sbus_method nss_sbus_methods[] = { + {SERVICE_METHOD_IDENTITY, service_identity}, + {SERVICE_METHOD_PING, service_pong}, + {SERVICE_METHOD_RELOAD, service_reload}, + {NULL, NULL} +}; + +static void set_nonblocking(int fd) +{ + unsigned v; + v = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, v | O_NONBLOCK); +} + +static void set_close_on_exec(int fd) +{ + unsigned v; + v = fcntl(fd, F_GETFD, 0); + fcntl(fd, F_SETFD, v | FD_CLOEXEC); +} + +static int client_destructor(struct cli_ctx *ctx) +{ + if (ctx->cfd > 0) close(ctx->cfd); + return 0; +} + +static void client_send(struct event_context *ev, struct cli_ctx *cctx) +{ + int ret; + + ret = sss_packet_send(cctx->creq->out, cctx->cfd); + if (ret == EAGAIN) { + /* not all data was sent, loop again */ + return; + } + if (ret != EOK) { + DEBUG(0, ("Failed to read request, aborting client!\n")); + talloc_free(cctx); + return; + } + + /* ok all sent */ + EVENT_FD_NOT_WRITEABLE(cctx->cfde); + EVENT_FD_READABLE(cctx->cfde); + talloc_free(cctx->creq); + cctx->creq = NULL; + return; +} + +static void client_recv(struct event_context *ev, struct cli_ctx *cctx) +{ + int ret; + + if (!cctx->creq) { + cctx->creq = talloc_zero(cctx, struct cli_request); + if (!cctx->creq) { + DEBUG(0, ("Failed to alloc request, aborting client!\n")); + talloc_free(cctx); + return; + } + } + + if (!cctx->creq->in) { + ret = sss_packet_new(cctx->creq, NSS_PACKET_MAX_RECV_SIZE, + 0, &cctx->creq->in); + if (ret != EOK) { + DEBUG(0, ("Failed to alloc request, aborting client!\n")); + talloc_free(cctx); + return; + } + } + + ret = sss_packet_recv(cctx->creq->in, cctx->cfd); + switch (ret) { + case EOK: + /* do not read anymore */ + EVENT_FD_NOT_READABLE(cctx->cfde); + /* execute command */ + ret = nss_cmd_execute(cctx); + if (ret != EOK) { + DEBUG(0, ("Failed to execute request, aborting client!\n")); + talloc_free(cctx); + } + /* past this point cctx can be freed at any time by callbacks + * in case of error, do not use it */ + return; + + case EAGAIN: + /* need to read still some data, loop again */ + break; + + case EINVAL: + DEBUG(6, ("Invalid data from client, closing connection!\n")); + talloc_free(cctx); + break; + + case ENODATA: + DEBUG(5, ("Client disconnected!\n")); + talloc_free(cctx); + break; + + default: + DEBUG(6, ("Failed to read request, aborting client!\n")); + talloc_free(cctx); + } + + return; +} + +static void client_fd_handler(struct event_context *ev, + struct fd_event *fde, + uint16_t flags, void *ptr) +{ + struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx); + + if (flags & EVENT_FD_READ) { + client_recv(ev, cctx); + return; + } + if (flags & EVENT_FD_WRITE) { + client_send(ev, cctx); + return; + } +} + +static void accept_fd_handler(struct event_context *ev, + struct fd_event *fde, + uint16_t flags, void *ptr) +{ + /* accept and attach new event handler */ + struct nss_ctx *nctx = talloc_get_type(ptr, struct nss_ctx); + struct cli_ctx *cctx; + socklen_t len; + + cctx = talloc_zero(nctx, struct cli_ctx); + if (!cctx) { + struct sockaddr_un addr; + int fd; + DEBUG(0, ("Out of memory trying to setup client context!\n")); + /* accept and close to signal the client we have a problem */ + memset(&addr, 0, sizeof(addr)); + len = sizeof(addr); + fd = accept(nctx->lfd, (struct sockaddr *)&addr, &len); + if (fd == -1) { + return; + } + close(fd); + return; + } + + len = sizeof(cctx->addr); + cctx->cfd = accept(nctx->lfd, (struct sockaddr *)&cctx->addr, &len); + if (cctx->cfd == -1) { + DEBUG(1, ("Accept failed [%s]", strerror(errno))); + talloc_free(cctx); + return; + } + + cctx->cfde = event_add_fd(ev, cctx, cctx->cfd, + EVENT_FD_READ, client_fd_handler, cctx); + if (!cctx->cfde) { + close(cctx->cfd); + talloc_free(cctx); + DEBUG(2, ("Failed to queue client handler\n")); + } + + cctx->ev = ev; + cctx->nctx = nctx; + + talloc_set_destructor(cctx, client_destructor); + + DEBUG(4, ("Client connected!\n")); + + return; +} + +static int service_identity(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + dbus_uint16_t version = NSS_SBUS_SERVICE_VERSION; + const char *name = NSS_SBUS_SERVICE_NAME; + DBusMessage *reply; + dbus_bool_t ret; + + DEBUG(4,("Sending ID reply: (%s,%d)\n", + name, version)); + + reply = dbus_message_new_method_return(message); + if (!reply) return ENOMEM; + + ret = dbus_message_append_args(reply, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT16, &version, + DBUS_TYPE_INVALID); + if (!ret) { + dbus_message_unref(reply); + return EIO; + } + + /* send reply back */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + return EOK; +} + +static int service_pong(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + DBusMessage *reply; + dbus_bool_t ret; + + reply = dbus_message_new_method_return(message); + if (!reply) return ENOMEM; + + ret = dbus_message_append_args(reply, DBUS_TYPE_INVALID); + if (!ret) { + dbus_message_unref(reply); + return EIO; + } + + /* send reply back */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + return EOK; +} + +static int service_reload(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + /* Monitor calls this function when we need to reload + * our configuration information. Perform whatever steps + * are needed to update the configuration objects. + */ + + /* Send an empty reply to acknowledge receipt */ + return service_pong(message, sconn); +} + +static int nss_sbus_init(struct nss_ctx *nctx) +{ + int ret; + char *sbus_address; + struct service_sbus_ctx *ss_ctx; + struct sbus_method_ctx *sm_ctx; + + /* Set up SBUS connection to the monitor */ + ret = monitor_get_sbus_address(nctx, nctx->cdb, &sbus_address); + if (ret != EOK) { + DEBUG(0, ("Could not locate monitor address.\n")); + return ret; + } + + ret = monitor_init_sbus_methods(nctx, nss_sbus_methods, &sm_ctx); + if (ret != EOK) { + DEBUG(0, ("Could not initialize SBUS methods.\n")); + return ret; + } + + ret = sbus_client_init(nctx, nctx->ev, + sbus_address, sm_ctx, + NULL /* Private Data */, + NULL /* Destructor */, + &ss_ctx); + if (ret != EOK) { + DEBUG(0, ("Failed to connect to monitor services.\n")); + return ret; + } + + /* Set up NSS-specific listeners */ + /* None currently used */ + + nctx->ss_ctx = ss_ctx; + + return EOK; +} + +/* create a unix socket and listen to it */ +static int set_unix_socket(struct nss_ctx *nctx) +{ + struct sockaddr_un addr; + char *default_pipe; + int ret; + + default_pipe = talloc_asprintf(nctx, "%s/%s", PIPE_PATH, SSS_NSS_PIPE_NAME); + if (!default_pipe) { + return ENOMEM; + } + + ret = confdb_get_string(nctx->cdb, nctx, + "config/services/nss", "unixSocket", + default_pipe, &nctx->sock_name); + if (ret != EOK) { + talloc_free(default_pipe); + return ret; + } + talloc_free(default_pipe); + + nctx->lfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (nctx->lfd == -1) { + return EIO; + } + + /* Set the umask so that permissions are set right on the socket. + * It must be readable and writable by anybody on the system. */ + umask(0111); + + set_nonblocking(nctx->lfd); + set_close_on_exec(nctx->lfd); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, nctx->sock_name, sizeof(addr.sun_path)); + + /* make sure we have no old sockets around */ + unlink(nctx->sock_name); + + if (bind(nctx->lfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + DEBUG(0,("Unable to bind on socket '%s'\n", nctx->sock_name)); + goto failed; + } + if (listen(nctx->lfd, 10) != 0) { + DEBUG(0,("Unable to listen on socket '%s'\n", nctx->sock_name)); + goto failed; + } + + nctx->lfde = event_add_fd(nctx->ev, nctx, nctx->lfd, + EVENT_FD_READ, accept_fd_handler, nctx); + + /* we want default permissions on created files to be very strict, + so set our umask to 0177 */ + umask(0177); + return EOK; + +failed: + /* we want default permissions on created files to be very strict, + so set our umask to 0177 */ + umask(0177); + close(nctx->lfd); + return EIO; +} + +/* domain names are case insensitive for now + * NOTE: this function is not utf-8 safe, + * only ASCII names for now */ +static int _domain_comparator(const void *key1, const void *key2) +{ + return strcasecmp((const char *)key1, (const char *)key2); +} + +static int nss_init_domains(struct nss_ctx *nctx) +{ + char *path; + char **domains; + char *provider; + TALLOC_CTX *tmp_ctx; + struct nss_domain_info *info; + int ret, i, c; + int retval; + + tmp_ctx = talloc_new(nctx); + ret = confdb_get_domains(nctx->cdb, tmp_ctx, &domains); + if (ret != EOK) { + retval = ret; + goto done; + } + + i = 0; + c = 0; + while (domains[i] != NULL) { + DEBUG(3, ("Adding domain %s to the map\n", domains[i])); + + path = talloc_asprintf(tmp_ctx, "config/domains/%s", domains[i]); + if (!path) { + retval = ENOMEM; + goto done; + } + + /* alloc on tmp_ctx, it will be stolen by btreemap_set_value */ + info = talloc_zero(tmp_ctx, struct nss_domain_info); + if (!info) { + retval = ENOMEM; + goto done; + } + + /* Build the basedn for this domain */ + info->basedn = talloc_asprintf(info, SYSDB_DOM_BASE, domains[i]); + DEBUG(3, ("BaseDN: %s\n", info->basedn)); + + ret = confdb_get_int(nctx->cdb, tmp_ctx, path, + "enumerate", false, &(info->enumerate)); + if (ret != EOK) { + DEBUG(0, ("Failed to fetch enumerate for [%s]!\n", domains[i])); + } + + ret = confdb_get_bool(nctx->cdb, tmp_ctx, path, + "legacy", false, &(info->legacy)); + if (ret != EOK) { + DEBUG(0, ("Failed to fetch legacy for [%s]!\n", domains[i])); + } + + ret = confdb_get_string(nctx->cdb, tmp_ctx, path, "provider", + NULL, &provider); + if (ret != EOK) { + DEBUG(0, ("Failed to fetch provider for [%s]!\n", domains[i])); + } + if (provider) info->has_provider = true; + + ret = btreemap_set_value(nctx, &nctx->domain_map, + domains[i], info, + _domain_comparator); + if (ret != EOK) { + DEBUG(1, ("Failed to store domain info, aborting!\n")); + retval = ret; + goto done; + } + + i++; + c++; + } + if (c == 0) { + /* No domains configured! + * Note: this should never happen, since LOCAL should + * always be configured */ + DEBUG(0, ("No domains configured on this client!\n")); + retval = EINVAL; + goto done; + } + + ret = confdb_get_string(nctx->cdb, nctx, + "config/domains", "default", + NULL, &nctx->default_domain); + if (ret != EOK) { + retval = ret; + goto done; + } + + retval = EOK; + +done: + talloc_free(tmp_ctx); + return retval; +} + +int nss_process_init(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct confdb_ctx *cdb) +{ + struct nss_ctx *nctx; + int ret; + + nctx = talloc_zero(mem_ctx, struct nss_ctx); + if (!nctx) { + DEBUG(0, ("fatal error initializing nss_ctx\n")); + return ENOMEM; + } + nctx->ev = ev; + nctx->cdb = cdb; + + ret = nss_init_domains(nctx); + if (ret != EOK) { + DEBUG(0, ("fatal error setting up domain map\n")); + return ret; + } + + ret = nss_sbus_init(nctx); + if (ret != EOK) { + DEBUG(0, ("fatal error setting up message bus\n")); + return ret; + } + + ret = nss_dp_init(nctx); + if (ret != EOK) { + DEBUG(0, ("fatal error setting up backend connector\n")); + return ret; + } + + ret = sysdb_init(nctx, ev, cdb, NULL, &nctx->sysdb); + if (ret != EOK) { + DEBUG(0, ("fatal error initializing nss_ctx\n")); + return ret; + } + + /* after all initializations we are ready to listen on our socket */ + ret = set_unix_socket(nctx); + if (ret != EOK) { + DEBUG(0, ("fatal error initializing socket\n")); + return ret; + } + + nctx->cache_timeout = 600; /* FIXME: read from conf */ + + DEBUG(1, ("NSS Initialization complete\n")); + + return EOK; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + struct main_context *main_ctx; + int ret; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + { NULL } + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + + poptFreeContext(pc); + + /* set up things like debug , signals, daemonization, etc... */ + ret = server_setup("sssd[nss]", 0, &main_ctx); + if (ret != EOK) return 2; + + ret = nss_process_init(main_ctx, + main_ctx->event_ctx, + main_ctx->confdb_ctx); + if (ret != EOK) return 3; + + /* loop on main */ + server_loop(main_ctx); + + return 0; +} + diff --git a/server/responder/nss/nsssrv.h b/server/responder/nss/nsssrv.h new file mode 100644 index 000000000..03dc35af2 --- /dev/null +++ b/server/responder/nss/nsssrv.h @@ -0,0 +1,118 @@ +/* + SSSD + + NSS Responder, header file + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __NSSSRV_H__ +#define __NSSSRV_H__ + +#include +#include +#include "talloc.h" +#include "tevent.h" +#include "ldb.h" +#include "../sss_client/sss_cli.h" +#include "dbus/dbus.h" +#include "sbus/sssd_dbus.h" +#include "responder/common/responder_cmd.h" + +#define NSS_SBUS_SERVICE_VERSION 0x0001 +#define NSS_SBUS_SERVICE_NAME "nss" + +#define NSS_PACKET_MAX_RECV_SIZE 1024 + +/* NSS_DOMAIN_DELIM can be specified in config.h */ +#include "config.h" +#ifndef NSS_DOMAIN_DELIM +#define NSS_DOMAIN_DELIM '@' +#endif + +#define NSS_ENUM_USERS 0x01 +#define NSS_ENUM_GROUPS 0x02 +#define NSS_ENUM_ALL 0x03 + +struct sysdb_ctx; +struct getent_ctx; + +struct nss_ctx { + struct event_context *ev; + struct fd_event *lfde; + int lfd; + struct sysdb_ctx *sysdb; + struct confdb_ctx *cdb; + char *sock_name; + struct service_sbus_ctx *ss_ctx; + struct service_sbus_ctx *dp_ctx; + struct btreemap *domain_map; + char *default_domain; + + int cache_timeout; + + struct sbus_method *sss_sbus_methods; + struct sss_cmd_table *sss_cmds; + const char *sss_pipe_name; + const char *confdb_socket_path; + struct sbus_method *dp_methods; +}; + +struct cli_ctx { + struct event_context *ev; + struct nss_ctx *nctx; + int cfd; + struct fd_event *cfde; + struct sockaddr_un addr; + struct cli_request *creq; + struct getent_ctx *gctx; +}; + +struct nss_domain_info { + char *basedn; + int enumerate; + bool has_provider; + bool legacy; +}; + +struct nss_packet; + +struct cli_request { + + /* original request from the wire */ + struct sss_packet *in; + + /* reply data */ + struct sss_packet *out; +}; + +int nss_cmd_execute(struct cli_ctx *cctx); + +/* from nsssrv_dp.c */ +#define NSS_DP_USER 1 +#define NSS_DP_GROUP 2 +#define NSS_DP_INITGROUPS 3 + +typedef void (*nss_dp_callback_t)(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); + +int nss_dp_send_acct_req(struct nss_ctx *nctx, TALLOC_CTX *memctx, + nss_dp_callback_t callback, void *callback_ctx, + int timeout, const char *domain, int type, + const char *opt_name, uint32_t opt_id); +int nss_dp_init(struct nss_ctx *nctx); + +#endif /* __NSSSRV_H__ */ diff --git a/server/responder/nss/nsssrv_cmd.c b/server/responder/nss/nsssrv_cmd.c new file mode 100644 index 000000000..338de0ead --- /dev/null +++ b/server/responder/nss/nsssrv_cmd.c @@ -0,0 +1,2238 @@ +/* + SSSD + + NSS Responder + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "util/btreemap.h" +#include "responder/common/responder_packet.h" +#include "responder/nss/nsssrv.h" +#include "db/sysdb.h" +#include + +struct nss_cmd_ctx { + struct cli_ctx *cctx; + const char *name; + uid_t id; + + bool immediate; + bool done; + int nr; +}; + +struct getent_ctx { + struct ldb_result *pwds; + struct ldb_result *grps; + int pwd_cur; + int grp_cur; +}; + +struct nss_dom_ctx { + struct nss_cmd_ctx *cmdctx; + const char *domain; + bool check_provider; + bool legacy; +}; + +struct nss_cmd_table { + enum sss_cli_command cmd; + int (*fn)(struct cli_ctx *cctx); +}; + +static void nss_cmd_done(struct nss_cmd_ctx *cmdctx) +{ + /* now that the packet is in place, unlock queue + * making the event writable */ + EVENT_FD_WRITEABLE(cmdctx->cctx->cfde); + + /* free all request related data through the talloc hierarchy */ + talloc_free(cmdctx); +} + +static int nss_cmd_send_error(struct nss_cmd_ctx *cmdctx, int err) +{ + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + + sss_packet_set_error(cctx->creq->out, err); + return EOK; +} + +#define NSS_CMD_FATAL_ERROR(cctx) do { \ + DEBUG(1,("Fatal error, killing connection!")); \ + talloc_free(cctx); \ + return; \ +} while(0) + +static int nss_parse_name(struct nss_dom_ctx *dctx, const char *fullname) +{ + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct nss_ctx *nctx = cmdctx->cctx->nctx; + struct nss_domain_info *info; + struct btreemap *domain_map; + char *delim; + char *domain; + + domain_map = nctx->domain_map; + + if ((delim = strchr(fullname, NSS_DOMAIN_DELIM)) != NULL) { + domain = delim+1; + } else { + domain = nctx->default_domain; + } + + /* Check for registered domain */ + info = btreemap_get_value(domain_map, (void *)domain); + if (!info) { + /* No such domain was registered. Return EINVAL. + * TODO: alternative approach? + * Alternatively, we could simply fail down to + * below, treating the entire construct as the + * full name if the domain is unspecified. + */ + return EINVAL; + } + + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; + + dctx->domain = talloc_strdup(dctx, domain); + if (!dctx->domain) return ENOMEM; + + if (delim) { + cmdctx->name = talloc_strndup(cmdctx, fullname, delim-fullname); + } else { + cmdctx->name = talloc_strdup(cmdctx, fullname); + } + if (!cmdctx->name) return ENOMEM; + + return EOK; +} + +static int nss_cmd_get_version(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + uint8_t *body; + size_t blen; + int ret; + + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + /* create response packet */ + ret = sss_packet_new(cctx->creq, sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = SSS_PROTOCOL_VERSION; + + nss_cmd_done(cmdctx); + return EOK; +} + +/**************************************************************************** + * PASSWD db related functions + ***************************************************************************/ + +static int fill_pwent(struct sss_packet *packet, + struct ldb_message **msgs, + int count) +{ + struct ldb_message *msg; + uint8_t *body; + const char *name; + const char *fullname; + const char *homedir; + const char *shell; + uint64_t uid; + uint64_t gid; + size_t rsize, rp, blen; + size_t s1, s2, s3, s4; + int i, ret, num; + + /* first 2 fields (len and reserved), filled up later */ + ret = sss_packet_grow(packet, 2*sizeof(uint32_t)); + rp = 2*sizeof(uint32_t); + + num = 0; + for (i = 0; i < count; i++) { + msg = msgs[i]; + + name = ldb_msg_find_attr_as_string(msg, SYSDB_PW_NAME, NULL); + fullname = ldb_msg_find_attr_as_string(msg, SYSDB_PW_FULLNAME, NULL); + homedir = ldb_msg_find_attr_as_string(msg, SYSDB_PW_HOMEDIR, NULL); + shell = ldb_msg_find_attr_as_string(msg, SYSDB_PW_SHELL, NULL); + uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_PW_UIDNUM, 0); + gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_PW_GIDNUM, 0); + + if (!name || !fullname || !homedir || !shell || !uid || !gid) { + DEBUG(1, ("Incomplete user object for %s[%llu]! Skipping\n", + name?name:"", (unsigned long long int)uid)); + continue; + } + + s1 = strlen(name) + 1; + s2 = strlen(fullname) + 1; + s3 = strlen(homedir) + 1; + s4 = strlen(shell) + 1; + rsize = 2*sizeof(uint64_t) +s1 + 2 + s2 + s3 +s4; + + ret = sss_packet_grow(packet, rsize); + if (ret != EOK) { + num = 0; + goto done; + } + sss_packet_get_body(packet, &body, &blen); + + ((uint64_t *)(&body[rp]))[0] = uid; + ((uint64_t *)(&body[rp]))[1] = gid; + rp += 2*sizeof(uint64_t); + memcpy(&body[rp], name, s1); + rp += s1; + memcpy(&body[rp], "x", 2); + rp += 2; + memcpy(&body[rp], fullname, s2); + rp += s2; + memcpy(&body[rp], homedir, s3); + rp += s3; + memcpy(&body[rp], shell, s4); + rp += s4; + + num++; + } + +done: + sss_packet_get_body(packet, &body, &blen); + ((uint32_t *)body)[0] = num; /* num results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + + return EOK; +} + +static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); + +static void nss_cmd_getpwnam_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int timeout; + uint64_t lastUpdate; + uint8_t *body; + size_t blen; + bool call_provider = false; + int ret; + + if (status != LDB_SUCCESS) { + ret = nss_cmd_send_error(cmdctx, status); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; + + case 1: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + break; + + default: + DEBUG(1, ("getpwnam call returned more than one result !?!\n")); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + } + + if (call_provider) { + + /* dont loop forever :-) */ + dctx->check_provider = false; + timeout = SSS_CLI_SOCKET_TIMEOUT/2; + + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getpwnam_dp_callback, dctx, + timeout, dctx->domain, NSS_DP_USER, + cmdctx->name, 0); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + return; + } + + switch (res->count) { + case 0: + + DEBUG(2, ("No results for getpwnam call\n")); + + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + break; + + case 1: + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + ret = fill_pwent(cctx->creq->out, res->msgs, res->count); + sss_packet_set_error(cctx->creq->out, ret); + + break; + + default: + DEBUG(1, ("getpwnam call returned more than one result !?!\n")); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + } + +done: + nss_cmd_done(cmdctx); +} + +static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getpwnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +static int nss_cmd_getpwnam(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + uint8_t *body; + size_t blen; + int ret; + + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + dctx->cmdctx = cmdctx; + + /* get user name to query */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + /* if not terminated fail */ + if (body[blen -1] != '\0') { + talloc_free(cmdctx); + return EINVAL; + } + + ret = nss_parse_name(dctx, (const char *)body); + if (ret != EOK) { + DEBUG(1, ("Invalid name received\n")); + talloc_free(cmdctx); + return ret; + } + DEBUG(4, ("Requesting info for [%s] from [%s]\n", + cmdctx->name, dctx->domain)); + + ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getpwnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); + } + return ret; + } + + return EOK; +} + +static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); + +static void nss_cmd_getpwuid_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int timeout; + uint64_t lastUpdate; + uint8_t *body; + size_t blen; + bool call_provider = false; + int ret; + + /* one less to go */ + cmdctx->nr--; + + /* check if another callback already replied */ + if (cmdctx->done) { + /* now check if this is the last callback */ + if (cmdctx->nr == 0) { + /* ok we are really done with this request */ + goto done; + } + } + + if (status != LDB_SUCCESS) { + ret = nss_cmd_send_error(cmdctx, status); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; + + case 1: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + break; + + default: + DEBUG(1, ("getpwuid call returned more than one result !?!\n")); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + } + + if (call_provider) { + + /* yet one more call to go */ + cmdctx->nr++; + + /* dont loop forever :-) */ + dctx->check_provider = false; + timeout = SSS_CLI_SOCKET_TIMEOUT/2; + + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getpwuid_dp_callback, dctx, + timeout, dctx->domain, NSS_DP_USER, + NULL, cmdctx->id); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + return; + } + + switch (res->count) { + case 0: + if (cmdctx->nr != 0) { + /* nothing to do */ + return; + } + + DEBUG(2, ("No results for getpwuid call\n")); + + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + break; + + case 1: + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + + ret = fill_pwent(cctx->creq->out, res->msgs, res->count); + sss_packet_set_error(cctx->creq->out, ret); + + break; + + default: + DEBUG(1, ("getpwnam call returned more than one result !?!\n")); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + } + +done: + if (cmdctx->nr != 0) { + cmdctx->done = true; /* signal that we are done */ + return; + } + nss_cmd_done(cmdctx); +} + +static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_getpwuid(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->id, + dctx->legacy, + nss_cmd_getpwuid_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + if (cmdctx->nr != 0) { + cmdctx->done = true; /* signal that we are done */ + return; + } + nss_cmd_done(cmdctx); + } +} + +static int nss_cmd_getpwuid(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + struct nss_domain_info *info; + const char **domains; + uint8_t *body; + size_t blen; + int i, num, ret; + + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + /* get uid to query */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + + if (blen != sizeof(uint64_t)) { + return EINVAL; + } + + cmdctx->id = (uid_t)*((uint64_t *)body); + + /* FIXME: Just ask all backends for now, until we check for ranges */ + dctx = NULL; + domains = NULL; + num = 0; + /* get domains list */ + btreemap_get_keys(cmdctx, cctx->nctx->domain_map, + (const void ***)&domains, &num); + + cmdctx->nr = num; + + for (i = 0; i < num; i++) { + info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + + dctx->cmdctx = cmdctx; + dctx->domain = talloc_strdup(dctx, domains[i]); + if (!dctx->domain) return ENOMEM; + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; + + + DEBUG(4, ("Requesting info for [%lu@%s]\n", + cmdctx->id, dctx->domain)); + + ret = sysdb_getpwuid(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->id, + dctx->legacy, + nss_cmd_getpwuid_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + /* shutdown ? */ + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); + } + return ret; + } + } + + return EOK; +} + +/* to keep it simple at this stage we are retrieving the + * full enumeration again for each request for each process + * and we also block on setpwent() for the full time needed + * to retrieve the data. And endpwent() frees all the data. + * Next steps are: + * - use an nsssrv wide cache with data already structured + * so that it can be immediately returned (see nscd way) + * - use mutexes so that setpwent() can return immediately + * even if the data is still being fetched + * - make getpwent() wait on the mutex + */ +static void nss_cmd_getpwent_callback(void *ptr, int status, + struct ldb_result *res); + +static void nss_cmd_setpwent_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; + struct getent_ctx *gctx = cctx->gctx; + struct ldb_result *store = gctx->pwds; + int i, j, c, ret; + + cmdctx->nr--; + + if (cmdctx->done) { + /* do not reply until all domain searches are done */ + if (cmdctx->nr != 0) return; + else goto done; + } + + if (status != LDB_SUCCESS) { + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + sss_packet_set_error(cctx->creq->out, status); + cmdctx->done = true; + return; + } + + if (store) { + c = store->count + res->count; + store->msgs = talloc_realloc(store, store->msgs, + struct ldb_message *, c); + if (!store->msgs) NSS_CMD_FATAL_ERROR(cctx); + + for (i = store->count, j = 0; i < c; i++, j++) { + store->msgs[i] = talloc_steal(store->msgs, res->msgs[j]); + if (!store->msgs[i]) NSS_CMD_FATAL_ERROR(cctx); + } + store->count = c; + talloc_free(res); + } else { + gctx->pwds = talloc_steal(gctx, res); + } + + /* do not reply until all domain searches are done */ + if (cmdctx->nr) return; + + if (cmdctx->immediate) { + /* this was a getpwent call w/o setpwent, + * return immediately one result */ + nss_cmd_getpwent_callback(ptr, status, res); + + return; + } + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + +done: + nss_cmd_done(cmdctx); +} + +static void nss_cmd_setpw_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_enumpwent(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, dctx->legacy, + nss_cmd_setpwent_callback, cmdctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +static int nss_cmd_setpwent_ext(struct cli_ctx *cctx, bool immediate) +{ + struct nss_domain_info *info; + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + struct getent_ctx *gctx; + const char **domains; + int timeout; + int i, ret, num; + + DEBUG(4, ("Requesting info for all users\n")); + + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + if (cctx->gctx == NULL) { + gctx = talloc_zero(cctx, struct getent_ctx); + if (!gctx) { + talloc_free(cmdctx); + return ENOMEM; + } + cctx->gctx = gctx; + } + if (cctx->gctx->pwds) { + talloc_free(cctx->gctx->pwds); + cctx->gctx->pwds = NULL; + cctx->gctx->pwd_cur = 0; + } + + cmdctx->immediate = immediate; + + domains = NULL; + num = 0; + /* get domains list */ + btreemap_get_keys(cmdctx, cctx->nctx->domain_map, + (const void ***)&domains, &num); + + /* check if enumeration is enabled in any domain */ + for (i = 0; i < num; i++) { + info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); + + if ((info->enumerate & NSS_ENUM_USERS) == 0) { + continue; + } + + /* TODO: enabled, check if we have a recent cached enumeration */ + + /* ok no cache, go and ask the backend to enumerate */ + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + + dctx->cmdctx = cmdctx; + dctx->domain = talloc_strdup(dctx, domains[i]); + if (!dctx->domain) return ENOMEM; + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; + + if (dctx->check_provider) { + timeout = SSS_CLI_SOCKET_TIMEOUT/(i+2); + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_setpw_dp_callback, dctx, + timeout, domains[i], NSS_DP_USER, + NULL, 0); + } else { + ret = sysdb_enumpwent(dctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, dctx->legacy, + nss_cmd_setpwent_callback, cmdctx); + } + if (ret != EOK) { + /* FIXME: shutdown ? */ + DEBUG(1, ("Failed to send enumeration request for domain [%s]!\n", + domains[i])); + continue; + } + + /* number of replies to wait for before setpwent is done */ + cmdctx->nr++; + } + + if (cmdctx->nr == 0) { + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + + sss_packet_set_error(cctx->creq->out, ret); + nss_cmd_done(cmdctx); + return EOK; + } + + return ret; +} + +static int nss_cmd_setpwent(struct cli_ctx *cctx) +{ + return nss_cmd_setpwent_ext(cctx, false); +} + + +static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) +{ + struct getent_ctx *gctx = cctx->gctx; + int n, ret; + + n = gctx->pwds->count - gctx->pwd_cur; + if (n > num) n = num; + + ret = fill_pwent(cctx->creq->out, + &(gctx->pwds->msgs[gctx->pwd_cur]), n); + gctx->pwd_cur += n; + + return ret; +} + +/* used only if a process calls getpwent() without first calling setpwent() + */ +static void nss_cmd_getpwent_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; + struct getent_ctx *gctx = cctx->gctx; + uint8_t *body; + size_t blen; + uint32_t num; + int ret; + + /* get max num of entries to return in one call */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + if (blen != sizeof(uint32_t)) { + NSS_CMD_FATAL_ERROR(cctx); + } + num = *((uint32_t *)body); + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + + if (status != LDB_SUCCESS) { + sss_packet_set_error(cctx->creq->out, status); + goto done; + } + + gctx->pwds = talloc_steal(gctx, res); + + ret = nss_cmd_retpwent(cctx, num); + sss_packet_set_error(cctx->creq->out, ret); + +done: + nss_cmd_done(cmdctx); +} + +static int nss_cmd_getpwent(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + struct getent_ctx *gctx; + uint8_t *body; + size_t blen; + uint32_t num; + int ret; + + DEBUG(4, ("Requesting info for all accounts\n")); + + /* get max num of entries to return in one call */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + if (blen != sizeof(uint32_t)) { + return EINVAL; + } + num = *((uint32_t *)body); + + /* see if we need to trigger an implicit setpwent() */ + if (cctx->gctx == NULL || cctx->gctx->pwds == NULL) { + if (cctx->gctx == NULL) { + gctx = talloc_zero(cctx, struct getent_ctx); + if (!gctx) { + return ENOMEM; + } + cctx->gctx = gctx; + } + if (cctx->gctx->pwds == NULL) { + ret = nss_cmd_setpwent_ext(cctx, true); + return ret; + } + } + + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + + ret = nss_cmd_retpwent(cctx, num); + sss_packet_set_error(cctx->creq->out, ret); + nss_cmd_done(cmdctx); + return EOK; +} + +static int nss_cmd_endpwent(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + int ret; + + DEBUG(4, ("Terminating request info for all accounts\n")); + + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + + if (cctx->gctx == NULL) goto done; + if (cctx->gctx->pwds == NULL) goto done; + + /* free results and reset */ + talloc_free(cctx->gctx->pwds); + cctx->gctx->pwds = NULL; + cctx->gctx->pwd_cur = 0; + +done: + nss_cmd_done(cmdctx); + return EOK; +} + +/**************************************************************************** + * GROUP db related functions + ***************************************************************************/ + +static int fill_grent(struct sss_packet *packet, + struct ldb_message **msgs, + int count) +{ + struct ldb_message_element *el; + struct ldb_message *msg; + uint8_t *body; + const char *name; + uint64_t gid; + size_t rsize, rp, blen, mnump; + int i, j, ret, num, memnum; + bool get_group = true; + bool memnum_set = false; + + /* first 2 fields (len and reserved), filled up later */ + ret = sss_packet_grow(packet, 2*sizeof(uint32_t)); + rp = 2*sizeof(uint32_t); + + num = 0; + mnump = 0; + for (i = 0; i < count; i++) { + msg = msgs[i]; + + if (get_group) { + /* find group name/gid */ + name = ldb_msg_find_attr_as_string(msg, SYSDB_GR_NAME, NULL); + gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GR_GIDNUM, 0); + if (!name || !gid) { + DEBUG(1, ("Incomplete group object for %s[%llu]! Aborting\n", + name?name:"", (unsigned long long int)gid)); + num = 0; + goto done; + } + + /* fill in gid and name and set pointer for number of members */ + rsize = sizeof(uint64_t) + sizeof(uint32_t) + strlen(name)+1 +2; + ret = sss_packet_grow(packet, rsize); + sss_packet_get_body(packet, &body, &blen); + rp = blen - rsize; + ((uint64_t *)(&body[rp]))[0] = gid; + rp += sizeof(uint64_t); + ((uint32_t *)(&body[rp]))[0] = 0; /* init members num to 0 */ + mnump = rp; /* keep around members num pointer to set later */ + rp += sizeof(uint32_t); + memcpy(&body[rp], name, strlen(name)+1); + body[blen-2] = 'x'; /* group passwd field */ + body[blen-1] = '\0'; + + memnum_set = false; + memnum = 0; + num++; + + /* legacy style group, members are in SYSDB_LEGACY_MEMBER */ + el = ldb_msg_find_element(msg, SYSDB_LEGACY_MEMBER); + if (el) { + /* legacy */ + memnum = el->num_values; + + for (j = 0; j < memnum; j++) { + rsize = el->values[j].length + 1; + ret = sss_packet_grow(packet, rsize); + if (ret != EOK) { + num = 0; + goto done; + } + + sss_packet_get_body(packet, &body, &blen); + rp = blen - rsize; + memcpy(&body[rp], el->values[j].data, el->values[j].length); + body[blen-1] = '\0'; + } + + sss_packet_get_body(packet, &body, &blen); + ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ + memnum_set = true; + + } else { + get_group = false; + } + + continue; + } + + name = ldb_msg_find_attr_as_string(msg, SYSDB_PW_NAME, NULL); + + if (!name) { + /* last member of previous group found, or error. + * set next element to be a group, and eventually + * fail there if here start bogus entries */ + get_group = true; + i--; + sss_packet_get_body(packet, &body, &blen); + ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ + memnum_set = true; + continue; + } + + rsize = strlen(name) + 1; + + ret = sss_packet_grow(packet, rsize); + if (ret != EOK) { + num = 0; + goto done; + } + sss_packet_get_body(packet, &body, &blen); + rp = blen - rsize; + memcpy(&body[rp], name, rsize); + + memnum++; + } + + if (!memnum_set) { + /* fill in the last group member count */ + if (mnump != 0) { + sss_packet_get_body(packet, &body, &blen); + ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ + } + } + +done: + sss_packet_get_body(packet, &body, &blen); + ((uint32_t *)body)[0] = num; /* num results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + + return EOK; +} + +static void nss_cmd_getgrnam_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); + +static void nss_cmd_getgrnam_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int timeout; + uint64_t lastUpdate; + uint8_t *body; + size_t blen; + bool call_provider = false; + int ret; + + if (status != LDB_SUCCESS) { + ret = nss_cmd_send_error(cmdctx, status); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + return; + } + + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; + + default: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + } + } + + if (call_provider) { + + /* dont loop forever :-) */ + dctx->check_provider = false; + timeout = SSS_CLI_SOCKET_TIMEOUT/2; + + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getgrnam_dp_callback, dctx, + timeout, dctx->domain, NSS_DP_GROUP, + cmdctx->name, 0); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + return; + } + + switch (res->count) { + case 0: + + DEBUG(2, ("No results for getgrnam call\n")); + + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + break; + + default: + + DEBUG(6, ("Returning info for group [%s]\n", cmdctx->name)); + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + + ret = fill_grent(cctx->creq->out, res->msgs, res->count); + sss_packet_set_error(cctx->creq->out, ret); + } + +done: + nss_cmd_done(cmdctx); +} + +static void nss_cmd_getgrnam_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_getgrnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getgrnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +static int nss_cmd_getgrnam(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + uint8_t *body; + size_t blen; + int ret; + + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + dctx->cmdctx = cmdctx; + + /* get user name to query */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + /* if not terminated fail */ + if (body[blen -1] != '\0') { + talloc_free(cmdctx); + return EINVAL; + } + + ret = nss_parse_name(dctx, (const char *)body); + if (ret != EOK) { + DEBUG(1, ("Invalid name received\n")); + talloc_free(cmdctx); + return ret; + } + DEBUG(4, ("Requesting info for [%s] from [%s]\n", + cmdctx->name, dctx->domain)); + + ret = sysdb_getgrnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getgrnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); + } + return ret; + } + + return EOK; +} + +static void nss_cmd_getgrgid_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); + +static void nss_cmd_getgrgid_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int timeout; + uint64_t lastUpdate; + uint8_t *body; + size_t blen; + bool call_provider = false; + int ret; + + /* one less to go */ + cmdctx->nr--; + + /* check if another callback already replied */ + if (cmdctx->done) { + /* now check if this is the last callback */ + if (cmdctx->nr == 0) { + /* ok we are really done with this request */ + goto done; + } + } + + if (status != LDB_SUCCESS) { + ret = nss_cmd_send_error(cmdctx, status); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; + + default: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + } + } + + if (call_provider) { + + /* yet one more call to go */ + cmdctx->nr++; + + /* dont loop forever :-) */ + dctx->check_provider = false; + timeout = SSS_CLI_SOCKET_TIMEOUT/2; + + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getgrgid_dp_callback, dctx, + timeout, dctx->domain, NSS_DP_GROUP, + NULL, cmdctx->id); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + return; + } + + switch (res->count) { + case 0: + if (cmdctx->nr != 0) { + /* nothing to do */ + return; + } + + DEBUG(2, ("No results for getgrgid call\n")); + + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + break; + + default: + + DEBUG(6, ("Returning info for group [%u]\n", (unsigned)cmdctx->id)); + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + + ret = fill_grent(cctx->creq->out, res->msgs, res->count); + sss_packet_set_error(cctx->creq->out, ret); + } + +done: + if (cmdctx->nr != 0) { + cmdctx->done = true; /* signal that we are done */ + return; + } + nss_cmd_done(cmdctx); +} + +static void nss_cmd_getgrgid_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_getgrgid(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->id, + dctx->legacy, + nss_cmd_getgrgid_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +static int nss_cmd_getgrgid(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + struct nss_domain_info *info; + const char **domains; + uint8_t *body; + size_t blen; + int i, num, ret; + + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + /* get uid to query */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + + if (blen != sizeof(uint64_t)) { + return EINVAL; + } + + cmdctx->id = (gid_t)*((uint64_t *)body); + + /* FIXME: Just ask all backends for now, until we check for ranges */ + dctx = NULL; + domains = NULL; + num = 0; + /* get domains list */ + btreemap_get_keys(cmdctx, cctx->nctx->domain_map, + (const void ***)&domains, &num); + + cmdctx->nr = num; + + for (i = 0; i < num; i++) { + info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + + dctx->cmdctx = cmdctx; + dctx->domain = talloc_strdup(dctx, domains[i]); + if (!dctx->domain) return ENOMEM; + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; + + DEBUG(4, ("Requesting info for [%lu@%s]\n", + cmdctx->id, dctx->domain)); + + ret = sysdb_getgrgid(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->id, + dctx->legacy, + nss_cmd_getgrgid_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + /* shutdown ? */ + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); + } + return ret; + } + } + + return EOK; +} + +/* to keep it simple at this stage we are retrieving the + * full enumeration again for each request for each process + * and we also block on setgrent() for the full time needed + * to retrieve the data. And endgrent() frees all the data. + * Next steps are: + * - use and nsssrv wide cache with data already structured + * so that it can be immediately returned (see nscd way) + * - use mutexes so that setgrent() can return immediately + * even if the data is still being fetched + * - make getgrent() wait on the mutex + */ +static void nss_cmd_getgrent_callback(void *ptr, int status, + struct ldb_result *res); + +static void nss_cmd_setgrent_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; + struct getent_ctx *gctx = cctx->gctx; + struct ldb_result *store = gctx->grps; + int i, j, c, ret; + + cmdctx->nr--; + + if (cmdctx->done) { + /* do not reply until all domain searches are done */ + if (cmdctx->nr != 0) return; + else goto done; + } + + if (status != LDB_SUCCESS) { + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + sss_packet_set_error(cctx->creq->out, status); + cmdctx->done = true; + return; + } + + if (store) { + c = store->count + res->count; + store->msgs = talloc_realloc(store, store->msgs, + struct ldb_message *, c); + if (!store->msgs) NSS_CMD_FATAL_ERROR(cctx); + + for (i = store->count, j = 0; i < c; i++, j++) { + store->msgs[i] = talloc_steal(store->msgs, res->msgs[j]); + if (!store->msgs[i]) NSS_CMD_FATAL_ERROR(cctx); + } + store->count = c; + talloc_free(res); + } else { + gctx->grps = talloc_steal(gctx, res); + } + + /* do not reply until all domain searches are done */ + if (cmdctx->nr) return; + + if (cmdctx->immediate) { + /* this was a getgrent call w/o setgrent, + * return immediately one result */ + nss_cmd_getgrent_callback(ptr, status, res); + return; + } + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + +done: + nss_cmd_done(cmdctx); +} + +static void nss_cmd_setgr_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_enumgrent(dctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, dctx->legacy, + nss_cmd_setgrent_callback, cmdctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +static int nss_cmd_setgrent_ext(struct cli_ctx *cctx, bool immediate) +{ + struct nss_domain_info *info; + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + struct getent_ctx *gctx; + const char **domains; + int timeout; + int i, ret, num; + + DEBUG(4, ("Requesting info for all groups\n")); + + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + if (cctx->gctx == NULL) { + gctx = talloc_zero(cctx, struct getent_ctx); + if (!gctx) { + talloc_free(cmdctx); + return ENOMEM; + } + cctx->gctx = gctx; + } + if (cctx->gctx->grps) { + talloc_free(cctx->gctx->grps); + cctx->gctx->grps = NULL; + cctx->gctx->grp_cur = 0; + } + + cmdctx->immediate = immediate; + + domains = NULL; + num = 0; + /* get domains list */ + btreemap_get_keys(cmdctx, cctx->nctx->domain_map, + (const void ***)&domains, &num); + + /* check if enumeration is enabled in any domain */ + for (i = 0; i < num; i++) { + info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); + + if ((info->enumerate & NSS_ENUM_GROUPS) == 0) { + continue; + } + + /* TODO: enabled, check if we have a recent cached enumeration */ + + /* ok no cache, go and ask the backend to enumerate */ + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + + dctx->cmdctx = cmdctx; + dctx->domain = talloc_strdup(dctx, domains[i]); + if (!dctx->domain) return ENOMEM; + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; + + if (dctx->check_provider) { + timeout = SSS_CLI_SOCKET_TIMEOUT/(i+2); + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_setgr_dp_callback, dctx, + timeout, domains[i], NSS_DP_GROUP, + NULL, 0); + } else { + ret = sysdb_enumgrent(dctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, dctx->legacy, + nss_cmd_setgrent_callback, cmdctx); + } + if (ret != EOK) { + /* FIXME: shutdown ? */ + DEBUG(1, ("Failed to send enumeration request for domain [%s]!\n", + domains[i])); + continue; + } + + cmdctx->nr++; + } + + if (cmdctx->nr == 0) { + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + + sss_packet_set_error(cctx->creq->out, ret); + nss_cmd_done(cmdctx); + return EOK; + } + + return ret; +} + +static int nss_cmd_setgrent(struct cli_ctx *cctx) +{ + return nss_cmd_setgrent_ext(cctx, false); +} + +static int nss_cmd_retgrent(struct cli_ctx *cctx, int num) +{ + struct getent_ctx *gctx = cctx->gctx; + int n, ret; + + n = gctx->grps->count - gctx->grp_cur; + if (n > num) n = num; + + ret = fill_grent(cctx->creq->out, + &(gctx->grps->msgs[gctx->grp_cur]), n); + gctx->grp_cur += n; + + return ret; +} + +/* used only if a process calls getpwent() without first calling setpwent() + * in this case we basically trigger an implicit setpwent() */ +static void nss_cmd_getgrent_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; + struct getent_ctx *gctx = cctx->gctx; + uint8_t *body; + size_t blen; + uint32_t num; + int ret; + + /* get max num of entries to return in one call */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + if (blen != sizeof(uint32_t)) { + ret = nss_cmd_send_error(cmdctx, EIO); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } + num = *((uint32_t *)body); + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + + if (status != LDB_SUCCESS) { + sss_packet_set_error(cctx->creq->out, status); + goto done; + } + + gctx->grps = talloc_steal(gctx, res); + + ret = nss_cmd_retgrent(cctx, num); + sss_packet_set_error(cctx->creq->out, ret); + +done: + nss_cmd_done(cmdctx); +} + +static int nss_cmd_getgrent(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + struct getent_ctx *gctx; + uint8_t *body; + size_t blen; + uint32_t num; + int ret; + + DEBUG(4, ("Requesting info for all groups\n")); + + /* get max num of entries to return in one call */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + if (blen != sizeof(uint32_t)) { + return EINVAL; + } + num = *((uint32_t *)body); + + /* see if we need to trigger an implicit setpwent() */ + if (cctx->gctx == NULL || cctx->gctx->grps == NULL) { + if (cctx->gctx == NULL) { + gctx = talloc_zero(cctx, struct getent_ctx); + if (!gctx) { + return ENOMEM; + } + cctx->gctx = gctx; + } + if (cctx->gctx->grps == NULL) { + ret = nss_cmd_setgrent_ext(cctx, true); + return ret; + } + } + + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + + ret = nss_cmd_retgrent(cctx, num); + sss_packet_set_error(cctx->creq->out, ret); + nss_cmd_done(cmdctx); + return EOK; +} + +static int nss_cmd_endgrent(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + int ret; + + DEBUG(4, ("Terminating request info for all groups\n")); + + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + + if (cctx->gctx == NULL) goto done; + if (cctx->gctx->grps == NULL) goto done; + + /* free results and reset */ + talloc_free(cctx->gctx->grps); + cctx->gctx->grps = NULL; + cctx->gctx->grp_cur = 0; + +done: + nss_cmd_done(cmdctx); + return EOK; +} + +static void nss_cmd_initgr_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; + uint8_t *body; + size_t blen; + uint64_t gid; + uint32_t num; + int ret, i; + + /* create response packet */ + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + + if (status != LDB_SUCCESS) { + sss_packet_set_error(cctx->creq->out, status); + goto done; + } + + num = res->count; + /* the first 64 bit uint is really 2 32 units used to hold the number of + * results */ + ret = sss_packet_grow(cctx->creq->out, (1 + num) * sizeof(uint64_t)); + if (ret != EOK) { + sss_packet_set_error(cctx->creq->out, ret); + goto done; + } + sss_packet_get_body(cctx->creq->out, &body, &blen); + + for (i = 0; i < num; i++) { + gid = ldb_msg_find_attr_as_uint64(res->msgs[i], SYSDB_GR_GIDNUM, 0); + if (!gid) { + DEBUG(1, ("Incomplete group object for initgroups! Aborting\n")); + sss_packet_set_error(cctx->creq->out, EIO); + num = 0; + goto done; + } + ((uint64_t *)body)[i+1] = gid; + } + + ((uint32_t *)body)[0] = num; /* num results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + +done: + nss_cmd_done(cmdctx); +} + +static void nss_cmd_getinitgr_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_initgroups(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_initgr_callback, cmdctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +static void nss_cmd_getinit_callback(void *ptr, int status, + struct ldb_result *res); + +static void nss_cmd_getinitnam_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getinit_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +static void nss_cmd_getinit_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int timeout; + uint64_t lastUpdate; + uint8_t *body; + size_t blen; + bool call_provider = false; + int ret; + + if (status != LDB_SUCCESS) { + ret = nss_cmd_send_error(cmdctx, status); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; + + default: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + } + } + + if (call_provider) { + + /* dont loop forever :-) */ + dctx->check_provider = false; + timeout = SSS_CLI_SOCKET_TIMEOUT/2; + + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getinitnam_callback, dctx, + timeout, dctx->domain, NSS_DP_USER, + cmdctx->name, 0); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + return; + } + + switch (res->count) { + case 0: + + DEBUG(2, ("No results for initgroups call\n")); + + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + break; + + case 1: + + timeout = SSS_CLI_SOCKET_TIMEOUT/2; + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getinitgr_callback, dctx, + timeout, dctx->domain, NSS_DP_INITGROUPS, + cmdctx->name, 0); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + return; + + default: + DEBUG(1, ("getpwnam call returned more than one result !?!\n")); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + } + +done: + nss_cmd_done(cmdctx); +} + +/* for now, if we are online, try to always query the backend */ +static int nss_cmd_initgroups(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + uint8_t *body; + size_t blen; + int ret; + + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + dctx->cmdctx = cmdctx; + + /* get user name to query */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + cmdctx->name = (const char *)body; + /* if not terminated fail */ + if (cmdctx->name[blen -1] != '\0') { + return EINVAL; + } + + ret = nss_parse_name(dctx, (const char *)body); + if (ret != EOK) { + DEBUG(1, ("Invalid name received\n")); + talloc_free(cmdctx); + return ret; + } + DEBUG(4, ("Requesting info for [%s] from [%s]\n", + cmdctx->name, dctx->domain)); + + ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getinit_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); + } + return ret; + } + + return EOK; +} + +struct nss_cmd_table sss_cmds[] = {}; +struct nss_cmd_table nss_cmds[] = { + {SSS_GET_VERSION, nss_cmd_get_version}, + {SSS_NSS_GETPWNAM, nss_cmd_getpwnam}, + {SSS_NSS_GETPWUID, nss_cmd_getpwuid}, + {SSS_NSS_SETPWENT, nss_cmd_setpwent}, + {SSS_NSS_GETPWENT, nss_cmd_getpwent}, + {SSS_NSS_ENDPWENT, nss_cmd_endpwent}, + {SSS_NSS_GETGRNAM, nss_cmd_getgrnam}, + {SSS_NSS_GETGRGID, nss_cmd_getgrgid}, + {SSS_NSS_SETGRENT, nss_cmd_setgrent}, + {SSS_NSS_GETGRENT, nss_cmd_getgrent}, + {SSS_NSS_ENDGRENT, nss_cmd_endgrent}, + {SSS_NSS_INITGR, nss_cmd_initgroups}, + {SSS_CLI_NULL, NULL} +}; + +int nss_cmd_execute(struct cli_ctx *cctx) +{ + enum sss_cli_command cmd; + int i; + + cmd = sss_packet_get_cmd(cctx->creq->in); + + for (i = 0; nss_cmds[i].cmd != SSS_CLI_NULL; i++) { + if (cmd == nss_cmds[i].cmd) { + return nss_cmds[i].fn(cctx); + } + } + + return EINVAL; +} + diff --git a/server/responder/nss/nsssrv_dp.c b/server/responder/nss/nsssrv_dp.c new file mode 100644 index 000000000..46e218014 --- /dev/null +++ b/server/responder/nss/nsssrv_dp.c @@ -0,0 +1,433 @@ +/* + SSSD + + NSS Responder - Data Provider Interfaces + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include "util/util.h" +#include "responder/common/responder_packet.h" +#include "responder/nss/nsssrv.h" +#include "providers/data_provider.h" +#include "sbus/sbus_client.h" +#include "providers/dp_sbus.h" + +struct nss_dp_req { + nss_dp_callback_t callback; + void *callback_ctx; + struct timed_event *te; + DBusPendingCall *pending_reply; +}; + +static int nss_dp_req_destructor(void *ptr) +{ + struct nss_dp_req *req = talloc_get_type(ptr, struct nss_dp_req); + + if (req->pending_reply) { + dbus_pending_call_cancel(req->pending_reply); + } + + return 0; +} + +static void nss_dp_send_acct_timeout(struct event_context *ev, + struct timed_event *te, + struct timeval t, void *data) +{ + struct nss_dp_req *ndp_req; + dbus_uint16_t err_maj = DP_ERR_TIMEOUT; + dbus_uint32_t err_min = EIO; + const char *err_msg = "Request timed out"; + + ndp_req = talloc_get_type(data, struct nss_dp_req); + + ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx); + + talloc_free(ndp_req); +} + +static int nss_dp_get_reply(DBusPendingCall *pending, + dbus_uint16_t *err_maj, + dbus_uint32_t *err_min, + const char **err_msg); + +static void nss_dp_send_acct_callback(DBusPendingCall *pending, void *ptr) +{ + struct nss_dp_req *ndp_req; + dbus_uint16_t err_maj; + dbus_uint32_t err_min; + const char *err_msg; + int ret; + + ndp_req = talloc_get_type(ptr, struct nss_dp_req); + + /* free timeout event and remove request destructor */ + talloc_free(ndp_req->te); + talloc_set_destructor(ndp_req, NULL); + + ret = nss_dp_get_reply(pending, &err_maj, &err_min, &err_msg); + if (ret != EOK) { + err_maj = DP_ERR_FATAL; + err_min = ret; + err_msg = "Failed to get reply from Data Provider"; + } + + ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx); + + talloc_free(ndp_req); +} + +int nss_dp_send_acct_req(struct nss_ctx *nctx, TALLOC_CTX *memctx, + nss_dp_callback_t callback, void *callback_ctx, + int timeout, const char *domain, int type, + const char *opt_name, uint32_t opt_id) +{ + struct nss_dp_req *ndp_req; + DBusMessage *msg; + DBusPendingCall *pending_reply; + DBusConnection *conn; + dbus_bool_t ret; + uint32_t be_type; + const char *attrs = "core"; + char *filter; + struct timeval tv; + + /* either, or, not both */ + if (opt_name && opt_id) { + return EINVAL; + } + + if (!domain) { + return EINVAL; + } + + switch (type) { + case NSS_DP_USER: + be_type = BE_REQ_USER; + break; + case NSS_DP_GROUP: + be_type = BE_REQ_GROUP; + break; + case NSS_DP_INITGROUPS: + be_type = BE_REQ_INITGROUPS; + break; + default: + return EINVAL; + } + + if (opt_name) { + filter = talloc_asprintf(memctx, "name=%s", opt_name); + } else if (opt_id) { + filter = talloc_asprintf(memctx, "idnumber=%u", opt_id); + } else { + filter = talloc_strdup(memctx, "name=*"); + } + if (!filter) { + talloc_free(nctx); + return ENOMEM; + } + + conn = sbus_get_connection(nctx->dp_ctx->scon_ctx); + + /* create the message */ + msg = dbus_message_new_method_call(NULL, + DP_CLI_PATH, + DP_CLI_INTERFACE, + DP_SRV_METHOD_GETACCTINFO); + if (msg == NULL) { + DEBUG(0,("Out of memory?!\n")); + return ENOMEM; + } + + DEBUG(4, ("Sending request for [%s][%u][%s][%s]\n", + domain, be_type, attrs, filter)); + + ret = dbus_message_append_args(msg, + DBUS_TYPE_STRING, &domain, + DBUS_TYPE_UINT32, &be_type, + DBUS_TYPE_STRING, &attrs, + DBUS_TYPE_STRING, &filter, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed to build message\n")); + return EIO; + } + + ret = dbus_connection_send_with_reply(conn, msg, &pending_reply, + 600000 /* TODO: set timeout */); + if (!ret) { + /* + * Critical Failure + * We can't communicate on this connection + * We'll drop it using the default destructor. + */ + DEBUG(0, ("D-BUS send failed.\n")); + dbus_message_unref(msg); + return EIO; + } + + ndp_req = talloc_zero(memctx, struct nss_dp_req); + if (!ndp_req) { + dbus_message_unref(msg); + return ENOMEM; + } + ndp_req->callback = callback; + ndp_req->callback_ctx = callback_ctx; + + /* set up destructor */ + ndp_req->pending_reply = pending_reply; + talloc_set_destructor((TALLOC_CTX *)ndp_req, nss_dp_req_destructor); + + /* setup the timeout handler */ + gettimeofday(&tv, NULL); + tv.tv_sec += timeout/1000; + tv.tv_usec += (timeout%1000) * 1000; + ndp_req->te = event_add_timed(nctx->ev, memctx, tv, + nss_dp_send_acct_timeout, ndp_req); + + /* Set up the reply handler */ + dbus_pending_call_set_notify(pending_reply, + nss_dp_send_acct_callback, + ndp_req, NULL); + dbus_message_unref(msg); + + return EOK; +} + +static int nss_dp_get_reply(DBusPendingCall *pending, + dbus_uint16_t *err_maj, + dbus_uint32_t *err_min, + const char **err_msg) +{ + DBusMessage *reply; + DBusError dbus_error; + dbus_bool_t ret; + int type; + int err = EOK; + + dbus_error_init(&dbus_error); + + reply = dbus_pending_call_steal_reply(pending); + if (!reply) { + /* reply should never be null. This function shouldn't be called + * until reply is valid or timeout has occurred. If reply is NULL + * here, something is seriously wrong and we should bail out. + */ + DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n")); + + /* FIXME: Destroy this connection ? */ + err = EIO; + goto done; + } + + type = dbus_message_get_type(reply); + switch (type) { + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + ret = dbus_message_get_args(reply, &dbus_error, + DBUS_TYPE_UINT16, err_maj, + DBUS_TYPE_UINT32, err_min, + DBUS_TYPE_STRING, err_msg, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Filed to parse message\n")); + /* FIXME: Destroy this connection ? */ + if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); + err = EIO; + goto done; + } + + DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n", + (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg)); + + break; + + case DBUS_MESSAGE_TYPE_ERROR: + DEBUG(0,("The Data Provider returned an error [%s]\n", + dbus_message_get_error_name(reply))); + /* Falling through to default intentionally*/ + default: + /* + * Timeout or other error occurred or something + * unexpected happened. + * It doesn't matter which, because either way we + * know that this connection isn't trustworthy. + * We'll destroy it now. + */ + + /* FIXME: Destroy this connection ? */ + err = EIO; + } + +done: + dbus_pending_call_unref(pending); + dbus_message_unref(reply); + + return err; +} + +static int nss_dp_identity(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + dbus_uint16_t version = DATA_PROVIDER_VERSION; + dbus_uint16_t clitype = DP_CLI_FRONTEND; + const char *cliname = "NSS"; + const char *nullname = ""; + DBusMessage *reply; + dbus_bool_t ret; + + DEBUG(4,("Sending ID reply: (%d,%d,%s)\n", + clitype, version, cliname)); + + reply = dbus_message_new_method_return(message); + if (!reply) return ENOMEM; + + ret = dbus_message_append_args(reply, + DBUS_TYPE_UINT16, &clitype, + DBUS_TYPE_UINT16, &version, + DBUS_TYPE_STRING, &cliname, + DBUS_TYPE_STRING, &nullname, + DBUS_TYPE_INVALID); + if (!ret) { + dbus_message_unref(reply); + return EIO; + } + + /* send reply back */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + return EOK; +} + +struct sbus_method nss_dp_methods[] = { + { DP_CLI_METHOD_IDENTITY, nss_dp_identity }, + { NULL, NULL } +}; + +struct nss_dp_pvt_ctx { + struct nss_ctx *nctx; + struct sbus_method *methods; + time_t last_retry; + int retries; +}; + +static int nss_dp_conn_destructor(void *data); +static void nss_dp_reconnect(struct event_context *ev, + struct timed_event *te, + struct timeval tv, void *data); + +static void nss_dp_conn_reconnect(struct nss_dp_pvt_ctx *pvt) +{ + struct nss_ctx *nctx; + struct timed_event *te; + struct timeval tv; + struct sbus_method_ctx *sm_ctx; + char *sbus_address; + time_t now; + int ret; + + now = time(NULL); + + /* reset retry if last reconnect was > 60 sec. ago */ + if (pvt->last_retry + 60 < now) pvt->retries = 0; + if (pvt->retries >= 3) { + DEBUG(4, ("Too many reconnect retries! Giving up\n")); + return; + } + + pvt->last_retry = now; + pvt->retries++; + + nctx = pvt->nctx; + + ret = dp_get_sbus_address(nctx, nctx->cdb, &sbus_address); + if (ret != EOK) { + DEBUG(0, ("Could not locate data provider address.\n")); + return; + } + + ret = dp_init_sbus_methods(nctx, pvt->methods, &sm_ctx); + if (ret != EOK) { + DEBUG(0, ("Could not initialize SBUS methods.\n")); + return; + } + + ret = sbus_client_init(nctx, nctx->ev, + sbus_address, sm_ctx, + pvt, nss_dp_conn_destructor, + &nctx->dp_ctx); + if (ret != EOK) { + DEBUG(4, ("Failed to reconnect [%d(%s)]!\n", ret, strerror(ret))); + + tv.tv_sec = now +5; + tv.tv_usec = 0; + te = event_add_timed(nctx->ev, nctx, tv, nss_dp_reconnect, pvt); + if (te == NULL) { + DEBUG(4, ("Failed to add timed event! Giving up\n")); + } else { + DEBUG(4, ("Retrying in 5 seconds\n")); + } + } +} + +static void nss_dp_reconnect(struct event_context *ev, + struct timed_event *te, + struct timeval tv, void *data) +{ + struct nss_dp_pvt_ctx *pvt; + + pvt = talloc_get_type(data, struct nss_dp_pvt_ctx); + + nss_dp_conn_reconnect(pvt); +} + +int nss_dp_conn_destructor(void *data) +{ + struct nss_dp_pvt_ctx *pvt; + struct sbus_conn_ctx *scon; + + scon = talloc_get_type(data, struct sbus_conn_ctx); + if (!scon) return 0; + + /* if this is a regular disconnect just quit */ + if (sbus_conn_disconnecting(scon)) return 0; + + pvt = talloc_get_type(sbus_conn_get_private_data(scon), + struct nss_dp_pvt_ctx); + if (pvt) return 0; + + nss_dp_conn_reconnect(pvt); + + return 0; +} + +int nss_dp_init(struct nss_ctx *nctx) +{ + struct nss_dp_pvt_ctx *pvt; + + pvt = talloc_zero(nctx, struct nss_dp_pvt_ctx); + if (!pvt) return ENOMEM; + + pvt->nctx = nctx; + pvt->methods = nss_dp_methods; + + nss_dp_conn_reconnect(pvt); + + return EOK; +} + diff --git a/server/responder/pam/pamsrv.c b/server/responder/pam/pamsrv.c new file mode 100644 index 000000000..b6593bcff --- /dev/null +++ b/server/responder/pam/pamsrv.c @@ -0,0 +1,171 @@ +/* + SSSD + + PAM Responder + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "popt.h" +#include "util/util.h" +#include "db/sysdb.h" +#include "confdb/confdb.h" +#include "dbus/dbus.h" +#include "sbus/sssd_dbus.h" +#include "util/btreemap.h" +#include "responder/common/responder_packet.h" +#include "responder/common/responder_cmd.h" +#include "responder/common/responder_common.h" +#include "providers/data_provider.h" +#include "monitor/monitor_sbus.h" +#include "monitor/monitor_interfaces.h" +#include "sbus/sbus_client.h" +#include "responder/pam/pamsrv.h" + +#define SSS_PAM_PIPE_NAME "pam" +#define PAM_SBUS_SERVICE_VERSION 0x0001 +#define PAM_SBUS_SERVICE_NAME "pam" +#define CONFDB_SOCKET_PATH "config/services/pam" + +static int service_identity(DBusMessage *message, struct sbus_conn_ctx *sconn); +static int service_pong(DBusMessage *message, struct sbus_conn_ctx *sconn); +static int service_reload(DBusMessage *message, struct sbus_conn_ctx *sconn); + +struct sbus_method sss_sbus_methods[] = { + {SERVICE_METHOD_IDENTITY, service_identity}, + {SERVICE_METHOD_PING, service_pong}, + {SERVICE_METHOD_RELOAD, service_reload}, + {NULL, NULL} +}; + +static int service_identity(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + dbus_uint16_t version = PAM_SBUS_SERVICE_VERSION; + const char *name = PAM_SBUS_SERVICE_NAME; + DBusMessage *reply; + dbus_bool_t ret; + + DEBUG(4,("Sending ID reply: (%s,%d)\n", name, version)); + + reply = dbus_message_new_method_return(message); + if (!reply) return ENOMEM; + + ret = dbus_message_append_args(reply, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT16, &version, + DBUS_TYPE_INVALID); + if (!ret) { + dbus_message_unref(reply); + return EIO; + } + + /* send reply back */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + return EOK; +} + +static int service_pong(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + DBusMessage *reply; + dbus_bool_t ret; + + reply = dbus_message_new_method_return(message); + if (!reply) return ENOMEM; + + ret = dbus_message_append_args(reply, DBUS_TYPE_INVALID); + if (!ret) { + return EIO; + } + + /* send reply back */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + return EOK; +} + +static int service_reload(DBusMessage *message, struct sbus_conn_ctx *sconn) { + /* Monitor calls this function when we need to reload + * our configuration information. Perform whatever steps + * are needed to update the configuration objects. + */ + + /* Send an empty reply to acknowledge receipt */ + return service_pong(message, sconn); +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + struct main_context *main_ctx; + int ret; + struct sbus_method *pam_dp_methods; + struct sss_cmd_table *sss_cmds; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + { NULL } + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + + poptFreeContext(pc); + + /* set up things like debug , signals, daemonization, etc... */ + ret = server_setup("sssd[pam]", 0, &main_ctx); + if (ret != EOK) return 2; + + pam_dp_methods = register_pam_dp_methods(); + sss_cmds = register_sss_cmds(); + ret = sss_process_init(main_ctx, + main_ctx->event_ctx, + main_ctx->confdb_ctx, + sss_sbus_methods, + sss_cmds, + SSS_PAM_PIPE_NAME, + CONFDB_SOCKET_PATH, + pam_dp_methods); + if (ret != EOK) return 3; + + /* loop on main */ + server_loop(main_ctx); + + return 0; +} + diff --git a/server/responder/pam/pamsrv.h b/server/responder/pam/pamsrv.h new file mode 100644 index 000000000..bb0082acc --- /dev/null +++ b/server/responder/pam/pamsrv.h @@ -0,0 +1,33 @@ +#include "sbus/sssd_dbus.h" +#include "responder/common/responder_cmd.h" + +#define PAM_DP_TIMEOUT 5000 + +#define DEBUG_PAM_DATA(level, pd) do { \ + if (level <= debug_level) pam_print_data(level, pd); \ +} while(0); + +struct pam_data { + int cmd; + uint32_t authtok_type; + uint32_t authtok_size; + uint32_t newauthtok_type; + uint32_t newauthtok_size; + char *domain; + char *user; + char *service; + char *tty; + char *ruser; + char *rhost; + uint8_t *authtok; + uint8_t *newauthtok; +}; + +void pam_print_data(int l, struct pam_data *pd); + +typedef void (*pam_dp_callback_t)(struct cli_ctx *cctx, int pam_status, char *domain); + +struct sbus_method *register_pam_dp_methods(void); +struct sss_cmd_table *register_sss_cmds(void); +int pam_dp_send_req(struct cli_ctx *cctx, pam_dp_callback_t callback, + int timeout, struct pam_data *pd); diff --git a/server/responder/pam/pamsrv_cmd.c b/server/responder/pam/pamsrv_cmd.c new file mode 100644 index 000000000..4fdded309 --- /dev/null +++ b/server/responder/pam/pamsrv_cmd.c @@ -0,0 +1,196 @@ +#include +#include + +#include "util/util.h" +#include "responder/common/responder_common.h" +#include "responder/common/responder_cmd.h" +#include "responder/common/responder_packet.h" +#include "responder/pam/pamsrv.h" + +static int pam_parse_in_data(uint8_t *body, size_t blen, struct pam_data *pd) { + int start; + int end; + int last=blen-1; + char *delim; + + start = end = 0; + while ( end < last && body[end++]!='\0'); + pd->user = (char *) &body[start]; + + delim = strchr(pd->user, SSS_DOMAIN_DELIM); + if (delim != NULL ) { + *delim = '\0'; + pd->domain = delim+1; + } else { + pd->domain = NULL; + } + + start = end; + while ( end < last && body[end++]!='\0'); + pd->service = (char *) &body[start]; + + start = end; + while ( end < last && body[end++]!='\0'); + pd->tty = (char *) &body[start]; + + start = end; + while ( end < last && body[end++]!='\0'); + pd->ruser = (char *) &body[start]; + + start = end; + while ( end < last && body[end++]!='\0'); + pd->rhost = (char *) &body[start]; + + start = end; + pd->authtok_type = (int) body[start]; + start += sizeof(uint32_t); + pd->authtok_size = (int) body[start]; + start += sizeof(uint32_t); + end = start+pd->authtok_size; + if ( pd->authtok_size == 0 ) { + pd->authtok = NULL; + } else { + if ( end <= blen ) { + pd->authtok = (uint8_t *) &body[start]; + } else { + DEBUG(1, ("Invalid authtok size: %d\n", pd->authtok_size)); + return EINVAL; + } + } + + start = end; + pd->newauthtok_type = (int) body[start]; + start += sizeof(uint32_t); + pd->newauthtok_size = (int) body[start]; + start += sizeof(uint32_t); + end = start+pd->newauthtok_size; + if ( pd->newauthtok_size == 0 ) { + pd->newauthtok = NULL; + } else { + if ( end <= blen ) { + pd->newauthtok = (uint8_t *) &body[start]; + } else { + DEBUG(1, ("Invalid newauthtok size: %d\n", pd->newauthtok_size)); + return EINVAL; + } + } + + DEBUG_PAM_DATA(4, pd); + + return EOK; +} + +static void pam_reply(struct cli_ctx *cctx, int pam_status, char *domain) { + struct sss_cmd_ctx *nctx; + int32_t ret_status = pam_status; + uint8_t *body; + size_t blen; + int ret; + int err = EOK; + + DEBUG(4, ("pam_reply get called.\n")); + nctx = talloc_zero(cctx, struct sss_cmd_ctx); + if (!nctx) { + err = ENOMEM; + goto done; + } + nctx->cctx = cctx; + nctx->check_expiration = true; + + ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + err = ret; + goto done; + } + + ret = sss_packet_grow(cctx->creq->out, sizeof(int) + strlen(domain)+1 ); + if (ret != EOK) { + err = ret; + goto done; + } + + sss_packet_get_body(cctx->creq->out, &body, &blen); + DEBUG(4, ("blen: %d\n", blen)); + memcpy(body, &ret_status, sizeof(int32_t)); + memcpy(body+sizeof(int32_t), domain, strlen(domain)+1); + +done: + sss_cmd_done(nctx); +} + +static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) +{ + uint8_t *body; + size_t blen; + int ret; + struct pam_data *pd; + + pd = talloc(cctx, struct pam_data); + if (pd == NULL) return ENOMEM; + + sss_packet_get_body(cctx->creq->in, &body, &blen); + if (blen >= sizeof(uint32_t) && + ((uint32_t *)(&body[blen - sizeof(uint32_t)]))[0] != END_OF_PAM_REQUEST) { + DEBUG(1, ("Received data not terminated.\n")); + talloc_free(pd); + return EINVAL; + } + + pd->cmd = pam_cmd; + ret=pam_parse_in_data(body, blen, pd); + if( ret != 0 ) { + talloc_free(pd); + return EINVAL; + } + + ret=pam_dp_send_req(cctx, pam_reply, PAM_DP_TIMEOUT, pd); + DEBUG(4, ("pam_dp_send_req returned %d\n", ret)); + + return ret; +} + +static int pam_cmd_authenticate(struct cli_ctx *cctx) { + DEBUG(4, ("entering pam_cmd_authenticate\n")); + return pam_forwarder(cctx, SSS_PAM_AUTHENTICATE); +} + +static int pam_cmd_setcred(struct cli_ctx *cctx) { + DEBUG(4, ("entering pam_cmd_setcred\n")); + return pam_forwarder(cctx, SSS_PAM_SETCRED); +} + +static int pam_cmd_acct_mgmt(struct cli_ctx *cctx) { + DEBUG(4, ("entering pam_cmd_acct_mgmt\n")); + return pam_forwarder(cctx, SSS_PAM_ACCT_MGMT); +} + +static int pam_cmd_open_session(struct cli_ctx *cctx) { + DEBUG(4, ("entering pam_cmd_open_session\n")); + return pam_forwarder(cctx, SSS_PAM_OPEN_SESSION); +} + +static int pam_cmd_close_session(struct cli_ctx *cctx) { + DEBUG(4, ("entering pam_cmd_close_session\n")); + return pam_forwarder(cctx, SSS_PAM_CLOSE_SESSION); +} + +static int pam_cmd_chauthtok(struct cli_ctx *cctx) { + DEBUG(4, ("entering pam_cmd_chauthtok\n")); + return pam_forwarder(cctx, SSS_PAM_CHAUTHTOK); +} + +struct sss_cmd_table *register_sss_cmds(void) { + static struct sss_cmd_table sss_cmds[] = { + {SSS_GET_VERSION, sss_cmd_get_version}, + {SSS_PAM_AUTHENTICATE, pam_cmd_authenticate}, + {SSS_PAM_SETCRED, pam_cmd_setcred}, + {SSS_PAM_ACCT_MGMT, pam_cmd_acct_mgmt}, + {SSS_PAM_OPEN_SESSION, pam_cmd_open_session}, + {SSS_PAM_CLOSE_SESSION, pam_cmd_close_session}, + {SSS_PAM_CHAUTHTOK, pam_cmd_chauthtok}, + {SSS_CLI_NULL, NULL} + }; + + return sss_cmds; +} diff --git a/server/responder/pam/pamsrv_dp.c b/server/responder/pam/pamsrv_dp.c new file mode 100644 index 000000000..8a10c6bd7 --- /dev/null +++ b/server/responder/pam/pamsrv_dp.c @@ -0,0 +1,215 @@ +/* + SSSD + + NSS Responder - Data Provider Interfaces + + Copyright (C) Simo Sorce 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include + +#include +#include + +#include "util/util.h" +#include "responder/common/responder_packet.h" +#include "responder/nss/nsssrv.h" +#include "providers/data_provider.h" +#include "sbus/sbus_client.h" +#include "providers/dp_sbus.h" +#include "responder/pam/pamsrv.h" + +struct pam_reply_ctx { + struct cli_ctx *cctx; + pam_dp_callback_t callback; +}; + +static void pam_process_dp_reply(DBusPendingCall *pending, void *ptr) { + DBusError dbus_error; + DBusMessage* msg; + int ret; + int type; + dbus_uint32_t pam_status; + char *domain; + struct pam_reply_ctx *rctx; + + rctx = talloc_get_type(ptr, struct pam_reply_ctx); + + dbus_error_init(&dbus_error); + + dbus_pending_call_block(pending); + msg = dbus_pending_call_steal_reply(pending); + if (msg == NULL) { + DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + + type = dbus_message_get_type(msg); + switch (type) { + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + ret = dbus_message_get_args(msg, &dbus_error, + DBUS_TYPE_UINT32, &pam_status, + DBUS_TYPE_STRING, &domain, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(0, ("Failed to parse reply.\n")); + pam_status = PAM_SYSTEM_ERR; + domain = ""; + goto done; + } + DEBUG(4, ("received: [%d][%s]\n", pam_status, domain)); + break; + case DBUS_MESSAGE_TYPE_ERROR: + DEBUG(0, ("Reply error.\n")); + pam_status = PAM_SYSTEM_ERR; + break; + default: + DEBUG(0, ("Default... what now?.\n")); + pam_status = PAM_SYSTEM_ERR; + } + + +done: + dbus_pending_call_unref(pending); + dbus_message_unref(msg); + rctx->callback(rctx->cctx, pam_status, domain); +} + +int pam_dp_send_req(struct cli_ctx *cctx, + pam_dp_callback_t callback, + int timeout, struct pam_data *pd) { + DBusMessage *msg; + DBusPendingCall *pending_reply; + DBusConnection *conn; + DBusError dbus_error; + dbus_bool_t ret; + struct pam_reply_ctx *rctx; + + rctx = talloc(cctx, struct pam_reply_ctx); + if (rctx == NULL) { + DEBUG(0,("Out of memory?!\n")); + return ENOMEM; + } + rctx->cctx=cctx; + rctx->callback=callback; + + if (pd->domain==NULL || + pd->user==NULL || + pd->service==NULL || + pd->tty==NULL || + pd->ruser==NULL || + pd->rhost==NULL ) { + return EINVAL; + } + + conn = sbus_get_connection(cctx->nctx->dp_ctx->scon_ctx); + dbus_error_init(&dbus_error); + + msg = dbus_message_new_method_call(NULL, + DP_CLI_PATH, + DP_CLI_INTERFACE, + DP_SRV_METHOD_PAMHANDLER); + if (msg == NULL) { + DEBUG(0,("Out of memory?!\n")); + return ENOMEM; + } + + + DEBUG(4, ("Sending request with the following data:\n")); + DEBUG_PAM_DATA(4, pd); + + ret = dbus_message_append_args(msg, + DBUS_TYPE_INT32, &(pd->cmd), + DBUS_TYPE_STRING, &(pd->domain), + DBUS_TYPE_STRING, &(pd->user), + DBUS_TYPE_STRING, &(pd->service), + DBUS_TYPE_STRING, &(pd->tty), + DBUS_TYPE_STRING, &(pd->ruser), + DBUS_TYPE_STRING, &(pd->rhost), + DBUS_TYPE_INT32, &(pd->authtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->authtok), pd->authtok_size, + DBUS_TYPE_INT32, &(pd->newauthtok_type), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &(pd->newauthtok), pd->newauthtok_size, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed to build message\n")); + return EIO; + } + + ret = dbus_connection_send_with_reply(conn, msg, &pending_reply, timeout); + if (!ret) { + /* + * Critical Failure + * We can't communicate on this connection + * We'll drop it using the default destructor. + */ + DEBUG(0, ("D-BUS send failed.\n")); + dbus_message_unref(msg); + return EIO; + } + + dbus_pending_call_set_notify(pending_reply, pam_process_dp_reply, rctx, + NULL); + dbus_message_unref(msg); + + return EOK; +} + +static int pam_dp_identity(DBusMessage *message, struct sbus_conn_ctx *sconn) +{ + dbus_uint16_t version = DATA_PROVIDER_VERSION; + dbus_uint16_t clitype = DP_CLI_FRONTEND; + const char *cliname = "PAM"; + const char *nullname = ""; + DBusMessage *reply; + dbus_bool_t ret; + + DEBUG(4,("Sending ID reply: (%d,%d,%s)\n", clitype, version, cliname)); + + reply = dbus_message_new_method_return(message); + if (!reply) return ENOMEM; + + ret = dbus_message_append_args(reply, + DBUS_TYPE_UINT16, &clitype, + DBUS_TYPE_UINT16, &version, + DBUS_TYPE_STRING, &cliname, + DBUS_TYPE_STRING, &nullname, + DBUS_TYPE_INVALID); + if (!ret) { + dbus_message_unref(reply); + return EIO; + } + + /* send reply back */ + sbus_conn_send_reply(sconn, reply); + dbus_message_unref(reply); + + return EOK; +} + +struct sbus_method *register_pam_dp_methods(void) { + static struct sbus_method pam_dp_methods[] = { + { DP_CLI_METHOD_IDENTITY, pam_dp_identity }, + { NULL, NULL } + }; + + return pam_dp_methods; +} diff --git a/server/responder/pam/pamsrv_util.c b/server/responder/pam/pamsrv_util.c new file mode 100644 index 000000000..5dab9b679 --- /dev/null +++ b/server/responder/pam/pamsrv_util.c @@ -0,0 +1,16 @@ +#include "util/util.h" +#include "responder/pam/pamsrv.h" + +void pam_print_data(int l, struct pam_data *pd) { + DEBUG(l, ("command: %d\n", pd->cmd)); + DEBUG(l, ("domain: %s\n", pd->domain)); + DEBUG(l, ("user: %s\n", pd->user)); + DEBUG(l, ("service: %s\n", pd->service)); + DEBUG(l, ("tty: %s\n", pd->tty)); + DEBUG(l, ("ruser: %s\n", pd->ruser)); + DEBUG(l, ("rhost: %s\n", pd->rhost)); + DEBUG(l, ("authtok type: %d\n", pd->authtok_type)); + DEBUG(l, ("authtok size: %d\n", pd->authtok_size)); + DEBUG(l, ("newauthtok type: %d\n", pd->newauthtok_type)); + DEBUG(l, ("newauthtok size: %d\n", pd->newauthtok_size)); +} diff --git a/server/server.mk b/server/server.mk index 744dc243d..386f56f16 100644 --- a/server/server.mk +++ b/server/server.mk @@ -15,6 +15,12 @@ UTIL_OBJ = \ db/sysdb.o \ db/sysdb_sync.o +RESPONDER_UTIL_OBJ = \ + responder/common/responder_dp.o \ + responder/common/responder_packet.o \ + responder/common/responder_common.o \ + responder/common/responder_cmd.o + SERVER_OBJ = \ monitor/monitor.o @@ -27,11 +33,13 @@ DP_BE_OBJ = \ PROXY_BE_OBJ = \ providers/proxy.o +LDAP_BE_OBJ = \ + providers/ldap_be.o + NSSSRV_OBJ = \ - nss/nsssrv.o \ - nss/nsssrv_packet.o \ - nss/nsssrv_cmd.o \ - nss/nsssrv_dp.o + responder/nss/nsssrv.o \ + responder/nss/nsssrv_cmd.o \ + responder/nss/nsssrv_dp.o INFOPIPE_OBJ = \ infopipe/infopipe.o \ @@ -51,17 +59,29 @@ SYSDB_TEST_OBJ = \ INFP_TEST_OBJ = \ tests/infopipe-tests.o +PAMSRV_OBJ = \ + responder/pam/pamsrv.o \ + responder/pam/pamsrv_cmd.o \ + responder/pam/pamsrv_dp.o + +PAMSRV_UTIL_OBJ = responder/pam/pamsrv_util.o + +PAM = -lpam + sbin/sssd: $(SERVER_OBJ) $(UTIL_OBJ) $(CC) -o sbin/sssd $(SERVER_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) -sbin/sssd_nss: $(NSSSRV_OBJ) $(UTIL_OBJ) - $(CC) -o sbin/sssd_nss $(NSSSRV_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) +sbin/sssd_nss: $(NSSSRV_OBJ) $(UTIL_OBJ) $(RESPONDER_UTIL_OBJ) + $(CC) -o sbin/sssd_nss $(NSSSRV_OBJ) $(UTIL_OBJ) $(RESPONDER_UTIL_OBJ) $(LDFLAGS) $(LIBS) + +sbin/sssd_pam: $(PAMSRV_OBJ) $(UTIL_OBJ) $(RESPONDER_UTIL_OBJ) $(PAMSRV_UTIL_OBJ) + $(CC) -o sbin/sssd_pam $(PAMSRV_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ) $(RESPONDER_UTIL_OBJ) $(LDFLAGS) $(LIBS) -sbin/sssd_dp: $(DP_OBJ) $(UTIL_OBJ) - $(CC) -o sbin/sssd_dp $(DP_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) +sbin/sssd_dp: $(DP_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ) + $(CC) -o sbin/sssd_dp $(DP_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ) $(LDFLAGS) $(LIBS) sbin/sssd_be: $(DP_BE_OBJ) $(UTIL_OBJ) - $(CC) -Wl,-E -o sbin/sssd_be $(DP_BE_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) + $(CC) -Wl,-E -o sbin/sssd_be $(DP_BE_OBJ) $(UTIL_OBJ) $(PAMSRV_UTIL_OBJ) $(LDFLAGS) $(LIBS) $(PAM) sbin/sssd_info: $(INFOPIPE_OBJ) $(UTIL_OBJ) $(CC) -o sbin/sssd_info $(INFOPIPE_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) @@ -70,9 +90,13 @@ sbin/sssd_pk: $(POLKIT_OBJ) $(UTIL_OBJ) $(CC) -o sbin/sssd_pk $(POLKIT_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) lib/$(PROXY_BE_SOBASE): $(PROXY_BE_OBJ) - $(SHLD) $(SHLD_FLAGS) $(SONAMEFLAG)$(PROXY_BE_SONAME) -o lib/$(PROXY_BE_SOLIB) $(PROXY_BE_OBJ) $(LDFLAGS) $(LIBS) + $(SHLD) $(SHLD_FLAGS) $(SONAMEFLAG)$(PROXY_BE_SONAME) -o lib/$(PROXY_BE_SOLIB) $(PROXY_BE_OBJ) $(LDFLAGS) $(LIBS) $(PAM_LIBS) ln -fs $(PROXY_BE_SOLIB) $@ +lib/$(LDAP_BE_SOBASE): $(LDAP_BE_OBJ) + $(SHLD) $(SHLD_FLAGS) $(SONAMEFLAG)$(LDAP_BE_SONAME) -o lib/$(LDAP_BE_SOLIB) $(LDAP_BE_OBJ) $(LDFLAGS) $(LIBS) $(LDAP_LIBS) + ln -fs $(LDAP_BE_SOLIB) $@ + lib/$(MEMBEROF_SOBASE): $(MEMBEROF_OBJ) $(SHLD) $(SHLD_FLAGS) $(SONAMEFLAG)$(MEMBEROF_SONAME) -o lib/$(MEMBEROF_SOLIB) $(MEMBEROF_OBJ) $(LDFLAGS) $(LDB_LIBS) ln -fs $(MEMBEROF_SOLIB) $@ -- cgit