diff options
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | src/db/sysdb_sudo.c | 333 | ||||
-rw-r--r-- | src/db/sysdb_sudo.h | 69 |
3 files changed, 406 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 116b26a7..5fd492e7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -314,6 +314,7 @@ dist_noinst_HEADERS = \ src/sbus/sssd_dbus.h \ src/sbus/sssd_dbus_private.h \ src/db/sysdb.h \ + src/db/sysdb_sudo.h \ src/db/sysdb_private.h \ src/confdb/confdb.h \ src/confdb/confdb_private.h \ @@ -390,6 +391,9 @@ libsss_util_la_LIBADD = \ $(UNICODE_LIBS) \ libsss_crypt.la \ libsss_debug.la +if BUILD_SUDO + libsss_util_la_SOURCES += src/db/sysdb_sudo.c +endif lib_LTLIBRARIES = libipa_hbac.la dist_pkgconfig_DATA += src/providers/ipa/ipa_hbac.pc diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c new file mode 100644 index 00000000..1703e78e --- /dev/null +++ b/src/db/sysdb_sudo.c @@ -0,0 +1,333 @@ +/* + 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 "db/sysdb.h" +#include "db/sysdb_sudo.h" + +#define NULL_CHECK(val, rval, label) do { \ + if (!val) { \ + rval = ENOMEM; \ + goto label; \ + } \ +} while(0) + +/* ==================== Utility functions ==================== */ +static char * +get_sudo_time_filter(TALLOC_CTX *mem_ctx) +{ + time_t now; + struct tm *tp; + char timebuffer[64]; + + /* Make sure we have a formatted timestamp for __now__. */ + time(&now); + if ((tp = gmtime(&now)) == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("unable to get GMT time\n")); + return NULL; + } + + /* Format the timestamp according to the RFC. */ + if (strftime(timebuffer, sizeof(timebuffer), "%Y%m%d%H%M%SZ", tp) == 0) { + DEBUG(SSSDBG_CRIT_FAILURE, ("unable to format timestamp\n")); + return NULL; + } + + return talloc_asprintf(mem_ctx, "(&(|(!(%s=*))(%s>=%s))" + "(|(!(%s=*))(%s<=%s)))", + SYSDB_SUDO_CACHE_AT_NOTAFTER, + SYSDB_SUDO_CACHE_AT_NOTAFTER, + timebuffer, + SYSDB_SUDO_CACHE_AT_NOTBEFORE, + SYSDB_SUDO_CACHE_AT_NOTBEFORE, + timebuffer); +} + +errno_t +sysdb_get_sudo_filter(TALLOC_CTX *mem_ctx, const char *username, + uid_t uid, char **groupnames, unsigned int flags, + char **_filter) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret; + char *filter; + char *t; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) return ENOMEM; + + /* AND with objectclass */ + filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)", + SYSDB_OBJECTCLASS, + SYSDB_SUDO_CACHE_AT_OC); + NULL_CHECK(filter, ret, done); + + /* And with the timed rules if requested */ + if (flags & SYSDB_SUDO_FILTER_TIMED) { + t = get_sudo_time_filter(filter); + filter = talloc_asprintf_append(filter, "%s", t); + talloc_free(t); + NULL_CHECK(filter, ret, done); + } + + /* Add global OR and the username */ + filter = talloc_asprintf_append(filter, "(|(%s=%s)", + SYSDB_SUDO_CACHE_AT_USER, + username); + NULL_CHECK(filter, ret, done); + + if (uid) { + filter = talloc_asprintf_append(filter, "(%s=#%llu)", + SYSDB_SUDO_CACHE_AT_USER, + (unsigned long long) uid); + NULL_CHECK(filter, ret, done); + } + + if (groupnames) { + for (i=0; groupnames[i]; i++) { + filter = talloc_asprintf_append(filter, "(%s=%%%s)", + SYSDB_SUDO_CACHE_AT_USER, + groupnames[i]); + NULL_CHECK(filter, ret, done); + } + } + + if (flags & SYSDB_SUDO_FILTER_NGRS) { + filter = talloc_asprintf_append(filter, "(%s=+*)", + SYSDB_SUDO_CACHE_AT_USER); + NULL_CHECK(filter, ret, done); + } + + if (flags & SYSDB_SUDO_FILTER_INCLUDE_ALL) { + filter = talloc_asprintf_append(filter, "(%s=ALL)", + SYSDB_SUDO_CACHE_AT_USER); + NULL_CHECK(filter, ret, done); + } + + if (flags & SYSDB_SUDO_FILTER_INCLUDE_DFL) { + filter = talloc_asprintf_append(filter, "(%s=defaults)", + SYSDB_NAME); + NULL_CHECK(filter, ret, done); + } + + /* end the global AND and OR filters */ + filter = talloc_asprintf_append(filter, "))"); + NULL_CHECK(filter, ret, done); + + + ret = EOK; + *_filter = talloc_steal(mem_ctx, filter); +done: + talloc_free(tmp_ctx); + return ret; +} + +errno_t +sysdb_get_sudo_user_info(TALLOC_CTX *mem_ctx, const char *username, + struct sysdb_ctx *sysdb, uid_t *_uid, + char ***groupnames) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret; + const char *attrs[3]; + struct ldb_message *msg; + char **sysdb_groupnames = NULL; + struct ldb_message_element *groups; + uid_t uid; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) return ENOMEM; + + attrs[0] = SYSDB_MEMBEROF; + attrs[1] = SYSDB_UIDNUM; + attrs[2] = NULL; + ret = sysdb_search_user_by_name(tmp_ctx, sysdb, username, + attrs, &msg); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error looking up user %s\n", username)); + goto done; + } + + uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0); + if (!uid) { + DEBUG(SSSDBG_CRIT_FAILURE, ("A user with no UID?\n")); + ret = EIO; + goto done; + } + + groups = ldb_msg_find_element(msg, SYSDB_MEMBEROF); + if (!groups || groups->num_values == 0) { + /* No groups for this user in sysdb currently */ + sysdb_groupnames = NULL; + } else { + sysdb_groupnames = talloc_array(tmp_ctx, char *, groups->num_values+1); + if (!sysdb_groupnames) { + ret = ENOMEM; + goto done; + } + + /* Get a list of the groups by groupname only */ + for (i=0; i < groups->num_values; i++) { + ret = sysdb_group_dn_name(sysdb, + sysdb_groupnames, + (const char *)groups->values[i].data, + &sysdb_groupnames[i]); + if (ret != EOK) { + ret = ENOMEM; + goto done; + } + } + sysdb_groupnames[groups->num_values] = NULL; + } + + ret = EOK; + *_uid = uid; + *groupnames = talloc_steal(mem_ctx, sysdb_groupnames); +done: + talloc_free(tmp_ctx); + return EOK; +} + +static errno_t +sysdb_sudo_purge_subdir(struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *subdir) +{ + struct ldb_dn *base_dn = NULL; + TALLOC_CTX *tmp_ctx = NULL; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n")); + ret = ENOMEM; + goto done; + } + + base_dn = sysdb_custom_subtree_dn(sysdb, tmp_ctx, domain->name, subdir); + if (base_dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_delete_recursive(sysdb, base_dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_delete_recursive failed.\n")); + goto done; + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return EOK; +} + +errno_t +sysdb_save_sudorule(struct sysdb_ctx *sysdb_ctx, + const char *rule_name, + struct sysdb_attrs *attrs) +{ + errno_t ret; + + DEBUG(SSSDBG_TRACE_FUNC, ("Adding sudo rule %s\n", rule_name)); + + ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCLASS, + SYSDB_SUDO_CACHE_AT_OC); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not set rule object class [%d]: %s\n", + ret, strerror(ret))); + return ret; + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, rule_name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not set name attribute [%d]: %s\n", + ret, strerror(ret))); + return ret; + } + + ret = sysdb_store_custom(sysdb_ctx, rule_name, SUDORULE_SUBDIR, attrs); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_store_custom failed [%d]: %s\n", + ret, strerror(ret))); + return ret; + } + + return EOK; +} + +errno_t +sysdb_purge_sudorule_subtree(struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *filter) +{ + TALLOC_CTX *tmp_ctx; + size_t count; + struct ldb_message **msgs; + const char *name; + int i; + errno_t ret; + const char *attrs[] = { SYSDB_OBJECTCLASS + SYSDB_SUDO_CACHE_AT_OC, + SYSDB_SUDO_CACHE_AT_CN }; + + /* just purge all if there's no filter */ + if (!filter) { + return sysdb_sudo_purge_subdir(sysdb, domain, SUDORULE_SUBDIR); + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) return ENOMEM; + + /* match entries based on the filter and remove them one by one */ + ret = sysdb_search_custom(tmp_ctx, sysdb, filter, + SUDORULE_SUBDIR, attrs, + &count, &msgs); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error looking up SUDO rules")); + goto done; + } if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_FUNC, ("No rules matched\n")); + ret = EOK; + goto done; + } + + for (i = 0; i < count; i++) { + name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("A rule without a name?\n")); + goto done; + } + + ret = sysdb_delete_custom(sysdb, name, SUDORULE_SUBDIR); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not delete rule %s\n", name)); + goto done; + } + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/db/sysdb_sudo.h b/src/db/sysdb_sudo.h new file mode 100644 index 00000000..70ee1f4c --- /dev/null +++ b/src/db/sysdb_sudo.h @@ -0,0 +1,69 @@ +/* + 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/>. +*/ + +#ifndef _SYSDB_SUDO_H_ +#define _SYSDB_SUDO_H_ + +#include "db/sysdb.h" + +/* subdirs in cn=custom in sysdb. We don't store sudo stuff in sysdb directly + * b/c it's not name-service-switch data */ +#define SUDORULE_SUBDIR "sudorules" + +/* sysdb attributes */ +#define SYSDB_SUDO_CACHE_AT_OC "sudoRule" +#define SYSDB_SUDO_CACHE_AT_CN "cn" +#define SYSDB_SUDO_CACHE_AT_USER "sudoUser" +#define SYSDB_SUDO_CACHE_AT_HOST "sudoHost" +#define SYSDB_SUDO_CACHE_AT_COMMAND "sudoCommand" +#define SYSDB_SUDO_CACHE_AT_OPTION "sudoOption" +#define SYSDB_SUDO_CACHE_AT_RUNASUSER "sudoRunAsUser" +#define SYSDB_SUDO_CACHE_AT_RUNASGROUP "sudoRunAsGroup" +#define SYSDB_SUDO_CACHE_AT_NOTBEFORE "sudoNotBefore" +#define SYSDB_SUDO_CACHE_AT_NOTAFTER "sudoNotAfter" +#define SYSDB_SUDO_CACHE_AT_ORDER "sudoOrder" + +/* When constructing a sysdb filter, OR these values to include.. */ +#define SYSDB_SUDO_FILTER_NONE 0x00 /* no additional filter */ +#define SYSDB_SUDO_FILTER_NGRS 0x01 /* netgroups */ +#define SYSDB_SUDO_FILTER_TIMED 0x02 /* timed rules */ +#define SYSDB_SUDO_FILTER_INCLUDE_ALL 0x04 /* ALL */ +#define SYSDB_SUDO_FILTER_INCLUDE_DFL 0x08 /* include cn=default */ + +errno_t +sysdb_get_sudo_filter(TALLOC_CTX *mem_ctx, const char *username, + uid_t uid, char **groupnames, unsigned int flags, + char **_filter); + +errno_t +sysdb_get_sudo_user_info(TALLOC_CTX *mem_ctx, const char *username, + struct sysdb_ctx *sysdb, uid_t *_uid, + char ***groupnames); + +errno_t +sysdb_save_sudorule(struct sysdb_ctx *sysdb_ctx, + const char *rule_name, + struct sysdb_attrs *attrs); + +errno_t sysdb_purge_sudorule_subtree(struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *filter); + +#endif /* _SYSDB_SUDO_H_ */ |