2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/cli_samr.h"
29 #include "rpc_client/cli_samr.h"
30 #include "../librpc/gen_ndr/ndr_netlogon.h"
31 #include "rpc_client/cli_netlogon.h"
33 #include "../lib/crypto/arcfour.h"
34 #include "../libcli/security/dom_sid.h"
36 #include "../librpc/gen_ndr/krb5pac.h"
39 #define DBGC_CLASS DBGC_WINBIND
41 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
43 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
44 struct winbindd_cli_state *state,
45 struct netr_SamInfo3 *info3)
50 state->response->data.auth.info3.logon_time =
51 nt_time_to_unix(info3->base.last_logon);
52 state->response->data.auth.info3.logoff_time =
53 nt_time_to_unix(info3->base.last_logoff);
54 state->response->data.auth.info3.kickoff_time =
55 nt_time_to_unix(info3->base.acct_expiry);
56 state->response->data.auth.info3.pass_last_set_time =
57 nt_time_to_unix(info3->base.last_password_change);
58 state->response->data.auth.info3.pass_can_change_time =
59 nt_time_to_unix(info3->base.allow_password_change);
60 state->response->data.auth.info3.pass_must_change_time =
61 nt_time_to_unix(info3->base.force_password_change);
63 state->response->data.auth.info3.logon_count = info3->base.logon_count;
64 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
66 state->response->data.auth.info3.user_rid = info3->base.rid;
67 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
68 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
70 state->response->data.auth.info3.num_groups = info3->base.groups.count;
71 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
73 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
74 state->response->data.auth.info3.num_other_sids = info3->sidcount;
76 fstrcpy(state->response->data.auth.info3.user_name,
77 info3->base.account_name.string);
78 fstrcpy(state->response->data.auth.info3.full_name,
79 info3->base.full_name.string);
80 fstrcpy(state->response->data.auth.info3.logon_script,
81 info3->base.logon_script.string);
82 fstrcpy(state->response->data.auth.info3.profile_path,
83 info3->base.profile_path.string);
84 fstrcpy(state->response->data.auth.info3.home_dir,
85 info3->base.home_directory.string);
86 fstrcpy(state->response->data.auth.info3.dir_drive,
87 info3->base.home_drive.string);
89 fstrcpy(state->response->data.auth.info3.logon_srv,
90 info3->base.logon_server.string);
91 fstrcpy(state->response->data.auth.info3.logon_dom,
92 info3->base.domain.string);
94 ex = talloc_strdup(state->mem_ctx, "");
95 NT_STATUS_HAVE_NO_MEMORY(ex);
97 for (i=0; i < info3->base.groups.count; i++) {
98 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
99 info3->base.groups.rids[i].rid,
100 info3->base.groups.rids[i].attributes);
101 NT_STATUS_HAVE_NO_MEMORY(ex);
104 for (i=0; i < info3->sidcount; i++) {
107 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
108 NT_STATUS_HAVE_NO_MEMORY(sid);
110 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
112 info3->sids[i].attributes);
113 NT_STATUS_HAVE_NO_MEMORY(ex);
118 state->response->extra_data.data = ex;
119 state->response->length += talloc_get_size(ex);
124 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
125 struct winbindd_cli_state *state,
126 struct netr_SamInfo3 *info3)
129 enum ndr_err_code ndr_err;
131 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
132 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
133 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
134 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
135 return ndr_map_error2ntstatus(ndr_err);
138 state->response->extra_data.data = blob.data;
139 state->response->length += blob.length;
144 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
145 struct winbindd_cli_state *state,
146 const struct netr_SamInfo3 *info3,
147 const char *name_domain,
148 const char *name_user)
150 /* We've been asked to return the unix username, per
151 'winbind use default domain' settings and the like */
153 const char *nt_username, *nt_domain;
155 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
157 /* If the server didn't give us one, just use the one
159 nt_domain = name_domain;
162 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
164 /* If the server didn't give us one, just use the one
166 nt_username = name_user;
169 fill_domain_username(state->response->data.auth.unix_username,
170 nt_domain, nt_username, true);
172 DEBUG(5,("Setting unix username to [%s]\n",
173 state->response->data.auth.unix_username));
178 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
179 struct winbindd_cli_state *state,
180 const struct netr_SamInfo3 *info3,
181 const char *name_domain,
182 const char *name_user)
184 char *afsname = NULL;
188 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
189 if (afsname == NULL) {
190 return NT_STATUS_NO_MEMORY;
193 afsname = talloc_string_sub(mem_ctx,
194 lp_afs_username_map(),
196 afsname = talloc_string_sub(mem_ctx, afsname,
198 afsname = talloc_string_sub(mem_ctx, afsname,
202 struct dom_sid user_sid;
205 sid_compose(&user_sid, info3->base.domain_sid,
207 sid_to_fstring(sidstr, &user_sid);
208 afsname = talloc_string_sub(mem_ctx, afsname,
212 if (afsname == NULL) {
213 return NT_STATUS_NO_MEMORY;
218 DEBUG(10, ("Generating token for user %s\n", afsname));
220 cell = strchr(afsname, '@');
223 return NT_STATUS_NO_MEMORY;
229 token = afs_createtoken_str(afsname, cell);
233 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
235 if (state->response->extra_data.data == NULL) {
236 return NT_STATUS_NO_MEMORY;
238 state->response->length +=
239 strlen((const char *)state->response->extra_data.data)+1;
244 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
245 const char *group_sid)
247 * Check whether a user belongs to a group or list of groups.
249 * @param mem_ctx talloc memory context.
250 * @param info3 user information, including group membership info.
251 * @param group_sid One or more groups , separated by commas.
253 * @return NT_STATUS_OK on success,
254 * NT_STATUS_LOGON_FAILURE if the user does not belong,
255 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
258 struct dom_sid *require_membership_of_sid;
259 size_t num_require_membership_of_sid;
264 struct nt_user_token *token;
265 TALLOC_CTX *frame = talloc_stackframe();
268 /* Parse the 'required group' SID */
270 if (!group_sid || !group_sid[0]) {
271 /* NO sid supplied, all users may access */
275 token = talloc_zero(talloc_tos(), struct nt_user_token);
277 DEBUG(0, ("talloc failed\n"));
279 return NT_STATUS_NO_MEMORY;
282 num_require_membership_of_sid = 0;
283 require_membership_of_sid = NULL;
287 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
288 if (!string_to_sid(&sid, req_sid)) {
289 DEBUG(0, ("check_info3_in_group: could not parse %s "
290 "as a SID!", req_sid));
292 return NT_STATUS_INVALID_PARAMETER;
295 status = add_sid_to_array(talloc_tos(), &sid,
296 &require_membership_of_sid,
297 &num_require_membership_of_sid);
298 if (!NT_STATUS_IS_OK(status)) {
299 DEBUG(0, ("add_sid_to_array failed\n"));
305 status = sid_array_from_info3(talloc_tos(), info3,
309 if (!NT_STATUS_IS_OK(status)) {
314 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
316 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
318 DEBUG(3, ("could not add aliases: %s\n",
324 debug_nt_user_token(DBGC_CLASS, 10, token);
326 for (i=0; i<num_require_membership_of_sid; i++) {
327 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
328 &require_membership_of_sid[i])));
329 if (nt_token_check_sid(&require_membership_of_sid[i],
331 DEBUG(10, ("Access ok\n"));
337 /* Do not distinguish this error from a wrong username/pw */
340 return NT_STATUS_LOGON_FAILURE;
343 struct winbindd_domain *find_auth_domain(uint8_t flags,
344 const char *domain_name)
346 struct winbindd_domain *domain;
349 domain = find_domain_from_name_noinit(domain_name);
350 if (domain == NULL) {
351 DEBUG(3, ("Authentication for domain [%s] refused "
352 "as it is not a trusted domain\n",
358 if (strequal(domain_name, get_global_sam_name())) {
359 return find_domain_from_name_noinit(domain_name);
362 /* we can auth against trusted domains */
363 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
364 domain = find_domain_from_name_noinit(domain_name);
365 if (domain == NULL) {
366 DEBUG(3, ("Authentication for domain [%s] skipped "
367 "as it is not a trusted domain\n",
374 return find_our_domain();
377 static void fill_in_password_policy(struct winbindd_response *r,
378 const struct samr_DomInfo1 *p)
380 r->data.auth.policy.min_length_password =
381 p->min_password_length;
382 r->data.auth.policy.password_history =
383 p->password_history_length;
384 r->data.auth.policy.password_properties =
385 p->password_properties;
386 r->data.auth.policy.expire =
387 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
388 r->data.auth.policy.min_passwordage =
389 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
392 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
393 struct winbindd_cli_state *state)
395 struct winbindd_methods *methods;
396 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
397 struct samr_DomInfo1 password_policy;
399 if ( !winbindd_can_contact_domain( domain ) ) {
400 DEBUG(5,("fillup_password_policy: No inbound trust to "
401 "contact domain %s\n", domain->name));
402 return NT_STATUS_NOT_SUPPORTED;
405 methods = domain->methods;
407 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
408 if (NT_STATUS_IS_ERR(status)) {
412 fill_in_password_policy(state->response, &password_policy);
417 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
419 uint16 *lockout_threshold)
421 struct winbindd_methods *methods;
422 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
423 struct samr_DomInfo12 lockout_policy;
425 *lockout_threshold = 0;
427 methods = domain->methods;
429 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
430 if (NT_STATUS_IS_ERR(status)) {
434 *lockout_threshold = lockout_policy.lockout_threshold;
439 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
441 uint32 *password_properties)
443 struct winbindd_methods *methods;
444 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
445 struct samr_DomInfo1 password_policy;
447 *password_properties = 0;
449 methods = domain->methods;
451 status = methods->password_policy(domain, mem_ctx, &password_policy);
452 if (NT_STATUS_IS_ERR(status)) {
456 *password_properties = password_policy.password_properties;
463 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
466 bool *internal_ccache)
468 /* accept FILE and WRFILE as krb5_cc_type from the client and then
469 * build the full ccname string based on the user's uid here -
472 const char *gen_cc = NULL;
474 *internal_ccache = true;
480 if (strequal(type, "FILE")) {
481 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
482 } else if (strequal(type, "WRFILE")) {
483 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
485 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
489 *internal_ccache = false;
493 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
496 if (gen_cc == NULL) {
497 DEBUG(0,("out of memory\n"));
501 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
506 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
508 const char *type = state->request->data.auth.krb5_cc_type;
510 state->response->data.auth.krb5ccname[0] = '\0';
512 if (type[0] == '\0') {
516 if (!strequal(type, "FILE") &&
517 !strequal(type, "WRFILE")) {
518 DEBUG(10,("won't return krbccname for a %s type ccache\n",
523 fstrcpy(state->response->data.auth.krb5ccname, cc);
528 uid_t get_uid_from_request(struct winbindd_request *request)
532 uid = request->data.auth.uid;
535 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
541 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
543 return get_uid_from_request(state->request);
546 /**********************************************************************
547 Authenticate a user with a clear text password using Kerberos and fill up
549 **********************************************************************/
551 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
552 struct winbindd_cli_state *state,
553 struct netr_SamInfo3 **info3)
556 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
557 krb5_error_code krb5_ret;
558 const char *cc = NULL;
559 const char *principal_s = NULL;
560 const char *service = NULL;
562 fstring name_domain, name_user;
563 time_t ticket_lifetime = 0;
564 time_t renewal_until = 0;
567 time_t time_offset = 0;
568 bool internal_ccache = true;
569 struct PAC_LOGON_INFO *logon_info = NULL;
574 * prepare a krb5_cc_cache string for the user */
576 uid = get_uid_from_state(state);
578 DEBUG(0,("no valid uid\n"));
581 cc = generate_krb5_ccache(state->mem_ctx,
582 state->request->data.auth.krb5_cc_type,
583 state->request->data.auth.uid,
586 return NT_STATUS_NO_MEMORY;
591 * get kerberos properties */
593 if (domain->private_data) {
594 ads = (ADS_STRUCT *)domain->private_data;
595 time_offset = ads->auth.time_offset;
600 * do kerberos auth and setup ccache as the user */
602 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
604 realm = domain->alt_name;
607 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
608 if (principal_s == NULL) {
609 return NT_STATUS_NO_MEMORY;
612 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
613 if (service == NULL) {
614 return NT_STATUS_NO_MEMORY;
617 /* if this is a user ccache, we need to act as the user to let the krb5
618 * library handle the chown, etc. */
620 /************************ ENTERING NON-ROOT **********************/
622 if (!internal_ccache) {
623 set_effective_uid(uid);
624 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
627 result = kerberos_return_pac(state->mem_ctx,
629 state->request->data.auth.pass,
636 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
639 if (!internal_ccache) {
640 gain_root_privilege();
643 /************************ RETURNED TO ROOT **********************/
645 if (!NT_STATUS_IS_OK(result)) {
649 *info3 = &logon_info->info3;
651 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
654 /* if we had a user's ccache then return that string for the pam
657 if (!internal_ccache) {
659 setup_return_cc_name(state, cc);
661 result = add_ccache_to_list(principal_s,
664 state->request->data.auth.user,
672 if (!NT_STATUS_IS_OK(result)) {
673 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
678 /* need to delete the memory cred cache, it is not used anymore */
680 krb5_ret = ads_kdestroy(cc);
682 DEBUG(3,("winbindd_raw_kerberos_login: "
683 "could not destroy krb5 credential cache: "
684 "%s\n", error_message(krb5_ret)));
693 /* we could have created a new credential cache with a valid tgt in it
694 * but we werent able to get or verify the service ticket for this
695 * local host and therefor didn't get the PAC, we need to remove that
696 * cache entirely now */
698 krb5_ret = ads_kdestroy(cc);
700 DEBUG(3,("winbindd_raw_kerberos_login: "
701 "could not destroy krb5 credential cache: "
702 "%s\n", error_message(krb5_ret)));
705 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
706 DEBUG(3,("winbindd_raw_kerberos_login: "
707 "could not remove ccache for user %s\n",
708 state->request->data.auth.user));
713 return NT_STATUS_NOT_SUPPORTED;
714 #endif /* HAVE_KRB5 */
717 /****************************************************************
718 ****************************************************************/
720 bool check_request_flags(uint32_t flags)
722 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
723 WBFLAG_PAM_INFO3_TEXT |
724 WBFLAG_PAM_INFO3_NDR;
726 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
727 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
728 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
729 !(flags & flags_edata) ) {
733 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
739 /****************************************************************
740 ****************************************************************/
742 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
743 struct netr_SamInfo3 *info3,
744 const char *name_domain,
745 const char *name_user)
748 uint32_t flags = state->request->flags;
750 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
751 memcpy(state->response->data.auth.user_session_key,
753 sizeof(state->response->data.auth.user_session_key)
757 if (flags & WBFLAG_PAM_LMKEY) {
758 memcpy(state->response->data.auth.first_8_lm_hash,
759 info3->base.LMSessKey.key,
760 sizeof(state->response->data.auth.first_8_lm_hash)
764 if (flags & WBFLAG_PAM_UNIX_NAME) {
765 result = append_unix_username(state->mem_ctx, state, info3,
766 name_domain, name_user);
767 if (!NT_STATUS_IS_OK(result)) {
768 DEBUG(10,("Failed to append Unix Username: %s\n",
774 /* currently, anything from here on potentially overwrites extra_data. */
776 if (flags & WBFLAG_PAM_INFO3_NDR) {
777 result = append_info3_as_ndr(state->mem_ctx, state, info3);
778 if (!NT_STATUS_IS_OK(result)) {
779 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
785 if (flags & WBFLAG_PAM_INFO3_TEXT) {
786 result = append_info3_as_txt(state->mem_ctx, state, info3);
787 if (!NT_STATUS_IS_OK(result)) {
788 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
794 if (flags & WBFLAG_PAM_AFS_TOKEN) {
795 result = append_afs_token(state->mem_ctx, state, info3,
796 name_domain, name_user);
797 if (!NT_STATUS_IS_OK(result)) {
798 DEBUG(10,("Failed to append AFS token: %s\n",
807 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
808 struct winbindd_cli_state *state,
809 struct netr_SamInfo3 **info3)
811 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
812 uint16 max_allowed_bad_attempts;
813 fstring name_domain, name_user;
815 enum lsa_SidType type;
816 uchar new_nt_pass[NT_HASH_LEN];
817 const uint8 *cached_nt_pass;
818 const uint8 *cached_salt;
819 struct netr_SamInfo3 *my_info3;
820 time_t kickoff_time, must_change_time;
821 bool password_good = false;
823 struct winbindd_tdc_domain *tdc_domain = NULL;
830 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
832 /* Parse domain and username */
834 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
837 if (!lookup_cached_name(name_domain,
841 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
842 return NT_STATUS_NO_SUCH_USER;
845 if (type != SID_NAME_USER) {
846 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
847 return NT_STATUS_LOGON_FAILURE;
850 result = winbindd_get_creds(domain,
856 if (!NT_STATUS_IS_OK(result)) {
857 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
863 E_md4hash(state->request->data.auth.pass, new_nt_pass);
865 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
866 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
868 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
872 /* In this case we didn't store the nt_hash itself,
873 but the MD5 combination of salt + nt_hash. */
874 uchar salted_hash[NT_HASH_LEN];
875 E_md5hash(cached_salt, new_nt_pass, salted_hash);
877 password_good = (memcmp(cached_nt_pass, salted_hash,
880 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
881 password_good = (memcmp(cached_nt_pass, new_nt_pass,
887 /* User *DOES* know the password, update logon_time and reset
890 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
892 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
893 return NT_STATUS_ACCOUNT_LOCKED_OUT;
896 if (my_info3->base.acct_flags & ACB_DISABLED) {
897 return NT_STATUS_ACCOUNT_DISABLED;
900 if (my_info3->base.acct_flags & ACB_WSTRUST) {
901 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
904 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
905 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
908 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
909 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
912 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
913 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
914 my_info3->base.acct_flags));
915 return NT_STATUS_LOGON_FAILURE;
918 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
919 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
920 return NT_STATUS_ACCOUNT_EXPIRED;
923 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
924 if (must_change_time != 0 && must_change_time < time(NULL)) {
925 /* we allow grace logons when the password has expired */
926 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
927 /* return NT_STATUS_PASSWORD_EXPIRED; */
932 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
933 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
934 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
935 /* used to cope with the case winbindd starting without network. */
936 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
939 const char *cc = NULL;
941 const char *principal_s = NULL;
942 const char *service = NULL;
943 bool internal_ccache = false;
945 uid = get_uid_from_state(state);
947 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
948 return NT_STATUS_INVALID_PARAMETER;
951 cc = generate_krb5_ccache(state->mem_ctx,
952 state->request->data.auth.krb5_cc_type,
953 state->request->data.auth.uid,
956 return NT_STATUS_NO_MEMORY;
959 realm = domain->alt_name;
962 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
963 if (principal_s == NULL) {
964 return NT_STATUS_NO_MEMORY;
967 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
968 if (service == NULL) {
969 return NT_STATUS_NO_MEMORY;
972 if (!internal_ccache) {
974 setup_return_cc_name(state, cc);
976 result = add_ccache_to_list(principal_s,
979 state->request->data.auth.user,
983 time(NULL) + lp_winbind_cache_time(),
984 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
987 if (!NT_STATUS_IS_OK(result)) {
988 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
989 "to add ccache to list: %s\n",
994 #endif /* HAVE_KRB5 */
996 /* FIXME: we possibly should handle logon hours as well (does xp when
997 * offline?) see auth/auth_sam.c:sam_account_ok for details */
999 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1000 my_info3->base.bad_password_count = 0;
1002 result = winbindd_update_creds_by_info3(domain,
1003 state->request->data.auth.user,
1004 state->request->data.auth.pass,
1006 if (!NT_STATUS_IS_OK(result)) {
1007 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1008 nt_errstr(result)));
1012 return NT_STATUS_OK;
1016 /* User does *NOT* know the correct password, modify info3 accordingly */
1018 /* failure of this is not critical */
1019 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1020 if (!NT_STATUS_IS_OK(result)) {
1021 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1022 "Won't be able to honour account lockout policies\n"));
1025 /* increase counter */
1026 my_info3->base.bad_password_count++;
1028 if (max_allowed_bad_attempts == 0) {
1033 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1035 uint32 password_properties;
1037 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1038 if (!NT_STATUS_IS_OK(result)) {
1039 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1042 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1043 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1044 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1049 result = winbindd_update_creds_by_info3(domain,
1050 state->request->data.auth.user,
1054 if (!NT_STATUS_IS_OK(result)) {
1055 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1056 nt_errstr(result)));
1059 return NT_STATUS_LOGON_FAILURE;
1062 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1063 struct winbindd_cli_state *state,
1064 struct netr_SamInfo3 **info3)
1066 struct winbindd_domain *contact_domain;
1067 fstring name_domain, name_user;
1070 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1072 /* Parse domain and username */
1074 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1076 /* what domain should we contact? */
1079 if (!(contact_domain = find_domain_from_name(name_domain))) {
1080 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1081 state->request->data.auth.user, name_domain, name_user, name_domain));
1082 result = NT_STATUS_NO_SUCH_USER;
1087 if (is_myname(name_domain)) {
1088 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1089 result = NT_STATUS_NO_SUCH_USER;
1093 contact_domain = find_domain_from_name(name_domain);
1094 if (contact_domain == NULL) {
1095 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1096 state->request->data.auth.user, name_domain, name_user, name_domain));
1098 contact_domain = find_our_domain();
1102 if (contact_domain->initialized &&
1103 contact_domain->active_directory) {
1107 if (!contact_domain->initialized) {
1108 init_dc_connection(contact_domain);
1111 if (!contact_domain->active_directory) {
1112 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1113 return NT_STATUS_INVALID_LOGON_TYPE;
1116 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1121 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1122 const char *domain, const char *user,
1123 const DATA_BLOB *challenge,
1124 const DATA_BLOB *lm_resp,
1125 const DATA_BLOB *nt_resp,
1126 struct netr_SamInfo3 **pinfo3)
1128 struct auth_usersupplied_info *user_info = NULL;
1131 status = make_user_info(&user_info, user, user, domain, domain,
1132 global_myname(), lm_resp, nt_resp, NULL, NULL,
1133 NULL, AUTH_PASSWORD_RESPONSE);
1134 if (!NT_STATUS_IS_OK(status)) {
1135 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1139 /* We don't want any more mapping of the username */
1140 user_info->mapped_state = True;
1142 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1144 free_user_info(&user_info);
1145 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1146 return NT_STATUS_OK;
1149 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1150 TALLOC_CTX *mem_ctx,
1151 uint32 logon_parameters,
1153 const char *username,
1155 const char *workstation,
1156 const uint8 chal[8],
1157 DATA_BLOB lm_response,
1158 DATA_BLOB nt_response,
1159 struct netr_SamInfo3 **info3);
1161 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1162 struct winbindd_cli_state *state,
1163 struct netr_SamInfo3 **info3)
1166 struct rpc_pipe_client *netlogon_pipe;
1171 unsigned char local_lm_response[24];
1172 unsigned char local_nt_response[24];
1173 fstring name_domain, name_user;
1176 struct netr_SamInfo3 *my_info3 = NULL;
1180 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1182 /* Parse domain and username */
1184 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1186 /* do password magic */
1188 generate_random_buffer(chal, sizeof(chal));
1190 if (lp_client_ntlmv2_auth()) {
1191 DATA_BLOB server_chal;
1192 DATA_BLOB names_blob;
1193 DATA_BLOB nt_response;
1194 DATA_BLOB lm_response;
1195 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1197 /* note that the 'workgroup' here is a best guess - we don't know
1198 the server's domain at this point. The 'server name' is also
1201 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1203 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1204 state->request->data.auth.pass,
1207 &lm_response, &nt_response, NULL, NULL)) {
1208 data_blob_free(&names_blob);
1209 data_blob_free(&server_chal);
1210 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1211 result = NT_STATUS_NO_MEMORY;
1214 data_blob_free(&names_blob);
1215 data_blob_free(&server_chal);
1216 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1217 lm_response.length);
1218 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1219 nt_response.length);
1220 data_blob_free(&lm_response);
1221 data_blob_free(&nt_response);
1224 if (lp_client_lanman_auth()
1225 && SMBencrypt(state->request->data.auth.pass,
1227 local_lm_response)) {
1228 lm_resp = data_blob_talloc(state->mem_ctx,
1230 sizeof(local_lm_response));
1232 lm_resp = data_blob_null;
1234 SMBNTencrypt(state->request->data.auth.pass,
1238 nt_resp = data_blob_talloc(state->mem_ctx,
1240 sizeof(local_nt_response));
1243 if (strequal(name_domain, get_global_sam_name())) {
1244 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1246 result = winbindd_dual_auth_passdb(
1247 state->mem_ctx, name_domain, name_user,
1248 &chal_blob, &lm_resp, &nt_resp, info3);
1252 /* check authentication loop */
1255 netlogon_fn_t logon_fn;
1257 ZERO_STRUCTP(my_info3);
1260 result = cm_connect_netlogon(domain, &netlogon_pipe);
1262 if (!NT_STATUS_IS_OK(result)) {
1263 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1267 /* It is really important to try SamLogonEx here,
1268 * because in a clustered environment, we want to use
1269 * one machine account from multiple physical
1272 * With a normal SamLogon call, we must keep the
1273 * credentials chain updated and intact between all
1274 * users of the machine account (which would imply
1275 * cross-node communication for every NTLM logon).
1277 * (The credentials chain is not per NETLOGON pipe
1278 * connection, but globally on the server/client pair
1281 * When using SamLogonEx, the credentials are not
1282 * supplied, but the session key is implied by the
1283 * wrapping SamLogon context.
1285 * -- abartlet 21 April 2008
1288 logon_fn = domain->can_do_samlogon_ex
1289 ? rpccli_netlogon_sam_network_logon_ex
1290 : rpccli_netlogon_sam_network_logon;
1292 result = logon_fn(netlogon_pipe,
1295 domain->dcname, /* server name */
1296 name_user, /* user name */
1297 name_domain, /* target domain */
1298 global_myname(), /* workstation */
1305 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1306 && domain->can_do_samlogon_ex) {
1307 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1308 "retrying with NetSamLogon\n"));
1309 domain->can_do_samlogon_ex = false;
1313 /* We have to try a second time as cm_connect_netlogon
1314 might not yet have noticed that the DC has killed
1317 if (!rpccli_is_connected(netlogon_pipe)) {
1321 /* if we get access denied, a possible cause was that we had
1322 and open connection to the DC, but someone changed our
1323 machine account password out from underneath us using 'net
1324 rpc changetrustpw' */
1326 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1327 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1328 "ACCESS_DENIED. Maybe the trust account "
1329 "password was changed and we didn't know it. "
1330 "Killing connections to domain %s\n",
1332 invalidate_cm_connection(&domain->conn);
1336 } while ( (attempts < 2) && retry );
1338 /* handle the case where a NT4 DC does not fill in the acct_flags in
1339 * the samlogon reply info3. When accurate info3 is required by the
1340 * caller, we look up the account flags ourselve - gd */
1342 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1343 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1345 struct rpc_pipe_client *samr_pipe;
1346 struct policy_handle samr_domain_handle, user_pol;
1347 union samr_UserInfo *info = NULL;
1348 NTSTATUS status_tmp;
1351 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1352 &samr_pipe, &samr_domain_handle);
1354 if (!NT_STATUS_IS_OK(status_tmp)) {
1355 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1356 nt_errstr(status_tmp)));
1360 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1361 &samr_domain_handle,
1362 MAXIMUM_ALLOWED_ACCESS,
1366 if (!NT_STATUS_IS_OK(status_tmp)) {
1367 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1368 nt_errstr(status_tmp)));
1372 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1377 if (!NT_STATUS_IS_OK(status_tmp)) {
1378 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1379 nt_errstr(status_tmp)));
1380 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1384 acct_flags = info->info16.acct_flags;
1386 if (acct_flags == 0) {
1387 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1391 my_info3->base.acct_flags = acct_flags;
1393 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1395 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1403 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1404 struct winbindd_cli_state *state)
1406 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1407 NTSTATUS krb5_result = NT_STATUS_OK;
1408 fstring name_domain, name_user;
1410 fstring domain_user;
1411 struct netr_SamInfo3 *info3 = NULL;
1412 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1414 /* Ensure null termination */
1415 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1417 /* Ensure null termination */
1418 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1420 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1421 state->request->data.auth.user));
1423 /* Parse domain and username */
1425 name_map_status = normalize_name_unmap(state->mem_ctx,
1426 state->request->data.auth.user,
1429 /* If the name normalization didnt' actually do anything,
1430 just use the original name */
1432 if (!NT_STATUS_IS_OK(name_map_status) &&
1433 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1435 mapped_user = state->request->data.auth.user;
1438 parse_domain_user(mapped_user, name_domain, name_user);
1440 if ( mapped_user != state->request->data.auth.user ) {
1441 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1442 *lp_winbind_separator(),
1444 safe_strcpy( state->request->data.auth.user, domain_user,
1445 sizeof(state->request->data.auth.user)-1 );
1448 if (!domain->online) {
1449 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1450 if (domain->startup) {
1451 /* Logons are very important to users. If we're offline and
1452 we get a request within the first 30 seconds of startup,
1453 try very hard to find a DC and go online. */
1455 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1456 "request in startup mode.\n", domain->name ));
1458 winbindd_flush_negative_conn_cache(domain);
1459 result = init_dc_connection(domain);
1463 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1465 /* Check for Kerberos authentication */
1466 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1468 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1469 /* save for later */
1470 krb5_result = result;
1473 if (NT_STATUS_IS_OK(result)) {
1474 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1475 goto process_result;
1477 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1480 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1481 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1482 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1483 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1484 set_domain_offline( domain );
1488 /* there are quite some NT_STATUS errors where there is no
1489 * point in retrying with a samlogon, we explictly have to take
1490 * care not to increase the bad logon counter on the DC */
1492 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1493 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1494 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1495 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1496 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1497 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1498 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1499 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1500 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1501 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1505 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1506 DEBUG(3,("falling back to samlogon\n"));
1514 /* Check for Samlogon authentication */
1515 if (domain->online) {
1516 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1518 if (NT_STATUS_IS_OK(result)) {
1519 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1520 /* add the Krb5 err if we have one */
1521 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1522 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1524 goto process_result;
1527 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1528 nt_errstr(result)));
1530 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1531 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1532 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1534 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1535 set_domain_offline( domain );
1539 if (domain->online) {
1540 /* We're still online - fail. */
1546 /* Check for Cached logons */
1547 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1548 lp_winbind_offline_logon()) {
1550 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1552 if (NT_STATUS_IS_OK(result)) {
1553 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1554 goto process_result;
1556 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1563 if (NT_STATUS_IS_OK(result)) {
1565 struct dom_sid user_sid;
1567 /* In all codepaths where result == NT_STATUS_OK info3 must have
1568 been initialized. */
1570 result = NT_STATUS_INTERNAL_ERROR;
1574 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1575 netsamlogon_cache_store(name_user, info3);
1577 /* save name_to_sid info as early as possible (only if
1578 this is our primary domain so we don't invalidate
1579 the cache entry by storing the seq_num for the wrong
1581 if ( domain->primary ) {
1582 sid_compose(&user_sid, info3->base.domain_sid,
1584 cache_name2sid(domain, name_domain, name_user,
1585 SID_NAME_USER, &user_sid);
1588 /* Check if the user is in the right group */
1590 result = check_info3_in_group(
1592 state->request->data.auth.require_membership_of_sid);
1593 if (!NT_STATUS_IS_OK(result)) {
1594 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1595 state->request->data.auth.user,
1596 state->request->data.auth.require_membership_of_sid));
1600 result = append_auth_data(state, info3, name_domain,
1602 if (!NT_STATUS_IS_OK(result)) {
1606 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1607 && lp_winbind_offline_logon()) {
1609 result = winbindd_store_creds(domain,
1610 state->request->data.auth.user,
1611 state->request->data.auth.pass,
1615 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1616 struct winbindd_domain *our_domain = find_our_domain();
1618 /* This is not entirely correct I believe, but it is
1619 consistent. Only apply the password policy settings
1620 too warn users for our own domain. Cannot obtain these
1621 from trusted DCs all the time so don't do it at all.
1624 result = NT_STATUS_NOT_SUPPORTED;
1625 if (our_domain == domain ) {
1626 result = fillup_password_policy(our_domain, state);
1629 if (!NT_STATUS_IS_OK(result)
1630 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1632 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1633 domain->name, nt_errstr(result)));
1638 result = NT_STATUS_OK;
1642 /* give us a more useful (more correct?) error code */
1643 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1644 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1645 result = NT_STATUS_NO_LOGON_SERVERS;
1648 set_auth_errors(state->response, result);
1650 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1651 state->request->data.auth.user,
1652 state->response->data.auth.nt_status_string,
1653 state->response->data.auth.pam_error));
1655 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1658 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1659 struct winbindd_cli_state *state)
1662 struct netr_SamInfo3 *info3 = NULL;
1663 struct rpc_pipe_client *netlogon_pipe;
1664 const char *name_user = NULL;
1665 const char *name_domain = NULL;
1666 const char *workstation;
1670 DATA_BLOB lm_resp, nt_resp;
1672 /* This is child-only, so no check for privileged access is needed
1675 /* Ensure null termination */
1676 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1677 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1679 name_user = state->request->data.auth_crap.user;
1680 name_domain = state->request->data.auth_crap.domain;
1681 workstation = state->request->data.auth_crap.workstation;
1683 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1684 name_domain, name_user));
1686 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1687 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1688 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1689 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1690 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1691 state->request->data.auth_crap.lm_resp_len,
1692 state->request->data.auth_crap.nt_resp_len));
1693 result = NT_STATUS_INVALID_PARAMETER;
1698 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1699 state->request->data.auth_crap.lm_resp_len);
1701 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1702 nt_resp = data_blob_talloc(state->mem_ctx,
1703 state->request->extra_data.data,
1704 state->request->data.auth_crap.nt_resp_len);
1706 nt_resp = data_blob_talloc(state->mem_ctx,
1707 state->request->data.auth_crap.nt_resp,
1708 state->request->data.auth_crap.nt_resp_len);
1711 if (strequal(name_domain, get_global_sam_name())) {
1712 DATA_BLOB chal_blob = data_blob_const(
1713 state->request->data.auth_crap.chal,
1714 sizeof(state->request->data.auth_crap.chal));
1716 result = winbindd_dual_auth_passdb(
1717 state->mem_ctx, name_domain, name_user,
1718 &chal_blob, &lm_resp, &nt_resp, &info3);
1719 goto process_result;
1723 netlogon_fn_t logon_fn;
1727 netlogon_pipe = NULL;
1728 result = cm_connect_netlogon(domain, &netlogon_pipe);
1730 if (!NT_STATUS_IS_OK(result)) {
1731 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1732 nt_errstr(result)));
1736 logon_fn = domain->can_do_samlogon_ex
1737 ? rpccli_netlogon_sam_network_logon_ex
1738 : rpccli_netlogon_sam_network_logon;
1740 result = logon_fn(netlogon_pipe,
1742 state->request->data.auth_crap.logon_parameters,
1746 /* Bug #3248 - found by Stefan Burkei. */
1747 workstation, /* We carefully set this above so use it... */
1748 state->request->data.auth_crap.chal,
1753 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1754 && domain->can_do_samlogon_ex) {
1755 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1756 "retrying with NetSamLogon\n"));
1757 domain->can_do_samlogon_ex = false;
1763 /* We have to try a second time as cm_connect_netlogon
1764 might not yet have noticed that the DC has killed
1767 if (!rpccli_is_connected(netlogon_pipe)) {
1771 /* if we get access denied, a possible cause was that we had and open
1772 connection to the DC, but someone changed our machine account password
1773 out from underneath us using 'net rpc changetrustpw' */
1775 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1776 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1777 "ACCESS_DENIED. Maybe the trust account "
1778 "password was changed and we didn't know it. "
1779 "Killing connections to domain %s\n",
1781 invalidate_cm_connection(&domain->conn);
1785 } while ( (attempts < 2) && retry );
1789 if (NT_STATUS_IS_OK(result)) {
1791 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1792 netsamlogon_cache_store(name_user, info3);
1794 /* Check if the user is in the right group */
1796 result = check_info3_in_group(
1798 state->request->data.auth_crap.require_membership_of_sid);
1799 if (!NT_STATUS_IS_OK(result)) {
1800 DEBUG(3, ("User %s is not in the required group (%s), so "
1801 "crap authentication is rejected\n",
1802 state->request->data.auth_crap.user,
1803 state->request->data.auth_crap.require_membership_of_sid));
1807 result = append_auth_data(state, info3, name_domain,
1809 if (!NT_STATUS_IS_OK(result)) {
1816 /* give us a more useful (more correct?) error code */
1817 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1818 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1819 result = NT_STATUS_NO_LOGON_SERVERS;
1822 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1823 result = nt_status_squash(result);
1826 set_auth_errors(state->response, result);
1828 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1829 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1832 state->response->data.auth.nt_status_string,
1833 state->response->data.auth.pam_error));
1835 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1838 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1839 struct winbindd_cli_state *state)
1842 char *newpass = NULL;
1843 struct policy_handle dom_pol;
1844 struct rpc_pipe_client *cli = NULL;
1845 bool got_info = false;
1846 struct samr_DomInfo1 *info = NULL;
1847 struct userPwdChangeFailureInformation *reject = NULL;
1848 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1849 fstring domain, user;
1851 ZERO_STRUCT(dom_pol);
1853 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1854 state->request->data.auth.user));
1856 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1860 /* Change password */
1862 oldpass = state->request->data.chauthtok.oldpass;
1863 newpass = state->request->data.chauthtok.newpass;
1865 /* Initialize reject reason */
1866 state->response->data.auth.reject_reason = Undefined;
1868 /* Get sam handle */
1870 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1872 if (!NT_STATUS_IS_OK(result)) {
1873 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1877 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1884 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1886 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1888 fill_in_password_policy(state->response, info);
1890 state->response->data.auth.reject_reason =
1891 reject->extendedFailureReason;
1896 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1897 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1898 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1899 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1901 /* only fallback when the chgpasswd_user3 call is not supported */
1902 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1903 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1904 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1905 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1907 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1908 nt_errstr(result)));
1910 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1912 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1913 Map to the same status code as Windows 2003. */
1915 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1916 result = NT_STATUS_PASSWORD_RESTRICTION;
1922 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1923 if (lp_winbind_offline_logon()) {
1924 result = winbindd_update_creds_by_name(contact_domain,
1926 /* Again, this happens when we login from gdm or xdm
1927 * and the password expires, *BUT* cached crendentials
1928 * doesn't exist. winbindd_update_creds_by_name()
1929 * returns NT_STATUS_NO_SUCH_USER.
1930 * This is not a failure.
1933 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1934 result = NT_STATUS_OK;
1937 if (!NT_STATUS_IS_OK(result)) {
1938 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1939 goto process_result;
1944 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1946 NTSTATUS policy_ret;
1948 policy_ret = fillup_password_policy(contact_domain, state);
1950 /* failure of this is non critical, it will just provide no
1951 * additional information to the client why the change has
1952 * failed - Guenther */
1954 if (!NT_STATUS_IS_OK(policy_ret)) {
1955 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1956 goto process_result;
1962 if (strequal(contact_domain->name, get_global_sam_name())) {
1963 /* FIXME: internal rpc pipe does not cache handles yet */
1965 if (is_valid_policy_hnd(&dom_pol)) {
1966 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1972 set_auth_errors(state->response, result);
1974 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1975 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1978 state->response->data.auth.nt_status_string,
1979 state->response->data.auth.pam_error));
1981 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1984 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1985 struct winbindd_cli_state *state)
1987 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1989 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1990 state->request->data.logoff.user));
1992 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1993 result = NT_STATUS_OK;
1994 goto process_result;
1997 if (state->request->data.logoff.krb5ccname[0] == '\0') {
1998 result = NT_STATUS_OK;
1999 goto process_result;
2004 if (state->request->data.logoff.uid < 0) {
2005 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2006 goto process_result;
2009 /* what we need here is to find the corresponding krb5 ccache name *we*
2010 * created for a given username and destroy it */
2012 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2013 result = NT_STATUS_OK;
2014 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2015 goto process_result;
2018 if (!ccache_entry_identical(state->request->data.logoff.user,
2019 state->request->data.logoff.uid,
2020 state->request->data.logoff.krb5ccname)) {
2021 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2022 goto process_result;
2025 result = remove_ccache(state->request->data.logoff.user);
2026 if (!NT_STATUS_IS_OK(result)) {
2027 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2028 nt_errstr(result)));
2029 goto process_result;
2033 result = NT_STATUS_NOT_SUPPORTED;
2039 set_auth_errors(state->response, result);
2041 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2044 /* Change user password with auth crap*/
2046 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2049 DATA_BLOB new_nt_password;
2050 DATA_BLOB old_nt_hash_enc;
2051 DATA_BLOB new_lm_password;
2052 DATA_BLOB old_lm_hash_enc;
2053 fstring domain,user;
2054 struct policy_handle dom_pol;
2055 struct winbindd_domain *contact_domain = domainSt;
2056 struct rpc_pipe_client *cli = NULL;
2058 ZERO_STRUCT(dom_pol);
2060 /* Ensure null termination */
2061 state->request->data.chng_pswd_auth_crap.user[
2062 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2063 state->request->data.chng_pswd_auth_crap.domain[
2064 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2068 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2069 (unsigned long)state->pid,
2070 state->request->data.chng_pswd_auth_crap.domain,
2071 state->request->data.chng_pswd_auth_crap.user));
2073 if (lp_winbind_offline_logon()) {
2074 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2075 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2076 result = NT_STATUS_ACCESS_DENIED;
2080 if (*state->request->data.chng_pswd_auth_crap.domain) {
2081 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2083 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2087 DEBUG(3,("no domain specified with username (%s) - "
2089 state->request->data.chng_pswd_auth_crap.user));
2090 result = NT_STATUS_NO_SUCH_USER;
2095 if (!*domain && lp_winbind_use_default_domain()) {
2096 fstrcpy(domain,(char *)lp_workgroup());
2100 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2103 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2104 (unsigned long)state->pid, domain, user));
2106 /* Change password */
2107 new_nt_password = data_blob_const(
2108 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2109 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2111 old_nt_hash_enc = data_blob_const(
2112 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2113 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2115 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2116 new_lm_password = data_blob_const(
2117 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2118 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2120 old_lm_hash_enc = data_blob_const(
2121 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2122 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2124 new_lm_password.length = 0;
2125 old_lm_hash_enc.length = 0;
2128 /* Get sam handle */
2130 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2131 if (!NT_STATUS_IS_OK(result)) {
2132 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2136 result = rpccli_samr_chng_pswd_auth_crap(
2137 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2138 new_lm_password, old_lm_hash_enc);
2142 if (strequal(contact_domain->name, get_global_sam_name())) {
2143 /* FIXME: internal rpc pipe does not cache handles yet */
2145 if (is_valid_policy_hnd(&dom_pol)) {
2146 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2152 set_auth_errors(state->response, result);
2154 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2155 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2157 state->response->data.auth.nt_status_string,
2158 state->response->data.auth.pam_error));
2160 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;