summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am15
-rw-r--r--src/confdb/confdb.c10
-rw-r--r--src/confdb/confdb.h6
-rw-r--r--src/config/SSSDConfig.py4
-rwxr-xr-xsrc/config/SSSDConfigTest.py14
-rw-r--r--src/config/etc/sssd.api.conf6
-rw-r--r--src/man/Makefile.am10
-rw-r--r--src/man/sssd.conf.5.xml28
-rw-r--r--src/monitor/monitor.c2
-rw-r--r--src/providers/data_provider.h1
-rw-r--r--src/responder/autofs/autofs_private.h95
-rw-r--r--src/responder/autofs/autofssrv.c229
-rw-r--r--src/responder/autofs/autofssrv_cmd.c1239
-rw-r--r--src/responder/autofs/autofssrv_dp.c155
-rw-r--r--src/responder/common/responder.h2
-rw-r--r--src/responder/nss/nsssrv_private.h3
17 files changed, 1809 insertions, 11 deletions
diff --git a/.gitignore b/.gitignore
index 7510e61c9..f5b393add 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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 f235d39b0..becc67ba0 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 8b3a046f3..57023f299 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 7699bba4f..6aa457a4d 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 dfad410c7..740ab27fa 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 66b3f9c7a..2d6fa5ad7 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 6eb08a5d9..446bf9504 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 6e23830a5..f0faf6900 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 7916e1f8a..7217c9dd7 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 d2717fae8..264256f04 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 3c03b0813..89528721b 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 000000000..753089d24
--- /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 000000000..76d270704
--- /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 000000000..d2b37f719
--- /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 000000000..7f51b61ab
--- /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 4a205c85b..6b6a25be7 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 0f581574a..c0bf2ee2c 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);