libsmbconf: Document smbconf_transaction_cancel().
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob6b086c8c0fa61f96f52b768b8a2fa6d3d05b5008
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"
40 #include "ntdomain.h"
42 #undef DBGC_CLASS
43 #define DBGC_CLASS DBGC_WINBIND
45 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
47 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
48 struct winbindd_response *resp,
49 struct netr_SamInfo3 *info3)
51 char *ex;
52 uint32_t i;
54 resp->data.auth.info3.logon_time =
55 nt_time_to_unix(info3->base.last_logon);
56 resp->data.auth.info3.logoff_time =
57 nt_time_to_unix(info3->base.last_logoff);
58 resp->data.auth.info3.kickoff_time =
59 nt_time_to_unix(info3->base.acct_expiry);
60 resp->data.auth.info3.pass_last_set_time =
61 nt_time_to_unix(info3->base.last_password_change);
62 resp->data.auth.info3.pass_can_change_time =
63 nt_time_to_unix(info3->base.allow_password_change);
64 resp->data.auth.info3.pass_must_change_time =
65 nt_time_to_unix(info3->base.force_password_change);
67 resp->data.auth.info3.logon_count = info3->base.logon_count;
68 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
70 resp->data.auth.info3.user_rid = info3->base.rid;
71 resp->data.auth.info3.group_rid = info3->base.primary_gid;
72 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
74 resp->data.auth.info3.num_groups = info3->base.groups.count;
75 resp->data.auth.info3.user_flgs = info3->base.user_flags;
77 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
78 resp->data.auth.info3.num_other_sids = info3->sidcount;
80 fstrcpy(resp->data.auth.info3.user_name,
81 info3->base.account_name.string);
82 fstrcpy(resp->data.auth.info3.full_name,
83 info3->base.full_name.string);
84 fstrcpy(resp->data.auth.info3.logon_script,
85 info3->base.logon_script.string);
86 fstrcpy(resp->data.auth.info3.profile_path,
87 info3->base.profile_path.string);
88 fstrcpy(resp->data.auth.info3.home_dir,
89 info3->base.home_directory.string);
90 fstrcpy(resp->data.auth.info3.dir_drive,
91 info3->base.home_drive.string);
93 fstrcpy(resp->data.auth.info3.logon_srv,
94 info3->base.logon_server.string);
95 fstrcpy(resp->data.auth.info3.logon_dom,
96 info3->base.domain.string);
98 ex = talloc_strdup(mem_ctx, "");
99 NT_STATUS_HAVE_NO_MEMORY(ex);
101 for (i=0; i < info3->base.groups.count; i++) {
102 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
103 info3->base.groups.rids[i].rid,
104 info3->base.groups.rids[i].attributes);
105 NT_STATUS_HAVE_NO_MEMORY(ex);
108 for (i=0; i < info3->sidcount; i++) {
109 char *sid;
111 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
112 NT_STATUS_HAVE_NO_MEMORY(sid);
114 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
115 sid,
116 info3->sids[i].attributes);
117 NT_STATUS_HAVE_NO_MEMORY(ex);
119 talloc_free(sid);
122 resp->extra_data.data = ex;
123 resp->length += talloc_get_size(ex);
125 return NT_STATUS_OK;
128 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
129 struct winbindd_response *resp,
130 struct netr_SamInfo3 *info3)
132 DATA_BLOB blob;
133 enum ndr_err_code ndr_err;
135 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
136 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
137 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
138 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
139 return ndr_map_error2ntstatus(ndr_err);
142 resp->extra_data.data = blob.data;
143 resp->length += blob.length;
145 return NT_STATUS_OK;
148 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
149 struct winbindd_response *resp,
150 const struct netr_SamInfo3 *info3,
151 const char *name_domain,
152 const char *name_user)
154 /* We've been asked to return the unix username, per
155 'winbind use default domain' settings and the like */
157 const char *nt_username, *nt_domain;
159 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
160 if (!nt_domain) {
161 /* If the server didn't give us one, just use the one
162 * we sent them */
163 nt_domain = name_domain;
166 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
167 if (!nt_username) {
168 /* If the server didn't give us one, just use the one
169 * we sent them */
170 nt_username = name_user;
173 fill_domain_username(resp->data.auth.unix_username,
174 nt_domain, nt_username, true);
176 DEBUG(5, ("Setting unix username to [%s]\n",
177 resp->data.auth.unix_username));
179 return NT_STATUS_OK;
182 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
183 struct winbindd_response *resp,
184 const struct netr_SamInfo3 *info3,
185 const char *name_domain,
186 const char *name_user)
188 char *afsname = NULL;
189 char *cell;
190 char *token;
192 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
193 if (afsname == NULL) {
194 return NT_STATUS_NO_MEMORY;
197 afsname = talloc_string_sub(mem_ctx,
198 lp_afs_username_map(),
199 "%D", name_domain);
200 afsname = talloc_string_sub(mem_ctx, afsname,
201 "%u", name_user);
202 afsname = talloc_string_sub(mem_ctx, afsname,
203 "%U", name_user);
206 struct dom_sid user_sid;
207 fstring sidstr;
209 sid_compose(&user_sid, info3->base.domain_sid,
210 info3->base.rid);
211 sid_to_fstring(sidstr, &user_sid);
212 afsname = talloc_string_sub(mem_ctx, afsname,
213 "%s", sidstr);
216 if (afsname == NULL) {
217 return NT_STATUS_NO_MEMORY;
220 strlower_m(afsname);
222 DEBUG(10, ("Generating token for user %s\n", afsname));
224 cell = strchr(afsname, '@');
226 if (cell == NULL) {
227 return NT_STATUS_NO_MEMORY;
230 *cell = '\0';
231 cell += 1;
233 token = afs_createtoken_str(afsname, cell);
234 if (token == NULL) {
235 return NT_STATUS_OK;
237 resp->extra_data.data = talloc_strdup(mem_ctx, token);
238 if (resp->extra_data.data == NULL) {
239 return NT_STATUS_NO_MEMORY;
241 resp->length += strlen((const char *)resp->extra_data.data)+1;
243 return NT_STATUS_OK;
246 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
247 const char *group_sid)
249 * Check whether a user belongs to a group or list of groups.
251 * @param mem_ctx talloc memory context.
252 * @param info3 user information, including group membership info.
253 * @param group_sid One or more groups , separated by commas.
255 * @return NT_STATUS_OK on success,
256 * NT_STATUS_LOGON_FAILURE if the user does not belong,
257 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
260 struct dom_sid *require_membership_of_sid;
261 uint32_t num_require_membership_of_sid;
262 char *req_sid;
263 const char *p;
264 struct dom_sid sid;
265 size_t i;
266 struct security_token *token;
267 TALLOC_CTX *frame = talloc_stackframe();
268 NTSTATUS status;
270 /* Parse the 'required group' SID */
272 if (!group_sid || !group_sid[0]) {
273 /* NO sid supplied, all users may access */
274 return NT_STATUS_OK;
277 token = talloc_zero(talloc_tos(), struct security_token);
278 if (token == NULL) {
279 DEBUG(0, ("talloc failed\n"));
280 TALLOC_FREE(frame);
281 return NT_STATUS_NO_MEMORY;
284 num_require_membership_of_sid = 0;
285 require_membership_of_sid = NULL;
287 p = group_sid;
289 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
290 if (!string_to_sid(&sid, req_sid)) {
291 DEBUG(0, ("check_info3_in_group: could not parse %s "
292 "as a SID!", req_sid));
293 TALLOC_FREE(frame);
294 return NT_STATUS_INVALID_PARAMETER;
297 status = add_sid_to_array(talloc_tos(), &sid,
298 &require_membership_of_sid,
299 &num_require_membership_of_sid);
300 if (!NT_STATUS_IS_OK(status)) {
301 DEBUG(0, ("add_sid_to_array failed\n"));
302 TALLOC_FREE(frame);
303 return status;
307 status = sid_array_from_info3(talloc_tos(), info3,
308 &token->sids,
309 &token->num_sids,
310 true, false);
311 if (!NT_STATUS_IS_OK(status)) {
312 TALLOC_FREE(frame);
313 return status;
316 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
317 token))
318 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
319 token))) {
320 DEBUG(3, ("could not add aliases: %s\n",
321 nt_errstr(status)));
322 TALLOC_FREE(frame);
323 return status;
326 security_token_debug(DBGC_CLASS, 10, token);
328 for (i=0; i<num_require_membership_of_sid; i++) {
329 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
330 &require_membership_of_sid[i])));
331 if (nt_token_check_sid(&require_membership_of_sid[i],
332 token)) {
333 DEBUG(10, ("Access ok\n"));
334 TALLOC_FREE(frame);
335 return NT_STATUS_OK;
339 /* Do not distinguish this error from a wrong username/pw */
341 TALLOC_FREE(frame);
342 return NT_STATUS_LOGON_FAILURE;
345 struct winbindd_domain *find_auth_domain(uint8_t flags,
346 const char *domain_name)
348 struct winbindd_domain *domain;
350 if (IS_DC) {
351 domain = find_domain_from_name_noinit(domain_name);
352 if (domain == NULL) {
353 DEBUG(3, ("Authentication for domain [%s] refused "
354 "as it is not a trusted domain\n",
355 domain_name));
357 return domain;
360 if (strequal(domain_name, get_global_sam_name())) {
361 return find_domain_from_name_noinit(domain_name);
364 /* we can auth against trusted domains */
365 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
366 domain = find_domain_from_name_noinit(domain_name);
367 if (domain == NULL) {
368 DEBUG(3, ("Authentication for domain [%s] skipped "
369 "as it is not a trusted domain\n",
370 domain_name));
371 } else {
372 return domain;
376 return find_our_domain();
379 static void fill_in_password_policy(struct winbindd_response *r,
380 const struct samr_DomInfo1 *p)
382 r->data.auth.policy.min_length_password =
383 p->min_password_length;
384 r->data.auth.policy.password_history =
385 p->password_history_length;
386 r->data.auth.policy.password_properties =
387 p->password_properties;
388 r->data.auth.policy.expire =
389 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
390 r->data.auth.policy.min_passwordage =
391 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
394 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
395 struct winbindd_response *response)
397 TALLOC_CTX *frame = talloc_stackframe();
398 struct winbindd_methods *methods;
399 NTSTATUS status;
400 struct samr_DomInfo1 password_policy;
402 if ( !winbindd_can_contact_domain( domain ) ) {
403 DEBUG(5,("fillup_password_policy: No inbound trust to "
404 "contact domain %s\n", domain->name));
405 status = NT_STATUS_NOT_SUPPORTED;
406 goto done;
409 methods = domain->methods;
411 status = methods->password_policy(domain, talloc_tos(), &password_policy);
412 if (NT_STATUS_IS_ERR(status)) {
413 goto done;
416 fill_in_password_policy(response, &password_policy);
418 done:
419 TALLOC_FREE(frame);
420 return NT_STATUS_OK;
423 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
424 TALLOC_CTX *mem_ctx,
425 uint16 *lockout_threshold)
427 struct winbindd_methods *methods;
428 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
429 struct samr_DomInfo12 lockout_policy;
431 *lockout_threshold = 0;
433 methods = domain->methods;
435 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
436 if (NT_STATUS_IS_ERR(status)) {
437 return status;
440 *lockout_threshold = lockout_policy.lockout_threshold;
442 return NT_STATUS_OK;
445 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
446 TALLOC_CTX *mem_ctx,
447 uint32 *password_properties)
449 struct winbindd_methods *methods;
450 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
451 struct samr_DomInfo1 password_policy;
453 *password_properties = 0;
455 methods = domain->methods;
457 status = methods->password_policy(domain, mem_ctx, &password_policy);
458 if (NT_STATUS_IS_ERR(status)) {
459 return status;
462 *password_properties = password_policy.password_properties;
464 return NT_STATUS_OK;
467 #ifdef HAVE_KRB5
469 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
470 const char *type,
471 uid_t uid,
472 const char **user_ccache_file)
474 /* accept FILE and WRFILE as krb5_cc_type from the client and then
475 * build the full ccname string based on the user's uid here -
476 * Guenther*/
478 const char *gen_cc = NULL;
480 if (uid != -1) {
481 if (strequal(type, "FILE")) {
482 gen_cc = talloc_asprintf(
483 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
485 if (strequal(type, "WRFILE")) {
486 gen_cc = talloc_asprintf(
487 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
491 *user_ccache_file = gen_cc;
493 if (gen_cc == NULL) {
494 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
496 if (gen_cc == NULL) {
497 DEBUG(0,("out of memory\n"));
498 return NULL;
501 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
502 (*user_ccache_file == NULL) ? " (internal)":""));
504 return gen_cc;
507 #endif
509 uid_t get_uid_from_request(struct winbindd_request *request)
511 uid_t uid;
513 uid = request->data.auth.uid;
515 if (uid < 0) {
516 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
517 return -1;
519 return uid;
522 /**********************************************************************
523 Authenticate a user with a clear text password using Kerberos and fill up
524 ccache if required
525 **********************************************************************/
527 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
528 struct winbindd_domain *domain,
529 const char *user,
530 const char *pass,
531 const char *krb5_cc_type,
532 uid_t uid,
533 struct netr_SamInfo3 **info3,
534 fstring krb5ccname)
536 #ifdef HAVE_KRB5
537 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
538 krb5_error_code krb5_ret;
539 const char *cc = NULL;
540 const char *principal_s = NULL;
541 const char *service = NULL;
542 char *realm = NULL;
543 fstring name_domain, name_user;
544 time_t ticket_lifetime = 0;
545 time_t renewal_until = 0;
546 ADS_STRUCT *ads;
547 time_t time_offset = 0;
548 const char *user_ccache_file;
549 struct PAC_LOGON_INFO *logon_info = NULL;
551 *info3 = NULL;
553 /* 1st step:
554 * prepare a krb5_cc_cache string for the user */
556 if (uid == -1) {
557 DEBUG(0,("no valid uid\n"));
560 cc = generate_krb5_ccache(mem_ctx,
561 krb5_cc_type,
562 uid,
563 &user_ccache_file);
564 if (cc == NULL) {
565 return NT_STATUS_NO_MEMORY;
569 /* 2nd step:
570 * get kerberos properties */
572 if (domain->private_data) {
573 ads = (ADS_STRUCT *)domain->private_data;
574 time_offset = ads->auth.time_offset;
578 /* 3rd step:
579 * do kerberos auth and setup ccache as the user */
581 parse_domain_user(user, name_domain, name_user);
583 realm = domain->alt_name;
584 strupper_m(realm);
586 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
587 if (principal_s == NULL) {
588 return NT_STATUS_NO_MEMORY;
591 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
592 if (service == NULL) {
593 return NT_STATUS_NO_MEMORY;
596 /* if this is a user ccache, we need to act as the user to let the krb5
597 * library handle the chown, etc. */
599 /************************ ENTERING NON-ROOT **********************/
601 if (user_ccache_file != NULL) {
602 set_effective_uid(uid);
603 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
606 result = kerberos_return_pac(mem_ctx,
607 principal_s,
608 pass,
609 time_offset,
610 &ticket_lifetime,
611 &renewal_until,
613 true,
614 true,
615 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
616 NULL,
617 &logon_info);
618 if (user_ccache_file != NULL) {
619 gain_root_privilege();
622 /************************ RETURNED TO ROOT **********************/
624 if (!NT_STATUS_IS_OK(result)) {
625 goto failed;
628 *info3 = &logon_info->info3;
630 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
631 principal_s));
633 /* if we had a user's ccache then return that string for the pam
634 * environment */
636 if (user_ccache_file != NULL) {
638 fstrcpy(krb5ccname, user_ccache_file);
640 result = add_ccache_to_list(principal_s,
642 service,
643 user,
644 realm,
645 uid,
646 time(NULL),
647 ticket_lifetime,
648 renewal_until,
649 false);
651 if (!NT_STATUS_IS_OK(result)) {
652 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
653 nt_errstr(result)));
655 } else {
657 /* need to delete the memory cred cache, it is not used anymore */
659 krb5_ret = ads_kdestroy(cc);
660 if (krb5_ret) {
661 DEBUG(3,("winbindd_raw_kerberos_login: "
662 "could not destroy krb5 credential cache: "
663 "%s\n", error_message(krb5_ret)));
668 return NT_STATUS_OK;
670 failed:
672 /* we could have created a new credential cache with a valid tgt in it
673 * but we werent able to get or verify the service ticket for this
674 * local host and therefor didn't get the PAC, we need to remove that
675 * cache entirely now */
677 krb5_ret = ads_kdestroy(cc);
678 if (krb5_ret) {
679 DEBUG(3,("winbindd_raw_kerberos_login: "
680 "could not destroy krb5 credential cache: "
681 "%s\n", error_message(krb5_ret)));
684 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
685 DEBUG(3,("winbindd_raw_kerberos_login: "
686 "could not remove ccache for user %s\n",
687 user));
690 return result;
691 #else
692 return NT_STATUS_NOT_SUPPORTED;
693 #endif /* HAVE_KRB5 */
696 /****************************************************************
697 ****************************************************************/
699 bool check_request_flags(uint32_t flags)
701 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
702 WBFLAG_PAM_INFO3_TEXT |
703 WBFLAG_PAM_INFO3_NDR;
705 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
706 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
707 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
708 !(flags & flags_edata) ) {
709 return true;
712 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
713 flags));
715 return false;
718 /****************************************************************
719 ****************************************************************/
721 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
722 struct winbindd_response *resp,
723 uint32_t request_flags,
724 struct netr_SamInfo3 *info3,
725 const char *name_domain,
726 const char *name_user)
728 NTSTATUS result;
730 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
731 memcpy(resp->data.auth.user_session_key,
732 info3->base.key.key,
733 sizeof(resp->data.auth.user_session_key)
734 /* 16 */);
737 if (request_flags & WBFLAG_PAM_LMKEY) {
738 memcpy(resp->data.auth.first_8_lm_hash,
739 info3->base.LMSessKey.key,
740 sizeof(resp->data.auth.first_8_lm_hash)
741 /* 8 */);
744 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
745 result = append_unix_username(mem_ctx, resp,
746 info3, name_domain, name_user);
747 if (!NT_STATUS_IS_OK(result)) {
748 DEBUG(10,("Failed to append Unix Username: %s\n",
749 nt_errstr(result)));
750 return result;
754 /* currently, anything from here on potentially overwrites extra_data. */
756 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
757 result = append_info3_as_ndr(mem_ctx, resp, info3);
758 if (!NT_STATUS_IS_OK(result)) {
759 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
760 nt_errstr(result)));
761 return result;
765 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
766 result = append_info3_as_txt(mem_ctx, resp, info3);
767 if (!NT_STATUS_IS_OK(result)) {
768 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
769 nt_errstr(result)));
770 return result;
774 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
775 result = append_afs_token(mem_ctx, resp,
776 info3, name_domain, name_user);
777 if (!NT_STATUS_IS_OK(result)) {
778 DEBUG(10,("Failed to append AFS token: %s\n",
779 nt_errstr(result)));
780 return result;
784 return NT_STATUS_OK;
787 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
788 struct winbindd_cli_state *state,
789 struct netr_SamInfo3 **info3)
791 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
792 uint16 max_allowed_bad_attempts;
793 fstring name_domain, name_user;
794 struct dom_sid sid;
795 enum lsa_SidType type;
796 uchar new_nt_pass[NT_HASH_LEN];
797 const uint8 *cached_nt_pass;
798 const uint8 *cached_salt;
799 struct netr_SamInfo3 *my_info3;
800 time_t kickoff_time, must_change_time;
801 bool password_good = false;
802 #ifdef HAVE_KRB5
803 struct winbindd_tdc_domain *tdc_domain = NULL;
804 #endif
806 *info3 = NULL;
808 ZERO_STRUCTP(info3);
810 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
812 /* Parse domain and username */
814 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
817 if (!lookup_cached_name(name_domain,
818 name_user,
819 &sid,
820 &type)) {
821 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
822 return NT_STATUS_NO_SUCH_USER;
825 if (type != SID_NAME_USER) {
826 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
827 return NT_STATUS_LOGON_FAILURE;
830 result = winbindd_get_creds(domain,
831 state->mem_ctx,
832 &sid,
833 &my_info3,
834 &cached_nt_pass,
835 &cached_salt);
836 if (!NT_STATUS_IS_OK(result)) {
837 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
838 return result;
841 *info3 = my_info3;
843 E_md4hash(state->request->data.auth.pass, new_nt_pass);
845 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
846 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
847 if (cached_salt) {
848 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
851 if (cached_salt) {
852 /* In this case we didn't store the nt_hash itself,
853 but the MD5 combination of salt + nt_hash. */
854 uchar salted_hash[NT_HASH_LEN];
855 E_md5hash(cached_salt, new_nt_pass, salted_hash);
857 password_good = (memcmp(cached_nt_pass, salted_hash,
858 NT_HASH_LEN) == 0);
859 } else {
860 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
861 password_good = (memcmp(cached_nt_pass, new_nt_pass,
862 NT_HASH_LEN) == 0);
865 if (password_good) {
867 /* User *DOES* know the password, update logon_time and reset
868 * bad_pw_count */
870 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
872 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
873 return NT_STATUS_ACCOUNT_LOCKED_OUT;
876 if (my_info3->base.acct_flags & ACB_DISABLED) {
877 return NT_STATUS_ACCOUNT_DISABLED;
880 if (my_info3->base.acct_flags & ACB_WSTRUST) {
881 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
884 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
885 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
888 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
889 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
892 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
893 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
894 my_info3->base.acct_flags));
895 return NT_STATUS_LOGON_FAILURE;
898 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
899 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
900 return NT_STATUS_ACCOUNT_EXPIRED;
903 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
904 if (must_change_time != 0 && must_change_time < time(NULL)) {
905 /* we allow grace logons when the password has expired */
906 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
907 /* return NT_STATUS_PASSWORD_EXPIRED; */
908 goto success;
911 #ifdef HAVE_KRB5
912 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
913 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
914 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
915 /* used to cope with the case winbindd starting without network. */
916 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
918 uid_t uid = -1;
919 const char *cc = NULL;
920 char *realm = NULL;
921 const char *principal_s = NULL;
922 const char *service = NULL;
923 const char *user_ccache_file;
925 uid = get_uid_from_request(state->request);
926 if (uid == -1) {
927 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
928 return NT_STATUS_INVALID_PARAMETER;
931 cc = generate_krb5_ccache(state->mem_ctx,
932 state->request->data.auth.krb5_cc_type,
933 state->request->data.auth.uid,
934 &user_ccache_file);
935 if (cc == NULL) {
936 return NT_STATUS_NO_MEMORY;
939 realm = domain->alt_name;
940 strupper_m(realm);
942 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
943 if (principal_s == NULL) {
944 return NT_STATUS_NO_MEMORY;
947 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
948 if (service == NULL) {
949 return NT_STATUS_NO_MEMORY;
952 if (user_ccache_file != NULL) {
954 fstrcpy(state->response->data.auth.krb5ccname,
955 user_ccache_file);
957 result = add_ccache_to_list(principal_s,
959 service,
960 state->request->data.auth.user,
961 domain->alt_name,
962 uid,
963 time(NULL),
964 time(NULL) + lp_winbind_cache_time(),
965 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
966 true);
968 if (!NT_STATUS_IS_OK(result)) {
969 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
970 "to add ccache to list: %s\n",
971 nt_errstr(result)));
975 #endif /* HAVE_KRB5 */
976 success:
977 /* FIXME: we possibly should handle logon hours as well (does xp when
978 * offline?) see auth/auth_sam.c:sam_account_ok for details */
980 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
981 my_info3->base.bad_password_count = 0;
983 result = winbindd_update_creds_by_info3(domain,
984 state->request->data.auth.user,
985 state->request->data.auth.pass,
986 my_info3);
987 if (!NT_STATUS_IS_OK(result)) {
988 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
989 nt_errstr(result)));
990 return result;
993 return NT_STATUS_OK;
997 /* User does *NOT* know the correct password, modify info3 accordingly */
999 /* failure of this is not critical */
1000 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1001 if (!NT_STATUS_IS_OK(result)) {
1002 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1003 "Won't be able to honour account lockout policies\n"));
1006 /* increase counter */
1007 my_info3->base.bad_password_count++;
1009 if (max_allowed_bad_attempts == 0) {
1010 goto failed;
1013 /* lockout user */
1014 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1016 uint32 password_properties;
1018 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1019 if (!NT_STATUS_IS_OK(result)) {
1020 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1023 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1024 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1025 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1029 failed:
1030 result = winbindd_update_creds_by_info3(domain,
1031 state->request->data.auth.user,
1032 NULL,
1033 my_info3);
1035 if (!NT_STATUS_IS_OK(result)) {
1036 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1037 nt_errstr(result)));
1040 return NT_STATUS_LOGON_FAILURE;
1043 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1044 struct winbindd_cli_state *state,
1045 struct netr_SamInfo3 **info3)
1047 struct winbindd_domain *contact_domain;
1048 fstring name_domain, name_user;
1049 NTSTATUS result;
1051 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1053 /* Parse domain and username */
1055 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1057 /* what domain should we contact? */
1059 if ( IS_DC ) {
1060 if (!(contact_domain = find_domain_from_name(name_domain))) {
1061 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1062 state->request->data.auth.user, name_domain, name_user, name_domain));
1063 result = NT_STATUS_NO_SUCH_USER;
1064 goto done;
1067 } else {
1068 if (is_myname(name_domain)) {
1069 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1070 result = NT_STATUS_NO_SUCH_USER;
1071 goto done;
1074 contact_domain = find_domain_from_name(name_domain);
1075 if (contact_domain == NULL) {
1076 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1077 state->request->data.auth.user, name_domain, name_user, name_domain));
1079 contact_domain = find_our_domain();
1083 if (contact_domain->initialized &&
1084 contact_domain->active_directory) {
1085 goto try_login;
1088 if (!contact_domain->initialized) {
1089 init_dc_connection(contact_domain);
1092 if (!contact_domain->active_directory) {
1093 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1094 return NT_STATUS_INVALID_LOGON_TYPE;
1096 try_login:
1097 result = winbindd_raw_kerberos_login(
1098 state->mem_ctx, contact_domain,
1099 state->request->data.auth.user,
1100 state->request->data.auth.pass,
1101 state->request->data.auth.krb5_cc_type,
1102 get_uid_from_request(state->request),
1103 info3, state->response->data.auth.krb5ccname);
1104 done:
1105 return result;
1108 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1109 const char *domain, const char *user,
1110 const DATA_BLOB *challenge,
1111 const DATA_BLOB *lm_resp,
1112 const DATA_BLOB *nt_resp,
1113 struct netr_SamInfo3 **pinfo3)
1115 struct auth_usersupplied_info *user_info = NULL;
1116 NTSTATUS status;
1118 status = make_user_info(&user_info, user, user, domain, domain,
1119 global_myname(), lm_resp, nt_resp, NULL, NULL,
1120 NULL, AUTH_PASSWORD_RESPONSE);
1121 if (!NT_STATUS_IS_OK(status)) {
1122 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1123 return status;
1126 /* We don't want any more mapping of the username */
1127 user_info->mapped_state = True;
1129 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1130 pinfo3);
1131 free_user_info(&user_info);
1132 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1133 user, nt_errstr(status)));
1134 return status;
1137 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1138 TALLOC_CTX *mem_ctx,
1139 uint32_t logon_parameters,
1140 const char *server,
1141 const char *username,
1142 const char *domainname,
1143 const char *workstation,
1144 const uint8_t chal[8],
1145 DATA_BLOB lm_response,
1146 DATA_BLOB nt_response,
1147 struct netr_SamInfo3 **info3)
1149 int attempts = 0;
1150 bool retry = false;
1151 NTSTATUS result;
1153 do {
1154 struct rpc_pipe_client *netlogon_pipe;
1155 const struct pipe_auth_data *auth;
1156 uint32_t neg_flags = 0;
1158 ZERO_STRUCTP(info3);
1159 retry = false;
1161 result = cm_connect_netlogon(domain, &netlogon_pipe);
1163 if (!NT_STATUS_IS_OK(result)) {
1164 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1165 nt_errstr(result)));
1166 return result;
1168 auth = netlogon_pipe->auth;
1169 if (netlogon_pipe->dc) {
1170 neg_flags = netlogon_pipe->dc->negotiate_flags;
1173 /* It is really important to try SamLogonEx here,
1174 * because in a clustered environment, we want to use
1175 * one machine account from multiple physical
1176 * computers.
1178 * With a normal SamLogon call, we must keep the
1179 * credentials chain updated and intact between all
1180 * users of the machine account (which would imply
1181 * cross-node communication for every NTLM logon).
1183 * (The credentials chain is not per NETLOGON pipe
1184 * connection, but globally on the server/client pair
1185 * by machine name).
1187 * When using SamLogonEx, the credentials are not
1188 * supplied, but the session key is implied by the
1189 * wrapping SamLogon context.
1191 * -- abartlet 21 April 2008
1193 * It's also important to use NetlogonValidationSamInfo4 (6),
1194 * because it relies on the rpc transport encryption
1195 * and avoids using the global netlogon schannel
1196 * session key to en/decrypt secret information
1197 * like the user_session_key for network logons.
1199 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1200 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1201 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1202 * are the indication that the server supports
1203 * NetlogonValidationSamInfo4 (6). And it must only
1204 * be used if "SealSecureChannel" is used.
1206 * -- metze 4 February 2011
1209 if (auth == NULL) {
1210 domain->can_do_validation6 = false;
1211 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1212 domain->can_do_validation6 = false;
1213 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1214 domain->can_do_validation6 = false;
1215 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1216 domain->can_do_validation6 = false;
1217 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1218 domain->can_do_validation6 = false;
1221 if (domain->can_do_samlogon_ex) {
1222 result = rpccli_netlogon_sam_network_logon_ex(
1223 netlogon_pipe,
1224 mem_ctx,
1226 server, /* server name */
1227 username, /* user name */
1228 domainname, /* target domain */
1229 workstation, /* workstation */
1230 chal,
1231 domain->can_do_validation6 ? 6 : 3,
1232 lm_response,
1233 nt_response,
1234 info3);
1235 } else {
1236 result = rpccli_netlogon_sam_network_logon(
1237 netlogon_pipe,
1238 mem_ctx,
1240 server, /* server name */
1241 username, /* user name */
1242 domainname, /* target domain */
1243 workstation, /* workstation */
1244 chal,
1245 domain->can_do_validation6 ? 6 : 3,
1246 lm_response,
1247 nt_response,
1248 info3);
1251 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)
1252 && domain->can_do_samlogon_ex) {
1253 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1254 "retrying with NetSamLogon\n"));
1255 domain->can_do_samlogon_ex = false;
1257 * It's likely that the server also does not support
1258 * validation level 6
1260 domain->can_do_validation6 = false;
1261 retry = true;
1262 continue;
1265 if (domain->can_do_validation6 &&
1266 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1267 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1268 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1269 DEBUG(3,("Got a DC that can not do validation level 6, "
1270 "retrying with level 3\n"));
1271 domain->can_do_validation6 = false;
1272 retry = true;
1273 continue;
1277 * we increment this after the "feature negotiation"
1278 * for can_do_samlogon_ex and can_do_validation6
1280 attempts += 1;
1282 /* We have to try a second time as cm_connect_netlogon
1283 might not yet have noticed that the DC has killed
1284 our connection. */
1286 if (!rpccli_is_connected(netlogon_pipe)) {
1287 retry = true;
1288 continue;
1291 /* if we get access denied, a possible cause was that we had
1292 and open connection to the DC, but someone changed our
1293 machine account password out from underneath us using 'net
1294 rpc changetrustpw' */
1296 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1297 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1298 "ACCESS_DENIED. Maybe the trust account "
1299 "password was changed and we didn't know it. "
1300 "Killing connections to domain %s\n",
1301 domainname));
1302 invalidate_cm_connection(&domain->conn);
1303 retry = true;
1306 } while ( (attempts < 2) && retry );
1308 return result;
1311 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1312 struct winbindd_domain *domain,
1313 const char *user,
1314 const char *pass,
1315 uint32_t request_flags,
1316 struct netr_SamInfo3 **info3)
1319 uchar chal[8];
1320 DATA_BLOB lm_resp;
1321 DATA_BLOB nt_resp;
1322 unsigned char local_nt_response[24];
1323 fstring name_domain, name_user;
1324 NTSTATUS result;
1325 struct netr_SamInfo3 *my_info3 = NULL;
1327 *info3 = NULL;
1329 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1331 /* Parse domain and username */
1333 parse_domain_user(user, name_domain, name_user);
1335 /* do password magic */
1337 generate_random_buffer(chal, sizeof(chal));
1339 if (lp_client_ntlmv2_auth()) {
1340 DATA_BLOB server_chal;
1341 DATA_BLOB names_blob;
1342 server_chal = data_blob_const(chal, 8);
1344 /* note that the 'workgroup' here is for the local
1345 machine. The 'server name' must match the
1346 'workstation' passed to the actual SamLogon call.
1348 names_blob = NTLMv2_generate_names_blob(
1349 mem_ctx, global_myname(), lp_workgroup());
1351 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1352 pass,
1353 &server_chal,
1354 &names_blob,
1355 &lm_resp, &nt_resp, NULL, NULL)) {
1356 data_blob_free(&names_blob);
1357 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1358 result = NT_STATUS_NO_MEMORY;
1359 goto done;
1361 data_blob_free(&names_blob);
1362 } else {
1363 lm_resp = data_blob_null;
1364 SMBNTencrypt(pass, chal, local_nt_response);
1366 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1367 sizeof(local_nt_response));
1370 if (strequal(name_domain, get_global_sam_name())) {
1371 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1373 result = winbindd_dual_auth_passdb(
1374 mem_ctx, name_domain, name_user,
1375 &chal_blob, &lm_resp, &nt_resp, info3);
1376 goto done;
1379 /* check authentication loop */
1381 result = winbind_samlogon_retry_loop(domain,
1382 mem_ctx,
1384 domain->dcname,
1385 name_user,
1386 name_domain,
1387 global_myname(),
1388 chal,
1389 lm_resp,
1390 nt_resp,
1391 &my_info3);
1392 if (!NT_STATUS_IS_OK(result)) {
1393 goto done;
1396 /* handle the case where a NT4 DC does not fill in the acct_flags in
1397 * the samlogon reply info3. When accurate info3 is required by the
1398 * caller, we look up the account flags ourselve - gd */
1400 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1401 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1403 struct rpc_pipe_client *samr_pipe;
1404 struct policy_handle samr_domain_handle, user_pol;
1405 union samr_UserInfo *info = NULL;
1406 NTSTATUS status_tmp, result_tmp;
1407 uint32 acct_flags;
1408 struct dcerpc_binding_handle *b;
1410 status_tmp = cm_connect_sam(domain, mem_ctx,
1411 &samr_pipe, &samr_domain_handle);
1413 if (!NT_STATUS_IS_OK(status_tmp)) {
1414 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1415 nt_errstr(status_tmp)));
1416 goto done;
1419 b = samr_pipe->binding_handle;
1421 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1422 &samr_domain_handle,
1423 MAXIMUM_ALLOWED_ACCESS,
1424 my_info3->base.rid,
1425 &user_pol,
1426 &result_tmp);
1428 if (!NT_STATUS_IS_OK(status_tmp)) {
1429 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1430 nt_errstr(status_tmp)));
1431 goto done;
1433 if (!NT_STATUS_IS_OK(result_tmp)) {
1434 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1435 nt_errstr(result_tmp)));
1436 goto done;
1439 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1440 &user_pol,
1442 &info,
1443 &result_tmp);
1445 if (!NT_STATUS_IS_OK(status_tmp)) {
1446 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1447 nt_errstr(status_tmp)));
1448 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1449 goto done;
1451 if (!NT_STATUS_IS_OK(result_tmp)) {
1452 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1453 nt_errstr(result_tmp)));
1454 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1455 goto done;
1458 acct_flags = info->info16.acct_flags;
1460 if (acct_flags == 0) {
1461 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1462 goto done;
1465 my_info3->base.acct_flags = acct_flags;
1467 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1469 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1472 *info3 = my_info3;
1473 done:
1474 return result;
1477 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1478 struct winbindd_cli_state *state)
1480 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1481 NTSTATUS krb5_result = NT_STATUS_OK;
1482 fstring name_domain, name_user;
1483 char *mapped_user;
1484 fstring domain_user;
1485 struct netr_SamInfo3 *info3 = NULL;
1486 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1488 /* Ensure null termination */
1489 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1491 /* Ensure null termination */
1492 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1494 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1495 state->request->data.auth.user));
1497 /* Parse domain and username */
1499 name_map_status = normalize_name_unmap(state->mem_ctx,
1500 state->request->data.auth.user,
1501 &mapped_user);
1503 /* If the name normalization didnt' actually do anything,
1504 just use the original name */
1506 if (!NT_STATUS_IS_OK(name_map_status) &&
1507 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1509 mapped_user = state->request->data.auth.user;
1512 parse_domain_user(mapped_user, name_domain, name_user);
1514 if ( mapped_user != state->request->data.auth.user ) {
1515 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1516 *lp_winbind_separator(),
1517 name_user );
1518 safe_strcpy( state->request->data.auth.user, domain_user,
1519 sizeof(state->request->data.auth.user)-1 );
1522 if (!domain->online) {
1523 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1524 if (domain->startup) {
1525 /* Logons are very important to users. If we're offline and
1526 we get a request within the first 30 seconds of startup,
1527 try very hard to find a DC and go online. */
1529 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1530 "request in startup mode.\n", domain->name ));
1532 winbindd_flush_negative_conn_cache(domain);
1533 result = init_dc_connection(domain);
1537 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1539 /* Check for Kerberos authentication */
1540 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1542 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1543 /* save for later */
1544 krb5_result = result;
1547 if (NT_STATUS_IS_OK(result)) {
1548 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1549 goto process_result;
1550 } else {
1551 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1554 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1555 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1556 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1557 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1558 set_domain_offline( domain );
1559 goto cached_logon;
1562 /* there are quite some NT_STATUS errors where there is no
1563 * point in retrying with a samlogon, we explictly have to take
1564 * care not to increase the bad logon counter on the DC */
1566 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1567 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1568 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1569 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1570 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1571 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1572 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1573 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1574 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1575 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1576 goto done;
1579 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1580 DEBUG(3,("falling back to samlogon\n"));
1581 goto sam_logon;
1582 } else {
1583 goto cached_logon;
1587 sam_logon:
1588 /* Check for Samlogon authentication */
1589 if (domain->online) {
1590 result = winbindd_dual_pam_auth_samlogon(
1591 state->mem_ctx, domain,
1592 state->request->data.auth.user,
1593 state->request->data.auth.pass,
1594 state->request->flags,
1595 &info3);
1597 if (NT_STATUS_IS_OK(result)) {
1598 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1599 /* add the Krb5 err if we have one */
1600 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1601 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1603 goto process_result;
1606 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1607 nt_errstr(result)));
1609 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1610 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1611 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1613 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1614 set_domain_offline( domain );
1615 goto cached_logon;
1618 if (domain->online) {
1619 /* We're still online - fail. */
1620 goto done;
1624 cached_logon:
1625 /* Check for Cached logons */
1626 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1627 lp_winbind_offline_logon()) {
1629 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1631 if (NT_STATUS_IS_OK(result)) {
1632 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1633 goto process_result;
1634 } else {
1635 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1636 goto done;
1640 process_result:
1642 if (NT_STATUS_IS_OK(result)) {
1644 struct dom_sid user_sid;
1646 /* In all codepaths where result == NT_STATUS_OK info3 must have
1647 been initialized. */
1648 if (!info3) {
1649 result = NT_STATUS_INTERNAL_ERROR;
1650 goto done;
1653 sid_compose(&user_sid, info3->base.domain_sid,
1654 info3->base.rid);
1656 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1657 &user_sid);
1658 netsamlogon_cache_store(name_user, info3);
1660 /* save name_to_sid info as early as possible (only if
1661 this is our primary domain so we don't invalidate
1662 the cache entry by storing the seq_num for the wrong
1663 domain). */
1664 if ( domain->primary ) {
1665 cache_name2sid(domain, name_domain, name_user,
1666 SID_NAME_USER, &user_sid);
1669 /* Check if the user is in the right group */
1671 result = check_info3_in_group(
1672 info3,
1673 state->request->data.auth.require_membership_of_sid);
1674 if (!NT_STATUS_IS_OK(result)) {
1675 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1676 state->request->data.auth.user,
1677 state->request->data.auth.require_membership_of_sid));
1678 goto done;
1681 result = append_auth_data(state->mem_ctx, state->response,
1682 state->request->flags, info3,
1683 name_domain, name_user);
1684 if (!NT_STATUS_IS_OK(result)) {
1685 goto done;
1688 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1689 && lp_winbind_offline_logon()) {
1691 result = winbindd_store_creds(domain,
1692 state->request->data.auth.user,
1693 state->request->data.auth.pass,
1694 info3);
1697 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1698 struct winbindd_domain *our_domain = find_our_domain();
1700 /* This is not entirely correct I believe, but it is
1701 consistent. Only apply the password policy settings
1702 too warn users for our own domain. Cannot obtain these
1703 from trusted DCs all the time so don't do it at all.
1704 -- jerry */
1706 result = NT_STATUS_NOT_SUPPORTED;
1707 if (our_domain == domain ) {
1708 result = fillup_password_policy(
1709 our_domain, state->response);
1712 if (!NT_STATUS_IS_OK(result)
1713 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1715 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1716 domain->name, nt_errstr(result)));
1717 goto done;
1721 result = NT_STATUS_OK;
1724 done:
1725 /* give us a more useful (more correct?) error code */
1726 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1727 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1728 result = NT_STATUS_NO_LOGON_SERVERS;
1731 set_auth_errors(state->response, result);
1733 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1734 state->request->data.auth.user,
1735 state->response->data.auth.nt_status_string,
1736 state->response->data.auth.pam_error));
1738 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1741 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1742 struct winbindd_cli_state *state)
1744 NTSTATUS result;
1745 struct netr_SamInfo3 *info3 = NULL;
1746 const char *name_user = NULL;
1747 const char *name_domain = NULL;
1748 const char *workstation;
1750 DATA_BLOB lm_resp, nt_resp;
1752 /* This is child-only, so no check for privileged access is needed
1753 anymore */
1755 /* Ensure null termination */
1756 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1757 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1759 name_user = state->request->data.auth_crap.user;
1760 name_domain = state->request->data.auth_crap.domain;
1761 workstation = state->request->data.auth_crap.workstation;
1763 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1764 name_domain, name_user));
1766 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1767 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1768 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1769 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1770 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1771 state->request->data.auth_crap.lm_resp_len,
1772 state->request->data.auth_crap.nt_resp_len));
1773 result = NT_STATUS_INVALID_PARAMETER;
1774 goto done;
1778 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1779 state->request->data.auth_crap.lm_resp_len);
1781 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1782 nt_resp = data_blob_talloc(state->mem_ctx,
1783 state->request->extra_data.data,
1784 state->request->data.auth_crap.nt_resp_len);
1785 } else {
1786 nt_resp = data_blob_talloc(state->mem_ctx,
1787 state->request->data.auth_crap.nt_resp,
1788 state->request->data.auth_crap.nt_resp_len);
1791 if (strequal(name_domain, get_global_sam_name())) {
1792 DATA_BLOB chal_blob = data_blob_const(
1793 state->request->data.auth_crap.chal,
1794 sizeof(state->request->data.auth_crap.chal));
1796 result = winbindd_dual_auth_passdb(
1797 state->mem_ctx, name_domain, name_user,
1798 &chal_blob, &lm_resp, &nt_resp, &info3);
1799 goto process_result;
1802 result = winbind_samlogon_retry_loop(domain,
1803 state->mem_ctx,
1804 state->request->data.auth_crap.logon_parameters,
1805 domain->dcname,
1806 name_user,
1807 name_domain,
1808 /* Bug #3248 - found by Stefan Burkei. */
1809 workstation, /* We carefully set this above so use it... */
1810 state->request->data.auth_crap.chal,
1811 lm_resp,
1812 nt_resp,
1813 &info3);
1814 if (!NT_STATUS_IS_OK(result)) {
1815 goto done;
1818 process_result:
1820 if (NT_STATUS_IS_OK(result)) {
1821 struct dom_sid user_sid;
1823 sid_compose(&user_sid, info3->base.domain_sid,
1824 info3->base.rid);
1825 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1826 &user_sid);
1827 netsamlogon_cache_store(name_user, info3);
1829 /* Check if the user is in the right group */
1831 result = check_info3_in_group(
1832 info3,
1833 state->request->data.auth_crap.require_membership_of_sid);
1834 if (!NT_STATUS_IS_OK(result)) {
1835 DEBUG(3, ("User %s is not in the required group (%s), so "
1836 "crap authentication is rejected\n",
1837 state->request->data.auth_crap.user,
1838 state->request->data.auth_crap.require_membership_of_sid));
1839 goto done;
1842 result = append_auth_data(state->mem_ctx, state->response,
1843 state->request->flags, info3,
1844 name_domain, name_user);
1845 if (!NT_STATUS_IS_OK(result)) {
1846 goto done;
1850 done:
1852 /* give us a more useful (more correct?) error code */
1853 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1854 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1855 result = NT_STATUS_NO_LOGON_SERVERS;
1858 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1859 result = nt_status_squash(result);
1862 set_auth_errors(state->response, result);
1864 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1865 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1866 name_domain,
1867 name_user,
1868 state->response->data.auth.nt_status_string,
1869 state->response->data.auth.pam_error));
1871 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1874 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1875 struct winbindd_cli_state *state)
1877 char *oldpass;
1878 char *newpass = NULL;
1879 struct policy_handle dom_pol;
1880 struct rpc_pipe_client *cli = NULL;
1881 bool got_info = false;
1882 struct samr_DomInfo1 *info = NULL;
1883 struct userPwdChangeFailureInformation *reject = NULL;
1884 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1885 fstring domain, user;
1886 struct dcerpc_binding_handle *b = NULL;
1888 ZERO_STRUCT(dom_pol);
1890 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1891 state->request->data.auth.user));
1893 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1894 goto done;
1897 /* Change password */
1899 oldpass = state->request->data.chauthtok.oldpass;
1900 newpass = state->request->data.chauthtok.newpass;
1902 /* Initialize reject reason */
1903 state->response->data.auth.reject_reason = Undefined;
1905 /* Get sam handle */
1907 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1908 &dom_pol);
1909 if (!NT_STATUS_IS_OK(result)) {
1910 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1911 goto done;
1914 b = cli->binding_handle;
1916 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1917 user,
1918 newpass,
1919 oldpass,
1920 &info,
1921 &reject);
1923 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1925 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1927 fill_in_password_policy(state->response, info);
1929 state->response->data.auth.reject_reason =
1930 reject->extendedFailureReason;
1932 got_info = true;
1935 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1936 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1937 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1938 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1940 /* only fallback when the chgpasswd_user3 call is not supported */
1941 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
1942 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
1943 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
1944 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1946 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1947 nt_errstr(result)));
1949 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1951 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1952 Map to the same status code as Windows 2003. */
1954 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1955 result = NT_STATUS_PASSWORD_RESTRICTION;
1959 done:
1961 if (NT_STATUS_IS_OK(result)
1962 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1963 && lp_winbind_offline_logon()) {
1964 result = winbindd_update_creds_by_name(contact_domain, user,
1965 newpass);
1966 /* Again, this happens when we login from gdm or xdm
1967 * and the password expires, *BUT* cached crendentials
1968 * doesn't exist. winbindd_update_creds_by_name()
1969 * returns NT_STATUS_NO_SUCH_USER.
1970 * This is not a failure.
1971 * --- BoYang
1972 * */
1973 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1974 result = NT_STATUS_OK;
1977 if (!NT_STATUS_IS_OK(result)) {
1978 DEBUG(10, ("Failed to store creds: %s\n",
1979 nt_errstr(result)));
1980 goto process_result;
1984 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1986 NTSTATUS policy_ret;
1988 policy_ret = fillup_password_policy(
1989 contact_domain, state->response);
1991 /* failure of this is non critical, it will just provide no
1992 * additional information to the client why the change has
1993 * failed - Guenther */
1995 if (!NT_STATUS_IS_OK(policy_ret)) {
1996 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1997 goto process_result;
2001 process_result:
2003 if (strequal(contact_domain->name, get_global_sam_name())) {
2004 /* FIXME: internal rpc pipe does not cache handles yet */
2005 if (b) {
2006 if (is_valid_policy_hnd(&dom_pol)) {
2007 NTSTATUS _result;
2008 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2010 TALLOC_FREE(cli);
2014 set_auth_errors(state->response, result);
2016 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2017 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2018 domain,
2019 user,
2020 state->response->data.auth.nt_status_string,
2021 state->response->data.auth.pam_error));
2023 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2026 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2027 struct winbindd_cli_state *state)
2029 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2031 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2032 state->request->data.logoff.user));
2034 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2035 result = NT_STATUS_OK;
2036 goto process_result;
2039 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2040 result = NT_STATUS_OK;
2041 goto process_result;
2044 #ifdef HAVE_KRB5
2046 if (state->request->data.logoff.uid < 0) {
2047 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2048 goto process_result;
2051 /* what we need here is to find the corresponding krb5 ccache name *we*
2052 * created for a given username and destroy it */
2054 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2055 result = NT_STATUS_OK;
2056 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2057 goto process_result;
2060 if (!ccache_entry_identical(state->request->data.logoff.user,
2061 state->request->data.logoff.uid,
2062 state->request->data.logoff.krb5ccname)) {
2063 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2064 goto process_result;
2067 result = remove_ccache(state->request->data.logoff.user);
2068 if (!NT_STATUS_IS_OK(result)) {
2069 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2070 nt_errstr(result)));
2071 goto process_result;
2074 #else
2075 result = NT_STATUS_NOT_SUPPORTED;
2076 #endif
2078 process_result:
2081 set_auth_errors(state->response, result);
2083 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2086 /* Change user password with auth crap*/
2088 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2090 NTSTATUS result;
2091 DATA_BLOB new_nt_password;
2092 DATA_BLOB old_nt_hash_enc;
2093 DATA_BLOB new_lm_password;
2094 DATA_BLOB old_lm_hash_enc;
2095 fstring domain,user;
2096 struct policy_handle dom_pol;
2097 struct winbindd_domain *contact_domain = domainSt;
2098 struct rpc_pipe_client *cli = NULL;
2099 struct dcerpc_binding_handle *b = NULL;
2101 ZERO_STRUCT(dom_pol);
2103 /* Ensure null termination */
2104 state->request->data.chng_pswd_auth_crap.user[
2105 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2106 state->request->data.chng_pswd_auth_crap.domain[
2107 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2108 *domain = 0;
2109 *user = 0;
2111 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2112 (unsigned long)state->pid,
2113 state->request->data.chng_pswd_auth_crap.domain,
2114 state->request->data.chng_pswd_auth_crap.user));
2116 if (lp_winbind_offline_logon()) {
2117 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2118 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2119 result = NT_STATUS_ACCESS_DENIED;
2120 goto done;
2123 if (*state->request->data.chng_pswd_auth_crap.domain) {
2124 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2125 } else {
2126 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2127 domain, user);
2129 if(!*domain) {
2130 DEBUG(3,("no domain specified with username (%s) - "
2131 "failing auth\n",
2132 state->request->data.chng_pswd_auth_crap.user));
2133 result = NT_STATUS_NO_SUCH_USER;
2134 goto done;
2138 if (!*domain && lp_winbind_use_default_domain()) {
2139 fstrcpy(domain,(char *)lp_workgroup());
2142 if(!*user) {
2143 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2146 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2147 (unsigned long)state->pid, domain, user));
2149 /* Change password */
2150 new_nt_password = data_blob_const(
2151 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2152 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2154 old_nt_hash_enc = data_blob_const(
2155 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2156 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2158 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2159 new_lm_password = data_blob_const(
2160 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2161 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2163 old_lm_hash_enc = data_blob_const(
2164 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2165 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2166 } else {
2167 new_lm_password.length = 0;
2168 old_lm_hash_enc.length = 0;
2171 /* Get sam handle */
2173 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2174 if (!NT_STATUS_IS_OK(result)) {
2175 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2176 goto done;
2179 b = cli->binding_handle;
2181 result = rpccli_samr_chng_pswd_auth_crap(
2182 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2183 new_lm_password, old_lm_hash_enc);
2185 done:
2187 if (strequal(contact_domain->name, get_global_sam_name())) {
2188 /* FIXME: internal rpc pipe does not cache handles yet */
2189 if (b) {
2190 if (is_valid_policy_hnd(&dom_pol)) {
2191 NTSTATUS _result;
2192 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2194 TALLOC_FREE(cli);
2198 set_auth_errors(state->response, result);
2200 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2201 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2202 domain, user,
2203 state->response->data.auth.nt_status_string,
2204 state->response->data.auth.pam_error));
2206 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;