summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2011-05-17 16:28:15 +0200
committerStephen Gallagher <sgallagh@redhat.com>2011-06-02 15:32:30 -0400
commit28dab46aaa2c0d65160492f1d9d7c88652d8c714 (patch)
treecf11aa59b6ef8ab49cdb39dcdb22a036e9e65de5 /src
parent807402e4e9ac43d0fe7a7533698102a74e23844b (diff)
downloadsssd-28dab46aaa2c0d65160492f1d9d7c88652d8c714.tar.gz
sssd-28dab46aaa2c0d65160492f1d9d7c88652d8c714.tar.xz
sssd-28dab46aaa2c0d65160492f1d9d7c88652d8c714.zip
Support overriding attribute values locally
Add a new option to override primary GID number https://fedorahosted.org/sssd/ticket/742 Add a new option to override home directory value https://fedorahosted.org/sssd/ticket/551 Add new options to override shell value https://fedorahosted.org/sssd/ticket/742 Conflicts: src/conf_macros.m4
Diffstat (limited to 'src')
-rw-r--r--src/conf_macros.m414
-rw-r--r--src/confdb/confdb.c16
-rw-r--r--src/confdb/confdb.h8
-rw-r--r--src/config/SSSDConfig.py4
-rwxr-xr-xsrc/config/SSSDConfigTest.py4
-rw-r--r--src/config/etc/sssd.api.conf5
-rw-r--r--src/man/sssd.conf.5.xml90
-rw-r--r--src/responder/nss/nsssrv.c89
-rw-r--r--src/responder/nss/nsssrv.h5
-rw-r--r--src/responder/nss/nsssrv_cmd.c183
10 files changed, 413 insertions, 5 deletions
diff --git a/src/conf_macros.m4 b/src/conf_macros.m4
index d759c48e9..8ead0b29b 100644
--- a/src/conf_macros.m4
+++ b/src/conf_macros.m4
@@ -251,3 +251,17 @@ AC_DEFUN([WITH_LIBNL],
AC_SUBST(BUILD_LIBNL)
fi
])
+
+AC_DEFUN([WITH_NOLOGIN_SHELL],
+ [ AC_ARG_WITH([nologin-shell],
+ [AC_HELP_STRING([--with-nologin-shell=PATH],
+ [The shell used to deny access to users [/sbin/nologin]]
+ )
+ ]
+ )
+ nologin_shell="/sbin/nologin"
+ if test x"$with_nologin_shell" != x; then
+ nologin_shell=$with_nologin_shell
+ fi
+ AC_DEFINE_UNQUOTED(NOLOGIN_SHELL, "$nologin_shell", [The shell used to deny access to users])
+ ])
diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
index 4975a4276..87d33f9bd 100644
--- a/src/confdb/confdb.c
+++ b/src/confdb/confdb.c
@@ -842,9 +842,23 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
goto done;
}
+ ret = get_entry_as_uint32(res->msgs[0], &domain->override_gid,
+ CONFDB_DOMAIN_OVERRIDE_GID, 0);
+ if (ret != EOK) {
+ DEBUG(0, ("Invalid value for [%s]\n", CONFDB_DOMAIN_OVERRIDE_GID));
+ goto done;
+ }
+
+ tmp = ldb_msg_find_attr_as_string(res->msgs[0],
+ CONFDB_NSS_OVERRIDE_HOMEDIR, NULL);
+ domain->override_homedir = talloc_strdup(domain, tmp);
+ if (!domain->name) {
+ ret = ENOMEM;
+ goto done;
+ }
+
*_domain = domain;
ret = EOK;
-
done:
talloc_free(tmp_ctx);
return ret;
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 7173c9fc8..c2ae9fcb7 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -41,6 +41,7 @@
#define CONFDB_DEFAULT_CONFIG_FILE SSSD_CONF_DIR"/sssd.conf"
#define SSSD_MIN_ID 1
#define SSSD_LOCAL_MINID 1000
+#define CONFDB_DEFAULT_SHELL_FALLBACK "/bin/sh"
/* Configuration options */
@@ -72,6 +73,9 @@
#define CONFDB_NSS_FILTER_USERS "filter_users"
#define CONFDB_NSS_FILTER_GROUPS "filter_groups"
#define CONFDB_NSS_PWFIELD "pwfield"
+#define CONFDB_NSS_OVERRIDE_HOMEDIR "override_homedir"
+#define CONFDB_NSS_ALLOWED_SHELL "allowed_shells"
+#define CONFDB_NSS_SHELL_FALLBACK "shell_fallback"
/* PAM */
#define CONFDB_PAM_CONF_ENTRY "config/pam"
@@ -109,6 +113,7 @@
#define CONFDB_DOMAIN_DNS_DISCOVERY_NAME "dns_discovery_domain"
#define CONFDB_DOMAIN_FAMILY_ORDER "lookup_family_order"
#define CONFDB_DOMAIN_ACCOUNT_CACHE_EXPIRATION "account_cache_expiration"
+#define CONFDB_DOMAIN_OVERRIDE_GID "override_gid"
/* Local Provider */
#define CONFDB_LOCAL_DEFAULT_SHELL "default_shell"
@@ -143,6 +148,9 @@ struct sss_domain_info {
bool cache_credentials;
bool legacy_passwords;
+ gid_t override_gid;
+ const char *override_homedir;
+
uint32_t entry_cache_timeout;
struct sss_domain_info *next;
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 6026bf4ff..fed19f079 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -58,6 +58,9 @@ option_strings = {
'filter_groups' : _('Groups that SSSD should explicitly ignore'),
'filter_users_in_groups' : _('Should filtered users appear in groups'),
'pwfield' : _('The value of the password field the NSS provider should return'),
+ 'override_homedir' : _('Override homedir value from the identity provider with this value'),
+ 'allowed_shells' : _('The list of shells users are allowed to log in with'),
+ 'shell_fallback' : _('If a shell stored in central directory is allowed but not available, use this fallback'),
# [pam]
'offline_credentials_expiration' : _('How long to allow cached logins between online logins (days)'),
@@ -85,6 +88,7 @@ option_strings = {
'account_cache_expiration' : _('How long to keep cached entries after last successful login (days)'),
'dns_resolver_timeout' : _('How long to wait for replies from DNS when resolving servers (seconds)'),
'dns_discovery_domain' : _('The domain part of service discovery DNS query'),
+ 'override_gid' : _('Override GID value from the identity provider with this value'),
# [provider/ipa]
'ipa_domain' : _('IPA domain'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index cad183ea0..0e5a630e0 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -479,6 +479,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
'account_cache_expiration',
'dns_resolver_timeout',
'dns_discovery_domain',
+ 'override_gid',
+ 'override_homedir',
'id_provider',
'auth_provider',
'access_provider',
@@ -808,6 +810,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
'lookup_family_order',
'dns_resolver_timeout',
'dns_discovery_domain',
+ 'override_gid',
+ 'override_homedir',
'id_provider',
'auth_provider',
'access_provider',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index e91597166..49836e457 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -27,6 +27,9 @@ filter_users = list, str, false
filter_groups = list, str, false
filter_users_in_groups = bool, None, false
pwfield = str, None, false
+override_homedir = str, None, false
+allowed_shells = list, str, false
+shell_fallback = str, None, false
[pam]
# Authentication service
@@ -63,6 +66,8 @@ filter_users = list, str, false
filter_groups = list, str, false
dns_resolver_timeout = int, None, false
dns_discovery_domain = str, None, false
+override_gid = int, None, false
+override_homedir = str, None, false
# Special providers
[provider/permit]
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 6ac9de890..b4f384657 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -354,6 +354,87 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>override_homedir (string)</term>
+ <listitem>
+ <para>
+ Override the user's home directory. You
+ can either provide an absolute value or a
+ template. In the template, the following
+ sequences are substituted:
+ <variablelist>
+ <varlistentry>
+ <term>%u</term>
+ <listitem><para>login name</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>%U</term>
+ <listitem><para>UID number</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>%d</term>
+ <listitem><para>domain name</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>%f</term>
+ <listitem><para>fully qualified user name (user@domain)</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>%%</term>
+ <listitem><para>a literal '%'</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ <para>
+ This option can also be set per-domain.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>allowed_shells (string)</term>
+ <listitem>
+ <para>
+ Restrict user shell to one of the listed values. The order of evaluation is:
+ </para>
+ <para>
+ 1. If the shell is present in
+ <quote>/etc/shells</quote>, it is used.
+ </para>
+ <para>
+ 2. If the shell is in the allowed_shells list but
+ not in <quote>/etc/shells</quote>, use the
+ value of the shell_fallback parameter.
+ </para>
+ <para>
+ 3. If the shell is not in the allowed_shells list and
+ not in <quote>/etc/shells</quote>, a nologin shell
+ is used.
+ </para>
+ <para>
+ An empty string for shell is passed as-is to libc.
+ </para>
+ <para>
+ The <quote>/etc/shells</quote> is only read on SSSD start up, which means that
+ a restart of the SSSD is required in case a new shell is installed.
+ </para>
+ <para>
+ Default: Not set. The user shell is automatically used.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>shell_fallback (string)</term>
+ <listitem>
+ <para>
+ The default shell to use if an allowed shell is not
+ installed on the machine.
+ </para>
+ <para>
+ Default: /bin/sh
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect2>
<refsect2 id='PAM'>
@@ -807,6 +888,15 @@
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>override_gid (integer)</term>
+ <listitem>
+ <para>
+ Override the primary GID value with the one specified.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
index dfb0312e8..dde2e95ef 100644
--- a/src/responder/nss/nsssrv.c
+++ b/src/responder/nss/nsssrv.c
@@ -48,6 +48,9 @@
#define DEFAULT_PWFIELD "*"
+#define SHELL_REALLOC_INCREMENT 5
+#define SHELL_REALLOC_MAX 50
+
struct sbus_method monitor_nss_methods[] = {
{ MON_CLI_METHOD_PING, monitor_common_pong },
{ MON_CLI_METHOD_RES_INIT, monitor_common_res_init },
@@ -63,6 +66,71 @@ struct sbus_interface monitor_nss_interface = {
NULL
};
+static errno_t nss_get_etc_shells(TALLOC_CTX *mem_ctx, char ***_shells)
+{
+ int i = 0;
+ char *sh;
+ char **shells = NULL;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ int size;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ shells = talloc_array(tmp_ctx, char *, SHELL_REALLOC_INCREMENT);
+ if (!shells) {
+ ret = ENOMEM;
+ goto done;
+ }
+ size = SHELL_REALLOC_INCREMENT;
+
+ setusershell();
+ while ((sh = getusershell())) {
+ shells[i] = talloc_strdup(shells, sh);
+ if (!shells[i]) {
+ endusershell();
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(6, ("Found shell %s in /etc/shells\n", shells[i]));
+ i++;
+
+ if (i == size) {
+ size += SHELL_REALLOC_INCREMENT;
+ if (size > SHELL_REALLOC_MAX) {
+ DEBUG(0, ("Reached maximum number of shells [%d]. "
+ "Users may be denied access. "
+ "Please check /etc/shells for sanity\n",
+ SHELL_REALLOC_MAX));
+ break;
+ }
+ shells = talloc_realloc(NULL, shells, char *,
+ size);
+ if (!shells) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+ endusershell();
+
+ if (i + 1 < size) {
+ shells = talloc_realloc(NULL, shells, char *, i + 1);
+ if (!shells) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ shells[i] = NULL;
+
+ *_shells = talloc_move(mem_ctx, &shells);
+ ret = EOK;
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
static int nss_get_config(struct nss_ctx *nctx,
struct resp_ctx *rctx,
struct confdb_ctx *cdb)
@@ -94,7 +162,7 @@ static int nss_get_config(struct nss_ctx *nctx,
if (ret != EOK) goto done;
if (nctx->cache_refresh_percent < 0 ||
nctx->cache_refresh_percent > 99) {
- DEBUG(0,("Configuration error: entry_cache_nowait_percentage is"
+ DEBUG(0,("Configuration error: entry_cache_nowait_percentage is "
"invalid. Disabling feature.\n"));
nctx->cache_refresh_percent = 0;
}
@@ -110,6 +178,25 @@ static int nss_get_config(struct nss_ctx *nctx,
&nctx->pwfield);
if (ret != EOK) goto done;
+ ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_OVERRIDE_HOMEDIR, NULL,
+ &nctx->override_homedir);
+ if (ret != EOK) goto done;
+
+ ret = confdb_get_string_as_list(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_ALLOWED_SHELL,
+ &nctx->allowed_shells);
+ if (ret != EOK && ret != ENOENT) goto done;
+
+ ret = nss_get_etc_shells(nctx, &nctx->etc_shells);
+ if (ret != EOK) goto done;
+
+ ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_SHELL_FALLBACK,
+ CONFDB_DEFAULT_SHELL_FALLBACK,
+ &nctx->shell_fallback);
+ if (ret != EOK) goto done;
+
ret = 0;
done:
talloc_free(tmpctx);
diff --git a/src/responder/nss/nsssrv.h b/src/responder/nss/nsssrv.h
index 062d937fc..f9aff5669 100644
--- a/src/responder/nss/nsssrv.h
+++ b/src/responder/nss/nsssrv.h
@@ -57,6 +57,11 @@ struct nss_ctx {
bool filter_users_in_groups;
char *pwfield;
+
+ char *override_homedir;
+ char **allowed_shells;
+ char **etc_shells;
+ char *shell_fallback;
};
struct nss_packet;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 2f9186064..74c56a311 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -169,6 +169,178 @@ struct setent_ctx {
/****************************************************************************
* PASSWD db related functions
***************************************************************************/
+char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
+ const char *username, uint32_t uid,
+ const char *domain)
+{
+ char *copy;
+ char *p;
+ char *n;
+ char *result = NULL;
+ char *res = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ if (template == NULL) {
+ DEBUG(1, ("Missing template.\n"));
+ return NULL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return NULL;
+
+ copy = talloc_strdup(tmp_ctx, template);
+ if (copy == NULL) {
+ DEBUG(1, ("talloc_strdup failed.\n"));
+ goto done;
+ }
+
+ result = talloc_strdup(tmp_ctx, "");
+ if (result == NULL) {
+ DEBUG(1, ("talloc_strdup failed.\n"));
+ goto done;
+ }
+
+ p = copy;
+ while ( (n = strchr(p, '%')) != NULL) {
+ *n = '\0';
+ n++;
+ if ( *n == '\0' ) {
+ DEBUG(1, ("format error, single %% at the end of the template.\n"));
+ goto done;
+ }
+ switch( *n ) {
+ case 'u':
+ if (username == NULL) {
+ DEBUG(1, ("Cannot expand user name template "
+ "because user name is empty.\n"));
+ goto done;
+ }
+ result = talloc_asprintf_append(result, "%s%s", p,
+ username);
+ break;
+
+ case 'U':
+ if (uid == 0) {
+ DEBUG(1, ("Cannot expand uid template "
+ "because uid is invalid.\n"));
+ goto done;
+ }
+ result = talloc_asprintf_append(result, "%s%d", p,
+ uid);
+ break;
+
+ case 'd':
+ if (domain == NULL) {
+ DEBUG(1, ("Cannot expand domain name template "
+ "because domain name is empty.\n"));
+ goto done;
+ }
+ result = talloc_asprintf_append(result, "%s%s", p,
+ domain);
+ break;
+
+ case 'f':
+ if (domain == NULL || username == NULL) {
+ DEBUG(1, ("Cannot expand fully qualified name template "
+ "because domain or user name is empty.\n"));
+ goto done;
+ }
+ result = talloc_asprintf_append(result, "%s%s@%s", p,
+ username, domain);
+ break;
+
+ case '%':
+ result = talloc_asprintf_append(result, "%s%%", p);
+ break;
+
+ default:
+ DEBUG(1, ("format error, unknown template [%%%c].\n", *n));
+ goto done;
+ }
+
+ if (result == NULL) {
+ DEBUG(1, ("talloc_asprintf_append failed.\n"));
+ goto done;
+ }
+
+ p = n + 1;
+ }
+
+ result = talloc_asprintf_append(result, "%s", p);
+ if (result == NULL) {
+ DEBUG(1, ("talloc_asprintf_append failed.\n"));
+ goto done;
+ }
+
+ res = talloc_move(mem_ctx, &result);
+done:
+ talloc_zfree(tmp_ctx);
+ return res;
+}
+
+static gid_t get_gid_override(struct ldb_message *msg,
+ struct sss_domain_info *dom)
+{
+ return dom->override_gid ?
+ dom->override_gid :
+ ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
+}
+
+static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct nss_ctx *nctx,
+ struct sss_domain_info *dom,
+ const char *name,
+ uint32_t uid)
+{
+ if (dom->override_homedir) {
+ return expand_homedir_template(mem_ctx, dom->override_homedir,
+ name, uid, dom->name);
+ } else if (nctx->override_homedir) {
+ return expand_homedir_template(mem_ctx, nctx->override_homedir,
+ name, uid, dom->name);
+ }
+
+ return talloc_strdup(mem_ctx,
+ ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL));
+}
+
+static const char *get_shell_override(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct nss_ctx *nctx)
+{
+ const char *user_shell;
+ int i;
+
+ user_shell = ldb_msg_find_attr_as_string(msg, SYSDB_SHELL, NULL);
+ if (!user_shell) return NULL;
+ if (!nctx->allowed_shells) return talloc_strdup(mem_ctx, user_shell);
+
+ for (i=0; nctx->etc_shells[i]; i++) {
+ if (strcmp(user_shell, nctx->etc_shells[i]) == 0) {
+ DEBUG(9, ("Shell %s found in /etc/shells\n",
+ nctx->etc_shells[i]));
+ break;
+ }
+ }
+
+ if (nctx->etc_shells[i]) {
+ DEBUG(9, ("Using original shell '%s'\n", user_shell));
+ return talloc_strdup(mem_ctx, user_shell);
+ }
+
+ for (i=0; nctx->allowed_shells[i]; i++) {
+ if (strcmp(nctx->allowed_shells[i], user_shell) == 0) {
+ DEBUG(5, ("The shell '%s' is allowed but does not exist. "
+ "Using fallback\n", user_shell));
+ return talloc_strdup(mem_ctx, nctx->shell_fallback);
+ }
+ }
+
+ DEBUG(5, ("The shell '%s' is not allowed and does not exist.\n",
+ user_shell));
+ return talloc_strdup(mem_ctx, NOLOGIN_SHELL);
+}
static int fill_pwent(struct sss_packet *packet,
struct sss_domain_info *dom,
@@ -195,6 +367,7 @@ static int fill_pwent(struct sss_packet *packet,
const char *namefmt = nctx->rctx->names->fq_fmt;
bool packet_initialized = false;
int ncret;
+ TALLOC_CTX *tmp_ctx = NULL;
if (add_domain) dom_len = strlen(domain);
@@ -202,11 +375,14 @@ static int fill_pwent(struct sss_packet *packet,
num = 0;
for (i = 0; i < *count; i++) {
+ talloc_zfree(tmp_ctx);
+ tmp_ctx = talloc_new(NULL);
+
msg = msgs[i];
name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
- gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
+ gid = get_gid_override(msg, dom);
if (!name || !uid || !gid) {
DEBUG(2, ("Incomplete or fake user object for %s[%llu]! Skipping\n",
@@ -233,8 +409,8 @@ static int fill_pwent(struct sss_packet *packet,
}
gecos = ldb_msg_find_attr_as_string(msg, SYSDB_GECOS, NULL);
- homedir = ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL);
- shell = ldb_msg_find_attr_as_string(msg, SYSDB_SHELL, NULL);
+ homedir = get_homedir_override(tmp_ctx, msg, nctx, dom, name, uid);
+ shell = get_shell_override(tmp_ctx, msg, nctx);
if (!gecos) gecos = "";
if (!homedir) homedir = "/";
@@ -298,6 +474,7 @@ static int fill_pwent(struct sss_packet *packet,
num++;
}
+ talloc_zfree(tmp_ctx);
done:
*count = i;