From 333c7c355f0b901fb899d81fc81202671c776406 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny@redhat.com>
Date: Tue, 24 Jan 2012 06:21:07 -0500
Subject: [PATCH 07/14] Added some SELinux-related sysdb routines

---
 Makefile.am            |    2 +
 src/db/sysdb.h         |    2 +
 src/db/sysdb_selinux.c |  465 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/db/sysdb_selinux.h |   66 +++++++
 4 files changed, 535 insertions(+), 0 deletions(-)
 create mode 100644 src/db/sysdb_selinux.c
 create mode 100644 src/db/sysdb_selinux.h

diff --git a/Makefile.am b/Makefile.am
index aed74ebb2d9517b31b3f4fae49feec64c5b3f8e1..3efae99f9299c06361bd2bfdf037787e08d4edc2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -325,6 +325,7 @@ dist_noinst_HEADERS = \
     src/sbus/sssd_dbus_private.h \
     src/db/sysdb.h \
     src/db/sysdb_sudo.h \
+    src/db/sysdb_selinux.h \
     src/db/sysdb_private.h \
     src/db/sysdb_services.h \
     src/confdb/confdb.h \
@@ -384,6 +385,7 @@ libsss_util_la_SOURCES = \
     src/db/sysdb.c \
     src/db/sysdb_ops.c \
     src/db/sysdb_search.c \
+    src/db/sysdb_selinux.c \
     src/db/sysdb_upgrade.c \
     src/db/sysdb_services.c \
     src/monitor/monitor_sbus.c \
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 573dc652577234a0eb57794238d989f19743026a..1ef427271c8b32300fb0e360ea3a419cc2c9de65 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -45,6 +45,8 @@
 #define SYSDB_GROUP_CLASS "group"
 #define SYSDB_NETGROUP_CLASS "netgroup"
 #define SYSDB_HOST_CLASS "host"
+#define SYSDB_SELINUX_USERMAP_CLASS "selinuxusermap"
+#define SYSDB_SELINUX_CLASS "selinux"
 
 #define SYSDB_NAME "name"
 #define SYSDB_NAME_ALIAS "nameAlias"
diff --git a/src/db/sysdb_selinux.c b/src/db/sysdb_selinux.c
new file mode 100644
index 0000000000000000000000000000000000000000..bf1448dc2e5c65094bb06503c90e1c825a420e93
--- /dev/null
+++ b/src/db/sysdb_selinux.c
@@ -0,0 +1,465 @@
+/*
+   SSSD
+
+   System Database - SELinux support
+
+   Copyright (C) Jan Zeleny <jzeleny@redhat.com>	2012
+
+   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 "util/sss_selinux.h"
+#include "db/sysdb_selinux.h"
+#include "db/sysdb_private.h"
+
+/* Some generic routines */
+static errno_t get_rm_msg(TALLOC_CTX *mem_ctx,
+                          struct ldb_message *old_msg,
+                          struct sysdb_attrs *new_attrs,
+                          struct ldb_message **_msg)
+{
+    struct ldb_message *rm_msg;
+    const char *tmp_str;
+    errno_t ret;
+    int i;
+
+    rm_msg = ldb_msg_new(mem_ctx);
+    if (rm_msg == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    rm_msg->dn = old_msg->dn;
+    rm_msg->elements = talloc_zero_array(rm_msg, struct ldb_message_element,
+                                         old_msg->num_elements);
+    rm_msg->num_elements = 0;
+
+    for (i = 0; i < old_msg->num_elements; i++) {
+        ret = sysdb_attrs_get_string(new_attrs, old_msg->elements[i].name, &tmp_str);
+        if (ret != ENOENT) {
+            continue;
+        }
+
+        rm_msg->elements[rm_msg->num_elements] = old_msg->elements[i];
+        rm_msg->elements[rm_msg->num_elements].flags = LDB_FLAG_MOD_DELETE;
+        rm_msg->num_elements++;
+    }
+
+done:
+    if (ret != EOK) {
+        talloc_free(rm_msg);
+    } else {
+        *_msg = rm_msg;
+    }
+
+    return ret;
+}
+
+static errno_t
+sysdb_add_selinux_entity(struct sysdb_ctx *sysdb,
+                         struct ldb_dn *dn,
+                         const char *objectclass,
+                         struct sysdb_attrs *attrs,
+                         time_t now)
+{
+    struct ldb_message *msg;
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    msg = ldb_msg_new(tmp_ctx);
+    if (!msg) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCLASS, objectclass);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not set map object class [%d]: %s\n",
+              ret, strerror(ret)));
+        return ret;
+    }
+
+    if (!now) {
+        now = time(NULL);
+    }
+
+    ret = sysdb_attrs_add_time_t(attrs, SYSDB_CREATE_TIME, now);
+    if (ret) goto done;
+
+    msg->dn = dn;
+    msg->elements = attrs->a;
+    msg->num_elements = attrs->num;
+
+    ret = ldb_add(sysdb->ldb, msg);
+    ret = sysdb_error_to_errno(ret);
+
+done:
+    if (ret) {
+        DEBUG(SSSDBG_TRACE_LIBS, ("Error: %d (%s)\n", ret, strerror(ret)));
+    }
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
+static errno_t sysdb_store_selinux_entity(struct sysdb_ctx *sysdb,
+                                          struct sysdb_attrs *attrs,
+                                          enum selinux_entity_type type)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_message *msg;
+    struct ldb_message *rm_msg;
+    bool in_transaction = false;
+    const char *objectclass;
+    const char *name;
+    char *clean_name;
+    struct ldb_dn *dn;
+    errno_t sret = EOK;
+    errno_t ret;
+    time_t now;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    switch (type) {
+        case SELINUX_USER_MAP:
+            objectclass = SYSDB_SELINUX_USERMAP_CLASS;
+            ret = sysdb_attrs_get_string(attrs, SYSDB_NAME, &name);
+            if (ret != EOK) {
+                goto done;
+            }
+
+            ret = sysdb_dn_sanitize(tmp_ctx, name, &clean_name);
+            if (ret != EOK) {
+                goto done;
+            }
+
+            dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_SEUSERMAP,
+                                clean_name, sysdb->domain->name);
+            break;
+        case SELINUX_CONFIG:
+            objectclass = SYSDB_SELINUX_CLASS;
+            dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_SELINUX_BASE,
+                                sysdb->domain->name);
+            break;
+    }
+
+    if (!dn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_transaction_start(sysdb);
+    if (ret != EOK) goto done;
+
+    in_transaction = true;
+
+    now = time(NULL);
+    ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+    if (ret) goto done;
+
+    if (type == SELINUX_CONFIG) {
+        ret = sysdb_search_selinux_config(tmp_ctx, sysdb, NULL, &msg);
+    } else if (type == SELINUX_USER_MAP) {
+        ret = sysdb_search_selinux_usermap_by_mapname(tmp_ctx, sysdb, name,
+                                                      NULL, &msg);
+    }
+
+    if (ret && ret != ENOENT) {
+        goto done;
+    }
+    if (ret == ENOENT) {
+        ret = sysdb_add_selinux_entity(sysdb, dn, objectclass, attrs, now);
+        goto done;
+    }
+
+    ret = sysdb_set_entry_attr(sysdb, dn, attrs, SYSDB_MOD_REP);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* Now delete attributes which are no longer present */
+    ret = get_rm_msg(tmp_ctx, msg, attrs, &rm_msg);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    if (rm_msg->num_elements > 0) {
+        ret = ldb_modify(sysdb->ldb, rm_msg);
+    }
+
+done:
+    if (in_transaction) {
+        if (ret == EOK) {
+            sret = sysdb_transaction_commit(sysdb);
+            if (sret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, ("Could not commit transaction\n"));
+            }
+        }
+
+        if (ret != EOK || sret != EOK){
+            sret = sysdb_transaction_cancel(sysdb);
+            if (sret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, ("Could not cancel transaction\n"));
+            }
+        }
+    }
+    if (ret) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Error: %d (%s)\n", ret, strerror(ret)));
+    }
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
+errno_t sysdb_store_selinux_usermap(struct sysdb_ctx *sysdb,
+                                    struct sysdb_attrs *attrs)
+{
+    return sysdb_store_selinux_entity(sysdb, attrs, SELINUX_USER_MAP);
+}
+
+errno_t sysdb_store_selinux_config(struct sysdb_ctx *sysdb,
+                                   const char *default_user,
+                                   const char *order)
+{
+    errno_t ret;
+    struct sysdb_attrs *attrs;
+
+    attrs = talloc_zero(NULL, struct sysdb_attrs);
+    if (attrs == NULL) {
+        return ENOMEM;
+    }
+
+    ret = sysdb_attrs_add_string(attrs, SYSDB_SELINUX_DEFAULT_USER,
+                                 default_user);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = sysdb_attrs_add_string(attrs, SYSDB_SELINUX_DEFAULT_ORDER,
+                                 order);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret =  sysdb_store_selinux_entity(sysdb, attrs, SELINUX_CONFIG);
+done:
+    talloc_free(attrs);
+    return ret;
+}
+
+
+
+/* --- SYSDB SELinux search routines --- */
+errno_t sysdb_search_selinux_usermap_by_mapname(TALLOC_CTX *mem_ctx,
+                                                struct sysdb_ctx *sysdb,
+                                                const char *name,
+                                                const char **attrs,
+                                                struct ldb_message **_usermap)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *def_attrs[] = { SYSDB_NAME,
+                                SYSDB_USER_CATEGORY,
+                                SYSDB_HOST_CATEGORY,
+                                SYSDB_ORIG_MEMBER_USER,
+                                SYSDB_ORIG_MEMBER_HOST,
+                                SYSDB_SELINUX_USER,
+                                NULL };
+    struct ldb_message **msgs = NULL;
+    struct ldb_dn *basedn;
+    size_t msgs_count = 0;
+    char *clean_name;
+    int ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    ret = sysdb_dn_sanitize(tmp_ctx, name, &clean_name);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    basedn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_SEUSERMAP,
+                            clean_name, sysdb->domain->name);
+    if (!basedn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_search_entry(tmp_ctx, sysdb, basedn, LDB_SCOPE_BASE, NULL,
+                             attrs?attrs:def_attrs, &msgs_count, &msgs);
+    if (ret) {
+        goto done;
+    }
+
+    *_usermap = talloc_steal(mem_ctx, msgs[0]);
+
+done:
+    if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("No such entry\n"));
+    }
+    else if (ret) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("Error: %d (%s)\n", ret, strerror(ret)));
+    }
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
+errno_t sysdb_search_selinux_usermap_by_username(TALLOC_CTX *mem_ctx,
+                                                 struct sysdb_ctx *sysdb,
+                                                 const char *username,
+                                                 struct ldb_message ***_usermaps)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *attrs[] = { SYSDB_NAME,
+                            SYSDB_USER_CATEGORY,
+                            SYSDB_HOST_CATEGORY,
+                            SYSDB_ORIG_MEMBER_USER,
+                            SYSDB_ORIG_MEMBER_HOST,
+                            SYSDB_SELINUX_USER,
+                            NULL };
+    struct ldb_message **msgs = NULL;
+    struct sysdb_attrs *user;
+    struct sysdb_attrs *tmp_attrs;
+    struct ldb_message **usermaps;
+    struct sss_domain_info *domain;
+    struct ldb_dn *basedn;
+    size_t msgs_count = 0;
+    size_t usermaps_cnt;
+    char *filter;
+    errno_t ret;
+    int i;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    /* Now extract user attributes */
+    ret = sss_selinux_extract_user(tmp_ctx, sysdb, username, &user);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* Now extract all SELinux user maps */
+    domain = sysdb_ctx_get_domain(sysdb);
+    basedn = ldb_dn_new_fmt(tmp_ctx, sysdb_ctx_get_ldb(sysdb),
+                            SYSDB_TMPL_SELINUX_BASE, domain->name);
+    if (!basedn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    filter = talloc_asprintf(tmp_ctx, "(objectclass=%s)", SYSDB_SELINUX_USERMAP_CLASS);
+    if (filter == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_search_entry(tmp_ctx, sysdb, basedn, LDB_SCOPE_SUBTREE, filter,
+                             attrs, &msgs_count, &msgs);
+    if (ret) {
+        goto done;
+    }
+
+    /* Now filter those that match */
+    tmp_attrs = talloc_zero(tmp_ctx, struct sysdb_attrs);
+    if (tmp_attrs == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    usermaps = talloc_zero_array(tmp_ctx, struct ldb_message *, msgs_count + 1);
+    if (usermaps == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    usermaps_cnt = 0;
+    for (i = 0; i < msgs_count; i++) {
+        tmp_attrs->a = msgs[i]->elements;
+        tmp_attrs->num = msgs[i]->num_elements;
+
+        if (sss_selinux_match(tmp_attrs, user, NULL)) {
+            usermaps[usermaps_cnt] = talloc_steal(usermaps, msgs[i]);
+            usermaps_cnt++;
+        } else {
+            talloc_zfree(msgs[i]);
+        }
+    }
+
+    if (usermaps[0] == NULL) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    *_usermaps = talloc_steal(mem_ctx, usermaps);
+
+done:
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
+errno_t sysdb_search_selinux_config(TALLOC_CTX *mem_ctx,
+                                    struct sysdb_ctx *sysdb,
+                                    const char **attrs,
+                                    struct ldb_message **_config)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *def_attrs[] = { SYSDB_SELINUX_DEFAULT_USER,
+                                SYSDB_SELINUX_DEFAULT_ORDER,
+                                NULL };
+    struct ldb_message **msgs;
+    size_t msgs_count;
+    struct ldb_dn *basedn;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    basedn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_SELINUX_BASE,
+                            sysdb->domain->name);
+    if (!basedn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_search_entry(tmp_ctx, sysdb, basedn, LDB_SCOPE_BASE, NULL,
+                             attrs?attrs:def_attrs, &msgs_count, &msgs);
+    if (ret) {
+        goto done;
+    }
+
+    *_config = talloc_steal(mem_ctx, msgs[0]);
+
+done:
+    if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("No SELinux root entry found\n"));
+    } else if (ret) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("Error: %d (%s)\n", ret, strerror(ret)));
+    }
+
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
diff --git a/src/db/sysdb_selinux.h b/src/db/sysdb_selinux.h
new file mode 100644
index 0000000000000000000000000000000000000000..7a0229236585c45051b3ce2461f84f82d33bf359
--- /dev/null
+++ b/src/db/sysdb_selinux.h
@@ -0,0 +1,66 @@
+/*
+   SSSD
+
+   System Database Header - SELinux support
+
+   Copyright (C) Jan Zeleny <jzeleny@redhat.com>	2012
+
+   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 __SYS_DB_SELINUX_H__
+#define __SYS_DB_SELINUX_H__
+
+#include "db/sysdb.h"
+
+#define SYSDB_SELINUX_CONTAINER "cn=selinux"
+#define SYSDB_TMPL_SELINUX_BASE SYSDB_SELINUX_CONTAINER",cn=%s,"SYSDB_BASE
+#define SYSDB_TMPL_SEUSERMAP SYSDB_NAME"=%s,"SYSDB_TMPL_SELINUX_BASE
+
+#define SYSDB_SELINUX_NAME "config"
+#define SYSDB_SELINUX_SEEALSO "seeAlso"
+#define SYSDB_SELINUX_USER "selinuxUser"
+#define SYSDB_SELINUX_ENABLED "enabled"
+#define SYSDB_SELINUX_DEFAULT_USER "user"
+#define SYSDB_SELINUX_DEFAULT_ORDER "order"
+
+enum selinux_entity_type {
+    SELINUX_CONFIG,
+    SELINUX_USER_MAP
+};
+
+errno_t sysdb_store_selinux_usermap(struct sysdb_ctx *sysdb,
+                                    struct sysdb_attrs *attrs);
+
+errno_t sysdb_store_selinux_config(struct sysdb_ctx *sysdb,
+                                   const char *default_map,
+                                   const char *order);
+
+errno_t sysdb_search_selinux_usermap_by_mapname(TALLOC_CTX *mem_ctx,
+                                                struct sysdb_ctx *sysdb,
+                                                const char *name,
+                                                const char **attrs,
+                                                struct ldb_message **_usermap);
+
+errno_t sysdb_search_selinux_usermap_by_username(TALLOC_CTX *mem_ctx,
+                                                 struct sysdb_ctx *sysdb,
+                                                 const char *username,
+                                                 struct ldb_message ***_usermaps);
+
+errno_t sysdb_search_selinux_config(TALLOC_CTX *mem_ctx,
+                                    struct sysdb_ctx *sysdb,
+                                    const char **attrs,
+                                    struct ldb_message **_config);
+
+#endif
-- 
1.7.6.4

