From 4c11f752e1f10cf5740d53a3206bb795e9e34fe8 Mon Sep 17 00:00:00 2001 From: Jan Zeleny Date: Tue, 24 Jan 2012 06:21:07 -0500 Subject: Added some SELinux-related sysdb routines --- Makefile.am | 2 + src/db/sysdb.h | 9 +- src/db/sysdb_selinux.c | 471 +++++++++++++++++++++++++++++++++++++++++++++++++ src/db/sysdb_selinux.h | 66 +++++++ 4 files changed, 547 insertions(+), 1 deletion(-) 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 2ac3b1c39..90b6d8139 100644 --- a/Makefile.am +++ b/Makefile.am @@ -333,6 +333,7 @@ dist_noinst_HEADERS = \ src/db/sysdb.h \ src/db/sysdb_sudo.h \ src/db/sysdb_autofs.h \ + src/db/sysdb_selinux.h \ src/db/sysdb_private.h \ src/db/sysdb_services.h \ src/confdb/confdb.h \ @@ -393,6 +394,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/db/sysdb_autofs.c \ diff --git a/src/db/sysdb.h b/src/db/sysdb.h index 698dc46d3..a74c9d431 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" @@ -65,6 +67,8 @@ #define SYSDB_MEMBER "member" #define SYSDB_MEMBERUID "memberUid" #define SYSDB_POSIX "isPosix" +#define SYSDB_USER_CATEGORY "userCategory" +#define SYSDB_HOST_CATEGORY "hostCategory" #define SYSDB_DEFAULTGROUP "defaultGroup" #define SYSDB_GECOS "gecos" @@ -91,6 +95,10 @@ #define SYSDB_NETGROUP_MEMBER "memberNisNetgroup" #define SYSDB_DESCRIPTION "description" +#define SYSDB_SELINUX_SEEALSO "seeAlso" +#define SYSDB_SELINUX_USER "selinuxUser" +#define SYSDB_SELINUX_ENABLED "enabled" + #define SYSDB_CACHEDPWD "cachedPassword" #define SYSDB_UUID "uniqueID" @@ -100,7 +108,6 @@ #define SYSDB_ORIG_DN "originalDN" #define SYSDB_ORIG_MODSTAMP "originalModifyTimestamp" #define SYSDB_ORIG_MEMBEROF "originalMemberOf" - #define SYSDB_ORIG_MEMBER_USER "originalMemberUser" #define SYSDB_ORIG_MEMBER_HOST "originalMemberHost" diff --git a/src/db/sysdb_selinux.c b/src/db/sysdb_selinux.c new file mode 100644 index 000000000..5df5fe0e3 --- /dev/null +++ b/src/db/sysdb_selinux.c @@ -0,0 +1,471 @@ +/* + SSSD + + System Database - SELinux support + + Copyright (C) Jan Zeleny 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 . +*/ + +#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 (type != SELINUX_CONFIG && type != SELINUX_USER_MAP) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Bad SELinux entity type: [%d]\n", type)); + ret = EINVAL; + goto done; + } + + 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 000000000..7a0229236 --- /dev/null +++ b/src/db/sysdb_selinux.h @@ -0,0 +1,66 @@ +/* + SSSD + + System Database Header - SELinux support + + Copyright (C) Jan Zeleny 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 . +*/ + +#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 -- cgit