lib/util: new merged debug system
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob3fe6254793931dcba3fa2ea54bc5127356336796
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"
39 #undef DBGC_CLASS
40 #define DBGC_CLASS DBGC_WINBIND
42 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
44 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
45 struct winbindd_response *resp,
46 struct netr_SamInfo3 *info3)
48 char *ex;
49 uint32_t i;
51 resp->data.auth.info3.logon_time =
52 nt_time_to_unix(info3->base.last_logon);
53 resp->data.auth.info3.logoff_time =
54 nt_time_to_unix(info3->base.last_logoff);
55 resp->data.auth.info3.kickoff_time =
56 nt_time_to_unix(info3->base.acct_expiry);
57 resp->data.auth.info3.pass_last_set_time =
58 nt_time_to_unix(info3->base.last_password_change);
59 resp->data.auth.info3.pass_can_change_time =
60 nt_time_to_unix(info3->base.allow_password_change);
61 resp->data.auth.info3.pass_must_change_time =
62 nt_time_to_unix(info3->base.force_password_change);
64 resp->data.auth.info3.logon_count = info3->base.logon_count;
65 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
67 resp->data.auth.info3.user_rid = info3->base.rid;
68 resp->data.auth.info3.group_rid = info3->base.primary_gid;
69 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
71 resp->data.auth.info3.num_groups = info3->base.groups.count;
72 resp->data.auth.info3.user_flgs = info3->base.user_flags;
74 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
75 resp->data.auth.info3.num_other_sids = info3->sidcount;
77 fstrcpy(resp->data.auth.info3.user_name,
78 info3->base.account_name.string);
79 fstrcpy(resp->data.auth.info3.full_name,
80 info3->base.full_name.string);
81 fstrcpy(resp->data.auth.info3.logon_script,
82 info3->base.logon_script.string);
83 fstrcpy(resp->data.auth.info3.profile_path,
84 info3->base.profile_path.string);
85 fstrcpy(resp->data.auth.info3.home_dir,
86 info3->base.home_directory.string);
87 fstrcpy(resp->data.auth.info3.dir_drive,
88 info3->base.home_drive.string);
90 fstrcpy(resp->data.auth.info3.logon_srv,
91 info3->base.logon_server.string);
92 fstrcpy(resp->data.auth.info3.logon_dom,
93 info3->base.domain.string);
95 ex = talloc_strdup(mem_ctx, "");
96 NT_STATUS_HAVE_NO_MEMORY(ex);
98 for (i=0; i < info3->base.groups.count; i++) {
99 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
100 info3->base.groups.rids[i].rid,
101 info3->base.groups.rids[i].attributes);
102 NT_STATUS_HAVE_NO_MEMORY(ex);
105 for (i=0; i < info3->sidcount; i++) {
106 char *sid;
108 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
109 NT_STATUS_HAVE_NO_MEMORY(sid);
111 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
112 sid,
113 info3->sids[i].attributes);
114 NT_STATUS_HAVE_NO_MEMORY(ex);
116 talloc_free(sid);
119 resp->extra_data.data = ex;
120 resp->length += talloc_get_size(ex);
122 return NT_STATUS_OK;
125 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
126 struct winbindd_response *resp,
127 struct netr_SamInfo3 *info3)
129 DATA_BLOB blob;
130 enum ndr_err_code ndr_err;
132 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
133 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
134 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
135 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
136 return ndr_map_error2ntstatus(ndr_err);
139 resp->extra_data.data = blob.data;
140 resp->length += blob.length;
142 return NT_STATUS_OK;
145 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
146 struct winbindd_response *resp,
147 const struct netr_SamInfo3 *info3,
148 const char *name_domain,
149 const char *name_user)
151 /* We've been asked to return the unix username, per
152 'winbind use default domain' settings and the like */
154 const char *nt_username, *nt_domain;
156 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
157 if (!nt_domain) {
158 /* If the server didn't give us one, just use the one
159 * we sent them */
160 nt_domain = name_domain;
163 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
164 if (!nt_username) {
165 /* If the server didn't give us one, just use the one
166 * we sent them */
167 nt_username = name_user;
170 fill_domain_username(resp->data.auth.unix_username,
171 nt_domain, nt_username, true);
173 DEBUG(5, ("Setting unix username to [%s]\n",
174 resp->data.auth.unix_username));
176 return NT_STATUS_OK;
179 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
180 struct winbindd_response *resp,
181 const struct netr_SamInfo3 *info3,
182 const char *name_domain,
183 const char *name_user)
185 char *afsname = NULL;
186 char *cell;
187 char *token;
189 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
190 if (afsname == NULL) {
191 return NT_STATUS_NO_MEMORY;
194 afsname = talloc_string_sub(mem_ctx,
195 lp_afs_username_map(),
196 "%D", name_domain);
197 afsname = talloc_string_sub(mem_ctx, afsname,
198 "%u", name_user);
199 afsname = talloc_string_sub(mem_ctx, afsname,
200 "%U", name_user);
203 struct dom_sid user_sid;
204 fstring sidstr;
206 sid_compose(&user_sid, info3->base.domain_sid,
207 info3->base.rid);
208 sid_to_fstring(sidstr, &user_sid);
209 afsname = talloc_string_sub(mem_ctx, afsname,
210 "%s", sidstr);
213 if (afsname == NULL) {
214 return NT_STATUS_NO_MEMORY;
217 strlower_m(afsname);
219 DEBUG(10, ("Generating token for user %s\n", afsname));
221 cell = strchr(afsname, '@');
223 if (cell == NULL) {
224 return NT_STATUS_NO_MEMORY;
227 *cell = '\0';
228 cell += 1;
230 token = afs_createtoken_str(afsname, cell);
231 if (token == NULL) {
232 return NT_STATUS_OK;
234 resp->extra_data.data = talloc_strdup(mem_ctx, token);
235 if (resp->extra_data.data == NULL) {
236 return NT_STATUS_NO_MEMORY;
238 resp->length += strlen((const char *)resp->extra_data.data)+1;
240 return NT_STATUS_OK;
243 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
244 const char *group_sid)
246 * Check whether a user belongs to a group or list of groups.
248 * @param mem_ctx talloc memory context.
249 * @param info3 user information, including group membership info.
250 * @param group_sid One or more groups , separated by commas.
252 * @return NT_STATUS_OK on success,
253 * NT_STATUS_LOGON_FAILURE if the user does not belong,
254 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
257 struct dom_sid *require_membership_of_sid;
258 uint32_t num_require_membership_of_sid;
259 char *req_sid;
260 const char *p;
261 struct dom_sid sid;
262 size_t i;
263 struct security_token *token;
264 TALLOC_CTX *frame = talloc_stackframe();
265 NTSTATUS status;
267 /* Parse the 'required group' SID */
269 if (!group_sid || !group_sid[0]) {
270 /* NO sid supplied, all users may access */
271 return NT_STATUS_OK;
274 token = talloc_zero(talloc_tos(), struct security_token);
275 if (token == NULL) {
276 DEBUG(0, ("talloc failed\n"));
277 TALLOC_FREE(frame);
278 return NT_STATUS_NO_MEMORY;
281 num_require_membership_of_sid = 0;
282 require_membership_of_sid = NULL;
284 p = group_sid;
286 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
287 if (!string_to_sid(&sid, req_sid)) {
288 DEBUG(0, ("check_info3_in_group: could not parse %s "
289 "as a SID!", req_sid));
290 TALLOC_FREE(frame);
291 return NT_STATUS_INVALID_PARAMETER;
294 status = add_sid_to_array(talloc_tos(), &sid,
295 &require_membership_of_sid,
296 &num_require_membership_of_sid);
297 if (!NT_STATUS_IS_OK(status)) {
298 DEBUG(0, ("add_sid_to_array failed\n"));
299 TALLOC_FREE(frame);
300 return status;
304 status = sid_array_from_info3(talloc_tos(), info3,
305 &token->sids,
306 &token->num_sids,
307 true, false);
308 if (!NT_STATUS_IS_OK(status)) {
309 TALLOC_FREE(frame);
310 return status;
313 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
314 token))
315 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
316 token))) {
317 DEBUG(3, ("could not add aliases: %s\n",
318 nt_errstr(status)));
319 TALLOC_FREE(frame);
320 return status;
323 security_token_debug(DBGC_CLASS, 10, token);
325 for (i=0; i<num_require_membership_of_sid; i++) {
326 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
327 &require_membership_of_sid[i])));
328 if (nt_token_check_sid(&require_membership_of_sid[i],
329 token)) {
330 DEBUG(10, ("Access ok\n"));
331 TALLOC_FREE(frame);
332 return NT_STATUS_OK;
336 /* Do not distinguish this error from a wrong username/pw */
338 TALLOC_FREE(frame);
339 return NT_STATUS_LOGON_FAILURE;
342 struct winbindd_domain *find_auth_domain(uint8_t flags,
343 const char *domain_name)
345 struct winbindd_domain *domain;
347 if (IS_DC) {
348 domain = find_domain_from_name_noinit(domain_name);
349 if (domain == NULL) {
350 DEBUG(3, ("Authentication for domain [%s] refused "
351 "as it is not a trusted domain\n",
352 domain_name));
354 return domain;
357 if (strequal(domain_name, get_global_sam_name())) {
358 return find_domain_from_name_noinit(domain_name);
361 /* we can auth against trusted domains */
362 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
363 domain = find_domain_from_name_noinit(domain_name);
364 if (domain == NULL) {
365 DEBUG(3, ("Authentication for domain [%s] skipped "
366 "as it is not a trusted domain\n",
367 domain_name));
368 } else {
369 return domain;
373 return find_our_domain();
376 static void fill_in_password_policy(struct winbindd_response *r,
377 const struct samr_DomInfo1 *p)
379 r->data.auth.policy.min_length_password =
380 p->min_password_length;
381 r->data.auth.policy.password_history =
382 p->password_history_length;
383 r->data.auth.policy.password_properties =
384 p->password_properties;
385 r->data.auth.policy.expire =
386 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
387 r->data.auth.policy.min_passwordage =
388 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
391 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
392 struct winbindd_response *response)
394 TALLOC_CTX *frame = talloc_stackframe();
395 struct winbindd_methods *methods;
396 NTSTATUS status;
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 status = NT_STATUS_NOT_SUPPORTED;
403 goto done;
406 methods = domain->methods;
408 status = methods->password_policy(domain, talloc_tos(), &password_policy);
409 if (NT_STATUS_IS_ERR(status)) {
410 goto done;
413 fill_in_password_policy(response, &password_policy);
415 done:
416 TALLOC_FREE(frame);
417 return NT_STATUS_OK;
420 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
421 TALLOC_CTX *mem_ctx,
422 uint16 *lockout_threshold)
424 struct winbindd_methods *methods;
425 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
426 struct samr_DomInfo12 lockout_policy;
428 *lockout_threshold = 0;
430 methods = domain->methods;
432 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
433 if (NT_STATUS_IS_ERR(status)) {
434 return status;
437 *lockout_threshold = lockout_policy.lockout_threshold;
439 return NT_STATUS_OK;
442 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
443 TALLOC_CTX *mem_ctx,
444 uint32 *password_properties)
446 struct winbindd_methods *methods;
447 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
448 struct samr_DomInfo1 password_policy;
450 *password_properties = 0;
452 methods = domain->methods;
454 status = methods->password_policy(domain, mem_ctx, &password_policy);
455 if (NT_STATUS_IS_ERR(status)) {
456 return status;
459 *password_properties = password_policy.password_properties;
461 return NT_STATUS_OK;
464 #ifdef HAVE_KRB5
466 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
467 const char *type,
468 uid_t uid,
469 const char **user_ccache_file)
471 /* accept FILE and WRFILE as krb5_cc_type from the client and then
472 * build the full ccname string based on the user's uid here -
473 * Guenther*/
475 const char *gen_cc = NULL;
477 if (uid != -1) {
478 if (strequal(type, "FILE")) {
479 gen_cc = talloc_asprintf(
480 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
482 if (strequal(type, "WRFILE")) {
483 gen_cc = talloc_asprintf(
484 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
488 *user_ccache_file = gen_cc;
490 if (gen_cc == NULL) {
491 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
493 if (gen_cc == NULL) {
494 DEBUG(0,("out of memory\n"));
495 return NULL;
498 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
499 (*user_ccache_file == NULL) ? " (internal)":""));
501 return gen_cc;
504 #endif
506 uid_t get_uid_from_request(struct winbindd_request *request)
508 uid_t uid;
510 uid = request->data.auth.uid;
512 if (uid < 0) {
513 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
514 return -1;
516 return uid;
519 /**********************************************************************
520 Authenticate a user with a clear text password using Kerberos and fill up
521 ccache if required
522 **********************************************************************/
524 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
525 struct winbindd_domain *domain,
526 const char *user,
527 const char *pass,
528 const char *krb5_cc_type,
529 uid_t uid,
530 struct netr_SamInfo3 **info3,
531 fstring krb5ccname)
533 #ifdef HAVE_KRB5
534 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
535 krb5_error_code krb5_ret;
536 const char *cc = NULL;
537 const char *principal_s = NULL;
538 const char *service = NULL;
539 char *realm = NULL;
540 fstring name_domain, name_user;
541 time_t ticket_lifetime = 0;
542 time_t renewal_until = 0;
543 ADS_STRUCT *ads;
544 time_t time_offset = 0;
545 const char *user_ccache_file;
546 struct PAC_LOGON_INFO *logon_info = NULL;
548 *info3 = NULL;
550 /* 1st step:
551 * prepare a krb5_cc_cache string for the user */
553 if (uid == -1) {
554 DEBUG(0,("no valid uid\n"));
557 cc = generate_krb5_ccache(mem_ctx,
558 krb5_cc_type,
559 uid,
560 &user_ccache_file);
561 if (cc == NULL) {
562 return NT_STATUS_NO_MEMORY;
566 /* 2nd step:
567 * get kerberos properties */
569 if (domain->private_data) {
570 ads = (ADS_STRUCT *)domain->private_data;
571 time_offset = ads->auth.time_offset;
575 /* 3rd step:
576 * do kerberos auth and setup ccache as the user */
578 parse_domain_user(user, name_domain, name_user);
580 realm = domain->alt_name;
581 strupper_m(realm);
583 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
584 if (principal_s == NULL) {
585 return NT_STATUS_NO_MEMORY;
588 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
589 if (service == NULL) {
590 return NT_STATUS_NO_MEMORY;
593 /* if this is a user ccache, we need to act as the user to let the krb5
594 * library handle the chown, etc. */
596 /************************ ENTERING NON-ROOT **********************/
598 if (user_ccache_file != NULL) {
599 set_effective_uid(uid);
600 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
603 result = kerberos_return_pac(mem_ctx,
604 principal_s,
605 pass,
606 time_offset,
607 &ticket_lifetime,
608 &renewal_until,
610 true,
611 true,
612 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
613 NULL,
614 &logon_info);
615 if (user_ccache_file != NULL) {
616 gain_root_privilege();
619 /************************ RETURNED TO ROOT **********************/
621 if (!NT_STATUS_IS_OK(result)) {
622 goto failed;
625 *info3 = &logon_info->info3;
627 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
628 principal_s));
630 /* if we had a user's ccache then return that string for the pam
631 * environment */
633 if (user_ccache_file != NULL) {
635 fstrcpy(krb5ccname, user_ccache_file);
637 result = add_ccache_to_list(principal_s,
639 service,
640 user,
641 realm,
642 uid,
643 time(NULL),
644 ticket_lifetime,
645 renewal_until,
646 false);
648 if (!NT_STATUS_IS_OK(result)) {
649 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
650 nt_errstr(result)));
652 } else {
654 /* need to delete the memory cred cache, it is not used anymore */
656 krb5_ret = ads_kdestroy(cc);
657 if (krb5_ret) {
658 DEBUG(3,("winbindd_raw_kerberos_login: "
659 "could not destroy krb5 credential cache: "
660 "%s\n", error_message(krb5_ret)));
665 return NT_STATUS_OK;
667 failed:
669 /* we could have created a new credential cache with a valid tgt in it
670 * but we werent able to get or verify the service ticket for this
671 * local host and therefor didn't get the PAC, we need to remove that
672 * cache entirely now */
674 krb5_ret = ads_kdestroy(cc);
675 if (krb5_ret) {
676 DEBUG(3,("winbindd_raw_kerberos_login: "
677 "could not destroy krb5 credential cache: "
678 "%s\n", error_message(krb5_ret)));
681 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
682 DEBUG(3,("winbindd_raw_kerberos_login: "
683 "could not remove ccache for user %s\n",
684 user));
687 return result;
688 #else
689 return NT_STATUS_NOT_SUPPORTED;
690 #endif /* HAVE_KRB5 */
693 /****************************************************************
694 ****************************************************************/
696 bool check_request_flags(uint32_t flags)
698 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
699 WBFLAG_PAM_INFO3_TEXT |
700 WBFLAG_PAM_INFO3_NDR;
702 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
703 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
704 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
705 !(flags & flags_edata) ) {
706 return true;
709 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
710 flags));
712 return false;
715 /****************************************************************
716 ****************************************************************/
718 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
719 struct winbindd_response *resp,
720 uint32_t request_flags,
721 struct netr_SamInfo3 *info3,
722 const char *name_domain,
723 const char *name_user)
725 NTSTATUS result;
727 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
728 memcpy(resp->data.auth.user_session_key,
729 info3->base.key.key,
730 sizeof(resp->data.auth.user_session_key)
731 /* 16 */);
734 if (request_flags & WBFLAG_PAM_LMKEY) {
735 memcpy(resp->data.auth.first_8_lm_hash,
736 info3->base.LMSessKey.key,
737 sizeof(resp->data.auth.first_8_lm_hash)
738 /* 8 */);
741 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
742 result = append_unix_username(mem_ctx, resp,
743 info3, name_domain, name_user);
744 if (!NT_STATUS_IS_OK(result)) {
745 DEBUG(10,("Failed to append Unix Username: %s\n",
746 nt_errstr(result)));
747 return result;
751 /* currently, anything from here on potentially overwrites extra_data. */
753 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
754 result = append_info3_as_ndr(mem_ctx, resp, info3);
755 if (!NT_STATUS_IS_OK(result)) {
756 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
757 nt_errstr(result)));
758 return result;
762 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
763 result = append_info3_as_txt(mem_ctx, resp, info3);
764 if (!NT_STATUS_IS_OK(result)) {
765 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
766 nt_errstr(result)));
767 return result;
771 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
772 result = append_afs_token(mem_ctx, resp,
773 info3, name_domain, name_user);
774 if (!NT_STATUS_IS_OK(result)) {
775 DEBUG(10,("Failed to append AFS token: %s\n",
776 nt_errstr(result)));
777 return result;
781 return NT_STATUS_OK;
784 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
785 struct winbindd_cli_state *state,
786 struct netr_SamInfo3 **info3)
788 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
789 uint16 max_allowed_bad_attempts;
790 fstring name_domain, name_user;
791 struct dom_sid sid;
792 enum lsa_SidType type;
793 uchar new_nt_pass[NT_HASH_LEN];
794 const uint8 *cached_nt_pass;
795 const uint8 *cached_salt;
796 struct netr_SamInfo3 *my_info3;
797 time_t kickoff_time, must_change_time;
798 bool password_good = false;
799 #ifdef HAVE_KRB5
800 struct winbindd_tdc_domain *tdc_domain = NULL;
801 #endif
803 *info3 = NULL;
805 ZERO_STRUCTP(info3);
807 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
809 /* Parse domain and username */
811 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
814 if (!lookup_cached_name(name_domain,
815 name_user,
816 &sid,
817 &type)) {
818 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
819 return NT_STATUS_NO_SUCH_USER;
822 if (type != SID_NAME_USER) {
823 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
824 return NT_STATUS_LOGON_FAILURE;
827 result = winbindd_get_creds(domain,
828 state->mem_ctx,
829 &sid,
830 &my_info3,
831 &cached_nt_pass,
832 &cached_salt);
833 if (!NT_STATUS_IS_OK(result)) {
834 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
835 return result;
838 *info3 = my_info3;
840 E_md4hash(state->request->data.auth.pass, new_nt_pass);
842 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
843 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
844 if (cached_salt) {
845 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
848 if (cached_salt) {
849 /* In this case we didn't store the nt_hash itself,
850 but the MD5 combination of salt + nt_hash. */
851 uchar salted_hash[NT_HASH_LEN];
852 E_md5hash(cached_salt, new_nt_pass, salted_hash);
854 password_good = (memcmp(cached_nt_pass, salted_hash,
855 NT_HASH_LEN) == 0);
856 } else {
857 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
858 password_good = (memcmp(cached_nt_pass, new_nt_pass,
859 NT_HASH_LEN) == 0);
862 if (password_good) {
864 /* User *DOES* know the password, update logon_time and reset
865 * bad_pw_count */
867 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
869 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
870 return NT_STATUS_ACCOUNT_LOCKED_OUT;
873 if (my_info3->base.acct_flags & ACB_DISABLED) {
874 return NT_STATUS_ACCOUNT_DISABLED;
877 if (my_info3->base.acct_flags & ACB_WSTRUST) {
878 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
881 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
882 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
885 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
886 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
889 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
890 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
891 my_info3->base.acct_flags));
892 return NT_STATUS_LOGON_FAILURE;
895 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
896 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
897 return NT_STATUS_ACCOUNT_EXPIRED;
900 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
901 if (must_change_time != 0 && must_change_time < time(NULL)) {
902 /* we allow grace logons when the password has expired */
903 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
904 /* return NT_STATUS_PASSWORD_EXPIRED; */
905 goto success;
908 #ifdef HAVE_KRB5
909 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
910 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
911 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
912 /* used to cope with the case winbindd starting without network. */
913 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
915 uid_t uid = -1;
916 const char *cc = NULL;
917 char *realm = NULL;
918 const char *principal_s = NULL;
919 const char *service = NULL;
920 const char *user_ccache_file;
922 uid = get_uid_from_request(state->request);
923 if (uid == -1) {
924 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
925 return NT_STATUS_INVALID_PARAMETER;
928 cc = generate_krb5_ccache(state->mem_ctx,
929 state->request->data.auth.krb5_cc_type,
930 state->request->data.auth.uid,
931 &user_ccache_file);
932 if (cc == NULL) {
933 return NT_STATUS_NO_MEMORY;
936 realm = domain->alt_name;
937 strupper_m(realm);
939 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
940 if (principal_s == NULL) {
941 return NT_STATUS_NO_MEMORY;
944 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
945 if (service == NULL) {
946 return NT_STATUS_NO_MEMORY;
949 if (user_ccache_file != NULL) {
951 fstrcpy(state->response->data.auth.krb5ccname,
952 user_ccache_file);
954 result = add_ccache_to_list(principal_s,
956 service,
957 state->request->data.auth.user,
958 domain->alt_name,
959 uid,
960 time(NULL),
961 time(NULL) + lp_winbind_cache_time(),
962 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
963 true);
965 if (!NT_STATUS_IS_OK(result)) {
966 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
967 "to add ccache to list: %s\n",
968 nt_errstr(result)));
972 #endif /* HAVE_KRB5 */
973 success:
974 /* FIXME: we possibly should handle logon hours as well (does xp when
975 * offline?) see auth/auth_sam.c:sam_account_ok for details */
977 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
978 my_info3->base.bad_password_count = 0;
980 result = winbindd_update_creds_by_info3(domain,
981 state->request->data.auth.user,
982 state->request->data.auth.pass,
983 my_info3);
984 if (!NT_STATUS_IS_OK(result)) {
985 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
986 nt_errstr(result)));
987 return result;
990 return NT_STATUS_OK;
994 /* User does *NOT* know the correct password, modify info3 accordingly */
996 /* failure of this is not critical */
997 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
998 if (!NT_STATUS_IS_OK(result)) {
999 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1000 "Won't be able to honour account lockout policies\n"));
1003 /* increase counter */
1004 my_info3->base.bad_password_count++;
1006 if (max_allowed_bad_attempts == 0) {
1007 goto failed;
1010 /* lockout user */
1011 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1013 uint32 password_properties;
1015 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1016 if (!NT_STATUS_IS_OK(result)) {
1017 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1020 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1021 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1022 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1026 failed:
1027 result = winbindd_update_creds_by_info3(domain,
1028 state->request->data.auth.user,
1029 NULL,
1030 my_info3);
1032 if (!NT_STATUS_IS_OK(result)) {
1033 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1034 nt_errstr(result)));
1037 return NT_STATUS_LOGON_FAILURE;
1040 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1041 struct winbindd_cli_state *state,
1042 struct netr_SamInfo3 **info3)
1044 struct winbindd_domain *contact_domain;
1045 fstring name_domain, name_user;
1046 NTSTATUS result;
1048 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1050 /* Parse domain and username */
1052 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1054 /* what domain should we contact? */
1056 if ( IS_DC ) {
1057 if (!(contact_domain = find_domain_from_name(name_domain))) {
1058 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1059 state->request->data.auth.user, name_domain, name_user, name_domain));
1060 result = NT_STATUS_NO_SUCH_USER;
1061 goto done;
1064 } else {
1065 if (is_myname(name_domain)) {
1066 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1067 result = NT_STATUS_NO_SUCH_USER;
1068 goto done;
1071 contact_domain = find_domain_from_name(name_domain);
1072 if (contact_domain == NULL) {
1073 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1074 state->request->data.auth.user, name_domain, name_user, name_domain));
1076 contact_domain = find_our_domain();
1080 if (contact_domain->initialized &&
1081 contact_domain->active_directory) {
1082 goto try_login;
1085 if (!contact_domain->initialized) {
1086 init_dc_connection(contact_domain);
1089 if (!contact_domain->active_directory) {
1090 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1091 return NT_STATUS_INVALID_LOGON_TYPE;
1093 try_login:
1094 result = winbindd_raw_kerberos_login(
1095 state->mem_ctx, contact_domain,
1096 state->request->data.auth.user,
1097 state->request->data.auth.pass,
1098 state->request->data.auth.krb5_cc_type,
1099 get_uid_from_request(state->request),
1100 info3, state->response->data.auth.krb5ccname);
1101 done:
1102 return result;
1105 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1106 const char *domain, const char *user,
1107 const DATA_BLOB *challenge,
1108 const DATA_BLOB *lm_resp,
1109 const DATA_BLOB *nt_resp,
1110 struct netr_SamInfo3 **pinfo3)
1112 struct auth_usersupplied_info *user_info = NULL;
1113 NTSTATUS status;
1115 status = make_user_info(&user_info, user, user, domain, domain,
1116 global_myname(), lm_resp, nt_resp, NULL, NULL,
1117 NULL, AUTH_PASSWORD_RESPONSE);
1118 if (!NT_STATUS_IS_OK(status)) {
1119 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1120 return status;
1123 /* We don't want any more mapping of the username */
1124 user_info->mapped_state = True;
1126 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1127 pinfo3);
1128 free_user_info(&user_info);
1129 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1130 user, nt_errstr(status)));
1131 return status;
1134 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1135 TALLOC_CTX *mem_ctx,
1136 uint32_t logon_parameters,
1137 const char *server,
1138 const char *username,
1139 const char *domainname,
1140 const char *workstation,
1141 const uint8_t chal[8],
1142 DATA_BLOB lm_response,
1143 DATA_BLOB nt_response,
1144 struct netr_SamInfo3 **info3)
1146 int attempts = 0;
1147 bool retry = false;
1148 NTSTATUS result;
1150 do {
1151 struct rpc_pipe_client *netlogon_pipe;
1152 const struct pipe_auth_data *auth;
1153 uint32_t neg_flags = 0;
1155 ZERO_STRUCTP(info3);
1156 retry = false;
1158 result = cm_connect_netlogon(domain, &netlogon_pipe);
1160 if (!NT_STATUS_IS_OK(result)) {
1161 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1162 nt_errstr(result)));
1163 return result;
1165 auth = netlogon_pipe->auth;
1166 if (netlogon_pipe->dc) {
1167 neg_flags = netlogon_pipe->dc->negotiate_flags;
1170 /* It is really important to try SamLogonEx here,
1171 * because in a clustered environment, we want to use
1172 * one machine account from multiple physical
1173 * computers.
1175 * With a normal SamLogon call, we must keep the
1176 * credentials chain updated and intact between all
1177 * users of the machine account (which would imply
1178 * cross-node communication for every NTLM logon).
1180 * (The credentials chain is not per NETLOGON pipe
1181 * connection, but globally on the server/client pair
1182 * by machine name).
1184 * When using SamLogonEx, the credentials are not
1185 * supplied, but the session key is implied by the
1186 * wrapping SamLogon context.
1188 * -- abartlet 21 April 2008
1190 * It's also important to use NetlogonValidationSamInfo4 (6),
1191 * because it relies on the rpc transport encryption
1192 * and avoids using the global netlogon schannel
1193 * session key to en/decrypt secret information
1194 * like the user_session_key for network logons.
1196 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1197 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1198 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1199 * are the indication that the server supports
1200 * NetlogonValidationSamInfo4 (6). And it must only
1201 * be used if "SealSecureChannel" is used.
1203 * -- metze 4 February 2011
1206 if (auth == NULL) {
1207 domain->can_do_validation6 = false;
1208 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1209 domain->can_do_validation6 = false;
1210 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1211 domain->can_do_validation6 = false;
1212 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1213 domain->can_do_validation6 = false;
1214 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1215 domain->can_do_validation6 = false;
1218 if (domain->can_do_samlogon_ex) {
1219 result = rpccli_netlogon_sam_network_logon_ex(
1220 netlogon_pipe,
1221 mem_ctx,
1223 server, /* server name */
1224 username, /* user name */
1225 domainname, /* target domain */
1226 workstation, /* workstation */
1227 chal,
1228 domain->can_do_validation6 ? 6 : 3,
1229 lm_response,
1230 nt_response,
1231 info3);
1232 } else {
1233 result = rpccli_netlogon_sam_network_logon(
1234 netlogon_pipe,
1235 mem_ctx,
1237 server, /* server name */
1238 username, /* user name */
1239 domainname, /* target domain */
1240 workstation, /* workstation */
1241 chal,
1242 domain->can_do_validation6 ? 6 : 3,
1243 lm_response,
1244 nt_response,
1245 info3);
1248 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1249 && domain->can_do_samlogon_ex) {
1250 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1251 "retrying with NetSamLogon\n"));
1252 domain->can_do_samlogon_ex = false;
1254 * It's likely that the server also does not support
1255 * validation level 6
1257 domain->can_do_validation6 = false;
1258 retry = true;
1259 continue;
1262 if (domain->can_do_validation6 &&
1263 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1264 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1265 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1266 DEBUG(3,("Got a DC that can not do validation level 6, "
1267 "retrying with level 3\n"));
1268 domain->can_do_validation6 = false;
1269 retry = true;
1270 continue;
1274 * we increment this after the "feature negotiation"
1275 * for can_do_samlogon_ex and can_do_validation6
1277 attempts += 1;
1279 /* We have to try a second time as cm_connect_netlogon
1280 might not yet have noticed that the DC has killed
1281 our connection. */
1283 if (!rpccli_is_connected(netlogon_pipe)) {
1284 retry = true;
1285 continue;
1288 /* if we get access denied, a possible cause was that we had
1289 and open connection to the DC, but someone changed our
1290 machine account password out from underneath us using 'net
1291 rpc changetrustpw' */
1293 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1294 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1295 "ACCESS_DENIED. Maybe the trust account "
1296 "password was changed and we didn't know it. "
1297 "Killing connections to domain %s\n",
1298 domainname));
1299 invalidate_cm_connection(&domain->conn);
1300 retry = true;
1303 } while ( (attempts < 2) && retry );
1305 return result;
1308 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1309 struct winbindd_domain *domain,
1310 const char *user,
1311 const char *pass,
1312 uint32_t request_flags,
1313 struct netr_SamInfo3 **info3)
1316 uchar chal[8];
1317 DATA_BLOB lm_resp;
1318 DATA_BLOB nt_resp;
1319 unsigned char local_nt_response[24];
1320 fstring name_domain, name_user;
1321 NTSTATUS result;
1322 struct netr_SamInfo3 *my_info3 = NULL;
1324 *info3 = NULL;
1326 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1328 /* Parse domain and username */
1330 parse_domain_user(user, name_domain, name_user);
1332 /* do password magic */
1334 generate_random_buffer(chal, sizeof(chal));
1336 if (lp_client_ntlmv2_auth()) {
1337 DATA_BLOB server_chal;
1338 DATA_BLOB names_blob;
1339 server_chal = data_blob_const(chal, 8);
1341 /* note that the 'workgroup' here is for the local
1342 machine. The 'server name' must match the
1343 'workstation' passed to the actual SamLogon call.
1345 names_blob = NTLMv2_generate_names_blob(
1346 mem_ctx, global_myname(), lp_workgroup());
1348 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1349 pass,
1350 &server_chal,
1351 &names_blob,
1352 &lm_resp, &nt_resp, NULL, NULL)) {
1353 data_blob_free(&names_blob);
1354 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1355 result = NT_STATUS_NO_MEMORY;
1356 goto done;
1358 data_blob_free(&names_blob);
1359 } else {
1360 lm_resp = data_blob_null;
1361 SMBNTencrypt(pass, chal, local_nt_response);
1363 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1364 sizeof(local_nt_response));
1367 if (strequal(name_domain, get_global_sam_name())) {
1368 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1370 result = winbindd_dual_auth_passdb(
1371 mem_ctx, name_domain, name_user,
1372 &chal_blob, &lm_resp, &nt_resp, info3);
1373 goto done;
1376 /* check authentication loop */
1378 result = winbind_samlogon_retry_loop(domain,
1379 mem_ctx,
1381 domain->dcname,
1382 name_user,
1383 name_domain,
1384 global_myname(),
1385 chal,
1386 lm_resp,
1387 nt_resp,
1388 &my_info3);
1389 if (!NT_STATUS_IS_OK(result)) {
1390 goto done;
1393 /* handle the case where a NT4 DC does not fill in the acct_flags in
1394 * the samlogon reply info3. When accurate info3 is required by the
1395 * caller, we look up the account flags ourselve - gd */
1397 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1398 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1400 struct rpc_pipe_client *samr_pipe;
1401 struct policy_handle samr_domain_handle, user_pol;
1402 union samr_UserInfo *info = NULL;
1403 NTSTATUS status_tmp, result_tmp;
1404 uint32 acct_flags;
1405 struct dcerpc_binding_handle *b;
1407 status_tmp = cm_connect_sam(domain, mem_ctx,
1408 &samr_pipe, &samr_domain_handle);
1410 if (!NT_STATUS_IS_OK(status_tmp)) {
1411 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1412 nt_errstr(status_tmp)));
1413 goto done;
1416 b = samr_pipe->binding_handle;
1418 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1419 &samr_domain_handle,
1420 MAXIMUM_ALLOWED_ACCESS,
1421 my_info3->base.rid,
1422 &user_pol,
1423 &result_tmp);
1425 if (!NT_STATUS_IS_OK(status_tmp)) {
1426 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1427 nt_errstr(status_tmp)));
1428 goto done;
1430 if (!NT_STATUS_IS_OK(result_tmp)) {
1431 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1432 nt_errstr(result_tmp)));
1433 goto done;
1436 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1437 &user_pol,
1439 &info,
1440 &result_tmp);
1442 if (!NT_STATUS_IS_OK(status_tmp)) {
1443 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1444 nt_errstr(status_tmp)));
1445 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1446 goto done;
1448 if (!NT_STATUS_IS_OK(result_tmp)) {
1449 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1450 nt_errstr(result_tmp)));
1451 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1452 goto done;
1455 acct_flags = info->info16.acct_flags;
1457 if (acct_flags == 0) {
1458 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1459 goto done;
1462 my_info3->base.acct_flags = acct_flags;
1464 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1466 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1469 *info3 = my_info3;
1470 done:
1471 return result;
1474 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1475 struct winbindd_cli_state *state)
1477 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1478 NTSTATUS krb5_result = NT_STATUS_OK;
1479 fstring name_domain, name_user;
1480 char *mapped_user;
1481 fstring domain_user;
1482 struct netr_SamInfo3 *info3 = NULL;
1483 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1485 /* Ensure null termination */
1486 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1488 /* Ensure null termination */
1489 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1491 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1492 state->request->data.auth.user));
1494 /* Parse domain and username */
1496 name_map_status = normalize_name_unmap(state->mem_ctx,
1497 state->request->data.auth.user,
1498 &mapped_user);
1500 /* If the name normalization didnt' actually do anything,
1501 just use the original name */
1503 if (!NT_STATUS_IS_OK(name_map_status) &&
1504 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1506 mapped_user = state->request->data.auth.user;
1509 parse_domain_user(mapped_user, name_domain, name_user);
1511 if ( mapped_user != state->request->data.auth.user ) {
1512 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1513 *lp_winbind_separator(),
1514 name_user );
1515 safe_strcpy( state->request->data.auth.user, domain_user,
1516 sizeof(state->request->data.auth.user)-1 );
1519 if (!domain->online) {
1520 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1521 if (domain->startup) {
1522 /* Logons are very important to users. If we're offline and
1523 we get a request within the first 30 seconds of startup,
1524 try very hard to find a DC and go online. */
1526 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1527 "request in startup mode.\n", domain->name ));
1529 winbindd_flush_negative_conn_cache(domain);
1530 result = init_dc_connection(domain);
1534 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1536 /* Check for Kerberos authentication */
1537 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1539 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1540 /* save for later */
1541 krb5_result = result;
1544 if (NT_STATUS_IS_OK(result)) {
1545 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1546 goto process_result;
1547 } else {
1548 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1551 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1552 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1553 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1554 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1555 set_domain_offline( domain );
1556 goto cached_logon;
1559 /* there are quite some NT_STATUS errors where there is no
1560 * point in retrying with a samlogon, we explictly have to take
1561 * care not to increase the bad logon counter on the DC */
1563 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1564 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1565 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1566 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1567 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1568 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1569 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1570 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1571 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1572 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1573 goto done;
1576 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1577 DEBUG(3,("falling back to samlogon\n"));
1578 goto sam_logon;
1579 } else {
1580 goto cached_logon;
1584 sam_logon:
1585 /* Check for Samlogon authentication */
1586 if (domain->online) {
1587 result = winbindd_dual_pam_auth_samlogon(
1588 state->mem_ctx, domain,
1589 state->request->data.auth.user,
1590 state->request->data.auth.pass,
1591 state->request->flags,
1592 &info3);
1594 if (NT_STATUS_IS_OK(result)) {
1595 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1596 /* add the Krb5 err if we have one */
1597 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1598 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1600 goto process_result;
1603 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1604 nt_errstr(result)));
1606 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1607 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1608 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1610 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1611 set_domain_offline( domain );
1612 goto cached_logon;
1615 if (domain->online) {
1616 /* We're still online - fail. */
1617 goto done;
1621 cached_logon:
1622 /* Check for Cached logons */
1623 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1624 lp_winbind_offline_logon()) {
1626 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1628 if (NT_STATUS_IS_OK(result)) {
1629 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1630 goto process_result;
1631 } else {
1632 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1633 goto done;
1637 process_result:
1639 if (NT_STATUS_IS_OK(result)) {
1641 struct dom_sid user_sid;
1643 /* In all codepaths where result == NT_STATUS_OK info3 must have
1644 been initialized. */
1645 if (!info3) {
1646 result = NT_STATUS_INTERNAL_ERROR;
1647 goto done;
1650 sid_compose(&user_sid, info3->base.domain_sid,
1651 info3->base.rid);
1653 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1654 &user_sid);
1655 netsamlogon_cache_store(name_user, info3);
1657 /* save name_to_sid info as early as possible (only if
1658 this is our primary domain so we don't invalidate
1659 the cache entry by storing the seq_num for the wrong
1660 domain). */
1661 if ( domain->primary ) {
1662 cache_name2sid(domain, name_domain, name_user,
1663 SID_NAME_USER, &user_sid);
1666 /* Check if the user is in the right group */
1668 result = check_info3_in_group(
1669 info3,
1670 state->request->data.auth.require_membership_of_sid);
1671 if (!NT_STATUS_IS_OK(result)) {
1672 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1673 state->request->data.auth.user,
1674 state->request->data.auth.require_membership_of_sid));
1675 goto done;
1678 result = append_auth_data(state->mem_ctx, state->response,
1679 state->request->flags, info3,
1680 name_domain, name_user);
1681 if (!NT_STATUS_IS_OK(result)) {
1682 goto done;
1685 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1686 && lp_winbind_offline_logon()) {
1688 result = winbindd_store_creds(domain,
1689 state->request->data.auth.user,
1690 state->request->data.auth.pass,
1691 info3);
1694 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1695 struct winbindd_domain *our_domain = find_our_domain();
1697 /* This is not entirely correct I believe, but it is
1698 consistent. Only apply the password policy settings
1699 too warn users for our own domain. Cannot obtain these
1700 from trusted DCs all the time so don't do it at all.
1701 -- jerry */
1703 result = NT_STATUS_NOT_SUPPORTED;
1704 if (our_domain == domain ) {
1705 result = fillup_password_policy(
1706 our_domain, state->response);
1709 if (!NT_STATUS_IS_OK(result)
1710 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1712 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1713 domain->name, nt_errstr(result)));
1714 goto done;
1718 result = NT_STATUS_OK;
1721 done:
1722 /* give us a more useful (more correct?) error code */
1723 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1724 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1725 result = NT_STATUS_NO_LOGON_SERVERS;
1728 set_auth_errors(state->response, result);
1730 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1731 state->request->data.auth.user,
1732 state->response->data.auth.nt_status_string,
1733 state->response->data.auth.pam_error));
1735 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1738 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1739 struct winbindd_cli_state *state)
1741 NTSTATUS result;
1742 struct netr_SamInfo3 *info3 = NULL;
1743 const char *name_user = NULL;
1744 const char *name_domain = NULL;
1745 const char *workstation;
1747 DATA_BLOB lm_resp, nt_resp;
1749 /* This is child-only, so no check for privileged access is needed
1750 anymore */
1752 /* Ensure null termination */
1753 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1754 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1756 name_user = state->request->data.auth_crap.user;
1757 name_domain = state->request->data.auth_crap.domain;
1758 workstation = state->request->data.auth_crap.workstation;
1760 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1761 name_domain, name_user));
1763 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1764 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1765 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1766 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1767 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1768 state->request->data.auth_crap.lm_resp_len,
1769 state->request->data.auth_crap.nt_resp_len));
1770 result = NT_STATUS_INVALID_PARAMETER;
1771 goto done;
1775 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1776 state->request->data.auth_crap.lm_resp_len);
1778 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1779 nt_resp = data_blob_talloc(state->mem_ctx,
1780 state->request->extra_data.data,
1781 state->request->data.auth_crap.nt_resp_len);
1782 } else {
1783 nt_resp = data_blob_talloc(state->mem_ctx,
1784 state->request->data.auth_crap.nt_resp,
1785 state->request->data.auth_crap.nt_resp_len);
1788 if (strequal(name_domain, get_global_sam_name())) {
1789 DATA_BLOB chal_blob = data_blob_const(
1790 state->request->data.auth_crap.chal,
1791 sizeof(state->request->data.auth_crap.chal));
1793 result = winbindd_dual_auth_passdb(
1794 state->mem_ctx, name_domain, name_user,
1795 &chal_blob, &lm_resp, &nt_resp, &info3);
1796 goto process_result;
1799 result = winbind_samlogon_retry_loop(domain,
1800 state->mem_ctx,
1801 state->request->data.auth_crap.logon_parameters,
1802 domain->dcname,
1803 name_user,
1804 name_domain,
1805 /* Bug #3248 - found by Stefan Burkei. */
1806 workstation, /* We carefully set this above so use it... */
1807 state->request->data.auth_crap.chal,
1808 lm_resp,
1809 nt_resp,
1810 &info3);
1811 if (!NT_STATUS_IS_OK(result)) {
1812 goto done;
1815 process_result:
1817 if (NT_STATUS_IS_OK(result)) {
1818 struct dom_sid user_sid;
1820 sid_compose(&user_sid, info3->base.domain_sid,
1821 info3->base.rid);
1822 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1823 &user_sid);
1824 netsamlogon_cache_store(name_user, info3);
1826 /* Check if the user is in the right group */
1828 result = check_info3_in_group(
1829 info3,
1830 state->request->data.auth_crap.require_membership_of_sid);
1831 if (!NT_STATUS_IS_OK(result)) {
1832 DEBUG(3, ("User %s is not in the required group (%s), so "
1833 "crap authentication is rejected\n",
1834 state->request->data.auth_crap.user,
1835 state->request->data.auth_crap.require_membership_of_sid));
1836 goto done;
1839 result = append_auth_data(state->mem_ctx, state->response,
1840 state->request->flags, info3,
1841 name_domain, name_user);
1842 if (!NT_STATUS_IS_OK(result)) {
1843 goto done;
1847 done:
1849 /* give us a more useful (more correct?) error code */
1850 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1851 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1852 result = NT_STATUS_NO_LOGON_SERVERS;
1855 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1856 result = nt_status_squash(result);
1859 set_auth_errors(state->response, result);
1861 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1862 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1863 name_domain,
1864 name_user,
1865 state->response->data.auth.nt_status_string,
1866 state->response->data.auth.pam_error));
1868 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1871 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1872 struct winbindd_cli_state *state)
1874 char *oldpass;
1875 char *newpass = NULL;
1876 struct policy_handle dom_pol;
1877 struct rpc_pipe_client *cli = NULL;
1878 bool got_info = false;
1879 struct samr_DomInfo1 *info = NULL;
1880 struct userPwdChangeFailureInformation *reject = NULL;
1881 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1882 fstring domain, user;
1883 struct dcerpc_binding_handle *b = NULL;
1885 ZERO_STRUCT(dom_pol);
1887 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1888 state->request->data.auth.user));
1890 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1891 goto done;
1894 /* Change password */
1896 oldpass = state->request->data.chauthtok.oldpass;
1897 newpass = state->request->data.chauthtok.newpass;
1899 /* Initialize reject reason */
1900 state->response->data.auth.reject_reason = Undefined;
1902 /* Get sam handle */
1904 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1905 &dom_pol);
1906 if (!NT_STATUS_IS_OK(result)) {
1907 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1908 goto done;
1911 b = cli->binding_handle;
1913 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1914 user,
1915 newpass,
1916 oldpass,
1917 &info,
1918 &reject);
1920 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1922 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1924 fill_in_password_policy(state->response, info);
1926 state->response->data.auth.reject_reason =
1927 reject->extendedFailureReason;
1929 got_info = true;
1932 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1933 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1934 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1935 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1937 /* only fallback when the chgpasswd_user3 call is not supported */
1938 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1939 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1940 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1941 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1943 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1944 nt_errstr(result)));
1946 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1948 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1949 Map to the same status code as Windows 2003. */
1951 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1952 result = NT_STATUS_PASSWORD_RESTRICTION;
1956 done:
1958 if (NT_STATUS_IS_OK(result)
1959 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1960 && lp_winbind_offline_logon()) {
1961 result = winbindd_update_creds_by_name(contact_domain, user,
1962 newpass);
1963 /* Again, this happens when we login from gdm or xdm
1964 * and the password expires, *BUT* cached crendentials
1965 * doesn't exist. winbindd_update_creds_by_name()
1966 * returns NT_STATUS_NO_SUCH_USER.
1967 * This is not a failure.
1968 * --- BoYang
1969 * */
1970 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1971 result = NT_STATUS_OK;
1974 if (!NT_STATUS_IS_OK(result)) {
1975 DEBUG(10, ("Failed to store creds: %s\n",
1976 nt_errstr(result)));
1977 goto process_result;
1981 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1983 NTSTATUS policy_ret;
1985 policy_ret = fillup_password_policy(
1986 contact_domain, state->response);
1988 /* failure of this is non critical, it will just provide no
1989 * additional information to the client why the change has
1990 * failed - Guenther */
1992 if (!NT_STATUS_IS_OK(policy_ret)) {
1993 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1994 goto process_result;
1998 process_result:
2000 if (strequal(contact_domain->name, get_global_sam_name())) {
2001 /* FIXME: internal rpc pipe does not cache handles yet */
2002 if (b) {
2003 if (is_valid_policy_hnd(&dom_pol)) {
2004 NTSTATUS _result;
2005 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2007 TALLOC_FREE(cli);
2011 set_auth_errors(state->response, result);
2013 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2014 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2015 domain,
2016 user,
2017 state->response->data.auth.nt_status_string,
2018 state->response->data.auth.pam_error));
2020 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2023 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2024 struct winbindd_cli_state *state)
2026 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2028 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2029 state->request->data.logoff.user));
2031 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2032 result = NT_STATUS_OK;
2033 goto process_result;
2036 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2037 result = NT_STATUS_OK;
2038 goto process_result;
2041 #ifdef HAVE_KRB5
2043 if (state->request->data.logoff.uid < 0) {
2044 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2045 goto process_result;
2048 /* what we need here is to find the corresponding krb5 ccache name *we*
2049 * created for a given username and destroy it */
2051 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2052 result = NT_STATUS_OK;
2053 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2054 goto process_result;
2057 if (!ccache_entry_identical(state->request->data.logoff.user,
2058 state->request->data.logoff.uid,
2059 state->request->data.logoff.krb5ccname)) {
2060 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2061 goto process_result;
2064 result = remove_ccache(state->request->data.logoff.user);
2065 if (!NT_STATUS_IS_OK(result)) {
2066 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2067 nt_errstr(result)));
2068 goto process_result;
2071 #else
2072 result = NT_STATUS_NOT_SUPPORTED;
2073 #endif
2075 process_result:
2078 set_auth_errors(state->response, result);
2080 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2083 /* Change user password with auth crap*/
2085 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2087 NTSTATUS result;
2088 DATA_BLOB new_nt_password;
2089 DATA_BLOB old_nt_hash_enc;
2090 DATA_BLOB new_lm_password;
2091 DATA_BLOB old_lm_hash_enc;
2092 fstring domain,user;
2093 struct policy_handle dom_pol;
2094 struct winbindd_domain *contact_domain = domainSt;
2095 struct rpc_pipe_client *cli = NULL;
2096 struct dcerpc_binding_handle *b = NULL;
2098 ZERO_STRUCT(dom_pol);
2100 /* Ensure null termination */
2101 state->request->data.chng_pswd_auth_crap.user[
2102 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2103 state->request->data.chng_pswd_auth_crap.domain[
2104 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2105 *domain = 0;
2106 *user = 0;
2108 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2109 (unsigned long)state->pid,
2110 state->request->data.chng_pswd_auth_crap.domain,
2111 state->request->data.chng_pswd_auth_crap.user));
2113 if (lp_winbind_offline_logon()) {
2114 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2115 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2116 result = NT_STATUS_ACCESS_DENIED;
2117 goto done;
2120 if (*state->request->data.chng_pswd_auth_crap.domain) {
2121 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2122 } else {
2123 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2124 domain, user);
2126 if(!*domain) {
2127 DEBUG(3,("no domain specified with username (%s) - "
2128 "failing auth\n",
2129 state->request->data.chng_pswd_auth_crap.user));
2130 result = NT_STATUS_NO_SUCH_USER;
2131 goto done;
2135 if (!*domain && lp_winbind_use_default_domain()) {
2136 fstrcpy(domain,(char *)lp_workgroup());
2139 if(!*user) {
2140 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2143 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2144 (unsigned long)state->pid, domain, user));
2146 /* Change password */
2147 new_nt_password = data_blob_const(
2148 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2149 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2151 old_nt_hash_enc = data_blob_const(
2152 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2153 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2155 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2156 new_lm_password = data_blob_const(
2157 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2158 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2160 old_lm_hash_enc = data_blob_const(
2161 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2162 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2163 } else {
2164 new_lm_password.length = 0;
2165 old_lm_hash_enc.length = 0;
2168 /* Get sam handle */
2170 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2171 if (!NT_STATUS_IS_OK(result)) {
2172 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2173 goto done;
2176 b = cli->binding_handle;
2178 result = rpccli_samr_chng_pswd_auth_crap(
2179 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2180 new_lm_password, old_lm_hash_enc);
2182 done:
2184 if (strequal(contact_domain->name, get_global_sam_name())) {
2185 /* FIXME: internal rpc pipe does not cache handles yet */
2186 if (b) {
2187 if (is_valid_policy_hnd(&dom_pol)) {
2188 NTSTATUS _result;
2189 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2191 TALLOC_FREE(cli);
2195 set_auth_errors(state->response, result);
2197 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2198 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2199 domain, user,
2200 state->response->data.auth.nt_status_string,
2201 state->response->data.auth.pam_error));
2203 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;