From 2d67a683b735d50f411a4bee9ee5ec17e9a60015 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 31 Jul 2002 12:05:30 +0000 Subject: Winbind updates! This updates the 'winbind' authentication module and winbind's 'PAM' (actually netlogon) code to allow smbd to cache connections to the DC. This is particulary relevent when we need mutex locks already - there is no parallelism to be gained anyway. The winbind code authenticates the user, and if successful, passes back the 'info3' struct describing the user. smbd then interprets that in exactly the same way as an 'ntdomain' logon. Also, add parinoia to winbind about null termination. Andrew Bartlett (This used to be commit 167f122b670d4ef67d78e6f79a2bae3f6e8d67df) --- source3/auth/auth_winbind.c | 70 ++++++++++++++------ source3/nsswitch/winbindd.c | 3 + source3/nsswitch/winbindd_group.c | 6 ++ source3/nsswitch/winbindd_nss.h | 12 +++- source3/nsswitch/winbindd_pam.c | 135 ++++++++++++++++++++++++++++++-------- source3/nsswitch/winbindd_sid.c | 15 +++++ source3/nsswitch/winbindd_user.c | 3 + source3/nsswitch/winbindd_wins.c | 6 ++ source3/rpc_parse/parse_net.c | 2 +- 9 files changed, 201 insertions(+), 51 deletions(-) diff --git a/source3/auth/auth_winbind.c b/source3/auth/auth_winbind.c index 671e198bf5..5bdccd39f3 100644 --- a/source3/auth/auth_winbind.c +++ b/source3/auth/auth_winbind.c @@ -32,6 +32,30 @@ NSS_STATUS winbindd_request(int req_type, struct winbindd_request *request, struct winbindd_response *response); +NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, NET_USER_INFO_3 *info3) +{ + uint8 *info3_ndr; + size_t len = response->length - sizeof(response); + prs_struct ps; + if (len > 0) { + info3_ndr = response->extra_data; + if (!prs_init(&ps, len, mem_ctx, UNMARSHALL)) { + return NT_STATUS_NO_MEMORY; + } + prs_append_data(&ps, info3_ndr, len); + ps.data_offset = 0; + if (!net_io_user_info3("", info3, &ps, 1, 3)) { + DEBUG(2, ("get_info3_from_ndr: could not parse info3 struct!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + prs_mem_free(&ps); + + return NT_STATUS_OK; + } else { + DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n")); + return NT_STATUS_UNSUCCESSFUL; + } +} /* Authenticate a user with a challenge/response */ @@ -44,11 +68,11 @@ static NTSTATUS check_winbind_security(const struct auth_context *auth_context, struct winbindd_request request; struct winbindd_response response; NSS_STATUS result; - struct passwd *pw; NTSTATUS nt_status; + NET_USER_INFO_3 info3; if (!user_info) { - return NT_STATUS_UNSUCCESSFUL; + return NT_STATUS_INVALID_PARAMETER; } if (!auth_context) { @@ -62,11 +86,14 @@ static NTSTATUS check_winbind_security(const struct auth_context *auth_context, ZERO_STRUCT(request); ZERO_STRUCT(response); - snprintf(request.data.auth_crap.user, sizeof(request.data.auth_crap.user), - "%s\\%s", user_info->domain.str, user_info->smb_name.str); + request.data.auth_crap.flags = WINBIND_PAM_INFO3_NDR; - fstrcpy(request.data.auth_crap.user, user_info->smb_name.str); - fstrcpy(request.data.auth_crap.domain, user_info->domain.str); + push_utf8_fstring(request.data.auth_crap.user, + user_info->smb_name.str); + push_utf8_fstring(request.data.auth_crap.domain, + user_info->domain.str); + push_utf8_fstring(request.data.auth_crap.workstation, + user_info->wksta_name.str); memcpy(request.data.auth_crap.chal, auth_context->challenge.data, sizeof(request.data.auth_crap.chal)); @@ -76,27 +103,28 @@ static NTSTATUS check_winbind_security(const struct auth_context *auth_context, sizeof(request.data.auth_crap.nt_resp)); memcpy(request.data.auth_crap.lm_resp, user_info->lm_resp.data, - sizeof(request.data.auth_crap.lm_resp_len)); - memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data, request.data.auth_crap.lm_resp_len); + memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data, + request.data.auth_crap.nt_resp_len); result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); - if (result == NSS_STATUS_SUCCESS) { - - pw = Get_Pwnam(user_info->internal_username.str); - - if (pw) { - if (make_server_info_pw(server_info, pw)) { - nt_status = NT_STATUS_OK; - } else { - nt_status = NT_STATUS_NO_MEMORY; + nt_status = NT_STATUS(response.data.auth.nt_status); + + if (result == NSS_STATUS_SUCCESS && response.extra_data) { + if (NT_STATUS_IS_OK(nt_status)) { + if (NT_STATUS_IS_OK(nt_status = get_info3_from_ndr(mem_ctx, &response, &info3))) { + nt_status = + make_server_info_info3(mem_ctx, + user_info->internal_username.str, + user_info->smb_name.str, + user_info->domain.str, + server_info, + &info3); } - } else { - nt_status = NT_STATUS_NO_SUCH_USER; } - } else { - nt_status = NT_STATUS_LOGON_FAILURE; + } else if (NT_STATUS_IS_OK(nt_status)) { + nt_status = NT_STATUS_UNSUCCESSFUL; } return nt_status; diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c index fbeb6b6347..047ea1accc 100644 --- a/source3/nsswitch/winbindd.c +++ b/source3/nsswitch/winbindd.c @@ -375,6 +375,9 @@ void winbind_process_packet(struct winbindd_cli_state *state) { /* Process request */ + /* Ensure null termination of entire request */ + state->request.domain[sizeof(state->request.domain)-1]='\0'; + state->pid = state->request.pid; process_request(state); diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c index 20563ba7bd..abb6b9da75 100644 --- a/source3/nsswitch/winbindd_group.c +++ b/source3/nsswitch/winbindd_group.c @@ -196,6 +196,9 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) gid_t gid; int gr_mem_len; + /* Ensure null termination */ + state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0'; + DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid, state->request.data.groupname)); @@ -783,6 +786,9 @@ enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) int i; TALLOC_CTX *mem_ctx; + /* Ensure null termination */ + state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + DEBUG(3, ("[%5d]: getgroups %s\n", state->pid, state->request.data.username)); diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h index 0f0e40a2ec..9eea94e7c0 100644 --- a/source3/nsswitch/winbindd_nss.h +++ b/source3/nsswitch/winbindd_nss.h @@ -36,7 +36,7 @@ /* Update this when you change the interface. */ -#define WINBIND_INTERFACE_VERSION 4 +#define WINBIND_INTERFACE_VERSION 5 /* Socket commands */ @@ -107,6 +107,12 @@ enum winbindd_cmd { WINBINDD_NUM_CMDS }; +#define WINBIND_PAM_INFO3_NDR 0x0001 +#define WINBIND_PAM_INFO3_TEXT 0x0002 +#define WINBIND_PAM_NTKEY 0x0004 +#define WINBIND_PAM_LMKEY 0x0008 +#define WINBIND_PAM_CONTACT_TRUSTDOM 0x0010 + /* Winbind request structure */ struct winbindd_request { @@ -132,6 +138,8 @@ struct winbindd_request { uint16 lm_resp_len; fstring nt_resp; uint16 nt_resp_len; + fstring workstation; + uint32 flags; } auth_crap; struct { fstring user; @@ -216,6 +224,8 @@ struct winbindd_response { fstring nt_status_string; fstring error_string; int pam_error; + char nt_session_key[16]; + char first_8_lm_hash[8]; } auth; } data; diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index a73b2ccd29..a8b508a49c 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -23,17 +23,41 @@ */ #include "winbindd.h" - #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND + +static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, + struct winbindd_cli_state *state, + NET_USER_INFO_3 *info3) +{ + prs_struct ps; + uint32 size; + if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) { + return NT_STATUS_NO_MEMORY; + } + if (!net_io_user_info3("", info3, &ps, 1, 3)) { + prs_mem_free(&ps); + return NT_STATUS_UNSUCCESSFUL; + } + + size = prs_data_size(&ps); + state->response.extra_data = memdup(prs_data_p(&ps), size); + if (!state->response.extra_data) { + prs_mem_free(&ps); + return NT_STATUS_NO_MEMORY; + } + state->response.length += size; + prs_mem_free(&ps); + return NT_STATUS_OK; +} + /* Return a password structure from a username. */ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) { NTSTATUS result; fstring name_domain, name_user; - int passlen; unsigned char trust_passwd[16]; time_t last_change_time; uint32 smb_uid_low; @@ -46,6 +70,12 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) extern pstring global_myname; + /* Ensure null termination */ + state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0'; + + /* Ensure null termination */ + state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0'; + DEBUG(3, ("[%5d]: pam auth %s\n", state->pid, state->request.data.auth.user)); @@ -64,8 +94,6 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) goto done; } - passlen = strlen(state->request.data.auth.pass); - { unsigned char local_lm_response[24]; unsigned char local_nt_response[24]; @@ -140,34 +168,67 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) NET_USER_INFO_3 info3; struct cli_state *cli = NULL; TALLOC_CTX *mem_ctx = NULL; - const char *domain = NULL; + char *user = NULL; + char *domain = NULL; + char *contact_domain; + char *workstation; DATA_BLOB lm_resp, nt_resp; extern pstring global_myname; - DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid, - state->request.data.auth_crap.domain, state->request.data.auth_crap.user)); + /* Ensure null termination */ + state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]='\0'; + + /* Ensure null termination */ + state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]='\0'; - if (!(mem_ctx = talloc_init_named("winbind pam auth crap for %s", state->request.data.auth.user))) { + if (!(mem_ctx = talloc_init_named("winbind pam auth crap for (utf8) %s", state->request.data.auth.user))) { DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n")); result = NT_STATUS_NO_MEMORY; goto done; } + if (pull_utf8_talloc(mem_ctx, &user, state->request.data.auth_crap.user) < 0) { + DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n")); + } + if (*state->request.data.auth_crap.domain) { - domain = talloc_strdup(mem_ctx, state->request.data.auth_crap.domain); + if (pull_utf8_talloc(mem_ctx, &domain, state->request.data.auth_crap.domain) < 0) { + DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n")); + } } else if (lp_winbind_use_default_domain()) { - domain = talloc_strdup(mem_ctx, lp_workgroup()); + domain = lp_workgroup(); } else { - DEBUG(5,("no domain specified with username (%s) - failing auth\n", state->request.data.auth.user)); + DEBUG(5,("no domain specified with username (%s) - failing auth\n", + user)); result = NT_STATUS_INVALID_PARAMETER; goto done; } - if (!domain) { - DEBUG(0,("winbindd_pam_auth_crap: talloc_strdup failed!\n")); - result = NT_STATUS_NO_MEMORY; + DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid, + domain, user)); + + if (lp_allow_trusted_domains() && (state->request.data.auth_crap.flags & WINBIND_PAM_CONTACT_TRUSTDOM)) { + contact_domain = domain; + } else { + contact_domain = lp_workgroup(); + } + + if (*state->request.data.auth_crap.workstation) { + if (pull_utf8_talloc(mem_ctx, &workstation, state->request.data.auth_crap.workstation) < 0) { + DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n")); + } + } else { + workstation = global_myname; + } + + if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp) + || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) { + DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", + state->request.data.auth_crap.lm_resp_len, + state->request.data.auth_crap.nt_resp_len)); + result = NT_STATUS_INVALID_PARAMETER; goto done; } @@ -175,13 +236,15 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len); /* - * Get the machine account password for our primary domain + * Get the machine account password for the domain to contact. + * This is either our own domain for a workstation, or possibly + * any domain for a PDC with trusted domains. */ - if (!secrets_fetch_trust_account_password( - lp_workgroup(), trust_passwd, &last_change_time)) { + if (!secrets_fetch_trust_account_password ( + contact_domain, trust_passwd, &last_change_time)) { DEBUG(0, ("winbindd_pam_auth: could not fetch trust account " - "password for domain %s\n", lp_workgroup())); + "password for domain %s\n", contact_domain)); result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; goto done; } @@ -189,7 +252,7 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) ZERO_STRUCT(info3); /* Don't shut this down - it belongs to the connection cache code */ - result = cm_get_netlogon_cli(lp_workgroup(), trust_passwd, &cli); + result = cm_get_netlogon_cli(contact_domain, trust_passwd, &cli); if (!NT_STATUS_IS_OK(result)) { DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", nt_errstr(result))); @@ -197,27 +260,43 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) } result = cli_netlogon_sam_network_logon(cli, mem_ctx, - state->request.data.auth_crap.user, domain, - global_myname, state->request.data.auth_crap.chal, + user, domain, + workstation, state->request.data.auth_crap.chal, lm_resp, nt_resp, &info3); if (NT_STATUS_IS_OK(result)) { uni_group_cache_store_netlogon(mem_ctx, &info3); + if (state->request.data.auth_crap.flags & WINBIND_PAM_INFO3_NDR) { + result = append_info3_as_ndr(mem_ctx, state, &info3); + } + +#if 0 + /* we don't currently do this stuff right */ + if (state->request.data.auth_crap.flags & WINBIND_PAM_NTKEY) { + SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) == sizeof(info3.user_sess_key)); + memcpy(state->response.data.auth.nt_session_key, info3.user_sess_key, sizeof(state->response.data.auth.nt_session_key) /* 16 */); + } + if (state->request.data.auth_crap.flags & WINBIND_PAM_LMKEY) { + SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) <= sizeof(info3.user_sess_key)); + memcpy(state->response.data.auth.first_8_lm_hash, info3.padding, sizeof(state->response.data.auth.nt_session_key) /* 16 */); + } +#endif } done: state->response.data.auth.nt_status = NT_STATUS_V(result); - fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); - fstrcpy(state->response.data.auth.error_string, nt_errstr(result)); + push_utf8_fstring(state->response.data.auth.nt_status_string, nt_errstr(result)); + push_utf8_fstring(state->response.data.auth.error_string, nt_errstr(result)); state->response.data.auth.pam_error = nt_status_to_pam(result); - DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("NTLM CRAP authenticaion for user [%s]\\[%s] returned %s (PAM: %d)\n", - state->request.data.auth_crap.domain, - state->request.data.auth_crap.user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); + DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, + ("NTLM CRAP authenticaion for user [%s]\\[%s] returned %s (PAM: %d)\n", + domain, + user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); if (mem_ctx) talloc_destroy(mem_ctx); diff --git a/source3/nsswitch/winbindd_sid.c b/source3/nsswitch/winbindd_sid.c index 372898a08a..44f857d6be 100644 --- a/source3/nsswitch/winbindd_sid.c +++ b/source3/nsswitch/winbindd_sid.c @@ -36,6 +36,9 @@ enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state) fstring name; fstring dom_name; + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + DEBUG(3, ("[%5d]: lookupsid %s\n", state->pid, state->request.data.sid)); @@ -79,6 +82,12 @@ enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state) DOM_SID sid; struct winbindd_domain *domain; + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.name.dom_name)-1]='\0'; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.name.name)-1]='\0'; + DEBUG(3, ("[%5d]: lookupname %s%s%s\n", state->pid, state->request.data.name.dom_name, lp_winbind_separator(), @@ -112,6 +121,9 @@ enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state) { DOM_SID sid; + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + DEBUG(3, ("[%5d]: sid to uid %s\n", state->pid, state->request.data.sid)); @@ -139,6 +151,9 @@ enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state) { DOM_SID sid; + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + DEBUG(3, ("[%5d]: sid to gid %s\n", state->pid, state->request.data.sid)); diff --git a/source3/nsswitch/winbindd_user.c b/source3/nsswitch/winbindd_user.c index 55593d6ae5..4f57fd2c72 100644 --- a/source3/nsswitch/winbindd_user.c +++ b/source3/nsswitch/winbindd_user.c @@ -103,6 +103,9 @@ enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state) struct winbindd_domain *domain; TALLOC_CTX *mem_ctx; + /* Ensure null termination */ + state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid, state->request.data.username)); diff --git a/source3/nsswitch/winbindd_wins.c b/source3/nsswitch/winbindd_wins.c index 8f9a7414bd..8ddd5dc10d 100644 --- a/source3/nsswitch/winbindd_wins.c +++ b/source3/nsswitch/winbindd_wins.c @@ -122,6 +122,9 @@ enum winbindd_result winbindd_wins_byip(struct winbindd_cli_state *state) int i, count, maxlen, size; struct node_status *status; + /* Ensure null termination */ + state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0'; + DEBUG(3, ("[%5d]: wins_byip %s\n", state->pid, state->request.data.winsreq)); @@ -166,6 +169,9 @@ enum winbindd_result winbindd_wins_byname(struct winbindd_cli_state *state) fstring response; char * addr; + /* Ensure null termination */ + state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0'; + DEBUG(3, ("[%5d]: wins_byname %s\n", state->pid, state->request.data.winsreq)); diff --git a/source3/rpc_parse/parse_net.c b/source3/rpc_parse/parse_net.c index 46fdce63ff..da49a6531d 100644 --- a/source3/rpc_parse/parse_net.c +++ b/source3/rpc_parse/parse_net.c @@ -1335,7 +1335,7 @@ void init_net_user_info3(TALLOC_CTX *ctx, NET_USER_INFO_3 *usr, ********************************************************************/ BOOL net_io_user_info3(const char *desc, NET_USER_INFO_3 *usr, prs_struct *ps, - int depth, uint16 validation_level) + int depth, uint16 validation_level) { int i; -- cgit