From ca29c71eb0b552ab1625f7ca1be300037c3b7ccd Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 11 May 2015 13:08:02 -0700 Subject: [PATCH 1/5] s3: winbindd: Add new internal interface wbint_CacheNameToSid() Allows smbd to store an entry in winbindd's name -> SID cache. Flags field not yet used. Signed-off-by: Jeremy Allison --- librpc/idl/winbind.idl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/librpc/idl/winbind.idl b/librpc/idl/winbind.idl index 39e89c3..92aab40 100644 --- a/librpc/idl/winbind.idl +++ b/librpc/idl/winbind.idl @@ -192,4 +192,11 @@ interface winbind [in,out,ref] NL_DNS_NAME_INFO_ARRAY *dns_names ); + NTSTATUS wbint_CacheNameToSid( + [in,string,charset(UTF8)] char *domname, + [in,string,charset(UTF8)] char *name, + [in] uint32 flags, + [in] lsa_SidType type, + [in] dom_sid *sid + ); } -- 2.2.0.rc0.207.ga3a616c From 441409423ea4f5f7020fe52b8878b5479b28611f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 11 May 2015 13:11:00 -0700 Subject: [PATCH 2/5] s3: winbindd: Implement server-side code for wbint_CacheNameToSid(). Signed-off-by: Jeremy Allison --- nsswitch/winbind_struct_protocol.h | 14 ++- source3/winbindd/winbindd.c | 3 + source3/winbindd/winbindd_cache_name_to_sid.c | 156 ++++++++++++++++++++++++++ source3/winbindd/winbindd_dual_srv.c | 24 ++++ source3/winbindd/winbindd_proto.h | 7 ++ source3/wscript_build | 3 +- 6 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 source3/winbindd/winbindd_cache_name_to_sid.c diff --git a/nsswitch/winbind_struct_protocol.h b/nsswitch/winbind_struct_protocol.h index 0dffa4b..f8e350c 100644 --- a/nsswitch/winbind_struct_protocol.h +++ b/nsswitch/winbind_struct_protocol.h @@ -53,8 +53,9 @@ typedef char fstring[FSTRING_LEN]; * removed WINBINDD_REMOVE_MAPPING * 26: added WINBINDD_DC_INFO * 27: added WINBINDD_LOOKUPSIDS + * 28: added CACHE_NAME_TO_SID */ -#define WINBIND_INTERFACE_VERSION 27 +#define WINBIND_INTERFACE_VERSION 28 /* Have to deal with time_t being 4 or 8 bytes due to structure alignment. On a 64bit Linux box, we have to support a constant structure size @@ -180,6 +181,9 @@ enum winbindd_cmd { WINBINDD_CCACHE_NTLMAUTH, WINBINDD_CCACHE_SAVE, + /* Cache a name to SID lookup from a PAC. */ + WINBINDD_CACHE_NAME_TO_SID, + WINBINDD_NUM_CMDS }; @@ -249,7 +253,7 @@ struct winbindd_request { union { fstring winsreq; /* WINS request */ - fstring username; /* getpwnam */ + fstring username; /* getpwnam, cache_name_to_sid */ fstring groupname; /* getgrnam */ uid_t uid; /* getpwuid, uid_to_sid */ gid_t gid; /* getgrgid, gid_to_sid */ @@ -298,7 +302,7 @@ struct winbindd_request { fstring krb5ccname; uid_t uid; } logoff; /* pam_winbind session module */ - fstring sid; /* lookupsid, sid_to_[ug]id */ + fstring sid; /* lookupsid, sid_to_[ug]id, cache_name_to_sid */ struct { fstring dom_name; /* lookupname */ fstring name; @@ -346,6 +350,10 @@ struct winbindd_request { fstring site_name; uint32_t flags; } dsgetdcname; + struct { + uint32_t type; + uint32_t flags; + } cache_name_to_sid; /* padding -- needed to fix alignment between 32bit and 64bit libs. The size is the sizeof the union without the padding aligned on diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c index 87bc532..0d8b29b 100644 --- a/source3/winbindd/winbindd.c +++ b/source3/winbindd/winbindd.c @@ -676,6 +676,9 @@ static struct winbindd_async_dispatch_table async_priv_table[] = { winbindd_change_machine_acct_send, winbindd_change_machine_acct_recv }, { WINBINDD_PAM_AUTH_CRAP, "PAM_AUTH_CRAP", winbindd_pam_auth_crap_send, winbindd_pam_auth_crap_recv }, + { WINBINDD_CACHE_NAME_TO_SID, "CACHE_NAME_TO_SID", + winbindd_cache_name_to_sid_send, winbindd_cache_name_to_sid_recv }, + { 0, NULL, NULL, NULL } }; diff --git a/source3/winbindd/winbindd_cache_name_to_sid.c b/source3/winbindd/winbindd_cache_name_to_sid.c new file mode 100644 index 0000000..137ae74 --- /dev/null +++ b/source3/winbindd/winbindd_cache_name_to_sid.c @@ -0,0 +1,156 @@ +/* + Unix SMB/CIFS implementation. + async implementation of WINBINDD_CACHE_NAME_TO_SID + + Allows smbd to intern a name -> sid conversion from a kerberos PAC. + Only available on the privileged pipe. + + Copyright (C) Jeremy Allison 2015 + + 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 "includes.h" +#include "winbindd.h" +#include "librpc/gen_ndr/ndr_winbind_c.h" +#include "libcli/security/dom_sid.h" + +struct winbindd_cache_name_to_sid_state { + fstring domname; + fstring username; + uint32_t flags; + enum lsa_SidType type; + struct dom_sid sid; +}; + +static void winbindd_cache_name_to_sid_done(struct tevent_req *subreq); + +struct tevent_req *winbindd_cache_name_to_sid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct winbindd_cli_state *cli, + struct winbindd_request *request) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct winbindd_cache_name_to_sid_state *state = NULL; + struct winbindd_domain *domain = NULL; + char *domuser = NULL, *mapped_user = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct winbindd_cache_name_to_sid_state); + if (req == NULL) { + return NULL; + } + + /* + * Do the same name normalization done in getpwnam. + */ + + /* Ensure null termination */ + request->data.username[sizeof(request->data.username)-1]='\0'; + + DEBUG(3, ("winbindd_cache_name_to_sid %s\n", request->data.username)); + + domuser = request->data.username; + + status = normalize_name_unmap(state, domuser, &mapped_user); + + if (NT_STATUS_IS_OK(status) + || NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) { + /* normalize_name_unmapped did something */ + domuser = mapped_user; + } + + if (!parse_domain_user(domuser, state->domname, state->username)) { + DEBUG(5, ("Could not parse domain user: %s\n", domuser)); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + if (!strequal(state->domname, request->domain_name)) { + DEBUG(5, ("domain name missmatch: %s %s\n", + state->domname, + request->domain_name)); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + if (lp_winbind_trusted_domains_only() + && strequal(state->domname, lp_workgroup())) { + DEBUG(7,("My domain -- " + "rejecting name_to_sid for %s\\%s.\n", + state->domname, state->username)); + tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER); + return tevent_req_post(req, ev); + } + + domain = find_domain_from_name(request->domain_name); + if (domain == NULL) { + tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN); + return tevent_req_post(req, ev); + } + if (domain->internal) { + /* + * Don't store internal name -> SID mappings. + */ + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + if (!string_to_sid(&state->sid, request->data.sid)) { + DEBUG(1, ("Could not convert sid from string %s\n", + request->data.sid)); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->type = (enum lsa_SidType)request->data.cache_name_to_sid.type; + state->flags = request->data.cache_name_to_sid.flags; + + subreq = dcerpc_wbint_CacheNameToSid_send(state, + ev, + dom_child_handle(domain), + state->domname, + state->username, + state->flags, + state->type, + &state->sid); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, winbindd_cache_name_to_sid_done, req); + return req; +} + +static void winbindd_cache_name_to_sid_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winbindd_cache_name_to_sid_state *state = tevent_req_data( + req, struct winbindd_cache_name_to_sid_state); + NTSTATUS status, result; + + status = dcerpc_wbint_ChangeMachineAccount_recv(subreq, state, &result); + if (any_nt_status_not_ok(status, result, &status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +NTSTATUS winbindd_cache_name_to_sid_recv(struct tevent_req *req, + struct winbindd_response *presp) +{ + return tevent_req_simple_recv_ntstatus(req); +} diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c index 97d8a1b..0bebd7b 100644 --- a/source3/winbindd/winbindd_dual_srv.c +++ b/source3/winbindd/winbindd_dual_srv.c @@ -870,3 +870,27 @@ NTSTATUS _winbind_SamLogon(struct pipes_struct *p, lm_response, nt_response, &r->out.validation.sam3); return status; } + +NTSTATUS _wbint_CacheNameToSid(struct pipes_struct *p, + struct wbint_CacheNameToSid *r) +{ + struct winbindd_domain *domain = wb_child_domain(); + + if (domain == NULL) { + return NT_STATUS_REQUEST_NOT_ACCEPTED; + } + + DEBUG(10,("cache_name2sid domain %s name %s type 0x%x sid %s\n", + r->in.domname, + r->in.name, + (unsigned int)r->in.type, + sid_string_dbg(r->in.sid))); + + cache_name2sid(domain, + r->in.domname, + r->in.name, + r->in.type, + r->in.sid); + + return NT_STATUS_OK; +} diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h index fedbbe7..e6ff733 100644 --- a/source3/winbindd/winbindd_proto.h +++ b/source3/winbindd/winbindd_proto.h @@ -913,6 +913,13 @@ struct tevent_req *winbindd_wins_byname_send(TALLOC_CTX *mem_ctx, NTSTATUS winbindd_wins_byname_recv(struct tevent_req *req, struct winbindd_response *presp); +struct tevent_req *winbindd_cache_name_to_sid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct winbindd_cli_state *cli, + struct winbindd_request *request); + +NTSTATUS winbindd_cache_name_to_sid_recv(struct tevent_req *req, + struct winbindd_response *presp); /* The following definitions come from winbindd/winbindd_samr.c */ diff --git a/source3/wscript_build b/source3/wscript_build index ef4c986..588e20b 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -973,7 +973,8 @@ bld.SAMBA3_BINARY('winbindd/winbindd', winbindd/winbindd_pam_logoff.c winbindd/winbindd_pam_chauthtok.c winbindd/winbindd_pam_auth_crap.c - winbindd/winbindd_pam_chng_pswd_auth_crap.c''', + winbindd/winbindd_pam_chng_pswd_auth_crap.c + winbindd/winbindd_cache_name_to_sid.c''', deps=''' talloc tevent -- 2.2.0.rc0.207.ga3a616c From e1b721a7c0895b81db5e219066557eb6bf6a5658 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 11 May 2015 13:11:59 -0700 Subject: [PATCH 3/5] s3: winbindd: Implement client side wbcCtxCacheNameToSid(). Signed-off-by: Jeremy Allison --- nsswitch/libwbclient/wbc_sid.c | 47 +++++++++++++++++++++++++++++++++++++++++ nsswitch/libwbclient/wbclient.h | 37 +++++++++++++++++++++++++++++++- nsswitch/libwbclient/wscript | 2 +- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/nsswitch/libwbclient/wbc_sid.c b/nsswitch/libwbclient/wbc_sid.c index cc71b9e..88fdccd 100644 --- a/nsswitch/libwbclient/wbc_sid.c +++ b/nsswitch/libwbclient/wbc_sid.c @@ -1084,3 +1084,50 @@ const char* wbcSidTypeString(enum wbcSidType type) default: return "Unknown type"; } } + +/* Cache a name -> SID translation incoming from a krb5 PAC. + name should be in the form DOMAIN\name. + This is a privileged operation as it's writing into + the name -> sid cache for this domain. +*/ +wbcErr wbcCtxCacheNameToSid(struct wbcContext *ctx, + const char *domain, + const char *name, + const struct wbcDomainSid *dom_sid, + enum wbcSidType sid_type) +{ + struct winbindd_request request; + struct winbindd_response response; + + /* Initialize request */ + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + strncpy(request.domain_name, domain, + sizeof(request.domain_name)-1); + + strncpy(request.data.username, name, + sizeof(request.data.username)-1); + + wbcSidToStringBuf(dom_sid, request.data.sid, + sizeof(request.data.sid)); + + request.data.cache_name_to_sid.type = (uint32_t)sid_type; + + return wbcRequestResponsePriv(ctx, + WINBINDD_CACHE_NAME_TO_SID, + &request, + &response); +} + +wbcErr wbcCacheNameToSid(const char *domain, + const char *name, + const struct wbcDomainSid *dom_sid, + enum wbcSidType sid_type) +{ + return wbcCtxCacheNameToSid(NULL, + domain, + name, + dom_sid, + sid_type); +} diff --git a/nsswitch/libwbclient/wbclient.h b/nsswitch/libwbclient/wbclient.h index adf8fe3..d7ffcd6 100644 --- a/nsswitch/libwbclient/wbclient.h +++ b/nsswitch/libwbclient/wbclient.h @@ -73,9 +73,10 @@ const char *wbcErrorString(wbcErr error); * 0.10: Added wbcPingDc2() * 0.11: Extended wbcAuthenticateUserEx to provide PAC parsing * 0.12: Added wbcCtxCreate and friends + * 0.13: Added wbcCtxCacheNameToSid() and wbcCacheNameToSid() **/ #define WBCLIENT_MAJOR_VERSION 0 -#define WBCLIENT_MINOR_VERSION 12 +#define WBCLIENT_MINOR_VERSION 13 #define WBCLIENT_VENDOR_VERSION "Samba libwbclient" struct wbcLibraryDetails { uint16_t major_version; @@ -1031,6 +1032,40 @@ wbcErr wbcSidsToUnixIds(const struct wbcDomainSid *sids, uint32_t num_sids, struct wbcUnixId *ids); /** + * @brief Store a name to sid translation in the winbindd cache. + * + * @param ctx wbcContext pointer + * @param domain Domain name to use + * @param name DOMAIN\name to store + * @param dom_sid sid to store + * @param name_type sid type to store. + * + * @return #wbcErr + * + **/ +wbcErr wbcCtxCacheNameToSid(struct wbcContext *ctx, + const char *domain, + const char *name, + const struct wbcDomainSid *dom_sid, + enum wbcSidType sid_type); + +/** + * @brief Store a name to sid translation in the winbindd cache. + * + * @param domain Domain name to use + * @param name DOMAIN\name to store + * @param dom_sid sid to store + * @param name_type sid type to store. + * + * @return #wbcErr + * + **/ +wbcErr wbcCacheNameToSid(const char *domain, + const char *name, + const struct wbcDomainSid *dom_sid, + enum wbcSidType sid_type); + +/** * @brief Obtain a new uid from Winbind * * @param *ctx wbclient Context diff --git a/nsswitch/libwbclient/wscript b/nsswitch/libwbclient/wscript index 8602c1c..5c5002a 100644 --- a/nsswitch/libwbclient/wscript +++ b/nsswitch/libwbclient/wscript @@ -3,7 +3,7 @@ import Options, Logs # Remember to also update wbclient.h -VERSION="0.12" +VERSION="0.13" # It may be useful at some point to allow Samba to build against a # system libwbclient, such as the one provided by Likewise. To to -- 2.2.0.rc0.207.ga3a616c From 2b61e94c9eb3fb7fbe35ac8111bb758e26eb4e49 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 11 May 2015 13:31:02 -0700 Subject: [PATCH 4/5] s3: smbd: auth : Move netsamlogon_cache_store() to just before smb_getpwnam() as that call uses the data. Signed-off-by: Jeremy Allison --- source3/auth/auth_generic.c | 13 +------------ source3/auth/proto.h | 3 ++- source3/auth/user_krb5.c | 17 +++++++++++++++-- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c index 584b52f..35e924e 100644 --- a/source3/auth/auth_generic.c +++ b/source3/auth/auth_generic.c @@ -93,7 +93,7 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, princ_name, logon_info, &is_mapped, &is_guest, &ntuser, &ntdomain, - &username, &pw); + &username, &pw, &info3_copy); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to map kerberos principal to system user " "(%s)\n", nt_errstr(status))); @@ -101,17 +101,6 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, goto done; } - /* save the PAC data if we have it */ - if (logon_info) { - status = create_info3_from_pac_logon_info(tmp_ctx, - logon_info, - &info3_copy); - if (!NT_STATUS_IS_OK(status)) { - goto done; - } - netsamlogon_cache_store(ntuser, info3_copy); - } - /* setup the string used by %U */ sub_set_smb_name(username); diff --git a/source3/auth/proto.h b/source3/auth/proto.h index 792e96d..5e80dba 100644 --- a/source3/auth/proto.h +++ b/source3/auth/proto.h @@ -370,7 +370,8 @@ NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, char **ntuser, char **ntdomain, char **username, - struct passwd **_pw); + struct passwd **_pw, + struct netr_SamInfo3 **pp_info3); NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, char *ntuser, char *ntdomain, diff --git a/source3/auth/user_krb5.c b/source3/auth/user_krb5.c index 7442ea4..26e5bfe 100644 --- a/source3/auth/user_krb5.c +++ b/source3/auth/user_krb5.c @@ -37,7 +37,8 @@ NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, char **ntuser, char **ntdomain, char **username, - struct passwd **_pw) + struct passwd **_pw, + struct netr_SamInfo3 **pp_info3) { NTSTATUS status; char *domain = NULL; @@ -128,6 +129,17 @@ NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, } *mapped_to_guest = false; + /* save the PAC data if we have it */ + if (logon_info) { + status = create_info3_from_pac_logon_info(mem_ctx, + logon_info, + pp_info3); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + netsamlogon_cache_store(user, *pp_info3); + } + pw = smb_getpwnam(mem_ctx, fuser, &unixuser, true); if (pw) { if (!unixuser) { @@ -288,7 +300,8 @@ NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, char **ntuser, char **ntdomain, char **username, - struct passwd **_pw) + struct passwd **_pw, + struct netr_SamInfo3 **pp_info3) { return NT_STATUS_NOT_IMPLEMENTED; } -- 2.2.0.rc0.207.ga3a616c From cafaf62ac8deeac16020fa0e073877f87684c751 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 11 May 2015 14:35:42 -0700 Subject: [PATCH 5/5] s3: smbd: auth: If we have access to the PAC, call wbcCacheNameToSid() to cache the name -> sid translation before calling smb_getpwnam(). Should save us from contacting the DC. Signed-off-by: Jeremy Allison --- source3/auth/user_krb5.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/source3/auth/user_krb5.c b/source3/auth/user_krb5.c index 26e5bfe..6749521 100644 --- a/source3/auth/user_krb5.c +++ b/source3/auth/user_krb5.c @@ -23,6 +23,7 @@ #include "nsswitch/libwbclient/wbclient.h" #include "passdb.h" #include "lib/param/loadparm.h" +#include "libcli/security/dom_sid.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_AUTH @@ -131,13 +132,58 @@ NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, /* save the PAC data if we have it */ if (logon_info) { + struct netr_SamInfo3 *info3 = NULL; + status = create_info3_from_pac_logon_info(mem_ctx, logon_info, pp_info3); if (!NT_STATUS_IS_OK(status)) { return status; } - netsamlogon_cache_store(user, *pp_info3); + info3 = *pp_info3; + netsamlogon_cache_store(user, info3); + + if (info3->base.acct_flags & ACB_NORMAL) { + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + struct dom_sid user_sid; + struct wbcDomainSid wbc_user_sid; + /* + * For a normal user account save the + * name -> sid mapping from + * the PAC into the winbindd cache. + * smb_getpwnam() will use this and + * we will avoid a call to the DC. + * + * This is an optimization, not a required + * call so ignore any error (just log it). + * This must be done as root as it is + * using the winbindd privileged pipe. + */ + if (!sid_compose(&user_sid, + info3->base.domain_sid, + info3->base.rid)) { + return NT_STATUS_INVALID_PARAMETER; + } + memcpy(&wbc_user_sid, + &user_sid, + sizeof(wbc_user_sid)); + + become_root(); + wbc_status = wbcCacheNameToSid(domain, + fuser, + &wbc_user_sid, + WBC_SID_NAME_USER); + unbecome_root(); + + DEBUG(10,("Caching domain %s " + "name %s to sid %s returned %s\n", + domain, + fuser, + dom_sid_string(mem_ctx, + &user_sid), + wbcErrorString(wbc_status))); + + } } pw = smb_getpwnam(mem_ctx, fuser, &unixuser, true); -- 2.2.0.rc0.207.ga3a616c