From a7a4d8e53332f8cae68462afab7dec86c991d96f Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Mon, 27 Nov 2023 08:05:29 +0100 Subject: [PATCH] idmap_nss: Add a parameter to use UPNs instead of plain names idmap config : backend = nss idmap config : 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 Reviewed-by: Alexander Bokovoy --- docs-xml/manpages/idmap_nss.8.xml | 21 +++- source3/winbindd/idmap_nss.c | 190 ++++++++++++++++++++++++++++-- 2 files changed, 197 insertions(+), 14 deletions(-) diff --git a/docs-xml/manpages/idmap_nss.8.xml b/docs-xml/manpages/idmap_nss.8.xml index 254f38a5f5b..a9c6eceedbc 100644 --- a/docs-xml/manpages/idmap_nss.8.xml +++ b/docs-xml/manpages/idmap_nss.8.xml @@ -42,6 +42,25 @@ remotely defined IDs. + + + use_upn = <yes | no> + + + Some NSS modules can return and handle UPNs and/or down-level + logon names (e.g., DOMAIN\user or user@REALM). + + + 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. + + Default: no + + + @@ -76,4 +95,4 @@ - \ No newline at end of file + diff --git a/source3/winbindd/idmap_nss.c b/source3/winbindd/idmap_nss.c index 8d2b23b4165..957c5c569cf 100644 --- a/source3/winbindd/idmap_nss.c +++ b/source3/winbindd/idmap_nss.c @@ -1,4 +1,4 @@ -/* +/* Unix SMB/CIFS implementation. idmap NSS backend @@ -30,12 +30,103 @@ #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; +} + /***************************** Initialise idmap database. *****************************/ static NTSTATUS idmap_nss_int_init(struct idmap_domain *dom) { + struct idmap_nss_context *ctx = NULL; + NTSTATUS status; + + status = idmap_nss_context_create(dom, dom, &ctx); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + dom->private_data = ctx; + + 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 +136,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,7 +158,6 @@ 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: @@ -96,16 +195,52 @@ static NTSTATUS idmap_nss_unixids_to_sids(struct idmap_domain *dom, struct id_ma 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; } @@ -141,8 +276,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; @@ -155,6 +299,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 @@ -185,13 +331,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; @@ -205,7 +368,7 @@ static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_ma case SID_NAME_ALIAS: case SID_NAME_WKN_GRP: - gr = getgrnam(name); + gr = getgrnam(sname); if (gr) { ids[i]->xid.id = gr->gr_gid; ids[i]->xid.type = ID_TYPE_GID; @@ -219,6 +382,7 @@ static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_ma } TALLOC_FREE(domain); TALLOC_FREE(name); + TALLOC_FREE(fqdn); } return NT_STATUS_OK; } -- 2.47.3