From f546088226872f24722bdd94388816792bd5891a Mon Sep 17 00:00:00 2001 From: Alexey Tikhonov Date: Sat, 1 May 2021 22:56:15 +0200 Subject: [PATCH] Basics of 'subid ranges' support for IPA provider. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :feature: Basic support of user's 'subuid and subgid ranges' for IPA provider and corresponding plugin for shadow-utils were introduced. Limitations: - single subid interval pair (subuid+subgid) per user - idviews aren't supported - only forward lookup (user -> subid ranges) Take a note, this is MVP of experimental feature. Significant changes might be required later, after initial feedback. Corresponding support in shadow-utils was merged upstream, but since there is no upstream release available yet, SSSD feature isn't built by default. Build can be enabled with `--with-subid` configure option. Plugin's install path can be configured with `--with-subid-lib-path=` ("${libdir}" by default) For additional details about support in shadow-utils please see discussion in https://github.com/shadow-maint/shadow/issues/154 and in related PRs. :config: New IPA provider's option `ipa_subid_ranges_search_base` allows configuration of search base for user's subid ranges. Default: `cn=subids,%basedn` Resolves: https://github.com/SSSD/sssd/issues/5197 Reviewed-by: Iker Pedrosa Reviewed-by: Pavel Březina --- Makefile.am | 34 +++ configure.ac | 2 + src/conf_macros.m4 | 30 +++ src/config/SSSDConfig/sssdoptions.py | 1 + src/config/cfg_rules.ini | 1 + src/config/etc/sssd.api.d/sssd-ipa.conf | 1 + src/db/sysdb.h | 6 + src/db/sysdb_subid.c | 163 +++++++++++ src/db/sysdb_subid.h | 39 +++ src/man/sssd-ipa.5.xml | 14 + src/providers/data_provider_req.c | 2 + src/providers/data_provider_req.h | 1 + src/providers/ipa/ipa_common.c | 38 +++ src/providers/ipa/ipa_common.h | 1 + src/providers/ipa/ipa_id.c | 17 ++ src/providers/ipa/ipa_opts.c | 12 + src/providers/ipa/ipa_opts.h | 2 + src/providers/ldap/ldap_common.h | 14 + src/providers/ldap/ldap_id.c | 31 +++ src/providers/ldap/ldap_id_subid.c | 255 ++++++++++++++++++ src/providers/ldap/sdap.h | 19 ++ src/responder/common/cache_req/cache_req.c | 4 + src/responder/common/cache_req/cache_req.h | 4 + .../common/cache_req/cache_req_data.c | 3 + .../common/cache_req/cache_req_plugin.h | 3 + .../plugins/cache_req_subid_ranges_by_name.c | 143 ++++++++++ src/responder/common/responder.h | 1 + src/responder/common/responder_dp.c | 4 + src/responder/nss/nss_cmd.c | 20 ++ src/responder/nss/nss_protocol.h | 8 + src/responder/nss/nss_protocol_subid.c | 60 +++++ src/sss_client/common.c | 2 +- src/sss_client/sss_cli.h | 11 + src/sss_client/subid/sss_subid.c | 209 ++++++++++++++ src/sss_client/subid/sss_subid.exports | 12 + src/systemtap/sssd_functions.stp | 2 +- src/tests/cwrap/Makefile.am | 3 + src/tests/dlopen-tests.c | 3 + src/util/sss_cli_cmd.c | 5 + src/util/util_errors.c | 1 + src/util/util_errors.h | 1 + 41 files changed, 1180 insertions(+), 2 deletions(-) create mode 100644 src/db/sysdb_subid.c create mode 100644 src/db/sysdb_subid.h create mode 100644 src/providers/ldap/ldap_id_subid.c create mode 100644 src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c create mode 100644 src/responder/nss/nss_protocol_subid.c create mode 100644 src/sss_client/subid/sss_subid.c create mode 100644 src/sss_client/subid/sss_subid.exports diff --git a/Makefile.am b/Makefile.am index 1dd06d74c..577935e7e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -65,6 +65,7 @@ localedir = @localedir@ nsslibdir = @nsslibdir@ pamlibdir = @pammoddir@ autofslibdir = @appmodpath@ +subidlibdir = @subidlibpath@ nfslibdir = @nfsidmaplibdir@ dbpath = @dbpath@ @@ -596,6 +597,9 @@ SSSD_CACHE_REQ_OBJ = \ src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c \ src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c \ $(NULL) +if BUILD_SUBID + SSSD_CACHE_REQ_OBJ += src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c +endif SSSD_RESPONDER_IFACE_OBJ = \ src/responder/common/responder_iface.c \ @@ -810,6 +814,7 @@ dist_noinst_HEADERS = \ src/db/sysdb_private.h \ src/db/sysdb_services.h \ src/db/sysdb_ssh.h \ + src/db/sysdb_subid.h \ src/db/sysdb_domain_resolution_order.h \ src/db/sysdb_computer.h \ src/db/sysdb_iphosts.h \ @@ -1247,6 +1252,7 @@ libsss_util_la_SOURCES = \ src/db/sysdb_ipnetworks.c \ src/util/sss_pam_data.c \ src/db/sysdb_computer.c \ + src/db/sysdb_subid.c \ src/util/util.c \ src/util/util_ext.c \ src/util/util_preauth.c \ @@ -1558,6 +1564,9 @@ sssd_nss_LDADD = \ libsss_iface.la \ libsss_sbus.la \ $(NULL) +if BUILD_SUBID + sssd_nss_SOURCES += src/responder/nss/nss_protocol_subid.c +endif sssd_pam_SOURCES = \ src/responder/pam/pam_LOCAL_domain.c \ @@ -2691,6 +2700,9 @@ nss_srv_tests_LDADD = \ libsss_iface.la \ libsss_sbus.la \ $(NULL) +if BUILD_SUBID + nss_srv_tests_SOURCES += src/responder/nss/nss_protocol_subid.c +endif EXTRA_pam_srv_tests_DEPENDENCIES = \ $(ldblib_LTLIBRARIES) \ @@ -4217,6 +4229,21 @@ libsss_autofs_la_LDFLAGS = \ -Wl,--version-script,$(srcdir)/src/sss_client/autofs/sss_autofs.exports endif +if BUILD_SUBID +subidlib_LTLIBRARIES = libsubid_sss.la +libsubid_sss_la_SOURCES = \ + src/sss_client/common.c \ + src/sss_client/sss_cli.h \ + src/sss_client/subid/sss_subid.c + +libsubid_sss_la_LIBADD = \ + $(CLIENT_LIBS) +libsubid_sss_la_LDFLAGS = \ + -module \ + -avoid-version \ + -Wl,--version-script,$(srcdir)/src/sss_client/subid/sss_subid.exports +endif + dist_noinst_DATA += \ src/sss_client/sss_nss.exports \ src/sss_client/sss_pam.exports \ @@ -4231,6 +4258,10 @@ if BUILD_AUTOFS dist_noinst_DATA += src/sss_client/autofs/sss_autofs.exports endif +if BUILD_SUBID +dist_noinst_DATA += src/sss_client/subid/sss_subid.exports +endif + #################### # Plugin Libraries # #################### @@ -4315,6 +4346,9 @@ libsss_ldap_common_la_LDFLAGS = \ if BUILD_SYSTEMTAP libsss_ldap_common_la_LIBADD += stap_generated_probes.lo endif +if BUILD_SUBID +libsss_ldap_common_la_SOURCES += src/providers/ldap/ldap_id_subid.c +endif if BUILD_SSH libsss_ldap_common_la_SOURCES += src/providers/ldap/sdap_hostid.c diff --git a/configure.ac b/configure.ac index e98487cae..c14a59eef 100644 --- a/configure.ac +++ b/configure.ac @@ -161,6 +161,8 @@ WITH_APP_LIBS WITH_SUDO WITH_SUDO_LIB_PATH WITH_AUTOFS +WITH_SUBID +WITH_SUBID_LIB_PATH WITH_SSH WITH_IFP WITH_SYSLOG diff --git a/src/conf_macros.m4 b/src/conf_macros.m4 index cdffba11c..0a1e6dd8f 100644 --- a/src/conf_macros.m4 +++ b/src/conf_macros.m4 @@ -675,6 +675,36 @@ AC_DEFUN([WITH_AUTOFS], AM_CONDITIONAL([BUILD_AUTOFS], [test x"$with_autofs" = xyes]) ]) +AC_DEFUN([WITH_SUBID], + [ AC_ARG_WITH([subid], + [AC_HELP_STRING([--with-subid], + [Whether to build with subid ranges support [no]] + ) + ], + [with_subid=$withval], + with_subid=no + ) + + if test x"$with_subid" = xyes; then + AC_DEFINE(BUILD_SUBID, 1, [whether to build with SUBID ranges support]) + fi + AM_CONDITIONAL([BUILD_SUBID], [test x"$with_subid" = xyes]) + ]) + +AC_DEFUN([WITH_SUBID_LIB_PATH], + [ AC_ARG_WITH([subid-lib-path], + [AC_HELP_STRING([--with-subid-lib-path=], + [Path to the subid library] + ) + ] + ) + subidlibpath="${libdir}" + if test x"$with_subid_lib_path" != x; then + subidlibpath=$with_subid_lib_path + fi + AC_SUBST(subidlibpath) + ]) + AC_DEFUN([WITH_SSH], [ AC_ARG_WITH([ssh], [AC_HELP_STRING([--with-ssh], diff --git a/src/config/SSSDConfig/sssdoptions.py b/src/config/SSSDConfig/sssdoptions.py index c4ce2588b..39380c462 100644 --- a/src/config/SSSDConfig/sssdoptions.py +++ b/src/config/SSSDConfig/sssdoptions.py @@ -265,6 +265,7 @@ class SSSDOptions(object): 'ipa_deskprofile_request_interval': _("The amount of time in minutes between lookups of Desktop Profiles " "rules against the IPA server when the last request did not find any " "rule"), + 'ipa_subid_ranges_search_base': _("Search base for SUBID ranges"), 'ipa_host_fqdn': _('The LDAP attribute that contains FQDN of the host.'), 'ipa_host_object_class': _('The object class of a host entry in LDAP.'), 'ipa_host_search_base': _('Use the given string as search base for host objects.'), diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini index 0a3c32a97..d8190c2f4 100644 --- a/src/config/cfg_rules.ini +++ b/src/config/cfg_rules.ini @@ -538,6 +538,7 @@ option = ipa_backup_server option = ipa_deskprofile_refresh option = ipa_deskprofile_request_interval option = ipa_deskprofile_search_base +option = ipa_subid_ranges_search_base option = ipa_domain option = ipa_dyndns_iface option = ipa_dyndns_ttl diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf index 9a81389ca..02c9089fd 100644 --- a/src/config/etc/sssd.api.d/sssd-ipa.conf +++ b/src/config/etc/sssd.api.d/sssd-ipa.conf @@ -4,6 +4,7 @@ ipa_server = str, None, false ipa_backup_server = str, None, false ipa_hostname = str, None, false ipa_deskprofile_search_base = str, None, false +ipa_subid_ranges_search_base = str, None, false ipa_dyndns_update = bool, None, false ipa_dyndns_ttl = int, None, false ipa_dyndns_iface = str, None, false diff --git a/src/db/sysdb.h b/src/db/sysdb.h index a29232f48..b638f4397 100644 --- a/src/db/sysdb.h +++ b/src/db/sysdb.h @@ -143,6 +143,12 @@ #define SYSDB_SSH_PUBKEY "sshPublicKey" +#define SYSDB_SUBID_UID_COUND "subUidCount" +#define SYSDB_SUBID_GID_COUNT "subGidCount" +#define SYSDB_SUBID_UID_NUMBER "subUidNumber" +#define SYSDB_SUBID_GID_NUMBER "subGidNumber" +#define SYSDB_SUBID_OWNER "subidOwner" + #define SYSDB_AUTH_TYPE "authType" #define SYSDB_USER_CERT "userCertificate" #define SYSDB_USER_MAPPED_CERT "userMappedCertificate" diff --git a/src/db/sysdb_subid.c b/src/db/sysdb_subid.c new file mode 100644 index 000000000..519b0834c --- /dev/null +++ b/src/db/sysdb_subid.c @@ -0,0 +1,163 @@ +/* + Copyright (C) 2021 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 . +*/ + +#include + +#include "db/sysdb_private.h" +#include "db/sysdb_subid.h" + +#define SUBID_SUBDIR "subid_ranges" + + +errno_t sysdb_store_subid_range(struct sss_domain_info *domain, + const char *name, + int expiration_period, + struct sysdb_attrs *attrs) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret, sret; + bool in_transaction = false; + time_t now = time(NULL); + + DEBUG(SSSDBG_TRACE_FUNC, "Storing subid ranges for %s, expiration period = %d\n", + name, expiration_period); + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + ret = sysdb_transaction_start(domain->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n"); + goto done; + } + + in_transaction = true; + + ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCLASS, SYSDB_SUBID_RANGE_OC); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Could not set object class [%d]: %s\n", ret, strerror(ret)); + goto done; + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Could not set name attribute [%d]: %s\n", ret, strerror(ret)); + goto done; + } + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Could not set sysdb lastUpdate [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE, + expiration_period ? (now + expiration_period) : 0); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not set sysdb cache expire [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + + ret = sysdb_store_custom(domain, name, SUBID_SUBDIR, attrs); + if (ret != EOK) { + goto done; + } + + ret = sysdb_transaction_commit(domain->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n"); + goto done; + } + + in_transaction = false; + + ret = EOK; + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(domain->sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n"); + } + } + + talloc_free(tmp_ctx); + + return ret; +} + +errno_t sysdb_get_subid_ranges(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *name, + const char **attrs, + struct ldb_message **_range) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret; + const char *filter; + struct ldb_message **ranges; + size_t num_ranges; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(%s=%s))", + SYSDB_OBJECTCLASS, SYSDB_SUBID_RANGE_OC, + SYSDB_NAME, name); + if (!filter) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_search_custom(tmp_ctx, domain, filter, + SUBID_SUBDIR, attrs, + &num_ranges, &ranges); + if (ret != EOK) { + goto done; + } + + if (num_ranges > 1) { + ret = EINVAL; + DEBUG(SSSDBG_CRIT_FAILURE, + "Found more than one range with name %s\n", name); + goto done; + } + + *_range = talloc_steal(mem_ctx, ranges[0]); + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +errno_t sysdb_delete_subid_range(struct sss_domain_info *domain, + const char *name) +{ + DEBUG(SSSDBG_TRACE_FUNC, "Deleting subid ranges for %s\n", name); + return sysdb_delete_custom(domain, name, SUBID_SUBDIR); +} diff --git a/src/db/sysdb_subid.h b/src/db/sysdb_subid.h new file mode 100644 index 000000000..4b4a86334 --- /dev/null +++ b/src/db/sysdb_subid.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2021 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 . +*/ + +#ifndef _SYSDB_SUBID_H_ +#define _SYSDB_SUBID_H_ + +#include "db/sysdb.h" + +#define SYSDB_SUBID_RANGE_OC "subordinateid" + +errno_t sysdb_store_subid_range(struct sss_domain_info *domain, + const char *name, + int expiration_period, + struct sysdb_attrs *attrs); + +errno_t sysdb_get_subid_ranges(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *name, + const char **attrs, + struct ldb_message **range); + +errno_t sysdb_delete_subid_range(struct sss_domain_info *domain, + const char *name); + +#endif /* _SYSDB_SSH_H_ */ diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml index 7b630493d..2bbf67c83 100644 --- a/src/man/sssd-ipa.5.xml +++ b/src/man/sssd-ipa.5.xml @@ -356,6 +356,20 @@ + + ipa_subid_ranges_search_base (string) + + + Optional. Use the given string as search base for + subordinate ranges related objects. + + + Default: the value of + cn=subids,%basedn + + + + ipa_hbac_search_base (string) diff --git a/src/providers/data_provider_req.c b/src/providers/data_provider_req.c index e22cfd71a..b7f4d152e 100644 --- a/src/providers/data_provider_req.c +++ b/src/providers/data_provider_req.c @@ -44,6 +44,8 @@ const char *be_req2str(dbus_uint32_t req_type) return be_req_to_str(BE_REQ_HOST); case BE_REQ_IP_NETWORK: return be_req_to_str(BE_REQ_IP_NETWORK); + case BE_REQ_SUBID_RANGES: + return be_req_to_str(BE_REQ_SUBID_RANGES); case BE_REQ_BY_SECID: return be_req_to_str(BE_REQ_BY_SECID); case BE_REQ_USER_AND_GROUP: diff --git a/src/providers/data_provider_req.h b/src/providers/data_provider_req.h index 75f7f9713..4c6ab5a7e 100644 --- a/src/providers/data_provider_req.h +++ b/src/providers/data_provider_req.h @@ -35,6 +35,7 @@ #define BE_REQ_SUDO_RULES 0x0007 #define BE_REQ_HOST 0x0008 #define BE_REQ_IP_NETWORK 0x0009 +#define BE_REQ_SUBID_RANGES 0x0010 #define BE_REQ_BY_SECID 0x0011 #define BE_REQ_USER_AND_GROUP 0x0012 #define BE_REQ_BY_UUID 0x0013 diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 23b358819..1509cb1ce 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -152,6 +152,9 @@ static errno_t ipa_parse_search_base(TALLOC_CTX *mem_ctx, case IPA_DESKPROFILE_SEARCH_BASE: class_name = "IPA_DESKPROFILE"; break; + case IPA_SUBID_RANGES_SEARCH_BASE: + class_name = "IPA_SUBID_RANGES"; + break; default: DEBUG(SSSDBG_CONF_SETTINGS, "Unknown search base type: [%d]\n", class); @@ -481,6 +484,41 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, &ipa_opts->deskprofile_search_bases); if (ret != EOK) goto done; +#ifdef BUILD_SUBID + if (NULL == dp_opt_get_string(ipa_opts->basic, + IPA_SUBID_RANGES_SEARCH_BASE)) { + value = talloc_asprintf(tmpctx, "cn=subids,%s", + ipa_opts->id->sdom->search_bases[0]->basedn); + if (!value) { + ret = ENOMEM; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->basic, IPA_SUBID_RANGES_SEARCH_BASE, value); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n", + ipa_opts->basic[IPA_SUBID_RANGES_SEARCH_BASE].opt_name, + dp_opt_get_string(ipa_opts->basic, + IPA_SUBID_RANGES_SEARCH_BASE)); + } + ret = ipa_parse_search_base(ipa_opts->basic, ipa_opts->basic, + IPA_SUBID_RANGES_SEARCH_BASE, + &ipa_opts->id->sdom->subid_ranges_search_bases); + if (ret != EOK) goto done; + + ret = sdap_get_map(ipa_opts->id, + cdb, conf_path, + ipa_subid_map, + SDAP_OPTS_SUBID_RANGE, + &ipa_opts->id->subid_map); + if (ret != EOK) { + goto done; + } +#endif + value = dp_opt_get_string(ipa_opts->id->basic, SDAP_DEREF); if (value != NULL) { ret = deref_string_to_val(value, &i); diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h index 480f7b664..eb0eda8eb 100644 --- a/src/providers/ipa/ipa_common.h +++ b/src/providers/ipa/ipa_common.h @@ -68,6 +68,7 @@ enum ipa_basic_opt { IPA_DESKPROFILE_SEARCH_BASE, IPA_DESKPROFILE_REFRESH, IPA_DESKPROFILE_REQUEST_INTERVAL, + IPA_SUBID_RANGES_SEARCH_BASE, IPA_OPTS_BASIC /* opts counter */ }; diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c index 2cbe0c9c7..636e07965 100644 --- a/src/providers/ipa/ipa_id.c +++ b/src/providers/ipa/ipa_id.c @@ -728,6 +728,22 @@ static errno_t ipa_id_get_account_info_get_original_step(struct tevent_req *req, struct ipa_id_get_account_info_state); struct tevent_req *subreq; +#ifdef BUILD_SUBID + if ((ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_SUBID_RANGES) { + if (!state->ctx->opts->sdom->subid_ranges_search_bases || + !state->ctx->opts->sdom->subid_ranges_search_bases[0] || + !state->ctx->opts->sdom->subid_ranges_search_bases[0]->basedn) { + DEBUG(SSSDBG_OP_FAILURE, "subid_ranges_search_bases isn't set\n"); + return EINVAL; + } + ar->extra_value = talloc_asprintf(ar, + "%s=%s,"SYSDB_USERS_CONTAINER",%s", + state->ctx->opts->user_map[SDAP_AT_USER_NAME].name, + ar->filter_value, + state->ctx->opts->sdom->user_search_bases[0]->basedn); + } +#endif + subreq = sdap_handle_acct_req_send(state, state->ctx->be, ar, state->ipa_ctx->sdap_id_ctx, state->ipa_ctx->sdap_id_ctx->opts->sdom, @@ -769,6 +785,7 @@ static void ipa_id_get_account_info_orig_done(struct tevent_req *subreq) } if (! is_object_overridable(state->ar)) { + DEBUG(SSSDBG_FUNC_DATA, "Object not overridable, ending request\n"); state->dp_error = DP_ERR_OK; tevent_req_done(req); return; diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c index 09e360a43..dc0a2201b 100644 --- a/src/providers/ipa/ipa_opts.c +++ b/src/providers/ipa/ipa_opts.c @@ -26,6 +26,7 @@ #include "db/sysdb_autofs.h" #include "db/sysdb_services.h" #include "db/sysdb_selinux.h" +#include "db/sysdb_subid.h" #include "providers/ldap/ldap_common.h" struct dp_option ipa_basic_opts[] = { @@ -51,6 +52,7 @@ struct dp_option ipa_basic_opts[] = { { "ipa_deskprofile_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ipa_deskprofile_refresh", DP_OPT_NUMBER, { .number = 5 }, NULL_NUMBER }, { "ipa_deskprofile_request_interval", DP_OPT_NUMBER, { .number = 60 }, NULL_NUMBER }, + { "ipa_subid_ranges_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }, DP_OPTION_TERMINATOR }; @@ -250,6 +252,16 @@ struct sdap_attr_map ipa_netgroup_map[] = { SDAP_ATTR_MAP_TERMINATOR }; +struct sdap_attr_map ipa_subid_map[] = { + { "ipa_subuid_object_class", "ipasubordinateid", SYSDB_SUBID_RANGE_OC, NULL }, + { "ipa_subuid_count", "ipaSubUidCount", SYSDB_SUBID_UID_COUND, NULL }, + { "ipa_subgid_count", "ipaSubGidCount", SYSDB_SUBID_GID_COUNT, NULL }, + { "ipa_subuid_number", "ipaSubUidNumber", SYSDB_SUBID_UID_NUMBER, NULL }, + { "ipa_subgid_number", "ipaSubGidNumber", SYSDB_SUBID_GID_NUMBER, NULL }, + { "ipa_owner", "ipaOwner", SYSDB_SUBID_OWNER, NULL }, + SDAP_ATTR_MAP_TERMINATOR +}; + struct sdap_attr_map ipa_host_map[] = { { "ipa_host_object_class", "ipaHost", SYSDB_HOST_CLASS, NULL }, { "ipa_host_name", "cn", SYSDB_NAME, NULL }, diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h index 378a9922c..6f54e57a9 100644 --- a/src/providers/ipa/ipa_opts.h +++ b/src/providers/ipa/ipa_opts.h @@ -40,6 +40,8 @@ extern struct sdap_attr_map ipa_group_map[]; extern struct sdap_attr_map ipa_netgroup_map[]; +extern struct sdap_attr_map ipa_subid_map[]; + extern struct sdap_attr_map ipa_host_map[]; extern struct sdap_attr_map ipa_hostgroup_map[]; diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index 13e6d4871..c78338b5d 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -446,4 +446,18 @@ errno_t users_get_handle_no_user(TALLOC_CTX *mem_ctx, errno_t groups_get_handle_no_group(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain, int filter_type, const char *filter_value); + +#ifdef BUILD_SUBID +struct tevent_req *subid_ranges_get_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *ctx, + struct sdap_domain *sdom, + struct sdap_id_conn_ctx *conn, + const char* filter_value, + const char *extra_value); + +int subid_ranges_get_recv(struct tevent_req *req, int *dp_error_out, + int *sdap_ret); +#endif + #endif /* _LDAP_COMMON_H_ */ diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c index ebc0ab8e4..9b67773a8 100644 --- a/src/providers/ldap/ldap_id.c +++ b/src/providers/ldap/ldap_id.c @@ -1449,6 +1449,24 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx, noexist_delete); break; + case BE_REQ_SUBID_RANGES: +#ifdef BUILD_SUBID + if (!ar->extra_value) { + ret = ERR_GET_ACCT_SUBID_RANGES_NOT_SUPPORTED; + state->err = "This id_provider doesn't support subid ranges"; + goto done; + } + subreq = subid_ranges_get_send(state, be_ctx->ev, id_ctx, + sdom, conn, + ar->filter_value, + ar->extra_value); +#else + ret = ERR_GET_ACCT_SUBID_RANGES_NOT_SUPPORTED; + state->err = "Subid ranges are not supported"; + goto done; +#endif + break; + case BE_REQ_NETGROUP: if (ar->filter_type != BE_FILTER_NAME) { ret = EINVAL; @@ -1533,6 +1551,11 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx, default: /*fail*/ ret = EINVAL; state->err = "Invalid request type"; + DEBUG(SSSDBG_OP_FAILURE, + "Unexpected request type: 0x%X [%s:%s] in %s\n", + ar->entry_type, ar->filter_value, + ar->extra_value?ar->extra_value:"-", + ar->domain); goto done; } @@ -1578,6 +1601,14 @@ sdap_handle_acct_req_done(struct tevent_req *subreq) err = "Init group lookup failed"; ret = groups_by_user_recv(subreq, &state->dp_error, &state->sdap_ret); break; + case BE_REQ_SUBID_RANGES: + err = "Subid ranges lookup failed"; +#ifdef BUILD_SUBID + ret = subid_ranges_get_recv(subreq, &state->dp_error, &state->sdap_ret); +#else + ret = EINVAL; +#endif + break; case BE_REQ_NETGROUP: err = "Netgroup lookup failed"; ret = ldap_netgroup_get_recv(subreq, &state->dp_error, &state->sdap_ret); diff --git a/src/providers/ldap/ldap_id_subid.c b/src/providers/ldap/ldap_id_subid.c new file mode 100644 index 000000000..d25c6aaac --- /dev/null +++ b/src/providers/ldap/ldap_id_subid.c @@ -0,0 +1,255 @@ +/* + SSSD + + LDAP Identity Backend Module - subid ranges support + + Copyright (C) 2021 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 . +*/ + +#include + +#include "db/sysdb_subid.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap_async.h" +#include "providers/ldap/sdap_ops.h" + +static int subid_ranges_get_retry(struct tevent_req *req); +static void subid_ranges_get_connect_done(struct tevent_req *subreq); +static void subid_ranges_get_search(struct tevent_req *req); +static void subid_ranges_get_done(struct tevent_req *subreq); + + +struct subid_ranges_get_state { + struct tevent_context *ev; + struct sdap_id_ctx *ctx; + struct sdap_domain *sdom; + struct sdap_id_conn_ctx *conn; + struct sdap_id_op *op; + struct sss_domain_info *domain; + + char *filter; + char *name; + const char **attrs; + + int dp_error; + int sdap_ret; +}; + +struct tevent_req *subid_ranges_get_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *ctx, + struct sdap_domain *sdom, + struct sdap_id_conn_ctx *conn, + const char *filter_value, + const char *extra_value) +{ + struct tevent_req *req; + struct subid_ranges_get_state *state; + int ret; + + req = tevent_req_create(memctx, &state, struct subid_ranges_get_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + state->sdom = sdom; + state->conn = conn; + state->dp_error = DP_ERR_FATAL; + state->name = talloc_strdup(state, filter_value); + if (!state->name) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed\n"); + ret = ENOMEM; + goto done; + } + + state->op = sdap_id_op_create(state, state->conn->conn_cache); + if (!state->op) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n"); + ret = ENOMEM; + goto done; + } + + state->domain = sdom->dom; + + state->filter = talloc_asprintf(state, + "(&(%s=%s)(%s=%s))", + SYSDB_OBJECTCLASS, + ctx->opts->subid_map[SDAP_OC_SUBID_RANGE].name, + ctx->opts->subid_map[SDAP_AT_SUBID_RANGE_OWNER].name, + extra_value); + + ret = subid_ranges_get_retry(req); + if (ret != EOK) { + goto done; + } + + return req; + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } + return tevent_req_post(req, ev); +} + +static int subid_ranges_get_retry(struct tevent_req *req) +{ + struct subid_ranges_get_state *state = tevent_req_data(req, + struct subid_ranges_get_state); + struct tevent_req *subreq; + int ret = EOK; + + subreq = sdap_id_op_connect_send(state->op, state, &ret); + if (!subreq) { + return ret; + } + + tevent_req_set_callback(subreq, subid_ranges_get_connect_done, req); + return EOK; +} + +static void subid_ranges_get_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct subid_ranges_get_state *state = tevent_req_data(req, + struct subid_ranges_get_state); + int dp_error = DP_ERR_FATAL; + int ret; + + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + + if (ret != EOK) { + state->dp_error = dp_error; + tevent_req_error(req, ret); + return; + } + + subid_ranges_get_search(req); +} + +static void subid_ranges_get_search(struct tevent_req *req) +{ + struct subid_ranges_get_state *state = tevent_req_data(req, + struct subid_ranges_get_state); + struct tevent_req *subreq = NULL; + const char **attrs; + int ret; + + ret = build_attrs_from_map(state, state->ctx->opts->subid_map, + SDAP_OPTS_SUBID_RANGE, NULL, &attrs, NULL); + if (ret != EOK) { + tevent_req_error(req, ENOMEM); + return; + } + + subreq = sdap_search_bases_send(state, state->ev, state->ctx->opts, + sdap_id_op_handle(state->op), + state->sdom->subid_ranges_search_bases, + state->ctx->opts->subid_map, + false, /* allow_paging */ + dp_opt_get_int(state->ctx->opts->basic, + SDAP_SEARCH_TIMEOUT), + state->filter, + attrs, + NULL); + talloc_free(attrs); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, subid_ranges_get_done, req); +} + +static void subid_ranges_get_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct subid_ranges_get_state *state = tevent_req_data(req, + struct subid_ranges_get_state); + int dp_error = DP_ERR_FATAL; + int ret; + struct sysdb_attrs **results; + size_t num_results; + + ret = sdap_search_bases_recv(subreq, state, &num_results, &results); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + ret = sdap_id_op_done(state->op, ret, &dp_error); + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = subid_ranges_get_retry(req); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + return; + } + state->sdap_ret = ret; + + if (ret && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to retrieve subid ranges.\n"); + state->dp_error = dp_error; + tevent_req_error(req, ret); + return; + } + + if (num_results == 0 || !results) { + DEBUG(SSSDBG_MINOR_FAILURE, + "No such user '%s' or user doesn't have subid range\n", + state->name); + sysdb_delete_subid_range(state->domain, state->name); + } else { + if (num_results > 1) { + DEBUG(SSSDBG_OP_FAILURE, + "Multiple subid ranges, only first will be processed\n"); + } + + /* store range */ + sysdb_store_subid_range(state->domain, state->name, + state->domain->user_timeout, + results[0]); + } + + state->dp_error = DP_ERR_OK; + tevent_req_done(req); +} + +int subid_ranges_get_recv(struct tevent_req *req, int *dp_error_out, + int *sdap_ret) +{ + struct subid_ranges_get_state *state = tevent_req_data(req, + struct subid_ranges_get_state); + + if (dp_error_out) { + *dp_error_out = state->dp_error; + } + + if (sdap_ret) { + *sdap_ret = state->sdap_ret; + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index ffcbee8a5..6382bec25 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -389,6 +389,19 @@ enum sdap_ipnetwork_entry_attrs { SDAP_OPTS_IPNETWORK /* attrs counter */ }; +#ifdef BUILD_SUBID +enum sdap_subid_range_attrs { + SDAP_OC_SUBID_RANGE = 0, + SDAP_AT_SUBID_RANGE_UID_COUNT, + SDAP_AT_SUBID_RANGE_GID_COUNT, + SDAP_AT_SUBID_RANGE_UID_NUMBER, + SDAP_AT_SUBID_RANGE_GID_NUMBER, + SDAP_AT_SUBID_RANGE_OWNER, + + SDAP_OPTS_SUBID_RANGE /* attrs counter */ +}; +#endif + enum sdap_autofs_map_attrs { SDAP_OC_AUTOFS_MAP, SDAP_AT_AUTOFS_MAP_NAME, @@ -453,6 +466,9 @@ struct sdap_domain { struct sdap_search_base **iphost_search_bases; struct sdap_search_base **ipnetwork_search_bases; struct sdap_search_base **autofs_search_bases; +#ifdef BUILD_SUBID + struct sdap_search_base **subid_ranges_search_bases; +#endif struct sdap_domain *next, *prev; /* Need to modify the list from a talloc destructor */ @@ -495,6 +511,9 @@ struct sdap_options { struct sdap_attr_map *service_map; struct sdap_attr_map *iphost_map; struct sdap_attr_map *ipnetwork_map; +#ifdef BUILD_SUBID + struct sdap_attr_map *subid_map; +#endif /* ID-mapping support */ struct sdap_idmap_ctx *idmap_ctx; diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c index 889547ba7..750d655c1 100644 --- a/src/responder/common/cache_req/cache_req.c +++ b/src/responder/common/cache_req/cache_req.c @@ -45,6 +45,10 @@ cache_req_get_plugin(enum cache_req_type type) &cache_req_initgroups_by_name, &cache_req_initgroups_by_upn, +#ifdef BUILD_SUBID + &cache_req_subid_ranges_by_name, +#endif + &cache_req_object_by_sid, &cache_req_object_by_name, &cache_req_object_by_id, diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h index d5c25ccf0..055fb405b 100644 --- a/src/responder/common/cache_req/cache_req.h +++ b/src/responder/common/cache_req/cache_req.h @@ -39,6 +39,10 @@ enum cache_req_type { CACHE_REQ_INITGROUPS, CACHE_REQ_INITGROUPS_BY_UPN, +#ifdef BUILD_SUBID + CACHE_REQ_SUBID_RANGES_BY_NAME, +#endif + CACHE_REQ_OBJECT_BY_SID, CACHE_REQ_OBJECT_BY_NAME, CACHE_REQ_OBJECT_BY_ID, diff --git a/src/responder/common/cache_req/cache_req_data.c b/src/responder/common/cache_req/cache_req_data.c index e82dc8ab6..3c60eb484 100644 --- a/src/responder/common/cache_req/cache_req_data.c +++ b/src/responder/common/cache_req/cache_req_data.c @@ -90,6 +90,9 @@ cache_req_data_create(TALLOC_CTX *mem_ctx, case CACHE_REQ_GROUP_BY_FILTER: case CACHE_REQ_INITGROUPS: case CACHE_REQ_INITGROUPS_BY_UPN: +#ifdef BUILD_SUBID + case CACHE_REQ_SUBID_RANGES_BY_NAME: +#endif case CACHE_REQ_NETGROUP_BY_NAME: case CACHE_REQ_OBJECT_BY_NAME: case CACHE_REQ_AUTOFS_MAP_ENTRIES: diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h index 9e4986f93..f86a02042 100644 --- a/src/responder/common/cache_req/cache_req_plugin.h +++ b/src/responder/common/cache_req/cache_req_plugin.h @@ -302,6 +302,9 @@ extern const struct cache_req_plugin cache_req_group_by_name; extern const struct cache_req_plugin cache_req_group_by_id; extern const struct cache_req_plugin cache_req_initgroups_by_name; extern const struct cache_req_plugin cache_req_initgroups_by_upn; +#ifdef BUILD_SUBID +extern const struct cache_req_plugin cache_req_subid_ranges_by_name; +#endif extern const struct cache_req_plugin cache_req_user_by_cert; extern const struct cache_req_plugin cache_req_user_by_filter; extern const struct cache_req_plugin cache_req_group_by_filter; diff --git a/src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c b/src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c new file mode 100644 index 000000000..54852711f --- /dev/null +++ b/src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c @@ -0,0 +1,143 @@ +/* + Copyright (C) 2021 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 . +*/ + +#include +#include + +#include "db/sysdb.h" +#include "db/sysdb_subid.h" +#include "util/util.h" +#include "providers/data_provider.h" +#include "responder/common/cache_req/cache_req_plugin.h" + +static errno_t +cache_req_subid_ranges_by_name_prepare_domain_data(struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + errno_t ret; + + if (cr->data->name.name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n"); + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + name = sss_get_cased_name(tmp_ctx, cr->data->name.name, + domain->case_sensitive); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_zfree(data->name.lookup); + data->name.lookup = talloc_steal(data, name); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char * +cache_req_subid_ranges_by_name_create_debug_name(TALLOC_CTX *mem_ctx, + struct cache_req_data *data, + struct sss_domain_info *domain) +{ + return talloc_strdup(mem_ctx, data->name.lookup); +} + +static errno_t +cache_req_subid_ranges_by_name_lookup(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result **_result) +{ + struct ldb_result *result; + struct ldb_message *msg; + errno_t ret; + + ret = sysdb_get_subid_ranges(mem_ctx, domain, data->name.name, + data->attrs, &msg); + if (ret != EOK) { + return ret; + } + + result = cache_req_create_ldb_result_from_msg(mem_ctx, msg); + if (result == NULL) { + return ENOMEM; + } + + *_result = result; + + return EOK; +} + +static struct tevent_req * +cache_req_subid_ranges_by_name_dp_send(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct cache_req_data *data, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + /* Views aren't yet supported */ + return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, + SSS_DP_SUBID_RANGES, cr->data->name.lookup, 0, NULL); +} + +const struct cache_req_plugin cache_req_subid_ranges_by_name = { + .name = "SubID ranges by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, + .require_enumeration = false, + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, + .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_subid_ranges_by_name_prepare_domain_data, + .create_debug_name_fn = cache_req_subid_ranges_by_name_create_debug_name, + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .ncache_filter_fn = NULL, + .lookup_fn = cache_req_subid_ranges_by_name_lookup, + .dp_send_fn = cache_req_subid_ranges_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv, + .dp_get_domain_check_fn = NULL, + .dp_get_domain_send_fn = NULL, + .dp_get_domain_recv_fn = NULL, +}; diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h index a5d6359a0..fbe46f335 100644 --- a/src/responder/common/responder.h +++ b/src/responder/common/responder.h @@ -273,6 +273,7 @@ enum sss_dp_acct_type { SSS_DP_USER = 1, SSS_DP_GROUP, SSS_DP_INITGROUPS, + SSS_DP_SUBID_RANGES, SSS_DP_NETGR, SSS_DP_SERVICES, SSS_DP_SECID, diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c index 8076e1e43..1b016dba1 100644 --- a/src/responder/common/responder_dp.c +++ b/src/responder/common/responder_dp.c @@ -70,6 +70,7 @@ sss_dp_account_files_params(struct sss_domain_info *dom, *_opt_name_out = opt_name_in; return EAGAIN; /* These are not handled by the files provider, just fall back */ + case SSS_DP_SUBID_RANGES: case SSS_DP_NETGR: case SSS_DP_SERVICES: case SSS_DP_SECID: @@ -109,6 +110,9 @@ sss_dp_get_account_filter(TALLOC_CTX *mem_ctx, case SSS_DP_INITGROUPS: entry_type = BE_REQ_INITGROUPS; break; + case SSS_DP_SUBID_RANGES: + entry_type = BE_REQ_SUBID_RANGES; + break; case SSS_DP_NETGR: entry_type = BE_REQ_NETGROUP; break; diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c index a487e1c24..ef59daea8 100644 --- a/src/responder/nss/nss_cmd.c +++ b/src/responder/nss/nss_cmd.c @@ -1041,6 +1041,25 @@ static errno_t nss_cmd_initgroups_ex(struct cli_ctx *cli_ctx) SSS_MC_INITGROUPS, nss_protocol_fill_initgr); } +static errno_t nss_cmd_subid_ranges(struct cli_ctx *cli_ctx) +{ +#ifdef BUILD_SUBID + const char *attrs[] = + { + SYSDB_SUBID_UID_COUND, + SYSDB_SUBID_GID_COUNT, + SYSDB_SUBID_UID_NUMBER, + SYSDB_SUBID_GID_NUMBER, + NULL + }; + + return nss_getby_name(cli_ctx, false, CACHE_REQ_SUBID_RANGES_BY_NAME, attrs, + SSS_MC_NONE, nss_protocol_fill_subid_ranges); +#else + return ENOTSUP; +#endif +} + static errno_t nss_cmd_setnetgrent(struct cli_ctx *cli_ctx) { struct nss_state_ctx *state_ctx; @@ -1332,6 +1351,7 @@ struct sss_cmd_table *get_nss_cmds(void) { SSS_NSS_GETGRENT, nss_cmd_getgrent }, { SSS_NSS_ENDGRENT, nss_cmd_endgrent }, { SSS_NSS_INITGR, nss_cmd_initgroups }, + { SSS_NSS_GET_SUBID_RANGES, nss_cmd_subid_ranges }, { SSS_NSS_SETNETGRENT, nss_cmd_setnetgrent }, /* { SSS_NSS_GETNETGRENT, "not needed" }, */ /* { SSS_NSS_ENDNETGRENT, "not needed" }, */ diff --git a/src/responder/nss/nss_protocol.h b/src/responder/nss/nss_protocol.h index 364f19c83..949b6291a 100644 --- a/src/responder/nss/nss_protocol.h +++ b/src/responder/nss/nss_protocol.h @@ -147,6 +147,14 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx, struct sss_packet *packet, struct cache_req_result *result); +#ifdef BUILD_SUBID +errno_t +nss_protocol_fill_subid_ranges(struct nss_ctx *nss_ctx, + struct nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, + struct cache_req_result *result); +#endif + errno_t nss_protocol_fill_netgrent(struct nss_ctx *nss_ctx, struct nss_cmd_ctx *cmd_ctx, diff --git a/src/responder/nss/nss_protocol_subid.c b/src/responder/nss/nss_protocol_subid.c new file mode 100644 index 000000000..29a957762 --- /dev/null +++ b/src/responder/nss/nss_protocol_subid.c @@ -0,0 +1,60 @@ +/* + Copyright (C) 2021 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 . +*/ + +#include "responder/nss/nss_protocol.h" + +errno_t +nss_protocol_fill_subid_ranges(struct nss_ctx *nss_ctx, + struct nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, + struct cache_req_result *result) +{ + static const uint32_t one = 1; + errno_t ret; + uint8_t *body; + size_t body_len; + size_t rp = 0; + uint32_t gid, uid, gidCount, uidCount; + + if (!result->count || !result->msgs) { + return ENOENT; + } + + uid = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_SUBID_UID_NUMBER, 0); + uidCount = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_SUBID_UID_COUND, 0); + gid = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_SUBID_GID_NUMBER, 0); + gidCount = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_SUBID_GID_COUNT, 0); + if (!uid || !gid || !gidCount || !uidCount) { + return ENOENT; + } + + /* only single uid & gid range is expected currently */ + ret = sss_packet_grow(packet, (2 + 2*2) * sizeof(uint32_t)); + if (ret != EOK) { + return ret; + } + + sss_packet_get_body(packet, &body, &body_len); + SAFEALIGN_COPY_UINT32(&body[rp], &one, &rp); + SAFEALIGN_COPY_UINT32(&body[rp], &one, &rp); + SAFEALIGN_COPY_UINT32(&body[rp], &uid, &rp); + SAFEALIGN_COPY_UINT32(&body[rp], &uidCount, &rp); + SAFEALIGN_COPY_UINT32(&body[rp], &gid, &rp); + SAFEALIGN_COPY_UINT32(&body[rp], &gidCount, &rp); + + return EOK; +} diff --git a/src/sss_client/common.c b/src/sss_client/common.c index d29332939..9416e21d1 100644 --- a/src/sss_client/common.c +++ b/src/sss_client/common.c @@ -1008,7 +1008,7 @@ void sss_pam_close_fd(void) sss_pam_unlock(); } -static enum sss_status +enum sss_status sss_cli_make_request_with_checks(enum sss_cli_command cmd, struct sss_cli_req_data *rd, int timeout, diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 2c3c71bc4..1347ce71d 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -284,6 +284,10 @@ SSS_NSS_GETSIDBYGID = 0x0119, /**< Takes an unsigned 32bit integer (POSIX GID) and return the zero terminated string representation of the SID of the object with the given UID. */ + +/* subid */ + SSS_NSS_GET_SUBID_RANGES = 0x0130, /**< Requests both subuid and subgid ranges + defined for a user. */ }; /** @@ -631,6 +635,13 @@ enum sss_cli_error_codes { const char *ssscli_err2string(int err); +enum sss_status sss_cli_make_request_with_checks(enum sss_cli_command cmd, + struct sss_cli_req_data *rd, + int timeout, + uint8_t **repbuf, size_t *replen, + int *errnop, + const char *socket_name); + enum nss_status sss_nss_make_request(enum sss_cli_command cmd, struct sss_cli_req_data *rd, uint8_t **repbuf, size_t *replen, diff --git a/src/sss_client/subid/sss_subid.c b/src/sss_client/subid/sss_subid.c new file mode 100644 index 000000000..ae74ece3c --- /dev/null +++ b/src/sss_client/subid/sss_subid.c @@ -0,0 +1,209 @@ +/* + Copyright (C) 2021 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 . +*/ + +#include +#include +#include +#include "sss_cli.h" + +/* This shadow-utils plugin contains partial SSSD implementation + * of `subid_nss_ops` API as described in + * https://github.com/shadow-maint/shadow/blob/d4b6d1549b2af48ce3cb6ff78d9892095fb8fdd9/lib/prototypes.h#L271 + */ + +/* Find all subid ranges delegated to a user. + * + * Usage in shadow-utils: + * libsubid: get_sub?id_ranges() -> list_owner_ranges() + * + * SUBID_RANGES Reply: + * + * 0-3: 32bit unsigned number of UID results + * 4-7: 32bit unsigned number of GID results + * For each result (sub-uid ranges first): + * 0-3: 32bit number with "start" id + * 4-7: 32bit number with "count" (range size) + */ +enum subid_status shadow_subid_list_owner_ranges(const char *user, + enum subid_type id_type, + struct subid_range **ranges, + int *count) +{ + size_t user_len; + enum sss_status ret; + uint8_t *repbuf = NULL; + size_t index = 0; + size_t replen; + int errnop; + struct sss_cli_req_data rd; + uint32_t num_results = 0; + uint32_t val; + + if ( !user || !ranges || !count || + ((id_type != ID_TYPE_UID) && (id_type != ID_TYPE_GID)) ) { + return SUBID_STATUS_ERROR; + } + + ret = sss_strnlen(user, SSS_NAME_MAX, &user_len); + if (ret != 0) { + return SUBID_STATUS_UNKNOWN_USER; + } + rd.len = user_len + 1; + rd.data = user; + + sss_nss_lock(); + /* Anticipated workflow will always request both + * sub-uid and sub-gid ranges anyway. + * So don't bother with dedicated commands - + * just request everything in one shot. + * The second request will get data from the cache. + */ + ret = sss_cli_make_request_with_checks(SSS_NSS_GET_SUBID_RANGES, &rd, + SSS_CLI_SOCKET_TIMEOUT, + &repbuf, &replen, &errnop, + SSS_NSS_SOCKET_NAME); + sss_nss_unlock(); + + if ((ret != SSS_STATUS_SUCCESS) || (errnop != EOK)) { + free(repbuf); + if (ret == SSS_STATUS_UNAVAIL) { + return SUBID_STATUS_ERROR_CONN; + } + return SUBID_STATUS_ERROR; + } + + SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL); + if (id_type == ID_TYPE_UID) { + index = 2 * sizeof(uint32_t); + } else { + index = (2 + 2*num_results) * sizeof(uint32_t); + SAFEALIGN_COPY_UINT32(&num_results, repbuf + sizeof(uint32_t), NULL); + } + if (num_results == 0) { + /* TODO: how to distinguish "user not found" vs "user doesn't have ranges defined" here? + * Options: + * - special "fake" entry in the cache + * - provide 'nss_protocol_done_fn' to 'nss_getby_name' to avoid "ENOENT -> "empty packet" logic + * - add custom error code for this case and handle in generic 'nss_protocol_done' + * + * Note: at the moment this is not important, since shadow-utils doesn't use return code internally + * and returns -1 from libsubid on any error anyway. + */ + free(repbuf); + return SUBID_STATUS_UNKNOWN_USER; + } + + *count = num_results; + if (*count < 0) { + free(repbuf); + return SUBID_STATUS_ERROR; + } + + *ranges = malloc(num_results * sizeof(struct subid_range)); + if (!*ranges) { + free(repbuf); + return SUBID_STATUS_ERROR; + } + + for (uint32_t c = 0; c < num_results; ++c) { + SAFEALIGN_COPY_UINT32(&val, repbuf + index, &index); + (*ranges)[c].start = val; + SAFEALIGN_COPY_UINT32(&val, repbuf + index, &index); + (*ranges)[c].count = val; + } + free(repbuf); + + return SUBID_STATUS_SUCCESS; +} + +/* Does a user own a given subid range? + * + * Usage in shadow-utils: + * newuidmap/user busy : have_sub_uids() -> has_range() + */ +enum subid_status shadow_subid_has_range(const char *owner, + unsigned long start, + unsigned long count, + enum subid_type id_type, + bool *result) +{ + enum subid_status ret; + struct subid_range *range; + int amount; + unsigned long end = start + count; + + if (!result || (end < start)) { + return SUBID_STATUS_ERROR; + } + + if (count == 0) { + *result = true; + return SUBID_STATUS_SUCCESS; + } + + /* Anticipated workflow is the following: + * + * 1) Podman figures out ranges available for a user: + * libsubid::get_subid_ranges() -> ... -> list_owner_ranges() + * + * 2) Podman maps available ranges: + * newuidmap -> have_sub_uids() -> has_range() + * At this point all ranges are available in a cache from step (1) + * so it doesn't make sense to try "smart" LDAP searches (even if possible) + * Let's just reuse list_owner_ranges() and do a check. + * + * It might have some sense to do a check at responder's side (i.e. without + * fetching all ranges), but range is just a couple of numbers (and FreeIPA + * only supports a single range per user anyway), so this optimization + * wouldn't save much traffic anyway, but would introduce new + * `sss_cli_command`/responder handler. + */ + + ret = shadow_subid_list_owner_ranges(owner, id_type, &range, &amount); + if (ret != SUBID_STATUS_SUCCESS) { + return ret; + } + + *result = false; + + for (int i = 0; i < amount; ++i) { + if ((range[i].start <= start) && + (range[i].start + range[i].count >= end)) { + *result = true; + } + /* TODO: handle coverage via multiple ranges (once IPA supports this) */ + } + + free(range); + return ret; +} + +/* Find uids who own a given subid. + * + * Usage in shadow-utils: + * libsubid: get_sub?id_owners() -> find_subid_owners() + */ +enum subid_status shadow_subid_find_subid_owners(unsigned long subid, + enum subid_type id_type, + uid_t **uids, + int *count) +{ + /* Not yet implemented. + * Currently there are no users of this function. + */ + return SUBID_STATUS_ERROR; +} diff --git a/src/sss_client/subid/sss_subid.exports b/src/sss_client/subid/sss_subid.exports new file mode 100644 index 000000000..87c073b48 --- /dev/null +++ b/src/sss_client/subid/sss_subid.exports @@ -0,0 +1,12 @@ +EXPORTED { + + # public functions + global: + shadow_subid_has_range; + shadow_subid_list_owner_ranges; + shadow_subid_find_subid_owners; + + # everything else is local + local: + *; +}; diff --git a/src/systemtap/sssd_functions.stp b/src/systemtap/sssd_functions.stp index 01f553177..513029046 100644 --- a/src/systemtap/sssd_functions.stp +++ b/src/systemtap/sssd_functions.stp @@ -29,8 +29,8 @@ function acct_req_desc(entry_type) str_entry_type = "host" } else if (entry_type == 0x0009) { str_entry_type = "ip_network" - # See src/providers/data_provider_req.h, no 0x0010 there.. } else if (entry_type == 0x0010) { + str_entry_type = "subid_ranges" } else if (entry_type == 0x0011) { str_entry_type = "by_secid" } else if (entry_type == 0x0012) { diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am index 60f71036e..6d3dc45e5 100644 --- a/src/tests/cwrap/Makefile.am +++ b/src/tests/cwrap/Makefile.am @@ -74,6 +74,9 @@ SSSD_CACHE_REQ_OBJ = \ ../../../src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c \ ../../../src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c \ $(NULL) +if BUILD_SUBID + SSSD_CACHE_REQ_OBJ += ../../../src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c +endif SSSD_RESPONDER_IFACE_OBJ = \ ../../../src/responder/common/responder_iface.c \ diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c index ab44c213c..6d3345cbe 100644 --- a/src/tests/dlopen-tests.c +++ b/src/tests/dlopen-tests.c @@ -60,6 +60,9 @@ struct so { #ifdef BUILD_AUTOFS { "libsss_autofs.so", { LIBPFX"libsss_autofs.so", NULL } }, #endif +#ifdef BUILD_SUBID + { "libsubid_sss.so", { LIBPFX"libsubid_sss.so", NULL } }, +#endif #ifdef HAVE_KRB5_LOCATOR_PLUGIN { "sssd_krb5_locator_plugin.so", { LIBPFX"sssd_krb5_locator_plugin.so", NULL } }, diff --git a/src/util/sss_cli_cmd.c b/src/util/sss_cli_cmd.c index 82dc33b33..bc9332aaa 100644 --- a/src/util/sss_cli_cmd.c +++ b/src/util/sss_cli_cmd.c @@ -230,6 +230,11 @@ const char *sss_cmd2str(enum sss_cli_command cmd) return "SSS_NSS_GETIDBYSID"; case SSS_NSS_GETORIGBYNAME: return "SSS_NSS_GETORIGBYNAME"; + + /* SUBID ranges */ + case SSS_NSS_GET_SUBID_RANGES: + return "SSS_NSS_GET_SUBID_RANGES"; + default: DEBUG(SSSDBG_MINOR_FAILURE, "Translation's string is missing for command [%#x].\n", cmd); diff --git a/src/util/util_errors.c b/src/util/util_errors.c index 7ba2621d1..f2d1d5dfc 100644 --- a/src/util/util_errors.c +++ b/src/util/util_errors.c @@ -120,6 +120,7 @@ struct err_string error_to_str[] = { { "Unable to verify peer" }, /* ERR_UNABLE_TO_VERIFY_PEER */ { "Unable to resolve host" }, /* ERR_UNABLE_TO_RESOLVE_HOST */ { "GetAccountDomain() not supported" }, /* ERR_GET_ACCT_DOM_NOT_SUPPORTED */ + { "Subid ranges are not supported by this provider" }, /* ERR_GET_ACCT_SUBID_RANGES_NOT_SUPPORTED */ { "The last GetAccountDomain() result is still valid" }, /* ERR_GET_ACCT_DOM_CACHED */ { "ID is outside the allowed range" }, /* ERR_ID_OUTSIDE_RANGE */ { "Group ID is duplicated" }, /* ERR_GID_DUPLICATED */ diff --git a/src/util/util_errors.h b/src/util/util_errors.h index 37ce2de23..4098e818d 100644 --- a/src/util/util_errors.h +++ b/src/util/util_errors.h @@ -141,6 +141,7 @@ enum sssd_errors { ERR_UNABLE_TO_VERIFY_PEER, ERR_UNABLE_TO_RESOLVE_HOST, ERR_GET_ACCT_DOM_NOT_SUPPORTED, + ERR_GET_ACCT_SUBID_RANGES_NOT_SUPPORTED, ERR_GET_ACCT_DOM_CACHED, ERR_ID_OUTSIDE_RANGE, ERR_GID_DUPLICATED, -- 2.26.3