The branch, master has been updated via 3a01ef710d4 tests: Add a test for the idmap_nss : use_upn setting via 086a90d52b0 idmap_nss: Install a messaging filter to reload the configuration via a7a4d8e5333 idmap_nss: Add a parameter to use UPNs instead of plain names via c8e4777a921 idmap_nss: Increase debug on failures via de2f59c61a0 docs: Document idmap_nss "range" option via 8e1f2ee5f7c s3:winbind: Register a messaging filter foreach domain child via c35937054cd s3:winbind: talloc the static locator child via e3d0574d796 s3:winbind: talloc the static idmap child from f642aff5544 buildtools: Remove ‘keep_underscore’ parameter
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 3a01ef710d4f0c11013214a4f8637ebdac8d9f5e Author: Samuel Cabrero <scabr...@samba.org> Date: Tue Dec 12 21:17:50 2023 +0100 tests: Add a test for the idmap_nss : use_upn setting Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Alexander Bokovoy <a...@samba.org> Autobuild-User(master): Samuel Cabrero <scabr...@samba.org> Autobuild-Date(master): Wed Dec 13 16:05:19 UTC 2023 on atb-devel-224 commit 086a90d52b0c4bd388bf5707159ae1a727f8e400 Author: Samuel Cabrero <scabr...@samba.org> Date: Tue Dec 12 16:02:33 2023 +0100 idmap_nss: Install a messaging filter to reload the configuration Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Alexander Bokovoy <a...@samba.org> commit a7a4d8e53332f8cae68462afab7dec86c991d96f Author: Samuel Cabrero <scabr...@suse.de> Date: Mon Nov 27 08:05:29 2023 +0100 idmap_nss: Add a parameter to use UPNs instead of plain names idmap config <DOMAIN> : backend = nss idmap config <DOMAIN> : use_upn = yes|no When translating a Unix ID to a SID the module calls get[pwu|grg]id() but the name returned by some NSS modules might be a UPN instead of a plain name. If the new parameter is enabled the returned name will be parsed and correctly handled. On the other hand, when translating a SID to a Unix ID the module first resolves the SID to a domain + name, and then calls get[pw|gr]name() with the plain name, or the UPN if the new parameter is enabled. Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Alexander Bokovoy <a...@samba.org> commit c8e4777a921132082ee6421b2b456c82028fed46 Author: Samuel Cabrero <scabr...@samba.org> Date: Wed Nov 29 12:55:13 2023 +0100 idmap_nss: Increase debug on failures Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Alexander Bokovoy <a...@samba.org> commit de2f59c61a0549c54546704c07a1f41410fc50d7 Author: Samuel Cabrero <scabr...@suse.de> Date: Mon Nov 27 10:20:05 2023 +0100 docs: Document idmap_nss "range" option Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Alexander Bokovoy <a...@samba.org> commit 8e1f2ee5f7c5b3ee4edfa7beca289889a4e99cca Author: Samuel Cabrero <scabr...@samba.org> Date: Tue Dec 12 15:55:20 2023 +0100 s3:winbind: Register a messaging filter foreach domain child Instead of registering the "classic" callback for MSG_SMB_CONF_UPDATED, install a message filter to allow other parts of the code to also listen for this message because classic callbacks are delivered only once (see commit a2436b67e5dd47d955a3bea2b83e0693b627ab96). Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Alexander Bokovoy <a...@samba.org> commit c35937054cd69580bbf5e3252fd9a1e8958f2f7b Author: Samuel Cabrero <scabr...@samba.org> Date: Tue Dec 12 15:49:07 2023 +0100 s3:winbind: talloc the static locator child Next commits will use talloc_get_type_abort() to get the reference. Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Alexander Bokovoy <a...@samba.org> commit e3d0574d7969b00723a6b3041a796dd4f29726e8 Author: Samuel Cabrero <scabr...@samba.org> Date: Tue Dec 12 15:44:21 2023 +0100 s3:winbind: talloc the static idmap child Next commits will use talloc_get_type_abort() to get the reference. Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Alexander Bokovoy <a...@samba.org> ----------------------------------------------------------------------- Summary of changes: docs-xml/manpages/idmap_nss.8.xml | 38 +++++ nsswitch/tests/test_idmap_nss_use_upn.sh | 79 ++++++++++ source3/selftest/tests.py | 4 +- source3/winbindd/idmap_nss.c | 249 +++++++++++++++++++++++++++++-- source3/winbindd/winbindd.c | 13 +- source3/winbindd/winbindd_dual.c | 34 +++-- source3/winbindd/winbindd_idmap.c | 29 ++-- source3/winbindd/winbindd_locator.c | 21 ++- source3/winbindd/winbindd_proto.h | 4 +- 9 files changed, 425 insertions(+), 46 deletions(-) create mode 100755 nsswitch/tests/test_idmap_nss_use_upn.sh Changeset truncated at 500 lines: diff --git a/docs-xml/manpages/idmap_nss.8.xml b/docs-xml/manpages/idmap_nss.8.xml index fc03445df2c..a9c6eceedbc 100644 --- a/docs-xml/manpages/idmap_nss.8.xml +++ b/docs-xml/manpages/idmap_nss.8.xml @@ -27,6 +27,44 @@ </para> </refsynopsisdiv> +<refsect1> + <title>IDMAP OPTIONS</title> + + <variablelist> + <varlistentry> + <term>range = low - high</term> + <listitem><para> + Defines the available matching UID and GID range for which the + backend is authoritative. Note that the range acts as a filter. + Returned UIDs or GIDs by NSS modules that fall outside the range + are ignored and the corresponding maps discarded. It is intended + as a way to avoid accidental UID/GID overlaps between local and + remotely defined IDs. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>use_upn = <yes | no></term> + <listitem> + <para> + Some NSS modules can return and handle UPNs and/or down-level + logon names (e.g., DOMAIN\user or user@REALM). + </para> + <para> + If this parameter is enabled the returned names from NSS will be + parsed and the resulting namespace will be used as the authoritative + namespace instead of the IDMAP domain name. Also, down-level logon + names will be sent to NSS instead of the plain username to give NSS + modules a hint about the user's correct domain. + </para> + <para>Default: no</para> + </listitem> + </varlistentry> + + </variablelist> +</refsect1> + + <refsect1> <title>EXAMPLES</title> diff --git a/nsswitch/tests/test_idmap_nss_use_upn.sh b/nsswitch/tests/test_idmap_nss_use_upn.sh new file mode 100755 index 00000000000..df2c67203d8 --- /dev/null +++ b/nsswitch/tests/test_idmap_nss_use_upn.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +wbinfo="$BINDIR/wbinfo" +smbcontrol="$BINDIR/smbcontrol" +net="$BINDIR/net" +global_inject_conf=$(dirname $SMB_CONF_PATH)/global_inject.conf + +failed=0 + +. $(dirname $0)/../../testprogs/blackbox/subunit.sh + +# Reset idmap_nss configuration and clear cache +echo "idmap config $DOMAIN : use_upn = no" >$global_inject_conf +$smbcontrol winbindd reload-config +if [ $? -ne 0 ]; then + echo "Could not reload config" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +$net cache flush +if [ $? -ne 0 ]; then + echo "Could not flush cache" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +# Get the user SID +USER="bob" +USER_SID=$($wbinfo --name-to-sid="$USER") +if [ $? -ne 0 ]; then + echo "Could not find SID for user '$USER'" | subunit_fail_test "test_idmap_nss_use_upn" + exit 1 +fi + +USER_SID=$(echo $USER_SID | cut -d " " -f 1) +if [ $? -ne 0 ]; then + echo "Could not find SID for user '$USER'" | subunit_fail_test "test_idmap_nss_use_upn" + exit 1 +fi + +testit "SID to UID (use_upn = no)" $wbinfo --sid-to-uid=${USER_SID} || failed=$(expr $failed + 1) + +echo "idmap config $DOMAIN : use_upn = yes" >$global_inject_conf +$smbcontrol winbindd reload-config +if [ $? -ne 0 ]; then + echo "Could not reload config" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +$net cache flush +if [ $? -ne 0 ]; then + echo "Could not flush cache" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +# The following test will fail because idmap_nss will search ADDOMAIN/bob, which does not +# exists in NSS_WRAPPER_PASSWD +testit_expect_failure "SID to UID (use_upn = yes)" $wbinfo --sid-to-uid=${USER_SID} || failed=$(expr $failed + 1) + +$net cache flush +if [ $? -ne 0 ]; then + echo "Could not flush cache" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +# Add the ADDOMAIN/bob temporarily +ENTRY="$(getent passwd bob)" +ENTRY="$DOMAIN/${ENTRY}" +sed -i "1i ${ENTRY}" $NSS_WRAPPER_PASSWD +testit "Get user UID (use_upn = yes)" $wbinfo --sid-to-uid=${USER_SID} || failed=$(expr $failed + 1) +sed -i "1d" $NSS_WRAPPER_PASSWD + +# Reset config +echo "idmap config $DOMAIN : use_upn = no" >$global_inject_conf +$smbcontrol winbindd reload-config +if [ $? -ne 0 ]; then + echo "Could not reload config" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +$net cache flush +if [ $? -ne 0 ]; then + echo "Could not flush cache" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +exit $failed diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 30740d66dcf..679ff4e9916 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -1106,7 +1106,7 @@ rpc = ["rpc.authcontext", local = ["local.nss"] -idmap = ["idmap.rfc2307", "idmap.alloc", "idmap.rid", "idmap.ad"] +idmap = ["idmap.rfc2307", "idmap.alloc", "idmap.rid", "idmap.ad", "idmap.nss"] rap = ["rap.basic", "rap.rpc", "rap.printing", "rap.sam"] @@ -1217,6 +1217,8 @@ for t in tests: '$DC_SERVER', '$DC_USERNAME', '$DC_PASSWORD']) elif t == "idmap.alloc": plantestsuite(t, "ad_member_rfc2307", [os.path.join(samba3srcdir, "../nsswitch/tests/test_idmap_nss.sh"), '$DOMAIN']) + elif t == "idmap.nss": + plantestsuite(t, "ad_member_idmap_nss:local", [os.path.join(samba3srcdir, "../nsswitch/tests/test_idmap_nss_use_upn.sh")]) elif t == "idmap.rid": plantestsuite(t, "ad_member_idmap_rid", [os.path.join(samba3srcdir, "../nsswitch/tests/test_idmap_rid.sh"), '$DOMAIN', '2000000']) plantestsuite(t, diff --git a/source3/winbindd/idmap_nss.c b/source3/winbindd/idmap_nss.c index 642d5141784..0af25362219 100644 --- a/source3/winbindd/idmap_nss.c +++ b/source3/winbindd/idmap_nss.c @@ -1,4 +1,4 @@ -/* +/* Unix SMB/CIFS implementation. idmap NSS backend @@ -26,16 +26,150 @@ #include "idmap.h" #include "lib/winbind_util.h" #include "libcli/security/dom_sid.h" +#include "lib/global_contexts.h" +#include "messages.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_IDMAP +struct idmap_nss_context { + struct idmap_domain *dom; + bool use_upn; +}; + +static int idmap_nss_context_destructor(struct idmap_nss_context *ctx) +{ + if ((ctx->dom != NULL) && (ctx->dom->private_data == ctx)) { + ctx->dom->private_data = NULL; + } + return 0; +} + +static NTSTATUS idmap_nss_context_create(TALLOC_CTX *mem_ctx, + struct idmap_domain *dom, + struct idmap_nss_context **pctx) +{ + struct idmap_nss_context *ctx = NULL; + + ctx = talloc_zero(mem_ctx, struct idmap_nss_context); + if (ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + ctx->dom = dom; + + talloc_set_destructor(ctx, idmap_nss_context_destructor); + + ctx->use_upn = idmap_config_bool(dom->name, "use_upn", false); + + *pctx = ctx; + return NT_STATUS_OK; +} + +static NTSTATUS idmap_nss_get_context(struct idmap_domain *dom, + struct idmap_nss_context **pctx) +{ + struct idmap_nss_context *ctx = NULL; + NTSTATUS status; + + if (dom->private_data != NULL) { + *pctx = talloc_get_type_abort(dom->private_data, + struct idmap_nss_context); + return NT_STATUS_OK; + } + + status = idmap_nss_context_create(dom, dom, &ctx); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("idmap_nss_context_create failed: %s\n", + nt_errstr(status)); + return status; + } + + dom->private_data = ctx; + *pctx = ctx; + return NT_STATUS_OK; +} + +static bool idmap_nss_msg_filter(struct messaging_rec *rec, void *private_data) +{ + struct idmap_domain *dom = talloc_get_type_abort(private_data, + struct idmap_domain); + struct idmap_nss_context *ctx = NULL; + NTSTATUS status; + bool ret; + + if (rec->msg_type == MSG_SMB_CONF_UPDATED) { + ret = lp_load_global(get_dyn_CONFIGFILE()); + if (!ret) { + DBG_WARNING("Failed to reload configuration\n"); + return false; + } + + status = idmap_nss_get_context(dom, &ctx); + if (NT_STATUS_IS_ERR(status)) { + DBG_WARNING("Failed to get idmap nss context: %s\n", + nt_errstr(status)); + return false; + } + + ctx->use_upn = idmap_config_bool(dom->name, "use_upn", false); + } + + return false; +} + /***************************** Initialise idmap database. *****************************/ static NTSTATUS idmap_nss_int_init(struct idmap_domain *dom) { + struct idmap_nss_context *ctx = NULL; + NTSTATUS status; + struct messaging_context *msg_ctx = global_messaging_context(); + struct tevent_req *req = NULL; + + status = idmap_nss_context_create(dom, dom, &ctx); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + dom->private_data = ctx; + + req = messaging_filtered_read_send( + dom, + messaging_tevent_context(msg_ctx), + msg_ctx, + idmap_nss_msg_filter, + dom); + if (req == NULL) { + DBG_WARNING("messaging_filtered_read_send failed\n"); + return NT_STATUS_UNSUCCESSFUL; + } + + return status; +} + +static NTSTATUS idmap_nss_lookup_name(const char *namespace, + const char *username, + struct dom_sid *sid, + enum lsa_SidType *type) +{ + bool ret; + + /* + * By default calls to winbindd are disabled + * the following call will not recurse so this is safe + */ + (void)winbind_on(); + ret = winbind_lookup_name(namespace, username, sid, type); + (void)winbind_off(); + + if (!ret) { + DBG_NOTICE("Failed to lookup name [%s] in namespace [%s]\n", + username, namespace); + return NT_STATUS_NOT_FOUND; + } + return NT_STATUS_OK; } @@ -45,8 +179,17 @@ static NTSTATUS idmap_nss_int_init(struct idmap_domain *dom) static NTSTATUS idmap_nss_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids) { + struct idmap_nss_context *ctx = NULL; + NTSTATUS status; int i; + status = idmap_nss_get_context(dom, &ctx); + if (NT_STATUS_IS_ERR(status)) { + DBG_WARNING("Failed to get idmap nss context: %s\n", + nt_errstr(status)); + return status; + } + /* initialize the status to avoid surprise */ for (i = 0; ids[i]; i++) { ids[i]->status = ID_UNKNOWN; @@ -58,42 +201,89 @@ static NTSTATUS idmap_nss_unixids_to_sids(struct idmap_domain *dom, struct id_ma const char *name; struct dom_sid sid; enum lsa_SidType type; - bool ret; switch (ids[i]->xid.type) { case ID_TYPE_UID: + errno = 0; pw = getpwuid((uid_t)ids[i]->xid.id); - if (!pw) { + DBG_DEBUG("getpwuid(%lu) failed: %s\n", + (unsigned long)ids[i]->xid.id, + errno != 0 + ? strerror(errno) + : "not found"); ids[i]->status = ID_UNMAPPED; continue; } name = pw->pw_name; break; case ID_TYPE_GID: + errno = 0; gr = getgrgid((gid_t)ids[i]->xid.id); - if (!gr) { + DBG_DEBUG("getgrgid(%lu) failed: %s\n", + (unsigned long)ids[i]->xid.id, + errno != 0 + ? strerror(errno) + : "not found"); ids[i]->status = ID_UNMAPPED; continue; } name = gr->gr_name; break; default: /* ?? */ + DBG_WARNING("Unexpected xid type %d\n", + ids[i]->xid.type); ids[i]->status = ID_UNKNOWN; continue; } - /* by default calls to winbindd are disabled - the following call will not recurse so this is safe */ - (void)winbind_on(); /* Lookup name from PDC using lsa_lookup_names() */ - ret = winbind_lookup_name(dom->name, name, &sid, &type); - (void)winbind_off(); + if (ctx->use_upn) { + char *p = NULL; + const char *namespace = NULL; + const char *domname = NULL; + const char *domuser = NULL; + + p = strstr(name, lp_winbind_separator()); + if (p != NULL) { + *p = '\0'; + domname = name; + namespace = domname; + domuser = p + 1; + } else { + p = strchr(name, '@'); + if (p != NULL) { + *p = '\0'; + namespace = p + 1; + domname = ""; + domuser = name; + } else { + namespace = dom->name; + domuser = name; + } + } - if (!ret) { - /* TODO: how do we know if the name is really not mapped, - * or something just failed ? */ + DBG_DEBUG("Using namespace [%s] from UPN instead " + "of [%s] to lookup the name [%s]\n", + namespace, dom->name, domuser); + + status = idmap_nss_lookup_name(namespace, + domuser, + &sid, + &type); + } else { + status = idmap_nss_lookup_name(dom->name, + name, + &sid, + &type); + } + + if (NT_STATUS_IS_ERR(status)) { + /* + * TODO: how do we know if the name is really + * not mapped, or something just failed ? + */ ids[i]->status = ID_UNMAPPED; continue; } @@ -129,8 +319,17 @@ static NTSTATUS idmap_nss_unixids_to_sids(struct idmap_domain *dom, struct id_ma static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids) { + struct idmap_nss_context *ctx = NULL; + NTSTATUS status; int i; + status = idmap_nss_get_context(dom, &ctx); + if (NT_STATUS_IS_ERR(status)) { + DBG_WARNING("Failed to get idmap nss context: %s\n", + nt_errstr(status)); + return status; + } + /* initialize the status to avoid surprise */ for (i = 0; ids[i]; i++) { ids[i]->status = ID_UNKNOWN; @@ -143,6 +342,8 @@ static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_ma const char *_name = NULL; char *domain = NULL; char *name = NULL; + char *fqdn = NULL; + char *sname = NULL; bool ret; /* by default calls to winbindd are disabled @@ -173,13 +374,30 @@ static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_ma continue; } + if (ctx->use_upn) { + fqdn = talloc_asprintf(talloc_tos(), + "%s%s%s", + domain, + lp_winbind_separator(), + name); + if (fqdn == NULL) { + DBG_ERR("No memory\n"); + ids[i]->status = ID_UNMAPPED; + continue; + } + DBG_DEBUG("Using UPN [%s] instead of plain name [%s]\n", + fqdn, name); + sname = fqdn; + } else { + sname = name; + } + switch (type) { case SID_NAME_USER: { struct passwd *pw; /* this will find also all lower case name and use username level */ - - pw = Get_Pwnam_alloc(talloc_tos(), name); + pw = Get_Pwnam_alloc(talloc_tos(), sname); if (pw) { ids[i]->xid.id = pw->pw_uid; ids[i]->xid.type = ID_TYPE_UID; @@ -193,7 +411,7 @@ static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_ma case SID_NAME_ALIAS: case SID_NAME_WKN_GRP: -- Samba Shared Repository