From 869348dfcbf66a88998cb80c00902848db96901f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 6 Apr 2004 16:44:24 +0000 Subject: r84: Implement --required-membership-of=, an ntlm_auth option that restricts all authentication to members of this particular group. Also implement an option to allow ntlm_auth to get 'squashed' error codes, which are safer to communicate to remote network clients. Andrew Bartlett (This used to be commit eb1c1b5eb086f49a230142ad2de45dc0e9691df3) --- source3/nsswitch/winbindd_nss.h | 3 + source3/nsswitch/winbindd_pam.c | 119 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 5 deletions(-) (limited to 'source3/nsswitch') diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h index d0c80412df..4ddce11951 100644 --- a/source3/nsswitch/winbindd_nss.h +++ b/source3/nsswitch/winbindd_nss.h @@ -157,6 +157,7 @@ typedef struct winbindd_gr { #define WBFLAG_ALLOCATE_RID 0x0040 #define WBFLAG_PAM_UNIX_NAME 0x0080 #define WBFLAG_PAM_AFS_TOKEN 0x0100 +#define WBFLAG_PAM_NT_STATUS_SQUASH 0x0200 /* Winbind request structure */ @@ -179,6 +180,7 @@ struct winbindd_request { character is. */ fstring user; fstring pass; + fstring required_membership_sid; } auth; /* pam_winbind auth module */ struct { unsigned char chal[8]; @@ -189,6 +191,7 @@ struct winbindd_request { fstring nt_resp; uint16 nt_resp_len; fstring workstation; + fstring required_membership_sid; } auth_crap; struct { fstring user; diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index f3e7daa6bf..a2ebe0ddbc 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -54,8 +54,98 @@ static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } +static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, + NET_USER_INFO_3 *info3, + const char *group_sid) +{ + DOM_SID required_membership_sid; + DOM_SID *all_sids; + size_t num_all_sids = (2 + info3->num_groups2 + info3->num_other_sids); + size_t i, j = 0; + + /* Parse the 'required group' SID */ + + if (!group_sid || !group_sid[0]) { + /* NO sid supplied, all users may access */ + return NT_STATUS_OK; + } + + if (!string_to_sid(&required_membership_sid, group_sid)) { + DEBUG(0, ("winbindd_pam_auth: could not parse %s as a SID!", + group_sid)); + + return NT_STATUS_INVALID_PARAMETER; + } + + all_sids = talloc(mem_ctx, sizeof(DOM_SID) * num_all_sids); + if (!all_sids) + return NT_STATUS_NO_MEMORY; + + /* and create (by appending rids) the 'domain' sids */ + + sid_copy(&all_sids[0], &(info3->dom_sid.sid)); + + if (!sid_append_rid(&all_sids[0], info3->user_rid)) { + DEBUG(3,("could not append user's primary RID 0x%x\n", + info3->user_rid)); + + return NT_STATUS_INVALID_PARAMETER; + } + j++; + + sid_copy(&all_sids[1], &(info3->dom_sid.sid)); + + if (!sid_append_rid(&all_sids[1], info3->group_rid)) { + DEBUG(3,("could not append additional group rid 0x%x\n", + info3->group_rid)); + + return NT_STATUS_INVALID_PARAMETER; + } + j++; + + for (i = 0; i < info3->num_groups2; i++) { + + sid_copy(&all_sids[j], &(info3->dom_sid.sid)); + + if (!sid_append_rid(&all_sids[j], info3->gids[j].g_rid)) { + DEBUG(3,("could not append additional group rid 0x%x\n", + info3->gids[j].g_rid)); + + return NT_STATUS_INVALID_PARAMETER; + } + j++; + } + + /* Copy 'other' sids. We need to do sid filtering here to + prevent possible elevation of privileges. See: + + http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp + */ + + for (i = 0; i < info3->num_other_sids; j++) { + sid_copy(&all_sids[info3->num_groups2 + i + 2], + &info3->other_sids[j].sid); + j++; + } + + for (i = 0; i < j; i++) { + fstring sid1, sid2; + DEBUG(10, ("User has SID: %s\n", + sid_to_string(sid1, &all_sids[i]))); + if (sid_equal(&required_membership_sid, &all_sids[i])) { + DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n", + sid_to_string(sid1, &required_membership_sid), sid_to_string(sid2, &all_sids[i]))); + return NT_STATUS_OK; + } + } + + /* Do not distinguish this error from a wrong username/pw */ + + return NT_STATUS_LOGON_FAILURE; +} + /********************************************************************** - Authenticate a user with a clear test password + Authenticate a user with a clear text password **********************************************************************/ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) @@ -125,7 +215,7 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) } if (!(contact_domain = find_our_domain())) { - DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n", + DEBUG(1, ("Authentication for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n", state->request.data.auth.user, name_domain, name_user)); result = NT_STATUS_NO_SUCH_USER; goto done; @@ -190,8 +280,16 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) if (NT_STATUS_IS_OK(result)) { netsamlogon_cache_store( cli->mem_ctx, &info3 ); wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); + + /* Check if the user is in the right group */ + + if (!NT_STATUS_IS_OK(result = check_info3_in_group(mem_ctx, &info3, state->request.data.auth.required_membership_sid))) { + DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", + state->request.data.auth.user, + state->request.data.auth.required_membership_sid)); + } } - + done: /* give us a more useful (more correct?) error code */ if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) { @@ -355,7 +453,7 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) if ( IS_DC ) { if (!(contact_domain = find_domain_from_name(name_domain))) { DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n", - state->request.data.auth.user, name_domain, name_user, name_domain)); + state->request.data.auth_crap.user, name_domain, name_user, name_domain)); result = NT_STATUS_NO_SUCH_USER; goto done; } @@ -369,7 +467,7 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) if (!(contact_domain = find_our_domain())) { DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n", - state->request.data.auth.user, name_domain, name_user)); + state->request.data.auth_crap.user, name_domain, name_user)); result = NT_STATUS_NO_SUCH_USER; goto done; } @@ -434,6 +532,13 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) netsamlogon_cache_store( cli->mem_ctx, &info3 ); wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); + if (!NT_STATUS_IS_OK(result = check_info3_in_group(mem_ctx, &info3, state->request.data.auth_crap.required_membership_sid))) { + DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", + state->request.data.auth_crap.user, + state->request.data.auth_crap.required_membership_sid)); + goto done; + } + if (state->request.flags & WBFLAG_PAM_INFO3_NDR) { result = append_info3_as_ndr(mem_ctx, state, &info3); } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) { @@ -477,6 +582,10 @@ done: if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) { result = NT_STATUS_NO_LOGON_SERVERS; } + + if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) { + result = nt_status_squash(result); + } state->response.data.auth.nt_status = NT_STATUS_V(result); push_utf8_fstring(state->response.data.auth.nt_status_string, nt_errstr(result)); -- cgit