From 74aa0bc6779af38018a03fd2cf4419fe85917904 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 07:31:45 +0200 Subject: Adding upstream version 2.9.4. Signed-off-by: Daniel Baumann --- src/responder/autofs/autofs_private.h | 74 +++ src/responder/autofs/autofssrv.c | 242 +++++++++ src/responder/autofs/autofssrv_cmd.c | 961 ++++++++++++++++++++++++++++++++++ 3 files changed, 1277 insertions(+) create mode 100644 src/responder/autofs/autofs_private.h create mode 100644 src/responder/autofs/autofssrv.c create mode 100644 src/responder/autofs/autofssrv_cmd.c (limited to 'src/responder/autofs') diff --git a/src/responder/autofs/autofs_private.h b/src/responder/autofs/autofs_private.h new file mode 100644 index 0000000..c15e028 --- /dev/null +++ b/src/responder/autofs/autofs_private.h @@ -0,0 +1,74 @@ +/* + Authors: + Jakub Hrozek + + 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 . +*/ + +#ifndef _AUTOFSSRV_PRIVATE_H_ +#define _AUTOFSSRV_PRIVATE_H_ + +#include + +#include "responder/common/responder.h" +#include "responder/common/cache_req/cache_req.h" + +#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 autofs_ctx *autofs_ctx; + struct cli_ctx *cli_ctx; + + const char *mapname; + const char *keyname; + uint32_t max_entries; + uint32_t cursor; +}; + +struct autofs_enum_ctx { + /* Results. First result is the map objects, next results are map entries. */ + struct cache_req_result *result; + + /* True if the map was found. */ + bool found; + + /* False if the result is being created. */ + bool ready; + + /* Enumeration context key. */ + const char *key; + + /* Hash table that contains this enumeration context. */ + hash_table_t *table; + + /* Requests that awaits the data. */ + struct setent_req_list *notify_list; +}; + +struct sss_cmd_table *get_autofs_cmds(void); +int autofs_connection_setup(struct cli_ctx *cctx); + +void autofs_orphan_maps(struct autofs_ctx *actx); + +#endif /* _AUTOFSSRV_PRIVATE_H_ */ diff --git a/src/responder/autofs/autofssrv.c b/src/responder/autofs/autofssrv.c new file mode 100644 index 0000000..1dbbe9f --- /dev/null +++ b/src/responder/autofs/autofssrv.c @@ -0,0 +1,242 @@ +/* + Authors: + Jakub Hrozek + + 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 . +*/ + +#include + +#include "util/util.h" +#include "confdb/confdb.h" +#include "responder/common/responder.h" +#include "providers/data_provider.h" +#include "responder/autofs/autofs_private.h" +#include "sss_iface/sss_iface_async.h" +#include "util/sss_ptr_hash.h" + +static errno_t +autofs_get_config(struct autofs_ctx *actx, + struct confdb_ctx *cdb) +{ + errno_t ret; + + ret = confdb_get_int(cdb, CONFDB_AUTOFS_CONF_ENTRY, + CONFDB_AUTOFS_MAP_NEG_TIMEOUT, 15, + &actx->neg_timeout); + return ret; +} + +static errno_t +autofs_clean_hash_table(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct autofs_ctx *actx) +{ + autofs_orphan_maps(actx); + + return EOK; +} + +static void +autofs_maps_delete_cb(hash_entry_t *item, + hash_destroy_enum deltype, + void *pvt) +{ + struct autofs_ctx *autofs_ctx; + struct autofs_enum_ctx *enum_ctx; + + autofs_ctx = talloc_get_type(pvt, struct autofs_ctx); + enum_ctx = talloc_get_type(item->value.ptr, struct autofs_enum_ctx); + + talloc_unlink(autofs_ctx->maps, enum_ctx); +} + +static errno_t +autofs_register_service_iface(struct autofs_ctx *autofs_ctx, + struct resp_ctx *rctx) +{ + errno_t ret; + + SBUS_INTERFACE(iface_svc, + sssd_service, + SBUS_METHODS( + SBUS_SYNC(METHOD, sssd_service, rotateLogs, responder_logrotate, rctx), + SBUS_SYNC(METHOD, sssd_service, clearEnumCache, autofs_clean_hash_table, autofs_ctx) + ), + SBUS_SIGNALS(SBUS_NO_SIGNALS), + SBUS_PROPERTIES( + SBUS_SYNC(GETTER, sssd_service, debug_level, generic_get_debug_level, NULL), + SBUS_SYNC(SETTER, sssd_service, debug_level, generic_set_debug_level, NULL) + ) + ); + + ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface" + "[%d]: %s\n", ret, sss_strerror(ret)); + } + + return ret; +} + +static int +autofs_process_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb) +{ + struct resp_ctx *rctx; + struct sss_cmd_table *autofs_cmds; + struct autofs_ctx *autofs_ctx; + int ret; + + autofs_cmds = get_autofs_cmds(); + ret = sss_process_init(mem_ctx, ev, cdb, + autofs_cmds, + SSS_AUTOFS_SOCKET_NAME, -1, NULL, -1, + CONFDB_AUTOFS_CONF_ENTRY, + SSS_BUS_AUTOFS, SSS_AUTOFS_SBUS_SERVICE_NAME, + autofs_connection_setup, + &rctx); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n"); + return ret; + } + + autofs_ctx = talloc_zero(rctx, struct autofs_ctx); + if (!autofs_ctx) { + DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing autofs_ctx\n"); + ret = ENOMEM; + goto fail; + } + + ret = autofs_get_config(autofs_ctx, cdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Cannot read autofs configuration\n"); + goto fail; + } + + autofs_ctx->rctx = rctx; + autofs_ctx->rctx->pvt_ctx = autofs_ctx; + + /* Create the lookup table for setautomntent results */ + autofs_ctx->maps = sss_ptr_hash_create(autofs_ctx, + autofs_maps_delete_cb, + autofs_ctx); + if (autofs_ctx->maps == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unable to initialize automount maps hash table\n"); + ret = EIO; + goto fail; + } + + ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL, NULL, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n"); + goto fail; + } + + /* The responder is initialized. Now tell it to the monitor. */ + ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_AUTOFS, + SSS_AUTOFS_SBUS_SERVICE_NAME, + SSS_AUTOFS_SBUS_SERVICE_VERSION, + MT_SVC_SERVICE, + &rctx->last_request_time, &rctx->mon_conn); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n"); + goto fail; + } + + ret = autofs_register_service_iface(autofs_ctx, rctx); + if (ret != EOK) { + goto fail; + } + + DEBUG(SSSDBG_TRACE_FUNC, "autofs Initialization complete\n"); + return EOK; + +fail: + talloc_free(rctx); + return ret; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + char *opt_logger = NULL; + struct main_context *main_ctx; + int ret; + uid_t uid = 0; + gid_t gid = 0; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + SSSD_LOGGER_OPTS + SSSD_SERVER_OPTS(uid, gid) + SSSD_RESPONDER_OPTS + POPT_TABLEEND + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + umask(DFL_RSP_UMASK); + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + + poptFreeContext(pc); + + /* set up things like debug, signals, daemonization, etc. */ + debug_log_file = "sssd_autofs"; + DEBUG_INIT(debug_level, opt_logger); + + ret = server_setup("autofs", true, 0, uid, gid, + CONFDB_AUTOFS_CONF_ENTRY, &main_ctx, true); + 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 0000000..7c80909 --- /dev/null +++ b/src/responder/autofs/autofssrv_cmd.c @@ -0,0 +1,961 @@ +/* + Authors: + Jakub Hrozek + + 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 . +*/ + +#include + +#include "util/util.h" +#include "responder/common/responder.h" +#include "responder/common/responder_packet.h" +#include "responder/common/cache_req/cache_req.h" +#include "responder/autofs/autofs_private.h" +#include "db/sysdb.h" +#include "db/sysdb_autofs.h" +#include "confdb/confdb.h" +#include "sss_iface/sss_iface_async.h" +#include "util/sss_ptr_hash.h" + +static int autofs_cmd_send_error(struct autofs_cmd_ctx *cmdctx, int err) +{ + return sss_cmd_send_error(cmdctx->cli_ctx, err); +} + +static int +autofs_cmd_send_empty(struct autofs_cmd_ctx *cmdctx) +{ + return sss_cmd_send_empty(cmdctx->cli_ctx); +} + +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; + } + sss_cmd_done(cmdctx->cli_ctx, cmdctx); + 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->cli_ctx, cmdctx); + break; + } + + return EOK; +} + +static errno_t +autofs_fill_entry(struct ldb_message *entry, struct sss_packet *packet, size_t *rp) +{ + errno_t ret; + const char *key; + size_t keylen; + const char *value; + size_t valuelen; + uint8_t *body; + size_t blen; + size_t len; + + 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) { + DEBUG(SSSDBG_MINOR_FAILURE, "Incomplete entry\n"); + return EINVAL; + } + + keylen = 1 + strlen(key); + valuelen = 1 + strlen(value); + len = sizeof(uint32_t) + sizeof(uint32_t) + keylen + sizeof(uint32_t) + valuelen; + + ret = sss_packet_grow(packet, len); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot grow packet\n"); + return ret; + } + + sss_packet_get_body(packet, &body, &blen); + + 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; + + return EOK; +} + +void +autofs_orphan_maps(struct autofs_ctx *autofs_ctx) +{ + /* It will automatically decrease the refcount of enum_ctx through + * delete callback. */ + sss_ptr_hash_delete_all(autofs_ctx->maps, false); +} + +static void +autofs_enumctx_lifetime_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt) +{ + struct autofs_enum_ctx *enum_ctx; + + enum_ctx = talloc_get_type(pvt, struct autofs_enum_ctx); + + /* Remove it from the table. It will automatically decrease the refcount. */ + sss_ptr_hash_delete(enum_ctx->table, enum_ctx->key, false); +} + +static void +autofs_set_enumctx_lifetime(struct autofs_ctx *autofs_ctx, + struct autofs_enum_ctx *enum_ctx, + uint32_t lifetime) +{ + struct timeval tv; + struct tevent_timer *te; + + tv = tevent_timeval_current_ofs(lifetime, 0); + te = tevent_add_timer(autofs_ctx->rctx->ev, enum_ctx, tv, + autofs_enumctx_lifetime_timeout, enum_ctx); + if (te == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Could not set up life timer for autofs maps. " + "Entries may become stale.\n"); + } +} + +static struct autofs_enum_ctx * +autofs_create_enumeration_context(TALLOC_CTX *mem_ctx, + struct autofs_ctx *autofs_ctx, + const char *mapname) +{ + struct autofs_enum_ctx *enum_ctx; + errno_t ret; + + enum_ctx = talloc_zero(mem_ctx, struct autofs_enum_ctx); + if (enum_ctx == NULL) { + return NULL; + } + + enum_ctx->ready = false; + enum_ctx->table = autofs_ctx->maps; + + enum_ctx->key = talloc_strdup(enum_ctx, mapname); + if (enum_ctx->key == NULL) { + talloc_free(enum_ctx); + return NULL; + } + + ret = sss_ptr_hash_add(autofs_ctx->maps, mapname, + enum_ctx, struct autofs_enum_ctx); + if (ret != EOK) { + talloc_free(enum_ctx); + return NULL; + } + + return enum_ctx; +} + +static void +autofs_orphan_master_map(struct autofs_ctx *autofs_ctx, + const char *mapname) +{ + struct sss_domain_info *dom; + errno_t ret; + + if (strcmp(mapname, "auto.master") != 0) { + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Invalidating master map\n"); + + /* Remove and invalidate all maps. */ + autofs_orphan_maps(autofs_ctx); + + DEBUG(SSSDBG_TRACE_FUNC, "Invalidating autofs maps\n"); + for (dom = autofs_ctx->rctx->domains; + dom != NULL; + dom = get_next_domain(dom, SSS_GND_DESCEND)) { + ret = sysdb_invalidate_autofs_maps(dom); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to invalidate maps in " + "%s [%d]: %s\n", dom->name, ret, sss_strerror(ret)); + } + } +} + +struct autofs_setent_state { + struct autofs_ctx *autofs_ctx; + struct autofs_enum_ctx *enum_ctx; +}; + +static void autofs_setent_done(struct tevent_req *subreq); + +static struct tevent_req * +autofs_setent_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct autofs_ctx *autofs_ctx, + const char *mapname) +{ + struct autofs_setent_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct autofs_setent_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->autofs_ctx = autofs_ctx; + + /* Lookup current results if available. */ + state->enum_ctx = sss_ptr_hash_lookup(autofs_ctx->maps, mapname, + struct autofs_enum_ctx); + if (state->enum_ctx != NULL) { + if (state->enum_ctx->ready) { + ret = EOK; + goto done; + } + + /* Map is still being created. We will watch the request. */ + ret = setent_add_ref(state, &state->enum_ctx->notify_list, req); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to watch enumeration request " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + ret = EAGAIN; + goto done; + } + + /* Map does not yet exist. Create the enumeration object and fetch data. */ + state->enum_ctx = autofs_create_enumeration_context(state, autofs_ctx, mapname); + if (state->enum_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create enumeration context!\n"); + ret = ENOMEM; + goto done; + } + + subreq = cache_req_autofs_map_entries_send(mem_ctx, ev, autofs_ctx->rctx, + autofs_ctx->rctx->ncache, + 0, NULL, mapname); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, autofs_setent_done, req); + + ret = EAGAIN; + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void autofs_setent_done(struct tevent_req *subreq) +{ + struct autofs_setent_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct autofs_setent_state); + + ret = cache_req_autofs_map_entries_recv(state, subreq, &result); + talloc_zfree(subreq); + + switch (ret) { + case EOK: + state->enum_ctx->found = true; + state->enum_ctx->result = talloc_steal(state->enum_ctx, result); + autofs_set_enumctx_lifetime(state->autofs_ctx, state->enum_ctx, + state->enum_ctx->result->domain->autofsmap_timeout); + break; + case ENOENT: + state->enum_ctx->found = false; + state->enum_ctx->result = NULL; + autofs_set_enumctx_lifetime(state->autofs_ctx, state->enum_ctx, + state->autofs_ctx->neg_timeout); + break; + default: + DEBUG(SSSDBG_OP_FAILURE, "Unable to get map data [%d]: %s\n", + ret, sss_strerror(ret)); + + setent_notify(&state->enum_ctx->notify_list, ret); + talloc_zfree(state->enum_ctx); + tevent_req_error(req, ret); + return; + } + + state->enum_ctx->ready = true; + + /* Make the enumeration context disappear with maps table. */ + talloc_steal(state->autofs_ctx->maps, state->enum_ctx); + + setent_notify_done(&state->enum_ctx->notify_list); + tevent_req_done(req); + return; +} + +static errno_t +autofs_setent_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct autofs_enum_ctx **_enum_ctx) +{ + struct autofs_setent_state *state; + state = tevent_req_data(req, struct autofs_setent_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_enum_ctx = talloc_reference(mem_ctx, state->enum_ctx); + + return EOK; +} + +static errno_t +autofs_read_setautomntent_input(struct cli_ctx *cli_ctx, + const char **_mapname) +{ + struct cli_protocol *pctx; + uint8_t *body; + size_t blen; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + sss_packet_get_body(pctx->creq->in, &body, &blen); + + /* if not terminated fail */ + if (body[blen - 1] != '\0') { + return EINVAL; + } + + /* If the body isn't valid UTF-8, fail */ + if (!sss_utf8_check(body, blen - 1)) { + return EINVAL; + } + + *_mapname = (const char *)body; + + return EOK; +} + +static errno_t +autofs_write_setautomntent_output(struct cli_ctx *cli_ctx, + struct cache_req_result *result) +{ + struct cli_protocol *pctx; + uint8_t *body; + size_t blen; + errno_t ret; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + if (ret != EOK) { + return ret; + } + + if (result == NULL || result->count == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "Map was not found\n"); + return sss_cmd_empty_packet(pctx->creq->out); + } + + DEBUG(SSSDBG_TRACE_FUNC, "Map found\n"); + + ret = sss_packet_grow(pctx->creq->out, 2 * sizeof(uint32_t)); + if (ret != EOK) { + return ret; + } + + sss_packet_get_body(pctx->creq->out, &body, &blen); + + /* Got some results */ + SAFEALIGN_SETMEM_UINT32(body, 1, NULL); + + /* Reserved padding */ + SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); + + return EOK; +} + +static void +sss_autofs_cmd_setautomntent_done(struct tevent_req *req); + +static int +sss_autofs_cmd_setautomntent(struct cli_ctx *cli_ctx) +{ + struct autofs_cmd_ctx *cmd_ctx; + struct autofs_ctx *autofs_ctx; + struct tevent_req *req; + errno_t ret; + + autofs_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct autofs_ctx); + + cmd_ctx = talloc_zero(cli_ctx, struct autofs_cmd_ctx); + if (cmd_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create command context.\n"); + return ENOMEM; + } + + cmd_ctx->cli_ctx = cli_ctx; + cmd_ctx->autofs_ctx = autofs_ctx; + + ret = autofs_read_setautomntent_input(cli_ctx, &cmd_ctx->mapname); + if (ret != EOK) { + goto done; + } + + autofs_orphan_master_map(autofs_ctx, cmd_ctx->mapname); + + DEBUG(SSSDBG_TRACE_FUNC, "Obtaining autofs map %s\n", + cmd_ctx->mapname); + + req = cache_req_autofs_map_by_name_send(cli_ctx, cli_ctx->ev, + autofs_ctx->rctx, + autofs_ctx->rctx->ncache, 0, NULL, + cmd_ctx->mapname); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_autofs_map_by_name_send failed\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(req, sss_autofs_cmd_setautomntent_done, cmd_ctx); + + ret = EOK; + +done: + return autofs_cmd_done(cmd_ctx, ret); +} + +static void +sss_autofs_cmd_setautomntent_done(struct tevent_req *req) +{ + struct cache_req_result *result; + struct autofs_cmd_ctx *cmd_ctx; + errno_t ret; + + cmd_ctx = tevent_req_callback_data(req, struct autofs_cmd_ctx); + + ret = cache_req_autofs_map_by_name_recv(cmd_ctx, req, &result); + talloc_zfree(req); + if (ret != EOK) { + autofs_cmd_done(cmd_ctx, ret); + return; + } + + ret = autofs_write_setautomntent_output(cmd_ctx->cli_ctx, result); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create reply packet " + "[%d]: %s\n", ret, sss_strerror(ret)); + autofs_cmd_done(cmd_ctx, ret); + return; + } + + sss_cmd_done(cmd_ctx->cli_ctx, NULL); +} + +static int +sss_autofs_cmd_endautomntent(struct cli_ctx *client) +{ + struct cli_protocol *pctx; + errno_t ret; + + DEBUG(SSSDBG_TRACE_FUNC, "endautomntent called\n"); + + pctx = talloc_get_type(client->protocol_ctx, struct cli_protocol); + + /* create response packet */ + ret = sss_packet_new(pctx->creq, 0, + sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + + if (ret != EOK) { + return ret; + } + + sss_cmd_done(client, NULL); + return EOK; +} + +static errno_t +autofs_read_getautomntent_input(struct cli_ctx *cli_ctx, + const char **_mapname, + uint32_t *_cursor, + uint32_t *_max_entries) +{ + struct cli_protocol *pctx; + const char *mapname; + uint32_t namelen; + uint8_t *body; + size_t blen; + size_t c = 0; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + sss_packet_get_body(pctx->creq->in, &body, &blen); + + SAFEALIGN_COPY_UINT32_CHECK(&namelen, body+c, blen, &c); + if (namelen == 0 || namelen > blen - c) { + return EINVAL; + } + + mapname = (const char *)body + c; + + /* if not null-terminated fail */ + if (mapname[namelen] != '\0') { + return EINVAL; + } + + /* If the name isn't valid UTF-8, fail */ + if (!sss_utf8_check((const uint8_t *)mapname, namelen - 1)) { + return EINVAL; + } + + SAFEALIGN_COPY_UINT32_CHECK(_cursor, body + c + namelen + 1, blen, &c); + SAFEALIGN_COPY_UINT32_CHECK(_max_entries, body + c + namelen + 1, blen, &c); + *_mapname = mapname; + + return EOK; +} + +static errno_t +autofs_write_getautomntent_output(struct cli_ctx *cli_ctx, + struct autofs_enum_ctx *enum_ctx, + uint32_t cursor, + uint32_t max_entries) +{ + struct cli_protocol *pctx; + struct ldb_message **entries; + struct ldb_message *entry; + size_t count; + size_t num_entries; + uint8_t *body; + size_t blen; + size_t rp; + uint32_t i; + uint32_t stop; + uint32_t left; + errno_t ret; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + count = enum_ctx->found ? enum_ctx->result->count - 1 : 0; + entries = count > 0 ? enum_ctx->result->msgs + 1 : NULL; + + ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + if (ret != EOK) { + return ret; + } + + if (!enum_ctx->found || count == 0 || cursor >= count) { + DEBUG(SSSDBG_TRACE_FUNC, "No entries was not found\n"); + return sss_cmd_empty_packet(pctx->creq->out); + } + + /* allocate memory for number of entries in the packet */ + ret = sss_packet_grow(pctx->creq->out, sizeof(uint32_t)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot grow packet\n"); + return ret; + } + + rp = sizeof(uint32_t); /* We will first write the elements. */ + left = count - cursor; + stop = max_entries < left ? max_entries : left; + + num_entries = 0; + for (i = 0; i < stop; i++) { + entry = entries[cursor]; + cursor++; + + ret = autofs_fill_entry(entry, pctx->creq->out, &rp); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot fill entry %d/%d, skipping\n", i, stop); + continue; + } + num_entries++; + } + + /* packet grows in fill_autofs_entry, body pointer may change, + * thus we have to obtain it here */ + sss_packet_get_body(pctx->creq->out, &body, &blen); + + rp = 0; + SAFEALIGN_SET_UINT32(&body[rp], num_entries, &rp); + + return EOK; +} + +static void +sss_autofs_cmd_getautomntent_done(struct tevent_req *req); + +static int +sss_autofs_cmd_getautomntent(struct cli_ctx *cli_ctx) +{ + struct autofs_cmd_ctx *cmd_ctx; + struct autofs_ctx *autofs_ctx; + struct tevent_req *req; + errno_t ret; + + autofs_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct autofs_ctx); + + cmd_ctx = talloc_zero(cli_ctx, struct autofs_cmd_ctx); + if (cmd_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create command context.\n"); + return ENOMEM; + } + + cmd_ctx->cli_ctx = cli_ctx; + cmd_ctx->autofs_ctx = autofs_ctx; + + ret = autofs_read_getautomntent_input(cli_ctx, &cmd_ctx->mapname, + &cmd_ctx->cursor, + &cmd_ctx->max_entries); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Obtaining enumeration context for %s\n", + cmd_ctx->mapname); + + req = autofs_setent_send(cli_ctx, cli_ctx->ev, autofs_ctx, cmd_ctx->mapname); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "autofs_setent_send failed\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(req, sss_autofs_cmd_getautomntent_done, cmd_ctx); + + ret = EOK; + +done: + return autofs_cmd_done(cmd_ctx, ret); +} + +static void +sss_autofs_cmd_getautomntent_done(struct tevent_req *req) +{ + struct autofs_enum_ctx *enum_ctx; + struct autofs_cmd_ctx *cmd_ctx; + errno_t ret; + + cmd_ctx = tevent_req_callback_data(req, struct autofs_cmd_ctx); + + ret = autofs_setent_recv(cmd_ctx, req, &enum_ctx); + talloc_zfree(req); + if (ret != EOK) { + autofs_cmd_done(cmd_ctx, ret); + return; + } + + ret = autofs_write_getautomntent_output(cmd_ctx->cli_ctx, enum_ctx, + cmd_ctx->cursor, + cmd_ctx->max_entries); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create reply packet " + "[%d]: %s\n", ret, sss_strerror(ret)); + autofs_cmd_done(cmd_ctx, ret); + return; + } + + sss_cmd_done(cmd_ctx->cli_ctx, NULL); +} + +static errno_t +autofs_read_getautomntbyname_input(struct cli_ctx *cli_ctx, + const char **_mapname, + const char **_keyname) +{ + struct cli_protocol *pctx; + const char *mapname; + const char *keyname; + uint32_t namelen; + uint32_t keylen; + uint8_t *body; + size_t blen; + size_t c = 0; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + sss_packet_get_body(pctx->creq->in, &body, &blen); + + /* Get map name. */ + SAFEALIGN_COPY_UINT32_CHECK(&namelen, body + c, blen, &c); + if (namelen == 0 || namelen > blen - c) { + return EINVAL; + } + + mapname = (const char *) body + c; + + /* if not null-terminated fail */ + if (mapname[namelen] != '\0') { + return EINVAL; + } + + /* If the name isn't valid UTF-8, fail */ + if (!sss_utf8_check((const uint8_t *)mapname, namelen - 1)) { + return EINVAL; + } + + c += namelen + 1; + + /* Get key name. */ + SAFEALIGN_COPY_UINT32_CHECK(&keylen, body + c, blen, &c); + if (keylen == 0 || keylen > blen - c) { + return EINVAL; + } + + keyname = (const char *) body + c; + + /* if not null-terminated fail */ + if (keyname[keylen] != '\0') { + return EINVAL; + } + + /* If the key isn't valid UTF-8, fail */ + if (!sss_utf8_check((const uint8_t *)keyname, keylen - 1)) { + return EINVAL; + } + + *_mapname = mapname; + *_keyname = keyname; + + return EOK; +} + +static errno_t +autofs_write_getautomntbyname_output(struct cli_ctx *cli_ctx, + struct cache_req_result *result, + const char *keyname) +{ + struct cli_protocol *pctx; + struct ldb_message *entry; + const char *value; + size_t value_len; + size_t len; + uint8_t *body; + size_t blen; + size_t rp; + errno_t ret; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + if (result == NULL || result->count == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "Key [%s] was not found\n", keyname); + return sss_cmd_empty_packet(pctx->creq->out); + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "Found key [%s]\n", keyname); + entry = result->msgs[0]; + + ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + if (ret != EOK) { + return ret; + } + + value = ldb_msg_find_attr_as_string(entry, SYSDB_AUTOFS_ENTRY_VALUE, NULL); + if (value == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No entry value found in [%s]\n", keyname); + return EINVAL; + } + + value_len = 1 + strlen(value); + len = sizeof(uint32_t) + sizeof(uint32_t) + value_len; + + ret = sss_packet_grow(pctx->creq->out, len); + if (ret != EOK) { + return ret; + } + + sss_packet_get_body(pctx->creq->out, &body, &blen); + + rp = 0; + SAFEALIGN_SET_UINT32(&body[rp], len, &rp); + + SAFEALIGN_SET_UINT32(&body[rp], value_len, &rp); + if (value_len == 1) { + body[rp] = '\0'; + } else { + memcpy(&body[rp], value, value_len); + } + + return EOK; +} + +static void +sss_autofs_cmd_getautomntbyname_done(struct tevent_req *req); + +static int +sss_autofs_cmd_getautomntbyname(struct cli_ctx *cli_ctx) +{ + struct autofs_cmd_ctx *cmd_ctx; + struct autofs_ctx *autofs_ctx; + struct tevent_req *req; + errno_t ret; + + autofs_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct autofs_ctx); + + cmd_ctx = talloc_zero(cli_ctx, struct autofs_cmd_ctx); + if (cmd_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create command context.\n"); + return ENOMEM; + } + + cmd_ctx->cli_ctx = cli_ctx; + cmd_ctx->autofs_ctx = autofs_ctx; + + ret = autofs_read_getautomntbyname_input(cli_ctx, &cmd_ctx->mapname, + &cmd_ctx->keyname); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Obtaining autofs entry %s:%s\n", + cmd_ctx->mapname, cmd_ctx->keyname); + + req = cache_req_autofs_entry_by_name_send(cli_ctx, cli_ctx->ev, + autofs_ctx->rctx, + autofs_ctx->rctx->ncache, 0, NULL, + cmd_ctx->mapname, + cmd_ctx->keyname); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_autofs_entry_by_name_send failed\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(req, sss_autofs_cmd_getautomntbyname_done, cmd_ctx); + + ret = EOK; + +done: + return autofs_cmd_done(cmd_ctx, ret); +} + +static void +sss_autofs_cmd_getautomntbyname_done(struct tevent_req *req) +{ + struct cache_req_result *result; + struct autofs_cmd_ctx *cmd_ctx; + errno_t ret; + + cmd_ctx = tevent_req_callback_data(req, struct autofs_cmd_ctx); + + ret = cache_req_autofs_entry_by_name_recv(cmd_ctx, req, &result); + talloc_zfree(req); + if (ret != EOK) { + autofs_cmd_done(cmd_ctx, ret); + return; + } + + ret = autofs_write_getautomntbyname_output(cmd_ctx->cli_ctx, result, + cmd_ctx->keyname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create reply packet " + "[%d]: %s\n", ret, sss_strerror(ret)); + autofs_cmd_done(cmd_ctx, ret); + return; + } + + sss_cmd_done(cmd_ctx->cli_ctx, NULL); +} + +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; +} + +int autofs_connection_setup(struct cli_ctx *cctx) +{ + int ret; + + ret = sss_connection_setup(cctx); + if (ret != EOK) return ret; + + return EOK; +} -- cgit v1.2.3