s3-winbind: don't try to do clever thing if the username is not found while authentic...
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob79189ba57e5b2ac9ff90477dce7044a493de9588
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, false);
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 realm,
644 uid,
645 time(NULL),
646 ticket_lifetime,
647 renewal_until,
648 false);
650 if (!NT_STATUS_IS_OK(result)) {
651 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
652 nt_errstr(result)));
654 } else {
656 /* need to delete the memory cred cache, it is not used anymore */
658 krb5_ret = ads_kdestroy(cc);
659 if (krb5_ret) {
660 DEBUG(3,("winbindd_raw_kerberos_login: "
661 "could not destroy krb5 credential cache: "
662 "%s\n", error_message(krb5_ret)));
667 return NT_STATUS_OK;
669 failed:
671 /* we could have created a new credential cache with a valid tgt in it
672 * but we werent able to get or verify the service ticket for this
673 * local host and therefor didn't get the PAC, we need to remove that
674 * cache entirely now */
676 krb5_ret = ads_kdestroy(cc);
677 if (krb5_ret) {
678 DEBUG(3,("winbindd_raw_kerberos_login: "
679 "could not destroy krb5 credential cache: "
680 "%s\n", error_message(krb5_ret)));
683 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
684 DEBUG(3,("winbindd_raw_kerberos_login: "
685 "could not remove ccache for user %s\n",
686 user));
689 return result;
690 #else
691 return NT_STATUS_NOT_SUPPORTED;
692 #endif /* HAVE_KRB5 */
695 /****************************************************************
696 ****************************************************************/
698 bool check_request_flags(uint32_t flags)
700 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
701 WBFLAG_PAM_INFO3_TEXT |
702 WBFLAG_PAM_INFO3_NDR;
704 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
705 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
706 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
707 !(flags & flags_edata) ) {
708 return true;
711 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
712 flags));
714 return false;
717 /****************************************************************
718 ****************************************************************/
720 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
721 struct winbindd_response *resp,
722 uint32_t request_flags,
723 struct netr_SamInfo3 *info3,
724 const char *name_domain,
725 const char *name_user)
727 NTSTATUS result;
729 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
730 memcpy(resp->data.auth.user_session_key,
731 info3->base.key.key,
732 sizeof(resp->data.auth.user_session_key)
733 /* 16 */);
736 if (request_flags & WBFLAG_PAM_LMKEY) {
737 memcpy(resp->data.auth.first_8_lm_hash,
738 info3->base.LMSessKey.key,
739 sizeof(resp->data.auth.first_8_lm_hash)
740 /* 8 */);
743 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
744 result = append_unix_username(mem_ctx, resp,
745 info3, name_domain, name_user);
746 if (!NT_STATUS_IS_OK(result)) {
747 DEBUG(10,("Failed to append Unix Username: %s\n",
748 nt_errstr(result)));
749 return result;
753 /* currently, anything from here on potentially overwrites extra_data. */
755 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
756 result = append_info3_as_ndr(mem_ctx, resp, info3);
757 if (!NT_STATUS_IS_OK(result)) {
758 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
759 nt_errstr(result)));
760 return result;
764 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
765 result = append_info3_as_txt(mem_ctx, resp, info3);
766 if (!NT_STATUS_IS_OK(result)) {
767 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
768 nt_errstr(result)));
769 return result;
773 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
774 result = append_afs_token(mem_ctx, resp,
775 info3, name_domain, name_user);
776 if (!NT_STATUS_IS_OK(result)) {
777 DEBUG(10,("Failed to append AFS token: %s\n",
778 nt_errstr(result)));
779 return result;
783 return NT_STATUS_OK;
786 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
787 struct winbindd_cli_state *state,
788 struct netr_SamInfo3 **info3)
790 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
791 uint16 max_allowed_bad_attempts;
792 fstring name_domain, name_user;
793 struct dom_sid sid;
794 enum lsa_SidType type;
795 uchar new_nt_pass[NT_HASH_LEN];
796 const uint8 *cached_nt_pass;
797 const uint8 *cached_salt;
798 struct netr_SamInfo3 *my_info3;
799 time_t kickoff_time, must_change_time;
800 bool password_good = false;
801 #ifdef HAVE_KRB5
802 struct winbindd_tdc_domain *tdc_domain = NULL;
803 #endif
805 *info3 = NULL;
807 ZERO_STRUCTP(info3);
809 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
811 /* Parse domain and username */
813 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
816 if (!lookup_cached_name(name_domain,
817 name_user,
818 &sid,
819 &type)) {
820 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
821 return NT_STATUS_NO_SUCH_USER;
824 if (type != SID_NAME_USER) {
825 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
826 return NT_STATUS_LOGON_FAILURE;
829 result = winbindd_get_creds(domain,
830 state->mem_ctx,
831 &sid,
832 &my_info3,
833 &cached_nt_pass,
834 &cached_salt);
835 if (!NT_STATUS_IS_OK(result)) {
836 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
837 return result;
840 *info3 = my_info3;
842 E_md4hash(state->request->data.auth.pass, new_nt_pass);
844 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
845 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
846 if (cached_salt) {
847 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
850 if (cached_salt) {
851 /* In this case we didn't store the nt_hash itself,
852 but the MD5 combination of salt + nt_hash. */
853 uchar salted_hash[NT_HASH_LEN];
854 E_md5hash(cached_salt, new_nt_pass, salted_hash);
856 password_good = (memcmp(cached_nt_pass, salted_hash,
857 NT_HASH_LEN) == 0);
858 } else {
859 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
860 password_good = (memcmp(cached_nt_pass, new_nt_pass,
861 NT_HASH_LEN) == 0);
864 if (password_good) {
866 /* User *DOES* know the password, update logon_time and reset
867 * bad_pw_count */
869 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
871 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
872 return NT_STATUS_ACCOUNT_LOCKED_OUT;
875 if (my_info3->base.acct_flags & ACB_DISABLED) {
876 return NT_STATUS_ACCOUNT_DISABLED;
879 if (my_info3->base.acct_flags & ACB_WSTRUST) {
880 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
883 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
884 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
887 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
888 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
891 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
892 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
893 my_info3->base.acct_flags));
894 return NT_STATUS_LOGON_FAILURE;
897 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
898 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
899 return NT_STATUS_ACCOUNT_EXPIRED;
902 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
903 if (must_change_time != 0 && must_change_time < time(NULL)) {
904 /* we allow grace logons when the password has expired */
905 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
906 /* return NT_STATUS_PASSWORD_EXPIRED; */
907 goto success;
910 #ifdef HAVE_KRB5
911 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
912 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
913 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
914 /* used to cope with the case winbindd starting without network. */
915 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
917 uid_t uid = -1;
918 const char *cc = NULL;
919 char *realm = NULL;
920 const char *principal_s = NULL;
921 const char *service = NULL;
922 const char *user_ccache_file;
924 uid = get_uid_from_request(state->request);
925 if (uid == -1) {
926 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
927 return NT_STATUS_INVALID_PARAMETER;
930 cc = generate_krb5_ccache(state->mem_ctx,
931 state->request->data.auth.krb5_cc_type,
932 state->request->data.auth.uid,
933 &user_ccache_file);
934 if (cc == NULL) {
935 return NT_STATUS_NO_MEMORY;
938 realm = domain->alt_name;
939 strupper_m(realm);
941 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
942 if (principal_s == NULL) {
943 return NT_STATUS_NO_MEMORY;
946 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
947 if (service == NULL) {
948 return NT_STATUS_NO_MEMORY;
951 if (user_ccache_file != NULL) {
953 fstrcpy(state->response->data.auth.krb5ccname,
954 user_ccache_file);
956 result = add_ccache_to_list(principal_s,
958 service,
959 state->request->data.auth.user,
960 domain->alt_name,
961 uid,
962 time(NULL),
963 time(NULL) + lp_winbind_cache_time(),
964 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
965 true);
967 if (!NT_STATUS_IS_OK(result)) {
968 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
969 "to add ccache to list: %s\n",
970 nt_errstr(result)));
974 #endif /* HAVE_KRB5 */
975 success:
976 /* FIXME: we possibly should handle logon hours as well (does xp when
977 * offline?) see auth/auth_sam.c:sam_account_ok for details */
979 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
980 my_info3->base.bad_password_count = 0;
982 result = winbindd_update_creds_by_info3(domain,
983 state->request->data.auth.user,
984 state->request->data.auth.pass,
985 my_info3);
986 if (!NT_STATUS_IS_OK(result)) {
987 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
988 nt_errstr(result)));
989 return result;
992 return NT_STATUS_OK;
996 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
997 if (domain->online == false) {
998 goto failed;
1001 /* failure of this is not critical */
1002 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1003 if (!NT_STATUS_IS_OK(result)) {
1004 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1005 "Won't be able to honour account lockout policies\n"));
1008 /* increase counter */
1009 my_info3->base.bad_password_count++;
1011 if (max_allowed_bad_attempts == 0) {
1012 goto failed;
1015 /* lockout user */
1016 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1018 uint32 password_properties;
1020 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1021 if (!NT_STATUS_IS_OK(result)) {
1022 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1025 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1026 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1027 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1031 failed:
1032 result = winbindd_update_creds_by_info3(domain,
1033 state->request->data.auth.user,
1034 NULL,
1035 my_info3);
1037 if (!NT_STATUS_IS_OK(result)) {
1038 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1039 nt_errstr(result)));
1042 return NT_STATUS_LOGON_FAILURE;
1045 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1046 struct winbindd_cli_state *state,
1047 struct netr_SamInfo3 **info3)
1049 struct winbindd_domain *contact_domain;
1050 fstring name_domain, name_user;
1051 NTSTATUS result;
1053 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1055 /* Parse domain and username */
1057 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1059 /* what domain should we contact? */
1061 if ( IS_DC ) {
1062 if (!(contact_domain = find_domain_from_name(name_domain))) {
1063 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1064 state->request->data.auth.user, name_domain, name_user, name_domain));
1065 result = NT_STATUS_NO_SUCH_USER;
1066 goto done;
1069 } else {
1070 if (is_myname(name_domain)) {
1071 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1072 result = NT_STATUS_NO_SUCH_USER;
1073 goto done;
1076 contact_domain = find_domain_from_name(name_domain);
1077 if (contact_domain == NULL) {
1078 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1079 state->request->data.auth.user, name_domain, name_user, name_domain));
1081 result = NT_STATUS_NO_SUCH_USER;
1082 goto done;
1086 if (contact_domain->initialized &&
1087 contact_domain->active_directory) {
1088 goto try_login;
1091 if (!contact_domain->initialized) {
1092 init_dc_connection(contact_domain);
1095 if (!contact_domain->active_directory) {
1096 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1097 return NT_STATUS_INVALID_LOGON_TYPE;
1099 try_login:
1100 result = winbindd_raw_kerberos_login(
1101 state->mem_ctx, contact_domain,
1102 state->request->data.auth.user,
1103 state->request->data.auth.pass,
1104 state->request->data.auth.krb5_cc_type,
1105 get_uid_from_request(state->request),
1106 info3, state->response->data.auth.krb5ccname);
1107 done:
1108 return result;
1111 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1112 const char *domain, const char *user,
1113 const DATA_BLOB *challenge,
1114 const DATA_BLOB *lm_resp,
1115 const DATA_BLOB *nt_resp,
1116 struct netr_SamInfo3 **pinfo3)
1118 struct auth_usersupplied_info *user_info = NULL;
1119 NTSTATUS status;
1121 status = make_user_info(&user_info, user, user, domain, domain,
1122 global_myname(), lm_resp, nt_resp, NULL, NULL,
1123 NULL, AUTH_PASSWORD_RESPONSE);
1124 if (!NT_STATUS_IS_OK(status)) {
1125 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1126 return status;
1129 /* We don't want any more mapping of the username */
1130 user_info->mapped_state = True;
1132 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1133 pinfo3);
1134 free_user_info(&user_info);
1135 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1136 user, nt_errstr(status)));
1137 return status;
1140 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1141 TALLOC_CTX *mem_ctx,
1142 uint32_t logon_parameters,
1143 const char *server,
1144 const char *username,
1145 const char *domainname,
1146 const char *workstation,
1147 const uint8_t chal[8],
1148 DATA_BLOB lm_response,
1149 DATA_BLOB nt_response,
1150 struct netr_SamInfo3 **info3)
1152 int attempts = 0;
1153 bool retry = false;
1154 NTSTATUS result;
1156 do {
1157 struct rpc_pipe_client *netlogon_pipe;
1158 const struct pipe_auth_data *auth;
1159 uint32_t neg_flags = 0;
1161 ZERO_STRUCTP(info3);
1162 retry = false;
1164 result = cm_connect_netlogon(domain, &netlogon_pipe);
1166 if (!NT_STATUS_IS_OK(result)) {
1167 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1168 nt_errstr(result)));
1169 return result;
1171 auth = netlogon_pipe->auth;
1172 if (netlogon_pipe->dc) {
1173 neg_flags = netlogon_pipe->dc->negotiate_flags;
1176 /* It is really important to try SamLogonEx here,
1177 * because in a clustered environment, we want to use
1178 * one machine account from multiple physical
1179 * computers.
1181 * With a normal SamLogon call, we must keep the
1182 * credentials chain updated and intact between all
1183 * users of the machine account (which would imply
1184 * cross-node communication for every NTLM logon).
1186 * (The credentials chain is not per NETLOGON pipe
1187 * connection, but globally on the server/client pair
1188 * by machine name).
1190 * When using SamLogonEx, the credentials are not
1191 * supplied, but the session key is implied by the
1192 * wrapping SamLogon context.
1194 * -- abartlet 21 April 2008
1196 * It's also important to use NetlogonValidationSamInfo4 (6),
1197 * because it relies on the rpc transport encryption
1198 * and avoids using the global netlogon schannel
1199 * session key to en/decrypt secret information
1200 * like the user_session_key for network logons.
1202 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1203 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1204 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1205 * are the indication that the server supports
1206 * NetlogonValidationSamInfo4 (6). And it must only
1207 * be used if "SealSecureChannel" is used.
1209 * -- metze 4 February 2011
1212 if (auth == NULL) {
1213 domain->can_do_validation6 = false;
1214 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1215 domain->can_do_validation6 = false;
1216 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1217 domain->can_do_validation6 = false;
1218 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1219 domain->can_do_validation6 = false;
1220 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1221 domain->can_do_validation6 = false;
1224 if (domain->can_do_samlogon_ex) {
1225 result = rpccli_netlogon_sam_network_logon_ex(
1226 netlogon_pipe,
1227 mem_ctx,
1228 logon_parameters,
1229 server, /* server name */
1230 username, /* user name */
1231 domainname, /* target domain */
1232 workstation, /* workstation */
1233 chal,
1234 domain->can_do_validation6 ? 6 : 3,
1235 lm_response,
1236 nt_response,
1237 info3);
1238 } else {
1239 result = rpccli_netlogon_sam_network_logon(
1240 netlogon_pipe,
1241 mem_ctx,
1242 logon_parameters,
1243 server, /* server name */
1244 username, /* user name */
1245 domainname, /* target domain */
1246 workstation, /* workstation */
1247 chal,
1248 domain->can_do_validation6 ? 6 : 3,
1249 lm_response,
1250 nt_response,
1251 info3);
1254 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1257 * It's likely that the server also does not support
1258 * validation level 6
1260 domain->can_do_validation6 = false;
1262 if (domain->can_do_samlogon_ex) {
1263 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1264 "retrying with NetSamLogon\n"));
1265 domain->can_do_samlogon_ex = false;
1266 retry = true;
1267 continue;
1271 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1272 * (no Ex). This happens against old Samba
1273 * DCs. Drop the connection.
1275 invalidate_cm_connection(&domain->conn);
1276 result = NT_STATUS_LOGON_FAILURE;
1277 break;
1280 if (domain->can_do_validation6 &&
1281 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1282 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1283 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1284 DEBUG(3,("Got a DC that can not do validation level 6, "
1285 "retrying with level 3\n"));
1286 domain->can_do_validation6 = false;
1287 retry = true;
1288 continue;
1292 * we increment this after the "feature negotiation"
1293 * for can_do_samlogon_ex and can_do_validation6
1295 attempts += 1;
1297 /* We have to try a second time as cm_connect_netlogon
1298 might not yet have noticed that the DC has killed
1299 our connection. */
1301 if (!rpccli_is_connected(netlogon_pipe)) {
1302 retry = true;
1303 continue;
1306 /* if we get access denied, a possible cause was that we had
1307 and open connection to the DC, but someone changed our
1308 machine account password out from underneath us using 'net
1309 rpc changetrustpw' */
1311 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1312 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1313 "ACCESS_DENIED. Maybe the trust account "
1314 "password was changed and we didn't know it. "
1315 "Killing connections to domain %s\n",
1316 domainname));
1317 invalidate_cm_connection(&domain->conn);
1318 retry = true;
1321 } while ( (attempts < 2) && retry );
1323 return result;
1326 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1327 struct winbindd_domain *domain,
1328 const char *user,
1329 const char *pass,
1330 uint32_t request_flags,
1331 struct netr_SamInfo3 **info3)
1334 uchar chal[8];
1335 DATA_BLOB lm_resp;
1336 DATA_BLOB nt_resp;
1337 unsigned char local_nt_response[24];
1338 fstring name_domain, name_user;
1339 NTSTATUS result;
1340 struct netr_SamInfo3 *my_info3 = NULL;
1342 *info3 = NULL;
1344 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1346 /* Parse domain and username */
1348 parse_domain_user(user, name_domain, name_user);
1350 /* do password magic */
1352 generate_random_buffer(chal, sizeof(chal));
1354 if (lp_client_ntlmv2_auth()) {
1355 DATA_BLOB server_chal;
1356 DATA_BLOB names_blob;
1357 server_chal = data_blob_const(chal, 8);
1359 /* note that the 'workgroup' here is for the local
1360 machine. The 'server name' must match the
1361 'workstation' passed to the actual SamLogon call.
1363 names_blob = NTLMv2_generate_names_blob(
1364 mem_ctx, global_myname(), lp_workgroup());
1366 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1367 pass,
1368 &server_chal,
1369 &names_blob,
1370 &lm_resp, &nt_resp, NULL, NULL)) {
1371 data_blob_free(&names_blob);
1372 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1373 result = NT_STATUS_NO_MEMORY;
1374 goto done;
1376 data_blob_free(&names_blob);
1377 } else {
1378 lm_resp = data_blob_null;
1379 SMBNTencrypt(pass, chal, local_nt_response);
1381 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1382 sizeof(local_nt_response));
1385 if (strequal(name_domain, get_global_sam_name())) {
1386 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1388 result = winbindd_dual_auth_passdb(
1389 mem_ctx, name_domain, name_user,
1390 &chal_blob, &lm_resp, &nt_resp, info3);
1391 goto done;
1394 /* check authentication loop */
1396 result = winbind_samlogon_retry_loop(domain,
1397 mem_ctx,
1399 domain->dcname,
1400 name_user,
1401 name_domain,
1402 global_myname(),
1403 chal,
1404 lm_resp,
1405 nt_resp,
1406 &my_info3);
1407 if (!NT_STATUS_IS_OK(result)) {
1408 goto done;
1411 /* handle the case where a NT4 DC does not fill in the acct_flags in
1412 * the samlogon reply info3. When accurate info3 is required by the
1413 * caller, we look up the account flags ourselve - gd */
1415 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1416 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1418 struct rpc_pipe_client *samr_pipe;
1419 struct policy_handle samr_domain_handle, user_pol;
1420 union samr_UserInfo *info = NULL;
1421 NTSTATUS status_tmp, result_tmp;
1422 uint32 acct_flags;
1423 struct dcerpc_binding_handle *b;
1425 status_tmp = cm_connect_sam(domain, mem_ctx,
1426 &samr_pipe, &samr_domain_handle);
1428 if (!NT_STATUS_IS_OK(status_tmp)) {
1429 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1430 nt_errstr(status_tmp)));
1431 goto done;
1434 b = samr_pipe->binding_handle;
1436 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1437 &samr_domain_handle,
1438 MAXIMUM_ALLOWED_ACCESS,
1439 my_info3->base.rid,
1440 &user_pol,
1441 &result_tmp);
1443 if (!NT_STATUS_IS_OK(status_tmp)) {
1444 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1445 nt_errstr(status_tmp)));
1446 goto done;
1448 if (!NT_STATUS_IS_OK(result_tmp)) {
1449 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1450 nt_errstr(result_tmp)));
1451 goto done;
1454 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1455 &user_pol,
1457 &info,
1458 &result_tmp);
1460 if (!NT_STATUS_IS_OK(status_tmp)) {
1461 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1462 nt_errstr(status_tmp)));
1463 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1464 goto done;
1466 if (!NT_STATUS_IS_OK(result_tmp)) {
1467 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1468 nt_errstr(result_tmp)));
1469 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1470 goto done;
1473 acct_flags = info->info16.acct_flags;
1475 if (acct_flags == 0) {
1476 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1477 goto done;
1480 my_info3->base.acct_flags = acct_flags;
1482 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1484 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1487 *info3 = my_info3;
1488 done:
1489 return result;
1492 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1493 struct winbindd_cli_state *state)
1495 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1496 NTSTATUS krb5_result = NT_STATUS_OK;
1497 fstring name_domain, name_user;
1498 char *mapped_user;
1499 fstring domain_user;
1500 struct netr_SamInfo3 *info3 = NULL;
1501 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1503 /* Ensure null termination */
1504 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1506 /* Ensure null termination */
1507 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1509 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1510 state->request->data.auth.user));
1512 /* Parse domain and username */
1514 name_map_status = normalize_name_unmap(state->mem_ctx,
1515 state->request->data.auth.user,
1516 &mapped_user);
1518 /* If the name normalization didnt' actually do anything,
1519 just use the original name */
1521 if (!NT_STATUS_IS_OK(name_map_status) &&
1522 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1524 mapped_user = state->request->data.auth.user;
1527 parse_domain_user(mapped_user, name_domain, name_user);
1529 if ( mapped_user != state->request->data.auth.user ) {
1530 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1531 *lp_winbind_separator(),
1532 name_user );
1533 safe_strcpy( state->request->data.auth.user, domain_user,
1534 sizeof(state->request->data.auth.user)-1 );
1537 if (!domain->online) {
1538 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1539 if (domain->startup) {
1540 /* Logons are very important to users. If we're offline and
1541 we get a request within the first 30 seconds of startup,
1542 try very hard to find a DC and go online. */
1544 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1545 "request in startup mode.\n", domain->name ));
1547 winbindd_flush_negative_conn_cache(domain);
1548 result = init_dc_connection(domain);
1552 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1554 /* Check for Kerberos authentication */
1555 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1557 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1558 /* save for later */
1559 krb5_result = result;
1562 if (NT_STATUS_IS_OK(result)) {
1563 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1564 goto process_result;
1565 } else {
1566 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1569 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1570 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1571 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1572 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1573 set_domain_offline( domain );
1574 goto cached_logon;
1577 /* there are quite some NT_STATUS errors where there is no
1578 * point in retrying with a samlogon, we explictly have to take
1579 * care not to increase the bad logon counter on the DC */
1581 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1582 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1583 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1584 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1585 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1586 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1587 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1588 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1589 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1590 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1591 goto done;
1594 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1595 DEBUG(3,("falling back to samlogon\n"));
1596 goto sam_logon;
1597 } else {
1598 goto cached_logon;
1602 sam_logon:
1603 /* Check for Samlogon authentication */
1604 if (domain->online) {
1605 result = winbindd_dual_pam_auth_samlogon(
1606 state->mem_ctx, domain,
1607 state->request->data.auth.user,
1608 state->request->data.auth.pass,
1609 state->request->flags,
1610 &info3);
1612 if (NT_STATUS_IS_OK(result)) {
1613 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1614 /* add the Krb5 err if we have one */
1615 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1616 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1618 goto process_result;
1621 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1622 nt_errstr(result)));
1624 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1625 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1626 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1628 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1629 set_domain_offline( domain );
1630 goto cached_logon;
1633 if (domain->online) {
1634 /* We're still online - fail. */
1635 goto done;
1639 cached_logon:
1640 /* Check for Cached logons */
1641 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1642 lp_winbind_offline_logon()) {
1644 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1646 if (NT_STATUS_IS_OK(result)) {
1647 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1648 goto process_result;
1649 } else {
1650 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1651 goto done;
1655 process_result:
1657 if (NT_STATUS_IS_OK(result)) {
1659 struct dom_sid user_sid;
1661 /* In all codepaths where result == NT_STATUS_OK info3 must have
1662 been initialized. */
1663 if (!info3) {
1664 result = NT_STATUS_INTERNAL_ERROR;
1665 goto done;
1668 sid_compose(&user_sid, info3->base.domain_sid,
1669 info3->base.rid);
1671 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1672 &user_sid);
1673 netsamlogon_cache_store(name_user, info3);
1675 /* save name_to_sid info as early as possible (only if
1676 this is our primary domain so we don't invalidate
1677 the cache entry by storing the seq_num for the wrong
1678 domain). */
1679 if ( domain->primary ) {
1680 cache_name2sid(domain, name_domain, name_user,
1681 SID_NAME_USER, &user_sid);
1684 /* Check if the user is in the right group */
1686 result = check_info3_in_group(
1687 info3,
1688 state->request->data.auth.require_membership_of_sid);
1689 if (!NT_STATUS_IS_OK(result)) {
1690 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1691 state->request->data.auth.user,
1692 state->request->data.auth.require_membership_of_sid));
1693 goto done;
1696 result = append_auth_data(state->mem_ctx, state->response,
1697 state->request->flags, info3,
1698 name_domain, name_user);
1699 if (!NT_STATUS_IS_OK(result)) {
1700 goto done;
1703 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1704 && lp_winbind_offline_logon()) {
1706 result = winbindd_store_creds(domain,
1707 state->request->data.auth.user,
1708 state->request->data.auth.pass,
1709 info3);
1712 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1713 struct winbindd_domain *our_domain = find_our_domain();
1715 /* This is not entirely correct I believe, but it is
1716 consistent. Only apply the password policy settings
1717 too warn users for our own domain. Cannot obtain these
1718 from trusted DCs all the time so don't do it at all.
1719 -- jerry */
1721 result = NT_STATUS_NOT_SUPPORTED;
1722 if (our_domain == domain ) {
1723 result = fillup_password_policy(
1724 our_domain, state->response);
1727 if (!NT_STATUS_IS_OK(result)
1728 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1730 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1731 domain->name, nt_errstr(result)));
1732 goto done;
1736 result = NT_STATUS_OK;
1739 done:
1740 /* give us a more useful (more correct?) error code */
1741 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1742 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1743 result = NT_STATUS_NO_LOGON_SERVERS;
1746 set_auth_errors(state->response, result);
1748 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1749 state->request->data.auth.user,
1750 state->response->data.auth.nt_status_string,
1751 state->response->data.auth.pam_error));
1753 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1756 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1757 struct winbindd_cli_state *state)
1759 NTSTATUS result;
1760 struct netr_SamInfo3 *info3 = NULL;
1761 const char *name_user = NULL;
1762 const char *name_domain = NULL;
1763 const char *workstation;
1765 DATA_BLOB lm_resp, nt_resp;
1767 /* This is child-only, so no check for privileged access is needed
1768 anymore */
1770 /* Ensure null termination */
1771 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1772 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1774 name_user = state->request->data.auth_crap.user;
1775 name_domain = state->request->data.auth_crap.domain;
1776 workstation = state->request->data.auth_crap.workstation;
1778 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1779 name_domain, name_user));
1781 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1782 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1783 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1784 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1785 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1786 state->request->data.auth_crap.lm_resp_len,
1787 state->request->data.auth_crap.nt_resp_len));
1788 result = NT_STATUS_INVALID_PARAMETER;
1789 goto done;
1793 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1794 state->request->data.auth_crap.lm_resp_len);
1796 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1797 nt_resp = data_blob_talloc(state->mem_ctx,
1798 state->request->extra_data.data,
1799 state->request->data.auth_crap.nt_resp_len);
1800 } else {
1801 nt_resp = data_blob_talloc(state->mem_ctx,
1802 state->request->data.auth_crap.nt_resp,
1803 state->request->data.auth_crap.nt_resp_len);
1806 if (strequal(name_domain, get_global_sam_name())) {
1807 DATA_BLOB chal_blob = data_blob_const(
1808 state->request->data.auth_crap.chal,
1809 sizeof(state->request->data.auth_crap.chal));
1811 result = winbindd_dual_auth_passdb(
1812 state->mem_ctx, name_domain, name_user,
1813 &chal_blob, &lm_resp, &nt_resp, &info3);
1814 goto process_result;
1817 result = winbind_samlogon_retry_loop(domain,
1818 state->mem_ctx,
1819 state->request->data.auth_crap.logon_parameters,
1820 domain->dcname,
1821 name_user,
1822 name_domain,
1823 /* Bug #3248 - found by Stefan Burkei. */
1824 workstation, /* We carefully set this above so use it... */
1825 state->request->data.auth_crap.chal,
1826 lm_resp,
1827 nt_resp,
1828 &info3);
1829 if (!NT_STATUS_IS_OK(result)) {
1830 goto done;
1833 process_result:
1835 if (NT_STATUS_IS_OK(result)) {
1836 struct dom_sid user_sid;
1838 sid_compose(&user_sid, info3->base.domain_sid,
1839 info3->base.rid);
1840 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1841 &user_sid);
1842 netsamlogon_cache_store(name_user, info3);
1844 /* Check if the user is in the right group */
1846 result = check_info3_in_group(
1847 info3,
1848 state->request->data.auth_crap.require_membership_of_sid);
1849 if (!NT_STATUS_IS_OK(result)) {
1850 DEBUG(3, ("User %s is not in the required group (%s), so "
1851 "crap authentication is rejected\n",
1852 state->request->data.auth_crap.user,
1853 state->request->data.auth_crap.require_membership_of_sid));
1854 goto done;
1857 result = append_auth_data(state->mem_ctx, state->response,
1858 state->request->flags, info3,
1859 name_domain, name_user);
1860 if (!NT_STATUS_IS_OK(result)) {
1861 goto done;
1865 done:
1867 /* give us a more useful (more correct?) error code */
1868 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1869 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1870 result = NT_STATUS_NO_LOGON_SERVERS;
1873 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1874 result = nt_status_squash(result);
1877 set_auth_errors(state->response, result);
1879 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1880 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1881 name_domain,
1882 name_user,
1883 state->response->data.auth.nt_status_string,
1884 state->response->data.auth.pam_error));
1886 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1889 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1890 struct winbindd_cli_state *state)
1892 char *oldpass;
1893 char *newpass = NULL;
1894 struct policy_handle dom_pol;
1895 struct rpc_pipe_client *cli = NULL;
1896 bool got_info = false;
1897 struct samr_DomInfo1 *info = NULL;
1898 struct userPwdChangeFailureInformation *reject = NULL;
1899 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1900 fstring domain, user;
1901 struct dcerpc_binding_handle *b = NULL;
1903 ZERO_STRUCT(dom_pol);
1905 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1906 state->request->data.auth.user));
1908 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1909 goto done;
1912 /* Change password */
1914 oldpass = state->request->data.chauthtok.oldpass;
1915 newpass = state->request->data.chauthtok.newpass;
1917 /* Initialize reject reason */
1918 state->response->data.auth.reject_reason = Undefined;
1920 /* Get sam handle */
1922 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1923 &dom_pol);
1924 if (!NT_STATUS_IS_OK(result)) {
1925 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1926 goto done;
1929 b = cli->binding_handle;
1931 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1932 user,
1933 newpass,
1934 oldpass,
1935 &info,
1936 &reject);
1938 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1940 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1942 fill_in_password_policy(state->response, info);
1944 state->response->data.auth.reject_reason =
1945 reject->extendedFailureReason;
1947 got_info = true;
1950 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1951 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1952 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1953 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1955 /* only fallback when the chgpasswd_user3 call is not supported */
1956 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
1957 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
1958 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
1959 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1961 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1962 nt_errstr(result)));
1964 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1966 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1967 Map to the same status code as Windows 2003. */
1969 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1970 result = NT_STATUS_PASSWORD_RESTRICTION;
1974 done:
1976 if (NT_STATUS_IS_OK(result)
1977 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1978 && lp_winbind_offline_logon()) {
1979 result = winbindd_update_creds_by_name(contact_domain, user,
1980 newpass);
1981 /* Again, this happens when we login from gdm or xdm
1982 * and the password expires, *BUT* cached crendentials
1983 * doesn't exist. winbindd_update_creds_by_name()
1984 * returns NT_STATUS_NO_SUCH_USER.
1985 * This is not a failure.
1986 * --- BoYang
1987 * */
1988 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1989 result = NT_STATUS_OK;
1992 if (!NT_STATUS_IS_OK(result)) {
1993 DEBUG(10, ("Failed to store creds: %s\n",
1994 nt_errstr(result)));
1995 goto process_result;
1999 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2001 NTSTATUS policy_ret;
2003 policy_ret = fillup_password_policy(
2004 contact_domain, state->response);
2006 /* failure of this is non critical, it will just provide no
2007 * additional information to the client why the change has
2008 * failed - Guenther */
2010 if (!NT_STATUS_IS_OK(policy_ret)) {
2011 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2012 goto process_result;
2016 process_result:
2018 if (strequal(contact_domain->name, get_global_sam_name())) {
2019 /* FIXME: internal rpc pipe does not cache handles yet */
2020 if (b) {
2021 if (is_valid_policy_hnd(&dom_pol)) {
2022 NTSTATUS _result;
2023 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2025 TALLOC_FREE(cli);
2029 set_auth_errors(state->response, result);
2031 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2032 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2033 domain,
2034 user,
2035 state->response->data.auth.nt_status_string,
2036 state->response->data.auth.pam_error));
2038 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2041 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2042 struct winbindd_cli_state *state)
2044 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2046 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2047 state->request->data.logoff.user));
2049 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2050 result = NT_STATUS_OK;
2051 goto process_result;
2054 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2055 result = NT_STATUS_OK;
2056 goto process_result;
2059 #ifdef HAVE_KRB5
2061 if (state->request->data.logoff.uid < 0) {
2062 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2063 goto process_result;
2066 /* what we need here is to find the corresponding krb5 ccache name *we*
2067 * created for a given username and destroy it */
2069 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2070 result = NT_STATUS_OK;
2071 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2072 goto process_result;
2075 if (!ccache_entry_identical(state->request->data.logoff.user,
2076 state->request->data.logoff.uid,
2077 state->request->data.logoff.krb5ccname)) {
2078 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2079 goto process_result;
2082 result = remove_ccache(state->request->data.logoff.user);
2083 if (!NT_STATUS_IS_OK(result)) {
2084 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2085 nt_errstr(result)));
2086 goto process_result;
2089 #else
2090 result = NT_STATUS_NOT_SUPPORTED;
2091 #endif
2093 process_result:
2096 set_auth_errors(state->response, result);
2098 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2101 /* Change user password with auth crap*/
2103 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2105 NTSTATUS result;
2106 DATA_BLOB new_nt_password;
2107 DATA_BLOB old_nt_hash_enc;
2108 DATA_BLOB new_lm_password;
2109 DATA_BLOB old_lm_hash_enc;
2110 fstring domain,user;
2111 struct policy_handle dom_pol;
2112 struct winbindd_domain *contact_domain = domainSt;
2113 struct rpc_pipe_client *cli = NULL;
2114 struct dcerpc_binding_handle *b = NULL;
2116 ZERO_STRUCT(dom_pol);
2118 /* Ensure null termination */
2119 state->request->data.chng_pswd_auth_crap.user[
2120 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2121 state->request->data.chng_pswd_auth_crap.domain[
2122 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2123 *domain = 0;
2124 *user = 0;
2126 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2127 (unsigned long)state->pid,
2128 state->request->data.chng_pswd_auth_crap.domain,
2129 state->request->data.chng_pswd_auth_crap.user));
2131 if (lp_winbind_offline_logon()) {
2132 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2133 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2134 result = NT_STATUS_ACCESS_DENIED;
2135 goto done;
2138 if (*state->request->data.chng_pswd_auth_crap.domain) {
2139 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2140 } else {
2141 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2142 domain, user);
2144 if(!*domain) {
2145 DEBUG(3,("no domain specified with username (%s) - "
2146 "failing auth\n",
2147 state->request->data.chng_pswd_auth_crap.user));
2148 result = NT_STATUS_NO_SUCH_USER;
2149 goto done;
2153 if (!*domain && lp_winbind_use_default_domain()) {
2154 fstrcpy(domain,(char *)lp_workgroup());
2157 if(!*user) {
2158 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2161 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2162 (unsigned long)state->pid, domain, user));
2164 /* Change password */
2165 new_nt_password = data_blob_const(
2166 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2167 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2169 old_nt_hash_enc = data_blob_const(
2170 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2171 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2173 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2174 new_lm_password = data_blob_const(
2175 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2176 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2178 old_lm_hash_enc = data_blob_const(
2179 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2180 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2181 } else {
2182 new_lm_password.length = 0;
2183 old_lm_hash_enc.length = 0;
2186 /* Get sam handle */
2188 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2189 if (!NT_STATUS_IS_OK(result)) {
2190 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2191 goto done;
2194 b = cli->binding_handle;
2196 result = rpccli_samr_chng_pswd_auth_crap(
2197 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2198 new_lm_password, old_lm_hash_enc);
2200 done:
2202 if (strequal(contact_domain->name, get_global_sam_name())) {
2203 /* FIXME: internal rpc pipe does not cache handles yet */
2204 if (b) {
2205 if (is_valid_policy_hnd(&dom_pol)) {
2206 NTSTATUS _result;
2207 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2209 TALLOC_FREE(cli);
2213 set_auth_errors(state->response, result);
2215 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2216 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2217 domain, user,
2218 state->response->data.auth.nt_status_string,
2219 state->response->data.auth.pam_error));
2221 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;