summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--src/db/sysdb_sudo.c333
-rw-r--r--src/db/sysdb_sudo.h69
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_ */