diff --git a/src/config/SSSDConfig/sssdoptions.py b/src/config/SSSDConfig/sssdoptions.py
index f3234781c88..163b7b2b3f8 100644
--- a/src/config/SSSDConfig/sssdoptions.py
+++ b/src/config/SSSDConfig/sssdoptions.py
@@ -466,6 +466,8 @@ def __init__(self):
'ldap_group_nesting_level': _('Maximum nesting level SSSD will follow'),
'ldap_group_search_filter': _('Filter for group lookups'),
'ldap_group_search_scope': _('Scope of group lookups'),
+ 'ldap_group_absent_gid_local': _('Use gid from /etc/group if group has no LDAP gid attribute'),
+ 'ldap_group_negative_gid_local': _('Use gid from /etc/group if LDAP gid is negative'),
'ldap_netgroup_search_base': _('Base DN for netgroup lookups'),
'ldap_netgroup_object_class': _('Objectclass for netgroups'),
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index d37ca3ae21c..989ba6f5c28 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -635,12 +635,14 @@ option = ldap_enumeration_refresh_timeout
option = ldap_enumeration_refresh_offset
option = ldap_enumeration_search_timeout
option = ldap_force_upper_case_realm
+option = ldap_group_absent_gid_local
option = ldap_group_entry_usn
option = ldap_group_external_member
option = ldap_group_gid_number
option = ldap_group_member
option = ldap_group_modify_timestamp
option = ldap_group_name
+option = ldap_group_negative_gid_local
option = ldap_group_nesting_level
option = ldap_group_object_class
option = ldap_group_objectsid
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index 430171632ce..7f35b6d7999 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -1006,6 +1006,56 @@
+
+ ldap_group_negative_gid_local
+
+
+ Enables merging LDAP groups with negative gid values
+ with local groups of the same name, picking up the
+ local gid. This is intended for assigning system
+ groups to users, e.g. the audio or bluetooth groups.
+ Any negative value is accepted in order to allow
+ the LDAP server to enforce uniqueness constraints on
+ gids for other groups. This option is preferable
+ over the one below (gid attribute entirely absent)
+ since it has a lower risk of unintended side effects.
+
+
+ Note this specifically checks local groups in
+ /etc/group; other sources of group IDs are not
+ considered. It must be combined with
+ "files [SUCCESS=merge] sss" in /etc/nssswitch.conf.
+
+
+ Default: False
+
+
+
+
+
+ ldap_group_absent_gid_local
+
+
+ Enables merging LDAP groups without gid attribute
+ with local groups of the same name, picking up the
+ local gid. This is intended for assigning system
+ groups to users, e.g. the audio or bluetooth groups.
+ Note that this may result in mapping some LDAP groups
+ that were not intended as POSIX groups, if they have
+ the same name.
+
+
+ Note this specifically checks local groups in
+ /etc/group; other sources of group IDs are not
+ considered. It must be combined with
+ "files [SUCCESS=merge] sss" in /etc/nssswitch.conf.
+
+
+ Default: False
+
+
+
+
ldap_sasl_mech (string)
diff --git a/src/providers/ldap/ldap_opts.c b/src/providers/ldap/ldap_opts.c
index c6ea741e4f0..88cb0eef578 100644
--- a/src/providers/ldap/ldap_opts.c
+++ b/src/providers/ldap/ldap_opts.c
@@ -48,6 +48,8 @@ struct dp_option default_basic_opts[] = {
{ "ldap_group_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ldap_group_search_scope", DP_OPT_STRING, { "sub" }, NULL_STRING },
{ "ldap_group_search_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ldap_group_absent_gid_local", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+ { "ldap_group_negative_gid_local", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
{ "ldap_host_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ldap_service_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ldap_sudo_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index aaed7d0fd74..4307e69f846 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -155,6 +155,8 @@ enum sdap_basic_opt {
SDAP_GROUP_SEARCH_BASE,
SDAP_GROUP_SEARCH_SCOPE,
SDAP_GROUP_SEARCH_FILTER,
+ SDAP_GROUP_ABSENT_GID_LOCAL,
+ SDAP_GROUP_NEGATIVE_GID_LOCAL,
SDAP_HOST_SEARCH_BASE,
SDAP_SERVICE_SEARCH_BASE,
SDAP_SUDO_SEARCH_BASE,
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index 6d0c7e49907..dfd51aca6ad 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -629,6 +629,10 @@ static int sdap_save_group(TALLOC_CTX *memctx,
group_name, dom->name);
ret = EINVAL;
goto done;
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "gid provided for [%s] in domain [%s] is %d.\n",
+ group_name, dom->name, (int)gid);
}
}
}
@@ -1893,6 +1897,84 @@ static void sdap_search_group_copy_batch(struct sdap_get_groups_state *state,
struct sysdb_attrs **groups,
size_t count);
+static void sdap_group_apply_remap(struct sysdb_attrs *attrs,
+ struct sdap_options *opts) {
+ int ret;
+ const char *group_name = NULL;
+ int32_t gid = -1;
+ char buf[2048];
+ char *grname, *p;
+ struct group gbuf, *gr;
+
+ ret = sysdb_attrs_get_string(
+ attrs, opts->group_map[SDAP_AT_GROUP_NAME].sys_name, &group_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "GID remap: group has no group name?\n");
+ return;
+ }
+
+ ret = sysdb_attrs_get_int32_t(
+ attrs, opts->group_map[SDAP_AT_GROUP_GID].sys_name, &gid);
+ switch (ret) {
+ case EOK:
+ if (gid >= 0)
+ return;
+ if (!dp_opt_get_bool(opts->basic, SDAP_GROUP_NEGATIVE_GID_LOCAL))
+ return;
+ break;
+
+ case ENOENT:
+ if (!dp_opt_get_bool(opts->basic, SDAP_GROUP_ABSENT_GID_LOCAL))
+ return;
+ break;
+
+ default:
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "GID remap: checking group %s\n", group_name);
+
+ FILE *etc_group = fopen("/etc/group", "r");
+ if (!etc_group) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "failed to open /etc/group: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ grname = strdup(group_name);
+ p = strchr(grname, '@');
+ if (p)
+ *p = '\0';
+
+ while (!fgetgrent_r(etc_group, &gbuf, buf, sizeof(buf), &gr)) {
+ if (strcmp(gr->gr_name, grname))
+ continue;
+
+ ret = sysdb_attrs_replace_name(
+ attrs, opts->group_map[SDAP_AT_GROUP_GID].sys_name, "origGid");
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "failed to rename gid attribute on %s\n",
+ group_name);
+ break;
+ }
+
+ ret = sysdb_attrs_add_uint32(
+ attrs, opts->group_map[SDAP_AT_GROUP_GID].sys_name, gr->gr_gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "failed to add remapped gid attribute on %s\n",
+ group_name);
+ break;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "GID remap: group %s mapped to local gid %d\n",
+ group_name, (int)gr->gr_gid);
+ break;
+ }
+
+ free(grname);
+ fclose(etc_group);
+}
+
static void sdap_get_groups_process(struct tevent_req *subreq)
{
struct tevent_req *req =
@@ -1936,6 +2018,9 @@ static void sdap_get_groups_process(struct tevent_req *subreq)
return;
}
+ for (i = 0; i < count; i++)
+ sdap_group_apply_remap(groups[i], state->opts);
+
sdap_search_group_copy_batch(state, groups, count);
}