diff options
author | Jakub Hrozek <jhrozek@redhat.com> | 2012-01-03 21:32:30 +0100 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2012-02-05 19:16:16 -0500 |
commit | 9e80079370ff3b943832adc3c5ef430e64be0a0c (patch) | |
tree | febc22370a3af43a220b111a1959865cf048e1c1 | |
parent | e24a0656252c167e644b4758e5e53afe69be02e1 (diff) | |
download | sssd-9e80079370ff3b943832adc3c5ef430e64be0a0c.tar.gz sssd-9e80079370ff3b943832adc3c5ef430e64be0a0c.tar.bz2 sssd-9e80079370ff3b943832adc3c5ef430e64be0a0c.zip |
AUTOFS: responder
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 15 | ||||
-rw-r--r-- | src/confdb/confdb.c | 10 | ||||
-rw-r--r-- | src/confdb/confdb.h | 6 | ||||
-rw-r--r-- | src/config/SSSDConfig.py | 4 | ||||
-rwxr-xr-x | src/config/SSSDConfigTest.py | 14 | ||||
-rw-r--r-- | src/config/etc/sssd.api.conf | 6 | ||||
-rw-r--r-- | src/man/Makefile.am | 10 | ||||
-rw-r--r-- | src/man/sssd.conf.5.xml | 28 | ||||
-rw-r--r-- | src/monitor/monitor.c | 2 | ||||
-rw-r--r-- | src/providers/data_provider.h | 1 | ||||
-rw-r--r-- | src/responder/autofs/autofs_private.h | 95 | ||||
-rw-r--r-- | src/responder/autofs/autofssrv.c | 229 | ||||
-rw-r--r-- | src/responder/autofs/autofssrv_cmd.c | 1239 | ||||
-rw-r--r-- | src/responder/autofs/autofssrv_dp.c | 155 | ||||
-rw-r--r-- | src/responder/common/responder.h | 2 | ||||
-rw-r--r-- | src/responder/nss/nsssrv_private.h | 3 |
17 files changed, 1809 insertions, 11 deletions
@@ -84,3 +84,4 @@ check_and_open-tests sssd_sudo sss_sudo_cli autofs_test_client +sssd_autofs diff --git a/Makefile.am b/Makefile.am index f235d39b..becc67ba 100644 --- a/Makefile.am +++ b/Makefile.am @@ -83,6 +83,9 @@ sssdlibexec_PROGRAMS = \ if BUILD_SUDO sssdlibexec_PROGRAMS += sssd_sudo endif +if BUILD_AUTOFS +sssdlibexec_PROGRAMS += sssd_autofs +endif if HAVE_CHECK non_interactive_check_based_tests = \ @@ -322,6 +325,7 @@ dist_noinst_HEADERS = \ src/responder/nss/nsssrv_services.h \ src/responder/common/negcache.h \ src/responder/sudo/sudosrv_private.h \ + src/responder/autofs/autofs_private.h \ src/sbus/sbus_client.h \ src/sbus/sssd_dbus.h \ src/sbus/sssd_dbus_private.h \ @@ -481,6 +485,17 @@ sssd_sudo_LDADD = \ libsss_util.la endif +if BUILD_AUTOFS +sssd_autofs_SOURCES = \ + src/responder/autofs/autofssrv.c \ + src/responder/autofs/autofssrv_cmd.c \ + src/responder/autofs/autofssrv_dp.c \ + $(SSSD_RESPONDER_OBJ) +sssd_autofs_LDADD = \ + $(SSSD_LIBS) \ + libsss_util.la +endif + sssd_be_SOURCES = \ src/providers/data_provider_be.c \ src/providers/data_provider_fo.c \ diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c index 8b3a046f..57023f29 100644 --- a/src/confdb/confdb.c +++ b/src/confdb/confdb.c @@ -889,6 +889,16 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, goto done; } + /* Override the service cache timeout, if specified */ + ret = get_entry_as_uint32(res->msgs[0], &domain->autofsmap_timeout, + CONFDB_DOMAIN_AUTOFS_CACHE_TIMEOUT, + entry_cache_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Invalid value for [%s]\n", + CONFDB_DOMAIN_AUTOFS_CACHE_TIMEOUT)); + goto done; + } ret = get_entry_as_uint32(res->msgs[0], &domain->override_gid, CONFDB_DOMAIN_OVERRIDE_GID, 0); diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index 7699bba4..6aa457a4 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -98,6 +98,10 @@ #define CONFDB_SUDO_TIMED "sudo_timed" #define CONFDB_DEFAULT_SUDO_TIMED false +/* autofs */ +#define CONFDB_AUTOFS_CONF_ENTRY "config/autofs" +#define CONFDB_AUTOFS_MAP_NEG_TIMEOUT "autofs_negative_timeout" + /* Data Provider */ #define CONFDB_DP_CONF_ENTRY "config/dp" @@ -133,6 +137,7 @@ #define CONFDB_DOMAIN_GROUP_CACHE_TIMEOUT "entry_cache_group_timeout" #define CONFDB_DOMAIN_NETGROUP_CACHE_TIMEOUT "entry_cache_netgroup_timeout" #define CONFDB_DOMAIN_SERVICE_CACHE_TIMEOUT "entry_cache_service_timeout" +#define CONFDB_DOMAIN_AUTOFS_CACHE_TIMEOUT "entry_cache_autofs_timeout" /* Local Provider */ #define CONFDB_LOCAL_DEFAULT_SHELL "default_shell" @@ -175,6 +180,7 @@ struct sss_domain_info { uint32_t group_timeout; uint32_t netgroup_timeout; uint32_t service_timeout; + uint32_t autofsmap_timeout; struct sss_domain_info *next; }; diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py index dfad410c..740ab27f 100644 --- a/src/config/SSSDConfig.py +++ b/src/config/SSSDConfig.py @@ -77,6 +77,9 @@ option_strings = { 'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'), 'sudo_cache_timeout' : _('How many seconds to keep sudorules cached before asking the provider again'), + # [autofs] + 'autofs_negative_timeout' : _('Negative cache timeout length (seconds)'), + # [provider] 'id_provider' : _('Identity provider'), 'auth_provider' : _('Authentication provider'), @@ -103,6 +106,7 @@ option_strings = { 'entry_cache_group_timeout' : _('Entry cache timeout length (seconds)'), 'entry_cache_netgroup_timeout' : _('Entry cache timeout length (seconds)'), 'entry_cache_service_timeout' : _('Entry cache timeout length (seconds)'), + 'entry_cache_autofs_timeout' : _('Entry cache timeout length (seconds)'), # [provider/ipa] 'ipa_domain' : _('IPA domain'), diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py index 66b3f9c7..2d6fa5ad 100755 --- a/src/config/SSSDConfigTest.py +++ b/src/config/SSSDConfigTest.py @@ -483,6 +483,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): 'entry_cache_group_timeout', 'entry_cache_netgroup_timeout', 'entry_cache_service_timeout', + 'entry_cache_autofs_timeout', 'lookup_family_order', 'account_cache_expiration', 'dns_resolver_timeout', @@ -494,7 +495,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): 'auth_provider', 'access_provider', 'chpass_provider', - 'sudo_provider'] + 'sudo_provider', + 'autofs_provider' ] self.assertTrue(type(options) == dict, "Options should be a dictionary") @@ -806,6 +808,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): 'entry_cache_group_timeout', 'entry_cache_netgroup_timeout', 'entry_cache_service_timeout', + 'entry_cache_autofs_timeout', 'account_cache_expiration', 'lookup_family_order', 'dns_resolver_timeout', @@ -817,7 +820,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): 'auth_provider', 'access_provider', 'chpass_provider', - 'sudo_provider'] + 'sudo_provider', + 'autofs_provider'] self.assertTrue(type(options) == dict, "Options should be a dictionary") @@ -1146,7 +1150,8 @@ class SSSDConfigTestSSSDConfig(unittest.TestCase): 'sssd', 'nss', 'pam', - 'sudo'] + 'sudo', + 'autofs'] for section in control_list: self.assertTrue(sssdconfig.has_section(section), "Section [%s] missing" % @@ -1188,7 +1193,8 @@ class SSSDConfigTestSSSDConfig(unittest.TestCase): 'sssd', 'pam', 'nss', - 'sudo'] + 'sudo', + 'autofs' ] service_list = sssdconfig.list_services() for service in control_list: self.assertTrue(service in service_list, diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf index 6eb08a5d..446bf950 100644 --- a/src/config/etc/sssd.api.conf +++ b/src/config/etc/sssd.api.conf @@ -48,6 +48,10 @@ pam_pwd_expiration_warning = int, None, false sudo_timed = bool, None, false sudo_cache_timeout = int, None, false +[autofs] +# autofs service +autofs_negative_timeout = int, None, false + [provider] #Available provider types id_provider = str, None, true @@ -55,6 +59,7 @@ auth_provider = str, None, false access_provider = str, None, false chpass_provider = str, None, false sudo_provider = str, None, false +autofs_provider = str, None, false [domain] # Options available to all domains @@ -86,6 +91,7 @@ entry_cache_user_timeout = int, None, false entry_cache_group_timeout = int, None, false entry_cache_netgroup_timeout = int, None, false entry_cache_service_timeout = int, None, false +entry_cache_autofs_timeout = int, None, false # Special providers [provider/permit] diff --git a/src/man/Makefile.am b/src/man/Makefile.am index 6e23830a..f0faf690 100644 --- a/src/man/Makefile.am +++ b/src/man/Makefile.am @@ -6,13 +6,17 @@ top_builddir = ../.. ############ -# If no conditions are given, *all* conditionals are expanded. We don't +# If no conditions are given, *all* conditionals are expanded. We don't want # to include any conditions by default, so we need to pass a phony conditional -CONDS=with_false if BUILD_SUDO # conditionals are delimeted with a semicolon -CONDS+=;with_sudo +SUDO_CONDS = ;with_sudo endif +if BUILD_AUTOFS +AUTOFS_CONDS = ;with_autofs +endif +CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS) + #Special Rules: export SGML_CATALOG_FILES diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index 7916e1f8..7217c9dd 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -645,6 +645,34 @@ </variablelist> </refsect2> + <refsect2 id='AUTOFS' condition="with_autofs"> + <title>AUTOFS configuration options</title> + <para> + These options can be used to configure the autofs service. + </para> + <para> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/experimental.xml" /> + </para> + + <variablelist> + <varlistentry> + <term>autofs_negative_timeout (integer)</term> + <listitem> + <para> + Specifies for how many seconds should the + autofs respondercache negative cache hits + (that is, queries for invalid map entries, + like nonexistent ones) before asking the back + end again. + </para> + <para> + Default: 15 + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect2> + </refsect1> <refsect1 id='domain-sections'> diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c index d2717fae..264256f0 100644 --- a/src/monitor/monitor.c +++ b/src/monitor/monitor.c @@ -754,7 +754,7 @@ static int check_local_domain_unique(struct sss_domain_info *domains) static char *check_services(char **services) { - const char *known_services[] = { "nss", "pam", "sudo", NULL }; + const char *known_services[] = { "nss", "pam", "sudo", "autofs", NULL }; int i; int ii; diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h index 3c03b081..89528721 100644 --- a/src/providers/data_provider.h +++ b/src/providers/data_provider.h @@ -141,6 +141,7 @@ #define BE_REQ_SUDO_ALL 0x0006 #define BE_REQ_SUDO_DEFAULTS 0x0007 #define BE_REQ_SUDO_USER 0x0008 +#define BE_REQ_AUTOFS 0x0007 #define BE_REQ_FAST 0x1000 /* AUTH related common data and functions */ diff --git a/src/responder/autofs/autofs_private.h b/src/responder/autofs/autofs_private.h new file mode 100644 index 00000000..753089d2 --- /dev/null +++ b/src/responder/autofs/autofs_private.h @@ -0,0 +1,95 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _AUTOFSSRV_PRIVATE_H_ +#define _AUTOFSSRV_PRIVATE_H_ + +#define SSS_AUTOFS_SBUS_SERVICE_VERSION 0x0001 +#define SSS_AUTOFS_SBUS_SERVICE_NAME "autofs" + +#define SSS_AUTOFS_PROTO_VERSION 0x001 + +struct autofs_ctx { + struct resp_ctx *rctx; + + int neg_timeout; + + hash_table_t *maps; +}; + +struct autofs_cmd_ctx { + struct cli_ctx *cctx; + char *mapname; + bool check_next; +}; + +struct autofs_dom_ctx { + struct autofs_cmd_ctx *cmd_ctx; + struct sss_domain_info *domain; + bool check_provider; + + /* cache results */ + struct ldb_message *map; + + size_t entry_count; + struct ldb_message **entries; + + struct autofs_map_ctx *map_ctx; +}; + +struct autofs_map_ctx { + /* state of the map entry */ + bool ready; + bool found; + + /* requests */ + struct setent_req_list *reqs; + + hash_table_t *map_table; + char *mapname; + + /* map entry */ + struct ldb_message *map; + size_t entry_count; + struct ldb_message **entries; +}; + +struct sss_cmd_table *get_autofs_cmds(void); + +enum sss_dp_autofs_type { + SSS_DP_AUTOFS +}; + +struct tevent_req * +sss_dp_get_autofs_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + enum sss_dp_autofs_type type, + const char *name); + +errno_t +sss_dp_get_autofs_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *dp_err, + dbus_uint32_t *dp_ret, + char **err_msg); + +#endif /* _AUTOFSSRV_PRIVATE_H_ */ diff --git a/src/responder/autofs/autofssrv.c b/src/responder/autofs/autofssrv.c new file mode 100644 index 00000000..76d27070 --- /dev/null +++ b/src/responder/autofs/autofssrv.c @@ -0,0 +1,229 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2012 Red Hat + + Autofs responder: the responder server + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <popt.h> + +#include "util/util.h" +#include "confdb/confdb.h" +#include "monitor/monitor_interfaces.h" +#include "responder/common/responder.h" +#include "providers/data_provider.h" +#include "responder/autofs/autofs_private.h" + +struct sbus_method monitor_autofs_methods[] = { + { MON_CLI_METHOD_PING, monitor_common_pong }, + { MON_CLI_METHOD_RES_INIT, monitor_common_res_init }, + { MON_CLI_METHOD_ROTATE, responder_logrotate }, + { NULL, NULL } +}; + +struct sbus_interface monitor_autofs_interface = { + MONITOR_INTERFACE, + MONITOR_PATH, + SBUS_DEFAULT_VTABLE, + monitor_autofs_methods, + NULL +}; + +static struct sbus_method autofs_dp_methods[] = { + { NULL, NULL } +}; + +struct sbus_interface autofs_dp_interface = { + DP_INTERFACE, + DP_PATH, + SBUS_DEFAULT_VTABLE, + autofs_dp_methods, + NULL +}; + +static errno_t +autofs_get_config(struct autofs_ctx *actx, + struct confdb_ctx *cdb) +{ + errno_t ret; + + ret = confdb_get_int(cdb, actx, CONFDB_AUTOFS_CONF_ENTRY, + CONFDB_AUTOFS_MAP_NEG_TIMEOUT, 15, + &actx->neg_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Cannot read %s from configuration [%d]: %s\n", + CONFDB_AUTOFS_MAP_NEG_TIMEOUT, ret, strerror(ret))); + return ret; + } + + return EOK; +} + +static void +autofs_dp_reconnect_init(struct sbus_connection *conn, + int status, void *pvt) +{ + struct be_conn *be_conn = talloc_get_type(pvt, struct be_conn); + int ret; + + /* Did we reconnect successfully? */ + if (status == SBUS_RECONNECT_SUCCESS) { + DEBUG(SSSDBG_TRACE_FUNC, ("Reconnected to the Data Provider.\n")); + + /* Identify ourselves to the data provider */ + ret = dp_common_send_id(be_conn->conn, + DATA_PROVIDER_VERSION, + "autofs"); + /* all fine */ + if (ret == EOK) { + handle_requests_after_reconnect(); + return; + } + } + + /* Failed to reconnect */ + DEBUG(SSSDBG_FATAL_FAILURE, ("Could not reconnect to %s provider.\n", + be_conn->domain->name)); +} + +static int +autofs_process_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb) +{ + struct sss_cmd_table *autofs_cmds; + struct autofs_ctx *autofs_ctx; + struct be_conn *iter; + int ret; + int hret; + int max_retries; + + autofs_ctx = talloc_zero(mem_ctx, struct autofs_ctx); + if (!autofs_ctx) { + DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error initializing autofs_ctx\n")); + return ENOMEM; + } + + ret = autofs_get_config(autofs_ctx, cdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, ("Cannot read autofs configuration\n")); + return ret; + } + + autofs_cmds = get_autofs_cmds(); + ret = sss_process_init(autofs_ctx, ev, cdb, + autofs_cmds, + SSS_AUTOFS_SOCKET_NAME, NULL, + CONFDB_AUTOFS_CONF_ENTRY, + SSS_AUTOFS_SBUS_SERVICE_NAME, + SSS_AUTOFS_SBUS_SERVICE_VERSION, + &monitor_autofs_interface, + "autofs", + &autofs_dp_interface, + &autofs_ctx->rctx); + if (ret != EOK) { + return ret; + } + autofs_ctx->rctx->pvt_ctx = autofs_ctx; + + /* Enable automatic reconnection to the Data Provider */ + ret = confdb_get_int(autofs_ctx->rctx->cdb, autofs_ctx->rctx, + CONFDB_AUTOFS_CONF_ENTRY, + CONFDB_SERVICE_RECON_RETRIES, + 3, &max_retries); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Failed to set up automatic reconnection\n")); + return ret; + } + + for (iter = autofs_ctx->rctx->be_conns; iter; iter = iter->next) { + sbus_reconnect_init(iter->conn, max_retries, + autofs_dp_reconnect_init, iter); + } + + /* Create the lookup table for setautomntent results */ + hret = sss_hash_create(autofs_ctx, 10, &autofs_ctx->maps); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Unable to initialize automount maps hash table\n")); + return EIO; + } + + DEBUG(SSSDBG_TRACE_FUNC, ("autofs 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 + POPT_TABLEEND + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + 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); + + CONVERT_AND_SET_DEBUG_LEVEL(debug_level); + + /* set up things like debug, signals, daemonization, etc... */ + debug_log_file = "sssd_autofs"; + + ret = server_setup("sssd[autofs]", 0, CONFDB_AUTOFS_CONF_ENTRY, &main_ctx); + if (ret != EOK) { + return 2; + } + + ret = die_if_parent_died(); + if (ret != EOK) { + /* This is not fatal, don't return */ + DEBUG(SSSDBG_OP_FAILURE, ("Could not set up to exit " + "when parent process does\n")); + } + + ret = autofs_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/src/responder/autofs/autofssrv_cmd.c b/src/responder/autofs/autofssrv_cmd.c new file mode 100644 index 00000000..d2b37f71 --- /dev/null +++ b/src/responder/autofs/autofssrv_cmd.c @@ -0,0 +1,1239 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2012 Red Hat + + Autofs responder: commands + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <talloc.h> + +#include "util/util.h" +#include "responder/common/responder.h" +#include "responder/common/responder_packet.h" +#include "responder/autofs/autofs_private.h" +#include "db/sysdb.h" +#include "db/sysdb_autofs.h" +#include "confdb/confdb.h" + +static int autofs_cmd_send_error(struct autofs_cmd_ctx *cmdctx, int err) +{ + return sss_cmd_send_error(cmdctx->cctx, err); +} + +static int +autofs_cmd_send_empty(struct autofs_cmd_ctx *cmdctx) +{ + return sss_cmd_send_empty(cmdctx->cctx, cmdctx); +} + +static int +autofs_cmd_done(struct autofs_cmd_ctx *cmdctx, int ret) +{ + switch (ret) { + case EOK: + /* all fine, just return here */ + break; + + case ENOENT: + ret = autofs_cmd_send_empty(cmdctx); + if (ret) { + return EFAULT; + } + break; + + case EAGAIN: + /* async processing, just return here */ + break; + + case EFAULT: + /* very bad error */ + return EFAULT; + + default: + ret = autofs_cmd_send_error(cmdctx, ret); + if (ret) { + return EFAULT; + } + sss_cmd_done(cmdctx->cctx, cmdctx); + break; + } + + return EOK; +} + +static errno_t +autofs_setent_add_ref(TALLOC_CTX *memctx, + struct autofs_map_ctx *map_ctx, + struct tevent_req *req) +{ + return setent_add_ref(memctx, map_ctx, &map_ctx->reqs, req); +} + +static void +autofs_setent_notify(struct autofs_map_ctx *map_ctx, errno_t ret) +{ + setent_notify(map_ctx->reqs, ret); +} + +static errno_t +get_autofs_map(struct autofs_ctx *actx, + char *mapname, + struct autofs_map_ctx **map) +{ + hash_key_t key; + hash_value_t value; + int hret; + + key.type = HASH_KEY_STRING; + key.str = mapname; + + hret = hash_lookup(actx->maps, &key, &value); + if (hret == HASH_SUCCESS) { + *map = talloc_get_type(value.ptr, struct autofs_map_ctx); + return EOK; + } else if (hret == HASH_ERROR_KEY_NOT_FOUND) { + return ENOENT; + } + + DEBUG(SSSDBG_CRIT_FAILURE, + ("Unexpected error reading from autofs map hash [%d][%s]\n", + hret, hash_error_string(hret))); + return EIO; +} + +static int autofs_map_hash_remove (TALLOC_CTX *ctx); + +static errno_t +set_autofs_map(struct autofs_ctx *actx, + struct autofs_map_ctx *map) +{ + hash_key_t key; + hash_value_t value; + int hret; + + if (map->mapname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Missing netgroup name.\n")); + return EINVAL; + } + + /* Add this entry to the hash table */ + key.type = HASH_KEY_STRING; + key.str = map->mapname; + value.type = HASH_VALUE_PTR; + value.ptr = map; + hret = hash_enter(actx->maps, &key, &value); + if (hret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Unable to add hash table entry for [%s]", key.str)); + DEBUG(SSSDBG_MINOR_FAILURE, + ("Hash error [%d][%s]", hret, hash_error_string(hret))); + return EIO; + } + talloc_steal(actx->maps, map); + talloc_set_destructor((TALLOC_CTX *) map, autofs_map_hash_remove); + + return EOK; +} + +static int +autofs_map_hash_remove(TALLOC_CTX *ctx) +{ + int hret; + hash_key_t key; + struct autofs_map_ctx *map = + talloc_get_type(ctx, struct autofs_map_ctx); + + key.type = HASH_KEY_STRING; + key.str = map->mapname; + + /* Remove the netgroup result object from the lookup table */ + hret = hash_delete(map->map_table, &key); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Could not remove key from table! [%d][%s]\n", + hret, hash_error_string(hret))); + return -1; + } + return 0; +} + +static struct tevent_req * +setautomntent_send(TALLOC_CTX *mem_ctx, + const char *rawname, + struct autofs_cmd_ctx *cmdctx); +static errno_t setautomntent_recv(struct tevent_req *req); +static void sss_autofs_cmd_setautomntent_done(struct tevent_req *req); + +/* FIXME - file a ticket to have per-responder private + * data instead of growing the cli_ctx structure */ +static int +sss_autofs_cmd_setautomntent(struct cli_ctx *client) +{ + struct autofs_cmd_ctx *cmdctx; + uint8_t *body; + size_t blen; + errno_t ret = EOK; + const char *rawname; + struct tevent_req *req; + + DEBUG(SSSDBG_TRACE_INTERNAL, ("sss_autofs_cmd_setautomntent\n")); + + cmdctx = talloc_zero(client, struct autofs_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = client; + + sss_packet_get_body(client->creq->in, &body, &blen); + + /* if not terminated fail */ + if (body[blen -1] != '\0') { + ret = EINVAL; + goto done; + } + + /* If the body isn't valid UTF-8, fail */ + if (!sss_utf8_check(body, blen -1)) { + ret = EINVAL; + goto done; + } + + rawname = (const char *)body; + DEBUG(SSSDBG_TRACE_FUNC, + ("Got request for automount map named %s\n", rawname)); + + req = setautomntent_send(cmdctx, rawname, cmdctx); + if (!req) { + DEBUG(0, ("Fatal error calling setautomntent_send\n")); + ret = EIO; + goto done; + } + tevent_req_set_callback(req, sss_autofs_cmd_setautomntent_done, cmdctx); + + ret = EOK; +done: + return autofs_cmd_done(cmdctx, ret); +} + +static void sss_autofs_cmd_setautomntent_done(struct tevent_req *req) +{ + struct autofs_cmd_ctx *cmdctx = + tevent_req_callback_data(req, struct autofs_cmd_ctx); + errno_t ret; + errno_t reqret; + struct sss_packet *packet; + uint8_t *body; + size_t blen; + + DEBUG(SSSDBG_TRACE_INTERNAL, ("setautomntent done\n")); + + reqret = setautomntent_recv(req); + talloc_zfree(req); + if (reqret != EOK && reqret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, ("setautomntent_recv failed\n")); + autofs_cmd_done(cmdctx, reqret); + return; + } + + /* Either we succeeded or no domains were eligible */ + ret = sss_packet_new(cmdctx->cctx->creq, 0, + sss_packet_get_cmd(cmdctx->cctx->creq->in), + &cmdctx->cctx->creq->out); + if (ret == EOK) { + if (reqret == ENOENT) { + DEBUG(SSSDBG_TRACE_FUNC, ("setautomntent did not find requested map\n")); + /* Notify the caller that this entry wasn't found */ + sss_cmd_empty_packet(cmdctx->cctx->creq->out); + } else { + DEBUG(SSSDBG_TRACE_FUNC, ("setautomntent found data\n")); + packet = cmdctx->cctx->creq->out; + ret = sss_packet_grow(packet, 2*sizeof(uint32_t)); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Couldn't grow the packet\n")); + talloc_free(cmdctx); + return; + } + + sss_packet_get_body(packet, &body, &blen); + ((uint32_t *)body)[0] = 1; /* Got some results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + } + + sss_cmd_done(cmdctx->cctx, NULL); + return; + } + + DEBUG(SSSDBG_CRIT_FAILURE, ("Error creating packet\n")); + return; +} + +struct setautomntent_state { + struct autofs_cmd_ctx *cmdctx; + struct autofs_dom_ctx *dctx; + + char *mapname; + struct autofs_map_ctx *map; +}; + +struct setautomntent_lookup_ctx { + struct autofs_ctx *actx; + struct autofs_dom_ctx *dctx; + struct resp_ctx *rctx; + struct cli_ctx *cctx; + + bool returned_to_mainloop; + + char *mapname; + struct autofs_map_ctx *map; +}; + +static errno_t +lookup_automntmap_step(struct setautomntent_lookup_ctx *lookup_ctx); + +static void +autofs_map_result_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt) +{ + struct autofs_map_ctx *map = + talloc_get_type(pvt, struct autofs_map_ctx); + + /* Free the autofs map result context + * The destructor for the autofs map will remove itself + * from the hash table + */ + talloc_free(map); +} + +static void +set_autofs_map_lifetime(uint32_t lifetime, + struct setautomntent_lookup_ctx *lookup_ctx, + struct autofs_map_ctx *map) +{ + struct timeval tv; + struct tevent_timer *te; + + tv = tevent_timeval_current_ofs(lifetime, 0); + te = tevent_add_timer(lookup_ctx->rctx->ev, + lookup_ctx->rctx, tv, + autofs_map_result_timeout, + map); + if (!te) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Could not set up life timer for autofs maps. " + "Entries may become stale.\n")); + } +} + +static struct tevent_req * +setautomntent_send(TALLOC_CTX *mem_ctx, + const char *rawname, + struct autofs_cmd_ctx *cmdctx) +{ + char *domname; + errno_t ret; + struct tevent_req *req; + struct setautomntent_state *state; + struct cli_ctx *client = cmdctx->cctx; + struct autofs_dom_ctx *dctx; + struct autofs_ctx *actx = + talloc_get_type(client->rctx->pvt_ctx, struct autofs_ctx); + struct setautomntent_lookup_ctx *lookup_ctx; + + req = tevent_req_create(mem_ctx, &state, struct setautomntent_state); + if (!req) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Could not create tevent request for setautomntent\n")); + goto fail; + } + state->cmdctx = cmdctx; + + dctx = talloc_zero(state, struct autofs_dom_ctx); + if (!dctx) { + DEBUG(SSSDBG_FATAL_FAILURE, ("Out of memory\n")); + goto fail; + } + dctx->cmd_ctx = state->cmdctx; + state->dctx = dctx; + + ret = sss_parse_name(state, client->rctx->names, rawname, + &domname, &state->mapname); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Invalid name received [%s]\n", rawname)); + goto fail; + } + + DEBUG(SSSDBG_TRACE_FUNC, + ("Requesting info for automount map [%s] from [%s]\n", + state->mapname, domname?domname:"<ALL>")); + + if (domname) { + dctx->domain = responder_get_domain(client->rctx->domains, domname); + if (!dctx->domain) { + goto fail; + } + + client->automntmap_name = talloc_strdup(client, rawname); + if (!client->automntmap_name) { + goto fail; + } + } else { + /* this is a multidomain search */ + dctx->domain = client->rctx->domains; + cmdctx->check_next = true; + + client->automntmap_name = talloc_strdup(client, state->mapname); + if (!client->automntmap_name) { + goto fail; + } + } + + dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider); + /* Is the result context already available? + * Check for existing lookups for this map + */ + ret = get_autofs_map(actx, state->mapname, &state->map); + if (ret == EOK) { + /* Another process already requested this map + * Check whether it's ready for processing. + */ + if (state->map->ready) { + if (state->map->found) { + DEBUG(SSSDBG_TRACE_LIBS, + ("Map %s is ready to be processed\n", state->mapname)); + tevent_req_done(req); + tevent_req_post(req, actx->rctx->ev); + return req; + } else { + DEBUG(SSSDBG_TRACE_LIBS, + ("Map %s was marked as nonexistent\n", state->mapname)); + tevent_req_error(req, ENOENT); + tevent_req_post(req, actx->rctx->ev); + return req; + } + } + + /* Result object is still being constructed + * Register for notification when it's ready + */ + DEBUG(SSSDBG_TRACE_LIBS, + ("Map %s is being looked up, registering for notification\n", + state->mapname)); + ret = autofs_setent_add_ref(cmdctx->cctx, state->map, req); + if (ret != EOK) { + goto fail; + } + /* Will return control below */ + } else if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_LIBS, + ("Map %s needs to be looked up\n", state->mapname)); + + state->map = talloc_zero(actx, struct autofs_map_ctx); + if (!state->map) { + ret = ENOMEM; + goto fail; + } + dctx->map_ctx = state->map; + + state->map->mapname = talloc_strdup(state->map, state->mapname); + if (!state->map->mapname) { + talloc_free(state->map); + ret = ENOMEM; + goto fail; + } + state->map->map_table = actx->maps; + + ret = autofs_setent_add_ref(cmdctx->cctx, state->map, req); + if (ret != EOK) { + talloc_free(state->map); + goto fail; + } + + ret = set_autofs_map(actx, state->map); + if (ret != EOK) { + talloc_free(state->map); + goto fail; + } + + /* Perform lookup */ + lookup_ctx = talloc_zero(state->map, struct setautomntent_lookup_ctx); + if (!lookup_ctx) { + talloc_free(state->map); + ret = ENOMEM; + goto fail; + } + + /* Steal the dom_ctx onto the lookup_ctx so it doesn't go out of scope if + * this request is canceled while other requests are in-progress. + */ + lookup_ctx->dctx = talloc_steal(lookup_ctx, state->dctx); + lookup_ctx->actx = actx; + lookup_ctx->map = state->map; + lookup_ctx->rctx = client->rctx; + lookup_ctx->mapname = + talloc_strdup(lookup_ctx, state->mapname); + if (!lookup_ctx->mapname) { + talloc_free(state->map); + ret = ENOMEM; + goto fail; + } + + ret = lookup_automntmap_step(lookup_ctx); + if (ret == EAGAIN) { + DEBUG(SSSDBG_TRACE_INTERNAL, ("lookup_automntmap_step " + "is refreshing the cache, re-entering the mainloop\n")); + return req; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Could not get data from cache\n")); + talloc_free(state->map); + goto fail; + } + + tevent_req_done(req); + tevent_req_post(req, cmdctx->cctx->ev); + return req; + } else { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Unexpected error from get_autofs_map [%d]: %s\n", + ret, strerror(ret))); + goto fail; + } + + return req; + +fail: + talloc_free(req); + return NULL; +} + +static errno_t +lookup_automntmap_update_cache(struct setautomntent_lookup_ctx *lookup_ctx); + +static errno_t +lookup_automntmap_step(struct setautomntent_lookup_ctx *lookup_ctx) +{ + errno_t ret; + struct sss_domain_info *dom = lookup_ctx->dctx->domain; + struct autofs_dom_ctx *dctx = lookup_ctx->dctx; + struct sysdb_ctx *sysdb; + struct autofs_map_ctx *map; + + /* Check each domain for this map name */ + while (dom) { + /* if it is a domainless search, skip domains that require fully + * qualified names instead */ + while (dom && dctx->cmd_ctx->check_next && dom->fqnames) { + dom = dom->next; + } + + /* No domains left to search */ + if (!dom) break; + + if (dom != dctx->domain) { + /* make sure we reset the check_provider flag when we check + * a new domain */ + dctx->check_provider = + NEED_CHECK_PROVIDER(dom->provider); + } + + /* make sure to update the dctx if we changed domain */ + dctx->domain = dom; + + DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%s@%s]\n", + lookup_ctx->mapname, dom->name)); + ret = sysdb_get_ctx_from_list(lookup_ctx->rctx->db_list, dom, &sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Fatal: Sysdb CTX not found for this domain!\n")); + return EIO; + } + + /* Look into the cache */ + talloc_free(dctx->map); + ret = sysdb_get_map_byname(dctx, sysdb, lookup_ctx->mapname, + &dctx->map); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not check cache\n")); + return ret; + } else if (ret == ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("No automount map [%s] in cache for domain [%s]\n", + lookup_ctx->mapname, dom->name)); + if (!dctx->check_provider) { + if (dctx->cmd_ctx->check_next) { + DEBUG(SSSDBG_TRACE_INTERNAL, ("Moving on to next domain\n")); + dom = dom->next; + continue; + } + else break; + } + ret = EOK; + } + + ret = get_autofs_map(lookup_ctx->actx, lookup_ctx->mapname, &map); + if (ret != EOK) { + /* Something really bad happened! */ + DEBUG(SSSDBG_CRIT_FAILURE, ("Autofs map entry was lost!\n")); + return ret; + } + + if (dctx->map == NULL && !dctx->check_provider) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Autofs map not found, setting negative cache\n")); + map->ready = true; + map->found = false; + set_autofs_map_lifetime(lookup_ctx->actx->neg_timeout, lookup_ctx, map); + return ENOENT; + } + + if (dctx->check_provider) { + ret = lookup_automntmap_update_cache(lookup_ctx); + if (ret == EAGAIN) { + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Looking up automount maps from the DP\n")); + return EAGAIN; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("Error looking up automount maps [%d]: %s\n", + ret, strerror(ret))); + return ret; + } + } + + /* OK, the map is in cache and valid. + * Let's get all members and return it + */ + ret = sysdb_autofs_entries_by_map(map, sysdb, map->mapname, + &map->entry_count, + &map->entries); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, + ("Error looking automount map entries [%d]: %s\n", + ret, strerror(ret))); + map->ready = true; + map->found = false; + set_autofs_map_lifetime(lookup_ctx->actx->neg_timeout, lookup_ctx, map); + return EIO; + } + + map->map = talloc_steal(map, dctx->map); + + DEBUG(SSSDBG_TRACE_FUNC, + ("setautomntent done for map %s\n", lookup_ctx->mapname)); + map->ready = true; + map->found = true; + set_autofs_map_lifetime(dom->autofsmap_timeout, lookup_ctx, map); + return EOK; + } + + map = talloc_zero(lookup_ctx->actx, struct autofs_map_ctx); + if (!map) { + return ENOMEM; + } + + map->ready = true; + map->found = false; + map->map_table = lookup_ctx->actx->maps; + + map->mapname = talloc_strdup(map, lookup_ctx->mapname); + if (!map->mapname) { + talloc_free(map); + return ENOMEM; + } + + ret = set_autofs_map(lookup_ctx->actx, map); + if (ret != EOK) { + talloc_free(map); + return ENOMEM; + } + + set_autofs_map_lifetime(lookup_ctx->actx->neg_timeout, lookup_ctx, map); + + /* If we've gotten here, then no domain contained this map */ + return ENOENT; +} + +static void lookup_automntmap_cache_updated(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); +static void autofs_dp_send_map_req_done(struct tevent_req *req); + +static errno_t +lookup_automntmap_update_cache(struct setautomntent_lookup_ctx *lookup_ctx) +{ + errno_t ret; + uint64_t cache_expire = 0; + struct autofs_dom_ctx *dctx = lookup_ctx->dctx; + struct tevent_req *req = NULL; + struct dp_callback_ctx *cb_ctx = NULL; + + if (dctx->map != NULL) { + cache_expire = ldb_msg_find_attr_as_uint64(dctx->map, + SYSDB_CACHE_EXPIRE, 0); + + /* if we have any reply let's check cache validity */ + ret = sss_cmd_check_cache(dctx->map, 0, cache_expire); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, ("Cached entry is valid, returning..\n")); + return EOK; + } else if (ret != EAGAIN && ret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error checking cache: %d\n", ret)); + goto error; + } + } + + /* dont loop forever :-) */ + dctx->check_provider = false; + + /* keep around current data in case backend is offline */ + /* FIXME - do this by default */ +#if 0 + if (dctx->res->count) { + dctx->res = talloc_steal(dctx, dctx->res); + } +#endif + + req = sss_dp_get_autofs_send(lookup_ctx->cctx, lookup_ctx->rctx, + lookup_ctx->dctx->domain, true, + SSS_DP_AUTOFS, lookup_ctx->mapname); + if (!req) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Out of memory sending data provider request\n")); + ret = ENOMEM; + goto error; + } + + cb_ctx = talloc_zero(lookup_ctx->dctx, struct dp_callback_ctx); + if(!cb_ctx) { + talloc_zfree(req); + ret = ENOMEM; + goto error; + } + cb_ctx->callback = lookup_automntmap_cache_updated; + cb_ctx->ptr = lookup_ctx; + cb_ctx->cctx = lookup_ctx->dctx->cmd_ctx->cctx; + cb_ctx->mem_ctx = lookup_ctx->dctx; + + tevent_req_set_callback(req, autofs_dp_send_map_req_done, cb_ctx); + + return EAGAIN; + +error: + ret = autofs_cmd_send_error(lookup_ctx->dctx->cmd_ctx, ret); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Fatal error, killing connection!\n")); + talloc_free(lookup_ctx->cctx); + return ret; + } + autofs_cmd_done(lookup_ctx->dctx->cmd_ctx, ret); + return EOK; +} + +static void autofs_dp_send_map_req_done(struct tevent_req *req) +{ + struct dp_callback_ctx *cb_ctx = + tevent_req_callback_data(req, struct dp_callback_ctx); + struct setautomntent_lookup_ctx *lookup_ctx = + talloc_get_type(cb_ctx->ptr, struct setautomntent_lookup_ctx); + + errno_t ret; + dbus_uint16_t err_maj; + dbus_uint32_t err_min; + char *err_msg; + + ret = sss_dp_get_autofs_recv(cb_ctx->mem_ctx, req, + &err_maj, &err_min, + &err_msg); + talloc_free(req); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Fatal error, killing connection!\n")); + talloc_free(lookup_ctx->cctx); + return; + } + + cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr); +} + +static void lookup_automntmap_cache_updated(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) +{ + struct setautomntent_lookup_ctx *lookup_ctx = + talloc_get_type(ptr, struct setautomntent_lookup_ctx); + struct autofs_dom_ctx *dctx = lookup_ctx->dctx; + errno_t ret; + + if (err_maj) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("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)); + /* Loop to the next domain if possible */ + if (dctx->domain->next && dctx->cmd_ctx->check_next) { + dctx->domain = dctx->domain->next; + dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider); + } + } + + /* ok the backend returned, search to see if we have updated results */ + ret = lookup_automntmap_step(lookup_ctx); + if (ret != EOK) { + if (ret == EAGAIN) { + return; + } + } + + /* We have results to return */ + autofs_setent_notify(lookup_ctx->map, ret); +} + +static errno_t +setautomntent_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +static errno_t +getautomntent_process(struct autofs_cmd_ctx *cmdctx, + struct autofs_map_ctx *map, + uint32_t cursor); + +static int +sss_autofs_cmd_getautomntent(struct cli_ctx *client) +{ + struct autofs_cmd_ctx *cmdctx; + struct autofs_map_ctx *map; + struct autofs_ctx *actx; + uint8_t *body; + size_t blen; + errno_t ret; + uint32_t namelen; + char *name; + uint32_t cursor; + size_t c = 0; + + DEBUG(SSSDBG_TRACE_INTERNAL, ("sss_autofs_cmd_getautomntent\n")); + + cmdctx = talloc_zero(client, struct autofs_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = client; + + actx = talloc_get_type(client->rctx->pvt_ctx, struct autofs_ctx); + if (!actx) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Missing autofs context\n")); + return EIO; + } + + /* get autofs map name and index to query */ + sss_packet_get_body(client->creq->in, &body, &blen); + + SAFEALIGN_COPY_UINT32_CHECK(&namelen, body+c, blen, &c); + + if (namelen == 0) { + ret = EINVAL; + goto done; + } + + name = (char *) body+c; + + /* if not null-terminated fail */ + if (name[namelen] != '\0') { + ret = EINVAL; + goto done; + } + + /* If the name isn't valid UTF-8, fail */ + if (!sss_utf8_check((const uint8_t *) name, namelen -1)) { + ret = EINVAL; + goto done; + } + + SAFEALIGN_COPY_UINT32_CHECK(&cursor, body+c+namelen+1, blen, &c); + + DEBUG(SSSDBG_TRACE_FUNC, + ("Requested data of map %s cursor %d\n", name, cursor)); + + ret = get_autofs_map(actx, name, &map); + if (ret == ENOENT) { + /* FIXME */ + DEBUG(SSSDBG_MINOR_FAILURE, + ("implicit setautomntent not yet implemented\n")); + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("An unexpected error occurred: [%d][%s]\n", + ret, strerror(ret))); + goto done; + } + + if (map->ready == false) { + /* FIXME */ + DEBUG(SSSDBG_MINOR_FAILURE, + ("implicit setautomntent not yet implemented\n")); + ret = ENOENT; + goto done; + } else if (map->found == false) { + DEBUG(SSSDBG_TRACE_FUNC, ("negative cache hit\n")); + ret = ENOENT; + goto done; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + ("returning entries for [%s]\n", map->mapname)); + + ret = getautomntent_process(cmdctx, map, cursor); + +done: + return autofs_cmd_done(cmdctx, ret); +} + +static errno_t +getautomntent_process(struct autofs_cmd_ctx *cmdctx, + struct autofs_map_ctx *map, + uint32_t cursor) +{ + struct cli_ctx *client = cmdctx->cctx; + errno_t ret; + const char *key; + size_t keylen; + const char *value; + size_t valuelen; + struct ldb_message *entry; + size_t len; + uint8_t *body; + size_t blen, rp; + + /* create response packet */ + ret = sss_packet_new(client->creq, 0, + sss_packet_get_cmd(client->creq->in), + &client->creq->out); + if (ret != EOK) { + return ret; + } + + if (!map->map || !map->entries || !map->entries[0] || + cursor >= map->entry_count) { + DEBUG(SSSDBG_MINOR_FAILURE, ("No entries found\n")); + ret = sss_cmd_empty_packet(client->creq->out); + if (ret != EOK) { + return autofs_cmd_done(cmdctx, ret); + } + goto done; + } + entry = map->entries[cursor]; + + key = ldb_msg_find_attr_as_string(entry, SYSDB_AUTOFS_ENTRY_KEY, NULL); + value = ldb_msg_find_attr_as_string(entry, SYSDB_AUTOFS_ENTRY_VALUE, NULL); + if (!key || !value) { + ret = EAGAIN; + DEBUG(SSSDBG_MINOR_FAILURE, ("Incomplete entry\n")); + goto done; + } + + /* FIXME - split below into a separate function */ + keylen = 1 + strlen(key); + valuelen = 1 + strlen(value); + len = sizeof(uint32_t) + sizeof(uint32_t) + keylen + sizeof(uint32_t)+ valuelen; + + ret = sss_packet_grow(client->creq->out, len); + if (ret != EOK) { + goto done; + } + + sss_packet_get_body(client->creq->out, &body, &blen); + + rp = 0; + SAFEALIGN_SET_UINT32(&body[rp], len, &rp); + SAFEALIGN_SET_UINT32(&body[rp], keylen, &rp); + + if (keylen == 1) { + body[rp] = '\0'; + } else { + memcpy(&body[rp], key, keylen); + } + rp += keylen; + + SAFEALIGN_SET_UINT32(&body[rp], valuelen, &rp); + if (valuelen == 1) { + body[rp] = '\0'; + } else { + memcpy(&body[rp], value, valuelen); + } + rp += valuelen; + + ret = EOK; +done: + sss_packet_set_error(client->creq->out, ret); + sss_cmd_done(client, cmdctx); + + return EOK; +} + +static errno_t +getautomnbyname_process(struct autofs_cmd_ctx *cmdctx, + struct autofs_map_ctx *map, + const char *key); + +static int +sss_autofs_cmd_getautomntbyname(struct cli_ctx *client) +{ + errno_t ret; + struct autofs_cmd_ctx *cmdctx; + struct autofs_map_ctx *map; + struct autofs_ctx *actx; + uint8_t *body; + size_t blen; + uint32_t namelen; + char *name; + uint32_t keylen; + char *key; + size_t c = 0; + + DEBUG(SSSDBG_TRACE_INTERNAL, ("sss_autofs_cmd_getautomnbyname\n")); + + cmdctx = talloc_zero(client, struct autofs_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = client; + + actx = talloc_get_type(client->rctx->pvt_ctx, struct autofs_ctx); + if (!actx) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Missing autofs context\n")); + return EIO; + } + + /* get autofs map name and index to query */ + sss_packet_get_body(client->creq->in, &body, &blen); + + /* FIXME - split out a function to get string from <len><str>\0 */ + SAFEALIGN_COPY_UINT32_CHECK(&namelen, body+c, blen, &c); + + if (namelen == 0) { + ret = EINVAL; + goto done; + } + + name = (char *) body+c; + + /* if not null-terminated fail */ + if (name[namelen] != '\0') { + ret = EINVAL; + goto done; + } + + /* If the name isn't valid UTF-8, fail */ + if (!sss_utf8_check((const uint8_t *) name, namelen -1)) { + ret = EINVAL; + goto done; + } + + c += namelen + 1; + + /* FIXME - split out a function to get string from <len><str>\0 */ + SAFEALIGN_COPY_UINT32_CHECK(&keylen, body+c, blen, &c); + + if (keylen == 0) { + ret = EINVAL; + goto done; + } + + key = (char *) body+c; + + /* if not null-terminated fail */ + if (key[keylen] != '\0') { + ret = EINVAL; + goto done; + } + + /* If the key isn't valid UTF-8, fail */ + if (!sss_utf8_check((const uint8_t *) key, keylen -1)) { + ret = EINVAL; + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, + ("Requested data of map %s key %s\n", name, key)); + + ret = get_autofs_map(actx, name, &map); + if (ret == ENOENT) { + /* FIXME */ + DEBUG(SSSDBG_MINOR_FAILURE, + ("implicit setautomntent not yet implemented\n")); + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("An unexpected error occurred: [%d][%s]\n", + ret, strerror(ret))); + goto done; + } + + if (map->ready == false) { + /* FIXME */ + DEBUG(SSSDBG_MINOR_FAILURE, + ("implicit setautomntent not yet implemented\n")); + ret = ENOENT; + goto done; + } else if (map->found == false) { + DEBUG(SSSDBG_TRACE_FUNC, ("negative cache hit\n")); + ret = ENOENT; + goto done; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Looking up value for [%s] in [%s]\n", key, map->mapname)); + + ret = getautomnbyname_process(cmdctx, map, key); + +done: + return autofs_cmd_done(cmdctx, ret); +} + +static errno_t +getautomnbyname_process(struct autofs_cmd_ctx *cmdctx, + struct autofs_map_ctx *map, + const char *key) +{ + struct cli_ctx *client = cmdctx->cctx; + errno_t ret; + size_t i; + const char *k; + const char *value; + size_t valuelen; + size_t len; + uint8_t *body; + size_t blen, rp; + + /* create response packet */ + ret = sss_packet_new(client->creq, 0, + sss_packet_get_cmd(client->creq->in), + &client->creq->out); + if (ret != EOK) { + return ret; + } + + if (!map->map || !map->entries || !map->entries[0]) { + DEBUG(SSSDBG_MINOR_FAILURE, ("No entries found\n")); + ret = sss_cmd_empty_packet(client->creq->out); + if (ret != EOK) { + return autofs_cmd_done(cmdctx, ret); + } + goto done; + } + + for (i=0; i < map->entry_count; i++) { + k = ldb_msg_find_attr_as_string(map->entries[i], + SYSDB_AUTOFS_ENTRY_KEY, NULL); + if (!k) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Skipping incomplete entry\n")); + continue; + } + + if (strcmp(k, key) == 0) { + DEBUG(SSSDBG_TRACE_INTERNAL, ("Found key [%s]\n", key)); + break; + } + } + + if (i >= map->entry_count) { + DEBUG(SSSDBG_MINOR_FAILURE, ("No key named [%s] found\n", key)); + ret = sss_cmd_empty_packet(client->creq->out); + if (ret != EOK) { + return autofs_cmd_done(cmdctx, ret); + } + goto done; + } + + value = ldb_msg_find_attr_as_string(map->entries[i], + SYSDB_AUTOFS_ENTRY_VALUE, NULL); + + valuelen = 1 + strlen(value); + len = sizeof(uint32_t) + sizeof(uint32_t) + valuelen; + + ret = sss_packet_grow(client->creq->out, len); + if (ret != EOK) { + goto done; + } + + sss_packet_get_body(client->creq->out, &body, &blen); + + rp = 0; + SAFEALIGN_SET_UINT32(&body[rp], len, &rp); + + SAFEALIGN_SET_UINT32(&body[rp], valuelen, &rp); + if (valuelen == 1) { + body[rp] = '\0'; + } else { + memcpy(&body[rp], value, valuelen); + } + rp += valuelen; + + ret = EOK; +done: + sss_packet_set_error(client->creq->out, ret); + sss_cmd_done(client, cmdctx); + + return EOK; +} + +static int +sss_autofs_cmd_endautomntent(struct cli_ctx *client) +{ + errno_t ret; + + DEBUG(SSSDBG_TRACE_FUNC, ("endautomntent called\n")); + + /* create response packet */ + ret = sss_packet_new(client->creq, 0, + sss_packet_get_cmd(client->creq->in), + &client->creq->out); + + if (ret != EOK) { + return ret; + } + + sss_cmd_done(client, NULL); + return EOK; +} + +struct cli_protocol_version *register_cli_protocol_version(void) +{ + static struct cli_protocol_version autofs_cli_protocol_version[] = { + { SSS_AUTOFS_PROTO_VERSION, NULL, NULL } + }; + + return autofs_cli_protocol_version; +} + +struct sss_cmd_table *get_autofs_cmds(void) +{ + static struct sss_cmd_table autofs_cmds[] = { + { SSS_GET_VERSION, sss_cmd_get_version }, + { SSS_AUTOFS_SETAUTOMNTENT, sss_autofs_cmd_setautomntent }, + { SSS_AUTOFS_GETAUTOMNTENT, sss_autofs_cmd_getautomntent }, + { SSS_AUTOFS_GETAUTOMNTBYNAME, sss_autofs_cmd_getautomntbyname }, + { SSS_AUTOFS_ENDAUTOMNTENT, sss_autofs_cmd_endautomntent }, + { SSS_CLI_NULL, NULL} + }; + + return autofs_cmds; +} diff --git a/src/responder/autofs/autofssrv_dp.c b/src/responder/autofs/autofssrv_dp.c new file mode 100644 index 00000000..7f51b61a --- /dev/null +++ b/src/responder/autofs/autofssrv_dp.c @@ -0,0 +1,155 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2011 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 <http://www.gnu.org/licenses/>. +*/ + +#include <talloc.h> +#include <tevent.h> +#include <dbus/dbus.h> +#include "sbus/sssd_dbus.h" + +#include "util/util.h" +#include "sbus/sbus_client.h" +#include "providers/data_provider.h" +#include "responder/common/responder.h" +#include "responder/autofs/autofs_private.h" + +struct sss_dp_get_autofs_info { + struct sss_domain_info *dom; + + bool fast_reply; + enum sss_dp_autofs_type type; + const char *name; +}; + +static DBusMessage * +sss_dp_get_autofs_msg(void *pvt); + +struct tevent_req * +sss_dp_get_autofs_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + enum sss_dp_autofs_type type, + const char *name) +{ + struct tevent_req *req; + struct sss_dp_req_state *state; + struct sss_dp_get_autofs_info *info; + errno_t ret; + char *key; + + req = tevent_req_create(mem_ctx, &state, struct sss_dp_req_state); + if (!req) { + ret = ENOMEM; + goto error; + } + + if (!dom) { + ret = EINVAL; + goto error; + } + + info = talloc_zero(state, struct sss_dp_get_autofs_info); + info->fast_reply = fast_reply; + info->type = type; + info->name = name; + info->dom = dom; + + key = talloc_asprintf(state, "%d:%s@%s", type, name, dom->name); + if (!key) { + ret = ENOMEM; + goto error; + } + + ret = sss_dp_issue_request(state, rctx, key, dom, sss_dp_get_autofs_msg, + info, req); + talloc_free(key); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("Could not issue DP request [%d]: %s\n", + ret, strerror(ret))); + goto error; + } + + return req; + +error: + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + return req; +} + +static DBusMessage * +sss_dp_get_autofs_msg(void *pvt) +{ + DBusMessage *msg; + dbus_bool_t dbret; + struct sss_dp_get_autofs_info *info; + uint32_t be_type = BE_REQ_AUTOFS; + char *filter; + + info = talloc_get_type(pvt, struct sss_dp_get_autofs_info); + + if (info->fast_reply) { + be_type |= BE_REQ_FAST; + } + + filter = talloc_asprintf(info, "mapname=%s", info->name); + if (!filter) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory?!\n")); + return NULL; + } + + msg = dbus_message_new_method_call(NULL, + DP_PATH, + DP_INTERFACE, + DP_METHOD_AUTOFSHANDLER); + if (msg == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory?!\n")); + return NULL; + } + + /* create the message */ + DEBUG(SSSDBG_TRACE_FUNC, + ("Creating autofs request for [%s][%u][%s]\n", + info->dom->name, be_type, filter)); + + dbret = dbus_message_append_args(msg, + DBUS_TYPE_UINT32, &be_type, + DBUS_TYPE_STRING, &filter, + DBUS_TYPE_INVALID); + talloc_free(filter); + if (!dbret) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to build message\n")); + dbus_message_unref(msg); + return NULL; + } + + return msg; +} + +errno_t +sss_dp_get_autofs_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *dp_err, + dbus_uint32_t *dp_ret, + char **err_msg) +{ + return sss_dp_req_recv(mem_ctx, req, dp_err, dp_ret, err_msg); +} diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h index 4a205c85..6b6a25be 100644 --- a/src/responder/common/responder.h +++ b/src/responder/common/responder.h @@ -118,6 +118,8 @@ struct cli_ctx { char *netgr_name; int netgrent_cur; + + char *automntmap_name; }; struct sss_cmd_table { diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h index 0f581574..c0bf2ee2 100644 --- a/src/responder/nss/nsssrv_private.h +++ b/src/responder/nss/nsssrv_private.h @@ -101,9 +101,6 @@ struct setent_step_ctx { /* Finish the request */ int nss_cmd_done(struct nss_cmd_ctx *cmdctx, int ret); -/* Respond with no entries */ -int fill_empty(struct sss_packet *packet); - errno_t nss_setent_add_ref(TALLOC_CTX *memctx, struct getent_ctx *getent_ctx, struct tevent_req *req); |