s3:winbind: BUG 9386: Failover if netlogon pipe is not available.
[Samba.git] / source3 / winbindd / winbindd_pam.c
bloba9662028884e2e4b2bf37c2d443babbac07725b5
1 /*
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/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/ndr_samr_c.h"
29 #include "rpc_client/cli_pipe.h"
30 #include "rpc_client/cli_samr.h"
31 #include "../librpc/gen_ndr/ndr_netlogon.h"
32 #include "rpc_client/cli_netlogon.h"
33 #include "smb_krb5.h"
34 #include "../lib/crypto/arcfour.h"
35 #include "../libcli/security/security.h"
36 #include "ads.h"
37 #include "../librpc/gen_ndr/krb5pac.h"
38 #include "passdb/machine_sid.h"
39 #include "auth.h"
41 #undef DBGC_CLASS
42 #define DBGC_CLASS DBGC_WINBIND
44 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
46 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
47 struct winbindd_response *resp,
48 struct netr_SamInfo3 *info3)
50 char *ex;
51 uint32_t i;
53 resp->data.auth.info3.logon_time =
54 nt_time_to_unix(info3->base.last_logon);
55 resp->data.auth.info3.logoff_time =
56 nt_time_to_unix(info3->base.last_logoff);
57 resp->data.auth.info3.kickoff_time =
58 nt_time_to_unix(info3->base.acct_expiry);
59 resp->data.auth.info3.pass_last_set_time =
60 nt_time_to_unix(info3->base.last_password_change);
61 resp->data.auth.info3.pass_can_change_time =
62 nt_time_to_unix(info3->base.allow_password_change);
63 resp->data.auth.info3.pass_must_change_time =
64 nt_time_to_unix(info3->base.force_password_change);
66 resp->data.auth.info3.logon_count = info3->base.logon_count;
67 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
69 resp->data.auth.info3.user_rid = info3->base.rid;
70 resp->data.auth.info3.group_rid = info3->base.primary_gid;
71 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
73 resp->data.auth.info3.num_groups = info3->base.groups.count;
74 resp->data.auth.info3.user_flgs = info3->base.user_flags;
76 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
77 resp->data.auth.info3.num_other_sids = info3->sidcount;
79 fstrcpy(resp->data.auth.info3.user_name,
80 info3->base.account_name.string);
81 fstrcpy(resp->data.auth.info3.full_name,
82 info3->base.full_name.string);
83 fstrcpy(resp->data.auth.info3.logon_script,
84 info3->base.logon_script.string);
85 fstrcpy(resp->data.auth.info3.profile_path,
86 info3->base.profile_path.string);
87 fstrcpy(resp->data.auth.info3.home_dir,
88 info3->base.home_directory.string);
89 fstrcpy(resp->data.auth.info3.dir_drive,
90 info3->base.home_drive.string);
92 fstrcpy(resp->data.auth.info3.logon_srv,
93 info3->base.logon_server.string);
94 fstrcpy(resp->data.auth.info3.logon_dom,
95 info3->base.domain.string);
97 ex = talloc_strdup(mem_ctx, "");
98 NT_STATUS_HAVE_NO_MEMORY(ex);
100 for (i=0; i < info3->base.groups.count; i++) {
101 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
102 info3->base.groups.rids[i].rid,
103 info3->base.groups.rids[i].attributes);
104 NT_STATUS_HAVE_NO_MEMORY(ex);
107 for (i=0; i < info3->sidcount; i++) {
108 char *sid;
110 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
111 NT_STATUS_HAVE_NO_MEMORY(sid);
113 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
114 sid,
115 info3->sids[i].attributes);
116 NT_STATUS_HAVE_NO_MEMORY(ex);
118 talloc_free(sid);
121 resp->extra_data.data = ex;
122 resp->length += talloc_get_size(ex);
124 return NT_STATUS_OK;
127 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
128 struct winbindd_response *resp,
129 struct netr_SamInfo3 *info3)
131 DATA_BLOB blob;
132 enum ndr_err_code ndr_err;
134 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
135 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
136 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
137 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
138 return ndr_map_error2ntstatus(ndr_err);
141 resp->extra_data.data = blob.data;
142 resp->length += blob.length;
144 return NT_STATUS_OK;
147 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
148 struct winbindd_response *resp,
149 const struct netr_SamInfo3 *info3,
150 const char *name_domain,
151 const char *name_user)
153 /* We've been asked to return the unix username, per
154 'winbind use default domain' settings and the like */
156 const char *nt_username, *nt_domain;
158 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
159 if (!nt_domain) {
160 /* If the server didn't give us one, just use the one
161 * we sent them */
162 nt_domain = name_domain;
165 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
166 if (!nt_username) {
167 /* If the server didn't give us one, just use the one
168 * we sent them */
169 nt_username = name_user;
172 fill_domain_username(resp->data.auth.unix_username,
173 nt_domain, nt_username, true);
175 DEBUG(5, ("Setting unix username to [%s]\n",
176 resp->data.auth.unix_username));
178 return NT_STATUS_OK;
181 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
182 struct winbindd_response *resp,
183 const struct netr_SamInfo3 *info3,
184 const char *name_domain,
185 const char *name_user)
187 char *afsname = NULL;
188 char *cell;
189 char *token;
191 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
192 if (afsname == NULL) {
193 return NT_STATUS_NO_MEMORY;
196 afsname = talloc_string_sub(mem_ctx,
197 lp_afs_username_map(),
198 "%D", name_domain);
199 afsname = talloc_string_sub(mem_ctx, afsname,
200 "%u", name_user);
201 afsname = talloc_string_sub(mem_ctx, afsname,
202 "%U", name_user);
205 struct dom_sid user_sid;
206 fstring sidstr;
208 sid_compose(&user_sid, info3->base.domain_sid,
209 info3->base.rid);
210 sid_to_fstring(sidstr, &user_sid);
211 afsname = talloc_string_sub(mem_ctx, afsname,
212 "%s", sidstr);
215 if (afsname == NULL) {
216 return NT_STATUS_NO_MEMORY;
219 strlower_m(afsname);
221 DEBUG(10, ("Generating token for user %s\n", afsname));
223 cell = strchr(afsname, '@');
225 if (cell == NULL) {
226 return NT_STATUS_NO_MEMORY;
229 *cell = '\0';
230 cell += 1;
232 token = afs_createtoken_str(afsname, cell);
233 if (token == NULL) {
234 return NT_STATUS_OK;
236 resp->extra_data.data = talloc_strdup(mem_ctx, token);
237 if (resp->extra_data.data == NULL) {
238 return NT_STATUS_NO_MEMORY;
240 resp->length += strlen((const char *)resp->extra_data.data)+1;
242 return NT_STATUS_OK;
245 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
246 const char *group_sid)
248 * Check whether a user belongs to a group or list of groups.
250 * @param mem_ctx talloc memory context.
251 * @param info3 user information, including group membership info.
252 * @param group_sid One or more groups , separated by commas.
254 * @return NT_STATUS_OK on success,
255 * NT_STATUS_LOGON_FAILURE if the user does not belong,
256 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
259 struct dom_sid *require_membership_of_sid;
260 uint32_t num_require_membership_of_sid;
261 char *req_sid;
262 const char *p;
263 struct dom_sid sid;
264 size_t i;
265 struct security_token *token;
266 TALLOC_CTX *frame = talloc_stackframe();
267 NTSTATUS status;
269 /* Parse the 'required group' SID */
271 if (!group_sid || !group_sid[0]) {
272 /* NO sid supplied, all users may access */
273 return NT_STATUS_OK;
276 token = talloc_zero(talloc_tos(), struct security_token);
277 if (token == NULL) {
278 DEBUG(0, ("talloc failed\n"));
279 TALLOC_FREE(frame);
280 return NT_STATUS_NO_MEMORY;
283 num_require_membership_of_sid = 0;
284 require_membership_of_sid = NULL;
286 p = group_sid;
288 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
289 if (!string_to_sid(&sid, req_sid)) {
290 DEBUG(0, ("check_info3_in_group: could not parse %s "
291 "as a SID!", req_sid));
292 TALLOC_FREE(frame);
293 return NT_STATUS_INVALID_PARAMETER;
296 status = add_sid_to_array(talloc_tos(), &sid,
297 &require_membership_of_sid,
298 &num_require_membership_of_sid);
299 if (!NT_STATUS_IS_OK(status)) {
300 DEBUG(0, ("add_sid_to_array failed\n"));
301 TALLOC_FREE(frame);
302 return status;
306 status = sid_array_from_info3(talloc_tos(), info3,
307 &token->sids,
308 &token->num_sids,
309 true);
310 if (!NT_STATUS_IS_OK(status)) {
311 TALLOC_FREE(frame);
312 return status;
315 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
316 token))
317 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
318 token))) {
319 DEBUG(3, ("could not add aliases: %s\n",
320 nt_errstr(status)));
321 TALLOC_FREE(frame);
322 return status;
325 security_token_debug(DBGC_CLASS, 10, token);
327 for (i=0; i<num_require_membership_of_sid; i++) {
328 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
329 &require_membership_of_sid[i])));
330 if (nt_token_check_sid(&require_membership_of_sid[i],
331 token)) {
332 DEBUG(10, ("Access ok\n"));
333 TALLOC_FREE(frame);
334 return NT_STATUS_OK;
338 /* Do not distinguish this error from a wrong username/pw */
340 TALLOC_FREE(frame);
341 return NT_STATUS_LOGON_FAILURE;
344 struct winbindd_domain *find_auth_domain(uint8_t flags,
345 const char *domain_name)
347 struct winbindd_domain *domain;
349 if (IS_DC) {
350 domain = find_domain_from_name_noinit(domain_name);
351 if (domain == NULL) {
352 DEBUG(3, ("Authentication for domain [%s] refused "
353 "as it is not a trusted domain\n",
354 domain_name));
356 return domain;
359 if (strequal(domain_name, get_global_sam_name())) {
360 return find_domain_from_name_noinit(domain_name);
363 /* we can auth against trusted domains */
364 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
365 domain = find_domain_from_name_noinit(domain_name);
366 if (domain == NULL) {
367 DEBUG(3, ("Authentication for domain [%s] skipped "
368 "as it is not a trusted domain\n",
369 domain_name));
370 } else {
371 return domain;
375 return find_our_domain();
378 static void fill_in_password_policy(struct winbindd_response *r,
379 const struct samr_DomInfo1 *p)
381 r->data.auth.policy.min_length_password =
382 p->min_password_length;
383 r->data.auth.policy.password_history =
384 p->password_history_length;
385 r->data.auth.policy.password_properties =
386 p->password_properties;
387 r->data.auth.policy.expire =
388 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
389 r->data.auth.policy.min_passwordage =
390 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
393 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
394 struct winbindd_response *response)
396 TALLOC_CTX *frame = talloc_stackframe();
397 struct winbindd_methods *methods;
398 NTSTATUS status;
399 struct samr_DomInfo1 password_policy;
401 if ( !winbindd_can_contact_domain( domain ) ) {
402 DEBUG(5,("fillup_password_policy: No inbound trust to "
403 "contact domain %s\n", domain->name));
404 status = NT_STATUS_NOT_SUPPORTED;
405 goto done;
408 methods = domain->methods;
410 status = methods->password_policy(domain, talloc_tos(), &password_policy);
411 if (NT_STATUS_IS_ERR(status)) {
412 goto done;
415 fill_in_password_policy(response, &password_policy);
417 done:
418 TALLOC_FREE(frame);
419 return NT_STATUS_OK;
422 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
423 TALLOC_CTX *mem_ctx,
424 uint16 *lockout_threshold)
426 struct winbindd_methods *methods;
427 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
428 struct samr_DomInfo12 lockout_policy;
430 *lockout_threshold = 0;
432 methods = domain->methods;
434 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
435 if (NT_STATUS_IS_ERR(status)) {
436 return status;
439 *lockout_threshold = lockout_policy.lockout_threshold;
441 return NT_STATUS_OK;
444 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
445 TALLOC_CTX *mem_ctx,
446 uint32 *password_properties)
448 struct winbindd_methods *methods;
449 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
450 struct samr_DomInfo1 password_policy;
452 *password_properties = 0;
454 methods = domain->methods;
456 status = methods->password_policy(domain, mem_ctx, &password_policy);
457 if (NT_STATUS_IS_ERR(status)) {
458 return status;
461 *password_properties = password_policy.password_properties;
463 return NT_STATUS_OK;
466 #ifdef HAVE_KRB5
468 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
469 const char *type,
470 uid_t uid,
471 const char **user_ccache_file)
473 /* accept FILE and WRFILE as krb5_cc_type from the client and then
474 * build the full ccname string based on the user's uid here -
475 * Guenther*/
477 const char *gen_cc = NULL;
479 if (uid != -1) {
480 if (strequal(type, "FILE")) {
481 gen_cc = talloc_asprintf(
482 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
484 if (strequal(type, "WRFILE")) {
485 gen_cc = talloc_asprintf(
486 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
490 *user_ccache_file = gen_cc;
492 if (gen_cc == NULL) {
493 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
495 if (gen_cc == NULL) {
496 DEBUG(0,("out of memory\n"));
497 return NULL;
500 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
501 (*user_ccache_file == NULL) ? " (internal)":""));
503 return gen_cc;
506 #endif
508 uid_t get_uid_from_request(struct winbindd_request *request)
510 uid_t uid;
512 uid = request->data.auth.uid;
514 if (uid < 0) {
515 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
516 return -1;
518 return uid;
521 /**********************************************************************
522 Authenticate a user with a clear text password using Kerberos and fill up
523 ccache if required
524 **********************************************************************/
526 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
527 struct winbindd_domain *domain,
528 const char *user,
529 const char *pass,
530 const char *krb5_cc_type,
531 uid_t uid,
532 struct netr_SamInfo3 **info3,
533 fstring krb5ccname)
535 #ifdef HAVE_KRB5
536 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
537 krb5_error_code krb5_ret;
538 const char *cc = NULL;
539 const char *principal_s = NULL;
540 const char *service = NULL;
541 char *realm = NULL;
542 fstring name_domain, name_user;
543 time_t ticket_lifetime = 0;
544 time_t renewal_until = 0;
545 ADS_STRUCT *ads;
546 time_t time_offset = 0;
547 const char *user_ccache_file;
548 struct PAC_LOGON_INFO *logon_info = NULL;
550 *info3 = NULL;
552 /* 1st step:
553 * prepare a krb5_cc_cache string for the user */
555 if (uid == -1) {
556 DEBUG(0,("no valid uid\n"));
559 cc = generate_krb5_ccache(mem_ctx,
560 krb5_cc_type,
561 uid,
562 &user_ccache_file);
563 if (cc == NULL) {
564 return NT_STATUS_NO_MEMORY;
568 /* 2nd step:
569 * get kerberos properties */
571 if (domain->private_data) {
572 ads = (ADS_STRUCT *)domain->private_data;
573 time_offset = ads->auth.time_offset;
577 /* 3rd step:
578 * do kerberos auth and setup ccache as the user */
580 parse_domain_user(user, name_domain, name_user);
582 realm = domain->alt_name;
583 strupper_m(realm);
585 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
586 if (principal_s == NULL) {
587 return NT_STATUS_NO_MEMORY;
590 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
591 if (service == NULL) {
592 return NT_STATUS_NO_MEMORY;
595 /* if this is a user ccache, we need to act as the user to let the krb5
596 * library handle the chown, etc. */
598 /************************ ENTERING NON-ROOT **********************/
600 if (user_ccache_file != NULL) {
601 set_effective_uid(uid);
602 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
605 result = kerberos_return_pac(mem_ctx,
606 principal_s,
607 pass,
608 time_offset,
609 &ticket_lifetime,
610 &renewal_until,
612 true,
613 true,
614 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
615 NULL,
616 &logon_info);
617 if (user_ccache_file != NULL) {
618 gain_root_privilege();
621 /************************ RETURNED TO ROOT **********************/
623 if (!NT_STATUS_IS_OK(result)) {
624 goto failed;
627 *info3 = &logon_info->info3;
629 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
630 principal_s));
632 /* if we had a user's ccache then return that string for the pam
633 * environment */
635 if (user_ccache_file != NULL) {
637 fstrcpy(krb5ccname, user_ccache_file);
639 result = add_ccache_to_list(principal_s,
641 service,
642 user,
643 pass,
644 realm,
645 uid,
646 time(NULL),
647 ticket_lifetime,
648 renewal_until,
649 false);
651 if (!NT_STATUS_IS_OK(result)) {
652 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
653 nt_errstr(result)));
655 } else {
657 /* need to delete the memory cred cache, it is not used anymore */
659 krb5_ret = ads_kdestroy(cc);
660 if (krb5_ret) {
661 DEBUG(3,("winbindd_raw_kerberos_login: "
662 "could not destroy krb5 credential cache: "
663 "%s\n", error_message(krb5_ret)));
668 return NT_STATUS_OK;
670 failed:
672 /* we could have created a new credential cache with a valid tgt in it
673 * but we werent able to get or verify the service ticket for this
674 * local host and therefor didn't get the PAC, we need to remove that
675 * cache entirely now */
677 krb5_ret = ads_kdestroy(cc);
678 if (krb5_ret) {
679 DEBUG(3,("winbindd_raw_kerberos_login: "
680 "could not destroy krb5 credential cache: "
681 "%s\n", error_message(krb5_ret)));
684 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
685 DEBUG(3,("winbindd_raw_kerberos_login: "
686 "could not remove ccache for user %s\n",
687 user));
690 return result;
691 #else
692 return NT_STATUS_NOT_SUPPORTED;
693 #endif /* HAVE_KRB5 */
696 /****************************************************************
697 ****************************************************************/
699 bool check_request_flags(uint32_t flags)
701 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
702 WBFLAG_PAM_INFO3_TEXT |
703 WBFLAG_PAM_INFO3_NDR;
705 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
706 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
707 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
708 !(flags & flags_edata) ) {
709 return true;
712 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
713 flags));
715 return false;
718 /****************************************************************
719 ****************************************************************/
721 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
722 struct winbindd_response *resp,
723 uint32_t request_flags,
724 struct netr_SamInfo3 *info3,
725 const char *name_domain,
726 const char *name_user)
728 NTSTATUS result;
730 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
731 memcpy(resp->data.auth.user_session_key,
732 info3->base.key.key,
733 sizeof(resp->data.auth.user_session_key)
734 /* 16 */);
737 if (request_flags & WBFLAG_PAM_LMKEY) {
738 memcpy(resp->data.auth.first_8_lm_hash,
739 info3->base.LMSessKey.key,
740 sizeof(resp->data.auth.first_8_lm_hash)
741 /* 8 */);
744 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
745 result = append_unix_username(mem_ctx, resp,
746 info3, name_domain, name_user);
747 if (!NT_STATUS_IS_OK(result)) {
748 DEBUG(10,("Failed to append Unix Username: %s\n",
749 nt_errstr(result)));
750 return result;
754 /* currently, anything from here on potentially overwrites extra_data. */
756 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
757 result = append_info3_as_ndr(mem_ctx, resp, info3);
758 if (!NT_STATUS_IS_OK(result)) {
759 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
760 nt_errstr(result)));
761 return result;
765 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
766 result = append_info3_as_txt(mem_ctx, resp, info3);
767 if (!NT_STATUS_IS_OK(result)) {
768 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
769 nt_errstr(result)));
770 return result;
774 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
775 result = append_afs_token(mem_ctx, resp,
776 info3, name_domain, name_user);
777 if (!NT_STATUS_IS_OK(result)) {
778 DEBUG(10,("Failed to append AFS token: %s\n",
779 nt_errstr(result)));
780 return result;
784 return NT_STATUS_OK;
787 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
788 struct winbindd_cli_state *state,
789 struct netr_SamInfo3 **info3)
791 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
792 uint16 max_allowed_bad_attempts;
793 fstring name_domain, name_user;
794 struct dom_sid sid;
795 enum lsa_SidType type;
796 uchar new_nt_pass[NT_HASH_LEN];
797 const uint8 *cached_nt_pass;
798 const uint8 *cached_salt;
799 struct netr_SamInfo3 *my_info3;
800 time_t kickoff_time, must_change_time;
801 bool password_good = false;
802 #ifdef HAVE_KRB5
803 struct winbindd_tdc_domain *tdc_domain = NULL;
804 #endif
806 *info3 = NULL;
808 ZERO_STRUCTP(info3);
810 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
812 /* Parse domain and username */
814 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
817 if (!lookup_cached_name(name_domain,
818 name_user,
819 &sid,
820 &type)) {
821 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
822 return NT_STATUS_NO_SUCH_USER;
825 if (type != SID_NAME_USER) {
826 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
827 return NT_STATUS_LOGON_FAILURE;
830 result = winbindd_get_creds(domain,
831 state->mem_ctx,
832 &sid,
833 &my_info3,
834 &cached_nt_pass,
835 &cached_salt);
836 if (!NT_STATUS_IS_OK(result)) {
837 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
838 return result;
841 *info3 = my_info3;
843 E_md4hash(state->request->data.auth.pass, new_nt_pass);
845 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
846 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
847 if (cached_salt) {
848 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
851 if (cached_salt) {
852 /* In this case we didn't store the nt_hash itself,
853 but the MD5 combination of salt + nt_hash. */
854 uchar salted_hash[NT_HASH_LEN];
855 E_md5hash(cached_salt, new_nt_pass, salted_hash);
857 password_good = (memcmp(cached_nt_pass, salted_hash,
858 NT_HASH_LEN) == 0);
859 } else {
860 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
861 password_good = (memcmp(cached_nt_pass, new_nt_pass,
862 NT_HASH_LEN) == 0);
865 if (password_good) {
867 /* User *DOES* know the password, update logon_time and reset
868 * bad_pw_count */
870 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
872 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
873 return NT_STATUS_ACCOUNT_LOCKED_OUT;
876 if (my_info3->base.acct_flags & ACB_DISABLED) {
877 return NT_STATUS_ACCOUNT_DISABLED;
880 if (my_info3->base.acct_flags & ACB_WSTRUST) {
881 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
884 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
885 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
888 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
889 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
892 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
893 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
894 my_info3->base.acct_flags));
895 return NT_STATUS_LOGON_FAILURE;
898 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
899 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
900 return NT_STATUS_ACCOUNT_EXPIRED;
903 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
904 if (must_change_time != 0 && must_change_time < time(NULL)) {
905 /* we allow grace logons when the password has expired */
906 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
907 /* return NT_STATUS_PASSWORD_EXPIRED; */
908 goto success;
911 #ifdef HAVE_KRB5
912 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
913 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
914 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
915 /* used to cope with the case winbindd starting without network. */
916 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
918 uid_t uid = -1;
919 const char *cc = NULL;
920 char *realm = NULL;
921 const char *principal_s = NULL;
922 const char *service = NULL;
923 const char *user_ccache_file;
925 uid = get_uid_from_request(state->request);
926 if (uid == -1) {
927 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
928 return NT_STATUS_INVALID_PARAMETER;
931 cc = generate_krb5_ccache(state->mem_ctx,
932 state->request->data.auth.krb5_cc_type,
933 state->request->data.auth.uid,
934 &user_ccache_file);
935 if (cc == NULL) {
936 return NT_STATUS_NO_MEMORY;
939 realm = domain->alt_name;
940 strupper_m(realm);
942 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
943 if (principal_s == NULL) {
944 return NT_STATUS_NO_MEMORY;
947 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
948 if (service == NULL) {
949 return NT_STATUS_NO_MEMORY;
952 if (user_ccache_file != NULL) {
954 fstrcpy(state->response->data.auth.krb5ccname,
955 user_ccache_file);
957 result = add_ccache_to_list(principal_s,
959 service,
960 state->request->data.auth.user,
961 state->request->data.auth.pass,
962 domain->alt_name,
963 uid,
964 time(NULL),
965 time(NULL) + lp_winbind_cache_time(),
966 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
967 true);
969 if (!NT_STATUS_IS_OK(result)) {
970 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
971 "to add ccache to list: %s\n",
972 nt_errstr(result)));
976 #endif /* HAVE_KRB5 */
977 success:
978 /* FIXME: we possibly should handle logon hours as well (does xp when
979 * offline?) see auth/auth_sam.c:sam_account_ok for details */
981 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
982 my_info3->base.bad_password_count = 0;
984 result = winbindd_update_creds_by_info3(domain,
985 state->request->data.auth.user,
986 state->request->data.auth.pass,
987 my_info3);
988 if (!NT_STATUS_IS_OK(result)) {
989 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
990 nt_errstr(result)));
991 return result;
994 return NT_STATUS_OK;
998 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
999 if (domain->online == false) {
1000 goto failed;
1003 /* failure of this is not critical */
1004 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1005 if (!NT_STATUS_IS_OK(result)) {
1006 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1007 "Won't be able to honour account lockout policies\n"));
1010 /* increase counter */
1011 my_info3->base.bad_password_count++;
1013 if (max_allowed_bad_attempts == 0) {
1014 goto failed;
1017 /* lockout user */
1018 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1020 uint32 password_properties;
1022 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1023 if (!NT_STATUS_IS_OK(result)) {
1024 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1027 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1028 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1029 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1033 failed:
1034 result = winbindd_update_creds_by_info3(domain,
1035 state->request->data.auth.user,
1036 NULL,
1037 my_info3);
1039 if (!NT_STATUS_IS_OK(result)) {
1040 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1041 nt_errstr(result)));
1044 return NT_STATUS_LOGON_FAILURE;
1047 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1048 struct winbindd_cli_state *state,
1049 struct netr_SamInfo3 **info3)
1051 struct winbindd_domain *contact_domain;
1052 fstring name_domain, name_user;
1053 NTSTATUS result;
1055 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1057 /* Parse domain and username */
1059 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1061 /* what domain should we contact? */
1063 if ( IS_DC ) {
1064 if (!(contact_domain = find_domain_from_name(name_domain))) {
1065 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1066 state->request->data.auth.user, name_domain, name_user, name_domain));
1067 result = NT_STATUS_NO_SUCH_USER;
1068 goto done;
1071 } else {
1072 if (is_myname(name_domain)) {
1073 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1074 result = NT_STATUS_NO_SUCH_USER;
1075 goto done;
1078 contact_domain = find_domain_from_name(name_domain);
1079 if (contact_domain == NULL) {
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));
1083 result = NT_STATUS_NO_SUCH_USER;
1084 goto done;
1088 if (contact_domain->initialized &&
1089 contact_domain->active_directory) {
1090 goto try_login;
1093 if (!contact_domain->initialized) {
1094 init_dc_connection(contact_domain);
1097 if (!contact_domain->active_directory) {
1098 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1099 return NT_STATUS_INVALID_LOGON_TYPE;
1101 try_login:
1102 result = winbindd_raw_kerberos_login(
1103 state->mem_ctx, contact_domain,
1104 state->request->data.auth.user,
1105 state->request->data.auth.pass,
1106 state->request->data.auth.krb5_cc_type,
1107 get_uid_from_request(state->request),
1108 info3, state->response->data.auth.krb5ccname);
1109 done:
1110 return result;
1113 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1114 const char *domain, const char *user,
1115 const DATA_BLOB *challenge,
1116 const DATA_BLOB *lm_resp,
1117 const DATA_BLOB *nt_resp,
1118 struct netr_SamInfo3 **pinfo3)
1120 struct auth_usersupplied_info *user_info = NULL;
1121 NTSTATUS status;
1123 status = make_user_info(&user_info, user, user, domain, domain,
1124 global_myname(), lm_resp, nt_resp, NULL, NULL,
1125 NULL, AUTH_PASSWORD_RESPONSE);
1126 if (!NT_STATUS_IS_OK(status)) {
1127 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1128 return status;
1131 /* We don't want any more mapping of the username */
1132 user_info->mapped_state = True;
1134 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1135 pinfo3);
1136 free_user_info(&user_info);
1137 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1138 user, nt_errstr(status)));
1139 return status;
1142 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1143 TALLOC_CTX *mem_ctx,
1144 uint32_t logon_parameters,
1145 const char *server,
1146 const char *username,
1147 const char *domainname,
1148 const char *workstation,
1149 const uint8_t chal[8],
1150 DATA_BLOB lm_response,
1151 DATA_BLOB nt_response,
1152 struct netr_SamInfo3 **info3)
1154 int attempts = 0;
1155 int netr_attempts = 0;
1156 bool retry = false;
1157 NTSTATUS result;
1159 do {
1160 struct rpc_pipe_client *netlogon_pipe;
1161 const struct pipe_auth_data *auth;
1162 uint32_t neg_flags = 0;
1164 ZERO_STRUCTP(info3);
1165 retry = false;
1167 result = cm_connect_netlogon(domain, &netlogon_pipe);
1169 if (!NT_STATUS_IS_OK(result)) {
1170 DEBUG(3,("Could not open handle to NETLOGON pipe "
1171 "(error: %s, attempts: %d)\n",
1172 nt_errstr(result), netr_attempts));
1174 /* After the first retry always close the connection */
1175 if (netr_attempts > 0) {
1176 DEBUG(3, ("This is again a problem for this "
1177 "particular call, forcing the close "
1178 "of this connection\n"));
1179 invalidate_cm_connection(&domain->conn);
1182 /* After the second retry failover to the next DC */
1183 if (netr_attempts > 1) {
1185 * If the netlogon server is not reachable then
1186 * it is possible that the DC is rebuilding
1187 * sysvol and shutdown netlogon for that time.
1188 * We should failover to the next dc.
1190 DEBUG(3, ("This is the third problem for this "
1191 "particular call, adding DC to the "
1192 "negative cache list\n"));
1193 add_failed_connection_entry(domain->name,
1194 domain->dcname,
1195 result);
1196 saf_delete(domain->name);
1199 /* Only allow 3 retries */
1200 if (netr_attempts < 3) {
1201 DEBUG(3, ("The connection to netlogon "
1202 "failed, retrying\n"));
1203 netr_attempts++;
1204 retry = true;
1205 continue;
1207 return result;
1209 netr_attempts = 0;
1211 auth = netlogon_pipe->auth;
1212 if (netlogon_pipe->dc) {
1213 neg_flags = netlogon_pipe->dc->negotiate_flags;
1216 /* It is really important to try SamLogonEx here,
1217 * because in a clustered environment, we want to use
1218 * one machine account from multiple physical
1219 * computers.
1221 * With a normal SamLogon call, we must keep the
1222 * credentials chain updated and intact between all
1223 * users of the machine account (which would imply
1224 * cross-node communication for every NTLM logon).
1226 * (The credentials chain is not per NETLOGON pipe
1227 * connection, but globally on the server/client pair
1228 * by machine name).
1230 * When using SamLogonEx, the credentials are not
1231 * supplied, but the session key is implied by the
1232 * wrapping SamLogon context.
1234 * -- abartlet 21 April 2008
1236 * It's also important to use NetlogonValidationSamInfo4 (6),
1237 * because it relies on the rpc transport encryption
1238 * and avoids using the global netlogon schannel
1239 * session key to en/decrypt secret information
1240 * like the user_session_key for network logons.
1242 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1243 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1244 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1245 * are the indication that the server supports
1246 * NetlogonValidationSamInfo4 (6). And it must only
1247 * be used if "SealSecureChannel" is used.
1249 * -- metze 4 February 2011
1252 if (auth == NULL) {
1253 domain->can_do_validation6 = false;
1254 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1255 domain->can_do_validation6 = false;
1256 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1257 domain->can_do_validation6 = false;
1258 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1259 domain->can_do_validation6 = false;
1260 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1261 domain->can_do_validation6 = false;
1264 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1265 result = rpccli_netlogon_sam_network_logon_ex(
1266 netlogon_pipe,
1267 mem_ctx,
1268 logon_parameters,
1269 server, /* server name */
1270 username, /* user name */
1271 domainname, /* target domain */
1272 workstation, /* workstation */
1273 chal,
1275 lm_response,
1276 nt_response,
1277 info3);
1278 } else {
1279 result = rpccli_netlogon_sam_network_logon(
1280 netlogon_pipe,
1281 mem_ctx,
1282 logon_parameters,
1283 server, /* server name */
1284 username, /* user name */
1285 domainname, /* target domain */
1286 workstation, /* workstation */
1287 chal,
1288 domain->can_do_validation6 ? 6 : 3,
1289 lm_response,
1290 nt_response,
1291 info3);
1294 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1297 * It's likely that the server also does not support
1298 * validation level 6
1300 domain->can_do_validation6 = false;
1302 if (domain->can_do_samlogon_ex) {
1303 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1304 "retrying with NetSamLogon\n"));
1305 domain->can_do_samlogon_ex = false;
1306 retry = true;
1307 continue;
1311 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1312 * (no Ex). This happens against old Samba
1313 * DCs. Drop the connection.
1315 invalidate_cm_connection(&domain->conn);
1316 result = NT_STATUS_LOGON_FAILURE;
1317 break;
1320 if (domain->can_do_validation6 &&
1321 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1322 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1323 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1324 DEBUG(3,("Got a DC that can not do validation level 6, "
1325 "retrying with level 3\n"));
1326 domain->can_do_validation6 = false;
1327 retry = true;
1328 continue;
1332 * we increment this after the "feature negotiation"
1333 * for can_do_samlogon_ex and can_do_validation6
1335 attempts += 1;
1337 /* We have to try a second time as cm_connect_netlogon
1338 might not yet have noticed that the DC has killed
1339 our connection. */
1341 if (!rpccli_is_connected(netlogon_pipe)) {
1342 retry = true;
1343 continue;
1346 /* if we get access denied, a possible cause was that we had
1347 and open connection to the DC, but someone changed our
1348 machine account password out from underneath us using 'net
1349 rpc changetrustpw' */
1351 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1352 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1353 "ACCESS_DENIED. Maybe the trust account "
1354 "password was changed and we didn't know it. "
1355 "Killing connections to domain %s\n",
1356 domainname));
1357 invalidate_cm_connection(&domain->conn);
1358 retry = true;
1361 } while ( (attempts < 2) && retry );
1363 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1364 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1365 "returned NT_STATUS_IO_TIMEOUT after the retry."
1366 "Killing connections to domain %s\n",
1367 domainname));
1368 invalidate_cm_connection(&domain->conn);
1370 return result;
1373 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1374 struct winbindd_domain *domain,
1375 const char *user,
1376 const char *pass,
1377 uint32_t request_flags,
1378 struct netr_SamInfo3 **info3)
1381 uchar chal[8];
1382 DATA_BLOB lm_resp;
1383 DATA_BLOB nt_resp;
1384 unsigned char local_nt_response[24];
1385 fstring name_domain, name_user;
1386 NTSTATUS result;
1387 struct netr_SamInfo3 *my_info3 = NULL;
1389 *info3 = NULL;
1391 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1393 /* Parse domain and username */
1395 parse_domain_user(user, name_domain, name_user);
1397 /* do password magic */
1399 generate_random_buffer(chal, sizeof(chal));
1401 if (lp_client_ntlmv2_auth()) {
1402 DATA_BLOB server_chal;
1403 DATA_BLOB names_blob;
1404 server_chal = data_blob_const(chal, 8);
1406 /* note that the 'workgroup' here is for the local
1407 machine. The 'server name' must match the
1408 'workstation' passed to the actual SamLogon call.
1410 names_blob = NTLMv2_generate_names_blob(
1411 mem_ctx, global_myname(), lp_workgroup());
1413 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1414 pass,
1415 &server_chal,
1416 &names_blob,
1417 &lm_resp, &nt_resp, NULL, NULL)) {
1418 data_blob_free(&names_blob);
1419 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1420 result = NT_STATUS_NO_MEMORY;
1421 goto done;
1423 data_blob_free(&names_blob);
1424 } else {
1425 lm_resp = data_blob_null;
1426 SMBNTencrypt(pass, chal, local_nt_response);
1428 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1429 sizeof(local_nt_response));
1432 if (strequal(name_domain, get_global_sam_name())) {
1433 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1435 result = winbindd_dual_auth_passdb(
1436 mem_ctx, name_domain, name_user,
1437 &chal_blob, &lm_resp, &nt_resp, info3);
1438 goto done;
1441 /* check authentication loop */
1443 result = winbind_samlogon_retry_loop(domain,
1444 mem_ctx,
1446 domain->dcname,
1447 name_user,
1448 name_domain,
1449 global_myname(),
1450 chal,
1451 lm_resp,
1452 nt_resp,
1453 &my_info3);
1454 if (!NT_STATUS_IS_OK(result)) {
1455 goto done;
1458 /* handle the case where a NT4 DC does not fill in the acct_flags in
1459 * the samlogon reply info3. When accurate info3 is required by the
1460 * caller, we look up the account flags ourselve - gd */
1462 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1463 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1465 struct rpc_pipe_client *samr_pipe;
1466 struct policy_handle samr_domain_handle, user_pol;
1467 union samr_UserInfo *info = NULL;
1468 NTSTATUS status_tmp, result_tmp;
1469 uint32 acct_flags;
1470 struct dcerpc_binding_handle *b;
1472 status_tmp = cm_connect_sam(domain, mem_ctx,
1473 &samr_pipe, &samr_domain_handle);
1475 if (!NT_STATUS_IS_OK(status_tmp)) {
1476 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1477 nt_errstr(status_tmp)));
1478 goto done;
1481 b = samr_pipe->binding_handle;
1483 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1484 &samr_domain_handle,
1485 MAXIMUM_ALLOWED_ACCESS,
1486 my_info3->base.rid,
1487 &user_pol,
1488 &result_tmp);
1490 if (!NT_STATUS_IS_OK(status_tmp)) {
1491 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1492 nt_errstr(status_tmp)));
1493 goto done;
1495 if (!NT_STATUS_IS_OK(result_tmp)) {
1496 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1497 nt_errstr(result_tmp)));
1498 goto done;
1501 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1502 &user_pol,
1504 &info,
1505 &result_tmp);
1507 if (!NT_STATUS_IS_OK(status_tmp)) {
1508 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1509 nt_errstr(status_tmp)));
1510 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1511 goto done;
1513 if (!NT_STATUS_IS_OK(result_tmp)) {
1514 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1515 nt_errstr(result_tmp)));
1516 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1517 goto done;
1520 acct_flags = info->info16.acct_flags;
1522 if (acct_flags == 0) {
1523 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1524 goto done;
1527 my_info3->base.acct_flags = acct_flags;
1529 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1531 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1534 *info3 = my_info3;
1535 done:
1536 return result;
1539 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1540 struct winbindd_cli_state *state)
1542 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1543 NTSTATUS krb5_result = NT_STATUS_OK;
1544 fstring name_domain, name_user;
1545 char *mapped_user;
1546 fstring domain_user;
1547 struct netr_SamInfo3 *info3 = NULL;
1548 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1550 /* Ensure null termination */
1551 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1553 /* Ensure null termination */
1554 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1556 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1557 state->request->data.auth.user));
1559 /* Parse domain and username */
1561 name_map_status = normalize_name_unmap(state->mem_ctx,
1562 state->request->data.auth.user,
1563 &mapped_user);
1565 /* If the name normalization didnt' actually do anything,
1566 just use the original name */
1568 if (!NT_STATUS_IS_OK(name_map_status) &&
1569 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1571 mapped_user = state->request->data.auth.user;
1574 parse_domain_user(mapped_user, name_domain, name_user);
1576 if ( mapped_user != state->request->data.auth.user ) {
1577 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1578 *lp_winbind_separator(),
1579 name_user );
1580 safe_strcpy( state->request->data.auth.user, domain_user,
1581 sizeof(state->request->data.auth.user)-1 );
1584 if (!domain->online) {
1585 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1586 if (domain->startup) {
1587 /* Logons are very important to users. If we're offline and
1588 we get a request within the first 30 seconds of startup,
1589 try very hard to find a DC and go online. */
1591 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1592 "request in startup mode.\n", domain->name ));
1594 winbindd_flush_negative_conn_cache(domain);
1595 result = init_dc_connection(domain);
1599 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1601 /* Check for Kerberos authentication */
1602 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1604 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1605 /* save for later */
1606 krb5_result = result;
1609 if (NT_STATUS_IS_OK(result)) {
1610 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1611 goto process_result;
1612 } else {
1613 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1616 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1617 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1618 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1619 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1620 set_domain_offline( domain );
1621 goto cached_logon;
1624 /* there are quite some NT_STATUS errors where there is no
1625 * point in retrying with a samlogon, we explictly have to take
1626 * care not to increase the bad logon counter on the DC */
1628 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1629 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1630 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1631 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1632 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1633 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1634 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1635 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1636 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1637 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1638 goto done;
1641 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1642 DEBUG(3,("falling back to samlogon\n"));
1643 goto sam_logon;
1644 } else {
1645 goto cached_logon;
1649 sam_logon:
1650 /* Check for Samlogon authentication */
1651 if (domain->online) {
1652 result = winbindd_dual_pam_auth_samlogon(
1653 state->mem_ctx, domain,
1654 state->request->data.auth.user,
1655 state->request->data.auth.pass,
1656 state->request->flags,
1657 &info3);
1659 if (NT_STATUS_IS_OK(result)) {
1660 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1661 /* add the Krb5 err if we have one */
1662 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1663 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1665 goto process_result;
1668 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1669 nt_errstr(result)));
1671 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1672 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1673 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1675 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1676 set_domain_offline( domain );
1677 goto cached_logon;
1680 if (domain->online) {
1681 /* We're still online - fail. */
1682 goto done;
1686 cached_logon:
1687 /* Check for Cached logons */
1688 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1689 lp_winbind_offline_logon()) {
1691 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1693 if (NT_STATUS_IS_OK(result)) {
1694 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1695 goto process_result;
1696 } else {
1697 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1698 goto done;
1702 process_result:
1704 if (NT_STATUS_IS_OK(result)) {
1706 struct dom_sid user_sid;
1708 /* In all codepaths where result == NT_STATUS_OK info3 must have
1709 been initialized. */
1710 if (!info3) {
1711 result = NT_STATUS_INTERNAL_ERROR;
1712 goto done;
1715 sid_compose(&user_sid, info3->base.domain_sid,
1716 info3->base.rid);
1718 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1719 &user_sid);
1720 netsamlogon_cache_store(name_user, info3);
1722 /* save name_to_sid info as early as possible (only if
1723 this is our primary domain so we don't invalidate
1724 the cache entry by storing the seq_num for the wrong
1725 domain). */
1726 if ( domain->primary ) {
1727 cache_name2sid(domain, name_domain, name_user,
1728 SID_NAME_USER, &user_sid);
1731 /* Check if the user is in the right group */
1733 result = check_info3_in_group(
1734 info3,
1735 state->request->data.auth.require_membership_of_sid);
1736 if (!NT_STATUS_IS_OK(result)) {
1737 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1738 state->request->data.auth.user,
1739 state->request->data.auth.require_membership_of_sid));
1740 goto done;
1743 result = append_auth_data(state->mem_ctx, state->response,
1744 state->request->flags, info3,
1745 name_domain, name_user);
1746 if (!NT_STATUS_IS_OK(result)) {
1747 goto done;
1750 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1751 && lp_winbind_offline_logon()) {
1753 result = winbindd_store_creds(domain,
1754 state->request->data.auth.user,
1755 state->request->data.auth.pass,
1756 info3);
1759 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1760 struct winbindd_domain *our_domain = find_our_domain();
1762 /* This is not entirely correct I believe, but it is
1763 consistent. Only apply the password policy settings
1764 too warn users for our own domain. Cannot obtain these
1765 from trusted DCs all the time so don't do it at all.
1766 -- jerry */
1768 result = NT_STATUS_NOT_SUPPORTED;
1769 if (our_domain == domain ) {
1770 result = fillup_password_policy(
1771 our_domain, state->response);
1774 if (!NT_STATUS_IS_OK(result)
1775 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1777 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1778 domain->name, nt_errstr(result)));
1779 goto done;
1783 result = NT_STATUS_OK;
1786 done:
1787 /* give us a more useful (more correct?) error code */
1788 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1789 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1790 result = NT_STATUS_NO_LOGON_SERVERS;
1793 set_auth_errors(state->response, result);
1795 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1796 state->request->data.auth.user,
1797 state->response->data.auth.nt_status_string,
1798 state->response->data.auth.pam_error));
1800 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1803 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1804 struct winbindd_cli_state *state)
1806 NTSTATUS result;
1807 struct netr_SamInfo3 *info3 = NULL;
1808 const char *name_user = NULL;
1809 const char *name_domain = NULL;
1810 const char *workstation;
1812 DATA_BLOB lm_resp, nt_resp;
1814 /* This is child-only, so no check for privileged access is needed
1815 anymore */
1817 /* Ensure null termination */
1818 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1819 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1821 name_user = state->request->data.auth_crap.user;
1822 name_domain = state->request->data.auth_crap.domain;
1823 workstation = state->request->data.auth_crap.workstation;
1825 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1826 name_domain, name_user));
1828 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1829 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1830 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1831 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1832 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1833 state->request->data.auth_crap.lm_resp_len,
1834 state->request->data.auth_crap.nt_resp_len));
1835 result = NT_STATUS_INVALID_PARAMETER;
1836 goto done;
1840 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1841 state->request->data.auth_crap.lm_resp_len);
1843 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1844 nt_resp = data_blob_talloc(state->mem_ctx,
1845 state->request->extra_data.data,
1846 state->request->data.auth_crap.nt_resp_len);
1847 } else {
1848 nt_resp = data_blob_talloc(state->mem_ctx,
1849 state->request->data.auth_crap.nt_resp,
1850 state->request->data.auth_crap.nt_resp_len);
1853 if (strequal(name_domain, get_global_sam_name())) {
1854 DATA_BLOB chal_blob = data_blob_const(
1855 state->request->data.auth_crap.chal,
1856 sizeof(state->request->data.auth_crap.chal));
1858 result = winbindd_dual_auth_passdb(
1859 state->mem_ctx, name_domain, name_user,
1860 &chal_blob, &lm_resp, &nt_resp, &info3);
1861 goto process_result;
1864 result = winbind_samlogon_retry_loop(domain,
1865 state->mem_ctx,
1866 state->request->data.auth_crap.logon_parameters,
1867 domain->dcname,
1868 name_user,
1869 name_domain,
1870 /* Bug #3248 - found by Stefan Burkei. */
1871 workstation, /* We carefully set this above so use it... */
1872 state->request->data.auth_crap.chal,
1873 lm_resp,
1874 nt_resp,
1875 &info3);
1876 if (!NT_STATUS_IS_OK(result)) {
1877 goto done;
1880 process_result:
1882 if (NT_STATUS_IS_OK(result)) {
1883 struct dom_sid user_sid;
1885 sid_compose(&user_sid, info3->base.domain_sid,
1886 info3->base.rid);
1887 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1888 &user_sid);
1889 netsamlogon_cache_store(name_user, info3);
1891 /* Check if the user is in the right group */
1893 result = check_info3_in_group(
1894 info3,
1895 state->request->data.auth_crap.require_membership_of_sid);
1896 if (!NT_STATUS_IS_OK(result)) {
1897 DEBUG(3, ("User %s is not in the required group (%s), so "
1898 "crap authentication is rejected\n",
1899 state->request->data.auth_crap.user,
1900 state->request->data.auth_crap.require_membership_of_sid));
1901 goto done;
1904 result = append_auth_data(state->mem_ctx, state->response,
1905 state->request->flags, info3,
1906 name_domain, name_user);
1907 if (!NT_STATUS_IS_OK(result)) {
1908 goto done;
1912 done:
1914 /* give us a more useful (more correct?) error code */
1915 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1916 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1917 result = NT_STATUS_NO_LOGON_SERVERS;
1920 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1921 result = nt_status_squash(result);
1924 set_auth_errors(state->response, result);
1926 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1927 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1928 name_domain,
1929 name_user,
1930 state->response->data.auth.nt_status_string,
1931 state->response->data.auth.pam_error));
1933 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1936 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1937 struct winbindd_cli_state *state)
1939 char *oldpass;
1940 char *newpass = NULL;
1941 struct policy_handle dom_pol;
1942 struct rpc_pipe_client *cli = NULL;
1943 bool got_info = false;
1944 struct samr_DomInfo1 *info = NULL;
1945 struct userPwdChangeFailureInformation *reject = NULL;
1946 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1947 fstring domain, user;
1948 struct dcerpc_binding_handle *b = NULL;
1950 ZERO_STRUCT(dom_pol);
1952 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1953 state->request->data.auth.user));
1955 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1956 goto done;
1959 /* Change password */
1961 oldpass = state->request->data.chauthtok.oldpass;
1962 newpass = state->request->data.chauthtok.newpass;
1964 /* Initialize reject reason */
1965 state->response->data.auth.reject_reason = Undefined;
1967 /* Get sam handle */
1969 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1970 &dom_pol);
1971 if (!NT_STATUS_IS_OK(result)) {
1972 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1973 goto done;
1976 b = cli->binding_handle;
1978 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1979 user,
1980 newpass,
1981 oldpass,
1982 &info,
1983 &reject);
1985 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1987 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1989 fill_in_password_policy(state->response, info);
1991 state->response->data.auth.reject_reason =
1992 reject->extendedFailureReason;
1994 got_info = true;
1997 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1998 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1999 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2000 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2002 /* only fallback when the chgpasswd_user3 call is not supported */
2003 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2004 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2005 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2006 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2008 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2009 nt_errstr(result)));
2011 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2013 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2014 Map to the same status code as Windows 2003. */
2016 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2017 result = NT_STATUS_PASSWORD_RESTRICTION;
2021 done:
2023 if (NT_STATUS_IS_OK(result)
2024 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2025 && lp_winbind_offline_logon()) {
2026 result = winbindd_update_creds_by_name(contact_domain, user,
2027 newpass);
2028 /* Again, this happens when we login from gdm or xdm
2029 * and the password expires, *BUT* cached crendentials
2030 * doesn't exist. winbindd_update_creds_by_name()
2031 * returns NT_STATUS_NO_SUCH_USER.
2032 * This is not a failure.
2033 * --- BoYang
2034 * */
2035 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2036 result = NT_STATUS_OK;
2039 if (!NT_STATUS_IS_OK(result)) {
2040 DEBUG(10, ("Failed to store creds: %s\n",
2041 nt_errstr(result)));
2042 goto process_result;
2046 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2048 NTSTATUS policy_ret;
2050 policy_ret = fillup_password_policy(
2051 contact_domain, state->response);
2053 /* failure of this is non critical, it will just provide no
2054 * additional information to the client why the change has
2055 * failed - Guenther */
2057 if (!NT_STATUS_IS_OK(policy_ret)) {
2058 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2059 goto process_result;
2063 process_result:
2065 if (strequal(contact_domain->name, get_global_sam_name())) {
2066 /* FIXME: internal rpc pipe does not cache handles yet */
2067 if (b) {
2068 if (is_valid_policy_hnd(&dom_pol)) {
2069 NTSTATUS _result;
2070 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2072 TALLOC_FREE(cli);
2076 set_auth_errors(state->response, result);
2078 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2079 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2080 domain,
2081 user,
2082 state->response->data.auth.nt_status_string,
2083 state->response->data.auth.pam_error));
2085 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2088 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2089 struct winbindd_cli_state *state)
2091 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2093 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2094 state->request->data.logoff.user));
2096 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2097 result = NT_STATUS_OK;
2098 goto process_result;
2101 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2102 result = NT_STATUS_OK;
2103 goto process_result;
2106 #ifdef HAVE_KRB5
2108 if (state->request->data.logoff.uid < 0) {
2109 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2110 goto process_result;
2113 /* what we need here is to find the corresponding krb5 ccache name *we*
2114 * created for a given username and destroy it */
2116 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2117 result = NT_STATUS_OK;
2118 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2119 goto process_result;
2122 if (!ccache_entry_identical(state->request->data.logoff.user,
2123 state->request->data.logoff.uid,
2124 state->request->data.logoff.krb5ccname)) {
2125 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2126 goto process_result;
2129 result = remove_ccache(state->request->data.logoff.user);
2130 if (!NT_STATUS_IS_OK(result)) {
2131 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2132 nt_errstr(result)));
2133 goto process_result;
2137 * Remove any mlock'ed memory creds in the child
2138 * we might be using for krb5 ticket renewal.
2141 winbindd_delete_memory_creds(state->request->data.logoff.user);
2143 #else
2144 result = NT_STATUS_NOT_SUPPORTED;
2145 #endif
2147 process_result:
2150 set_auth_errors(state->response, result);
2152 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2155 /* Change user password with auth crap*/
2157 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2159 NTSTATUS result;
2160 DATA_BLOB new_nt_password;
2161 DATA_BLOB old_nt_hash_enc;
2162 DATA_BLOB new_lm_password;
2163 DATA_BLOB old_lm_hash_enc;
2164 fstring domain,user;
2165 struct policy_handle dom_pol;
2166 struct winbindd_domain *contact_domain = domainSt;
2167 struct rpc_pipe_client *cli = NULL;
2168 struct dcerpc_binding_handle *b = NULL;
2170 ZERO_STRUCT(dom_pol);
2172 /* Ensure null termination */
2173 state->request->data.chng_pswd_auth_crap.user[
2174 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2175 state->request->data.chng_pswd_auth_crap.domain[
2176 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2177 *domain = 0;
2178 *user = 0;
2180 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2181 (unsigned long)state->pid,
2182 state->request->data.chng_pswd_auth_crap.domain,
2183 state->request->data.chng_pswd_auth_crap.user));
2185 if (lp_winbind_offline_logon()) {
2186 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2187 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2188 result = NT_STATUS_ACCESS_DENIED;
2189 goto done;
2192 if (*state->request->data.chng_pswd_auth_crap.domain) {
2193 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2194 } else {
2195 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2196 domain, user);
2198 if(!*domain) {
2199 DEBUG(3,("no domain specified with username (%s) - "
2200 "failing auth\n",
2201 state->request->data.chng_pswd_auth_crap.user));
2202 result = NT_STATUS_NO_SUCH_USER;
2203 goto done;
2207 if (!*domain && lp_winbind_use_default_domain()) {
2208 fstrcpy(domain,(char *)lp_workgroup());
2211 if(!*user) {
2212 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2215 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2216 (unsigned long)state->pid, domain, user));
2218 /* Change password */
2219 new_nt_password = data_blob_const(
2220 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2221 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2223 old_nt_hash_enc = data_blob_const(
2224 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2225 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2227 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2228 new_lm_password = data_blob_const(
2229 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2230 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2232 old_lm_hash_enc = data_blob_const(
2233 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2234 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2235 } else {
2236 new_lm_password.length = 0;
2237 old_lm_hash_enc.length = 0;
2240 /* Get sam handle */
2242 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2243 if (!NT_STATUS_IS_OK(result)) {
2244 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2245 goto done;
2248 b = cli->binding_handle;
2250 result = rpccli_samr_chng_pswd_auth_crap(
2251 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2252 new_lm_password, old_lm_hash_enc);
2254 done:
2256 if (strequal(contact_domain->name, get_global_sam_name())) {
2257 /* FIXME: internal rpc pipe does not cache handles yet */
2258 if (b) {
2259 if (is_valid_policy_hnd(&dom_pol)) {
2260 NTSTATUS _result;
2261 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2263 TALLOC_FREE(cli);
2267 set_auth_errors(state->response, result);
2269 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2270 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2271 domain, user,
2272 state->response->data.auth.nt_status_string,
2273 state->response->data.auth.pam_error));
2275 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;