wintest: Give the Windows VM a little more time to start back up
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob93034adb84f9ac4e715dca95f2fbf8129fa9c1ad
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 "../lib/tsocket/tsocket.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.logon_time);
56 resp->data.auth.info3.logoff_time =
57 nt_time_to_unix(info3->base.logoff_time);
58 resp->data.auth.info3.kickoff_time =
59 nt_time_to_unix(info3->base.kickoff_time);
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.logon_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.logon_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((const NTTIME *)&(p->max_password_age));
390 r->data.auth.policy.min_passwordage =
391 nt_time_to_unix_abs((const 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.kickoff_time);
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.logon_time, 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, but only if online */
998 if (domain->online == false) {
999 goto failed;
1002 /* failure of this is not critical */
1003 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1004 if (!NT_STATUS_IS_OK(result)) {
1005 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1006 "Won't be able to honour account lockout policies\n"));
1009 /* increase counter */
1010 my_info3->base.bad_password_count++;
1012 if (max_allowed_bad_attempts == 0) {
1013 goto failed;
1016 /* lockout user */
1017 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1019 uint32 password_properties;
1021 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1022 if (!NT_STATUS_IS_OK(result)) {
1023 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1026 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1027 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1028 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1032 failed:
1033 result = winbindd_update_creds_by_info3(domain,
1034 state->request->data.auth.user,
1035 NULL,
1036 my_info3);
1038 if (!NT_STATUS_IS_OK(result)) {
1039 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1040 nt_errstr(result)));
1043 return NT_STATUS_LOGON_FAILURE;
1046 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1047 struct winbindd_cli_state *state,
1048 struct netr_SamInfo3 **info3)
1050 struct winbindd_domain *contact_domain;
1051 fstring name_domain, name_user;
1052 NTSTATUS result;
1054 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1056 /* Parse domain and username */
1058 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1060 /* what domain should we contact? */
1062 if ( IS_DC ) {
1063 if (!(contact_domain = find_domain_from_name(name_domain))) {
1064 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1065 state->request->data.auth.user, name_domain, name_user, name_domain));
1066 result = NT_STATUS_NO_SUCH_USER;
1067 goto done;
1070 } else {
1071 if (is_myname(name_domain)) {
1072 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1073 result = NT_STATUS_NO_SUCH_USER;
1074 goto done;
1077 contact_domain = find_domain_from_name(name_domain);
1078 if (contact_domain == NULL) {
1079 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1080 state->request->data.auth.user, name_domain, name_user, name_domain));
1082 result = NT_STATUS_NO_SUCH_USER;
1083 goto done;
1087 if (contact_domain->initialized &&
1088 contact_domain->active_directory) {
1089 goto try_login;
1092 if (!contact_domain->initialized) {
1093 init_dc_connection(contact_domain);
1096 if (!contact_domain->active_directory) {
1097 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1098 return NT_STATUS_INVALID_LOGON_TYPE;
1100 try_login:
1101 result = winbindd_raw_kerberos_login(
1102 state->mem_ctx, contact_domain,
1103 state->request->data.auth.user,
1104 state->request->data.auth.pass,
1105 state->request->data.auth.krb5_cc_type,
1106 get_uid_from_request(state->request),
1107 info3, state->response->data.auth.krb5ccname);
1108 done:
1109 return result;
1112 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1113 const char *domain, const char *user,
1114 const DATA_BLOB *challenge,
1115 const DATA_BLOB *lm_resp,
1116 const DATA_BLOB *nt_resp,
1117 struct netr_SamInfo3 **pinfo3)
1119 struct auth_usersupplied_info *user_info = NULL;
1120 struct tsocket_address *local;
1121 NTSTATUS status;
1122 int rc;
1124 rc = tsocket_address_inet_from_strings(mem_ctx,
1125 "ip",
1126 "127.0.0.1",
1128 &local);
1129 if (rc < 0) {
1130 return NT_STATUS_NO_MEMORY;
1132 status = make_user_info(&user_info, user, user, domain, domain,
1133 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1134 NULL, AUTH_PASSWORD_RESPONSE);
1135 if (!NT_STATUS_IS_OK(status)) {
1136 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1137 return status;
1140 /* We don't want any more mapping of the username */
1141 user_info->mapped_state = True;
1143 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1144 pinfo3);
1145 free_user_info(&user_info);
1146 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1147 user, nt_errstr(status)));
1148 return status;
1151 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1152 TALLOC_CTX *mem_ctx,
1153 uint32_t logon_parameters,
1154 const char *server,
1155 const char *username,
1156 const char *domainname,
1157 const char *workstation,
1158 const uint8_t chal[8],
1159 DATA_BLOB lm_response,
1160 DATA_BLOB nt_response,
1161 struct netr_SamInfo3 **info3)
1163 int attempts = 0;
1164 bool retry = false;
1165 NTSTATUS result;
1167 do {
1168 struct rpc_pipe_client *netlogon_pipe;
1169 const struct pipe_auth_data *auth;
1170 uint32_t neg_flags = 0;
1172 ZERO_STRUCTP(info3);
1173 retry = false;
1175 result = cm_connect_netlogon(domain, &netlogon_pipe);
1177 if (!NT_STATUS_IS_OK(result)) {
1178 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1179 nt_errstr(result)));
1180 return result;
1182 auth = netlogon_pipe->auth;
1183 if (netlogon_pipe->dc) {
1184 neg_flags = netlogon_pipe->dc->negotiate_flags;
1187 /* It is really important to try SamLogonEx here,
1188 * because in a clustered environment, we want to use
1189 * one machine account from multiple physical
1190 * computers.
1192 * With a normal SamLogon call, we must keep the
1193 * credentials chain updated and intact between all
1194 * users of the machine account (which would imply
1195 * cross-node communication for every NTLM logon).
1197 * (The credentials chain is not per NETLOGON pipe
1198 * connection, but globally on the server/client pair
1199 * by machine name).
1201 * When using SamLogonEx, the credentials are not
1202 * supplied, but the session key is implied by the
1203 * wrapping SamLogon context.
1205 * -- abartlet 21 April 2008
1207 * It's also important to use NetlogonValidationSamInfo4 (6),
1208 * because it relies on the rpc transport encryption
1209 * and avoids using the global netlogon schannel
1210 * session key to en/decrypt secret information
1211 * like the user_session_key for network logons.
1213 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1214 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1215 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1216 * are the indication that the server supports
1217 * NetlogonValidationSamInfo4 (6). And it must only
1218 * be used if "SealSecureChannel" is used.
1220 * -- metze 4 February 2011
1223 if (auth == NULL) {
1224 domain->can_do_validation6 = false;
1225 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1226 domain->can_do_validation6 = false;
1227 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1228 domain->can_do_validation6 = false;
1229 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1230 domain->can_do_validation6 = false;
1231 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1232 domain->can_do_validation6 = false;
1235 if (domain->can_do_samlogon_ex) {
1236 result = rpccli_netlogon_sam_network_logon_ex(
1237 netlogon_pipe,
1238 mem_ctx,
1239 logon_parameters,
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);
1249 } else {
1250 result = rpccli_netlogon_sam_network_logon(
1251 netlogon_pipe,
1252 mem_ctx,
1253 logon_parameters,
1254 server, /* server name */
1255 username, /* user name */
1256 domainname, /* target domain */
1257 workstation, /* workstation */
1258 chal,
1259 domain->can_do_validation6 ? 6 : 3,
1260 lm_response,
1261 nt_response,
1262 info3);
1265 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1268 * It's likely that the server also does not support
1269 * validation level 6
1271 domain->can_do_validation6 = false;
1273 if (domain->can_do_samlogon_ex) {
1274 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1275 "retrying with NetSamLogon\n"));
1276 domain->can_do_samlogon_ex = false;
1277 retry = true;
1278 continue;
1282 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1283 * (no Ex). This happens against old Samba
1284 * DCs. Drop the connection.
1286 invalidate_cm_connection(&domain->conn);
1287 result = NT_STATUS_LOGON_FAILURE;
1288 break;
1291 if (domain->can_do_validation6 &&
1292 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1293 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1294 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1295 DEBUG(3,("Got a DC that can not do validation level 6, "
1296 "retrying with level 3\n"));
1297 domain->can_do_validation6 = false;
1298 retry = true;
1299 continue;
1303 * we increment this after the "feature negotiation"
1304 * for can_do_samlogon_ex and can_do_validation6
1306 attempts += 1;
1308 /* We have to try a second time as cm_connect_netlogon
1309 might not yet have noticed that the DC has killed
1310 our connection. */
1312 if (!rpccli_is_connected(netlogon_pipe)) {
1313 retry = true;
1314 continue;
1317 /* if we get access denied, a possible cause was that we had
1318 and open connection to the DC, but someone changed our
1319 machine account password out from underneath us using 'net
1320 rpc changetrustpw' */
1322 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1323 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1324 "ACCESS_DENIED. Maybe the trust account "
1325 "password was changed and we didn't know it. "
1326 "Killing connections to domain %s\n",
1327 domainname));
1328 invalidate_cm_connection(&domain->conn);
1329 retry = true;
1332 } while ( (attempts < 2) && retry );
1334 return result;
1337 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1338 struct winbindd_domain *domain,
1339 const char *user,
1340 const char *pass,
1341 uint32_t request_flags,
1342 struct netr_SamInfo3 **info3)
1345 uchar chal[8];
1346 DATA_BLOB lm_resp;
1347 DATA_BLOB nt_resp;
1348 unsigned char local_nt_response[24];
1349 fstring name_domain, name_user;
1350 NTSTATUS result;
1351 struct netr_SamInfo3 *my_info3 = NULL;
1353 *info3 = NULL;
1355 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1357 /* Parse domain and username */
1359 parse_domain_user(user, name_domain, name_user);
1361 /* do password magic */
1363 generate_random_buffer(chal, sizeof(chal));
1365 if (lp_client_ntlmv2_auth()) {
1366 DATA_BLOB server_chal;
1367 DATA_BLOB names_blob;
1368 server_chal = data_blob_const(chal, 8);
1370 /* note that the 'workgroup' here is for the local
1371 machine. The 'server name' must match the
1372 'workstation' passed to the actual SamLogon call.
1374 names_blob = NTLMv2_generate_names_blob(
1375 mem_ctx, lp_netbios_name(), lp_workgroup());
1377 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1378 pass,
1379 &server_chal,
1380 &names_blob,
1381 &lm_resp, &nt_resp, NULL, NULL)) {
1382 data_blob_free(&names_blob);
1383 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1384 result = NT_STATUS_NO_MEMORY;
1385 goto done;
1387 data_blob_free(&names_blob);
1388 } else {
1389 lm_resp = data_blob_null;
1390 SMBNTencrypt(pass, chal, local_nt_response);
1392 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1393 sizeof(local_nt_response));
1396 if (strequal(name_domain, get_global_sam_name())) {
1397 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1399 result = winbindd_dual_auth_passdb(
1400 mem_ctx, name_domain, name_user,
1401 &chal_blob, &lm_resp, &nt_resp, info3);
1402 goto done;
1405 /* check authentication loop */
1407 result = winbind_samlogon_retry_loop(domain,
1408 mem_ctx,
1410 domain->dcname,
1411 name_user,
1412 name_domain,
1413 lp_netbios_name(),
1414 chal,
1415 lm_resp,
1416 nt_resp,
1417 &my_info3);
1418 if (!NT_STATUS_IS_OK(result)) {
1419 goto done;
1422 /* handle the case where a NT4 DC does not fill in the acct_flags in
1423 * the samlogon reply info3. When accurate info3 is required by the
1424 * caller, we look up the account flags ourselve - gd */
1426 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1427 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1429 struct rpc_pipe_client *samr_pipe;
1430 struct policy_handle samr_domain_handle, user_pol;
1431 union samr_UserInfo *info = NULL;
1432 NTSTATUS status_tmp, result_tmp;
1433 uint32 acct_flags;
1434 struct dcerpc_binding_handle *b;
1436 status_tmp = cm_connect_sam(domain, mem_ctx,
1437 &samr_pipe, &samr_domain_handle);
1439 if (!NT_STATUS_IS_OK(status_tmp)) {
1440 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1441 nt_errstr(status_tmp)));
1442 goto done;
1445 b = samr_pipe->binding_handle;
1447 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1448 &samr_domain_handle,
1449 MAXIMUM_ALLOWED_ACCESS,
1450 my_info3->base.rid,
1451 &user_pol,
1452 &result_tmp);
1454 if (!NT_STATUS_IS_OK(status_tmp)) {
1455 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1456 nt_errstr(status_tmp)));
1457 goto done;
1459 if (!NT_STATUS_IS_OK(result_tmp)) {
1460 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1461 nt_errstr(result_tmp)));
1462 goto done;
1465 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1466 &user_pol,
1468 &info,
1469 &result_tmp);
1471 if (!NT_STATUS_IS_OK(status_tmp)) {
1472 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1473 nt_errstr(status_tmp)));
1474 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1475 goto done;
1477 if (!NT_STATUS_IS_OK(result_tmp)) {
1478 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1479 nt_errstr(result_tmp)));
1480 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1481 goto done;
1484 acct_flags = info->info16.acct_flags;
1486 if (acct_flags == 0) {
1487 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1488 goto done;
1491 my_info3->base.acct_flags = acct_flags;
1493 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1495 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1498 *info3 = my_info3;
1499 done:
1500 return result;
1503 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1504 struct winbindd_cli_state *state)
1506 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1507 NTSTATUS krb5_result = NT_STATUS_OK;
1508 fstring name_domain, name_user;
1509 char *mapped_user;
1510 fstring domain_user;
1511 struct netr_SamInfo3 *info3 = NULL;
1512 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1514 /* Ensure null termination */
1515 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1517 /* Ensure null termination */
1518 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1520 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1521 state->request->data.auth.user));
1523 /* Parse domain and username */
1525 name_map_status = normalize_name_unmap(state->mem_ctx,
1526 state->request->data.auth.user,
1527 &mapped_user);
1529 /* If the name normalization didnt' actually do anything,
1530 just use the original name */
1532 if (!NT_STATUS_IS_OK(name_map_status) &&
1533 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1535 mapped_user = state->request->data.auth.user;
1538 parse_domain_user(mapped_user, name_domain, name_user);
1540 if ( mapped_user != state->request->data.auth.user ) {
1541 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1542 *lp_winbind_separator(),
1543 name_user );
1544 strlcpy( state->request->data.auth.user, domain_user,
1545 sizeof(state->request->data.auth.user));
1548 if (!domain->online) {
1549 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1550 if (domain->startup) {
1551 /* Logons are very important to users. If we're offline and
1552 we get a request within the first 30 seconds of startup,
1553 try very hard to find a DC and go online. */
1555 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1556 "request in startup mode.\n", domain->name ));
1558 winbindd_flush_negative_conn_cache(domain);
1559 result = init_dc_connection(domain);
1563 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1565 /* Check for Kerberos authentication */
1566 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1568 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1569 /* save for later */
1570 krb5_result = result;
1573 if (NT_STATUS_IS_OK(result)) {
1574 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1575 goto process_result;
1576 } else {
1577 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1580 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1581 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1582 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1583 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1584 set_domain_offline( domain );
1585 goto cached_logon;
1588 /* there are quite some NT_STATUS errors where there is no
1589 * point in retrying with a samlogon, we explictly have to take
1590 * care not to increase the bad logon counter on the DC */
1592 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1593 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1594 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1595 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1596 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1597 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1598 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1599 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1600 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1601 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1602 goto done;
1605 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1606 DEBUG(3,("falling back to samlogon\n"));
1607 goto sam_logon;
1608 } else {
1609 goto cached_logon;
1613 sam_logon:
1614 /* Check for Samlogon authentication */
1615 if (domain->online) {
1616 result = winbindd_dual_pam_auth_samlogon(
1617 state->mem_ctx, domain,
1618 state->request->data.auth.user,
1619 state->request->data.auth.pass,
1620 state->request->flags,
1621 &info3);
1623 if (NT_STATUS_IS_OK(result)) {
1624 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1625 /* add the Krb5 err if we have one */
1626 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1627 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1629 goto process_result;
1632 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1633 nt_errstr(result)));
1635 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1636 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1637 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1639 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1640 set_domain_offline( domain );
1641 goto cached_logon;
1644 if (domain->online) {
1645 /* We're still online - fail. */
1646 goto done;
1650 cached_logon:
1651 /* Check for Cached logons */
1652 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1653 lp_winbind_offline_logon()) {
1655 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1657 if (NT_STATUS_IS_OK(result)) {
1658 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1659 goto process_result;
1660 } else {
1661 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1662 goto done;
1666 process_result:
1668 if (NT_STATUS_IS_OK(result)) {
1670 struct dom_sid user_sid;
1672 /* In all codepaths where result == NT_STATUS_OK info3 must have
1673 been initialized. */
1674 if (!info3) {
1675 result = NT_STATUS_INTERNAL_ERROR;
1676 goto done;
1679 sid_compose(&user_sid, info3->base.domain_sid,
1680 info3->base.rid);
1682 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1683 &user_sid);
1684 netsamlogon_cache_store(name_user, info3);
1686 /* save name_to_sid info as early as possible (only if
1687 this is our primary domain so we don't invalidate
1688 the cache entry by storing the seq_num for the wrong
1689 domain). */
1690 if ( domain->primary ) {
1691 cache_name2sid(domain, name_domain, name_user,
1692 SID_NAME_USER, &user_sid);
1695 /* Check if the user is in the right group */
1697 result = check_info3_in_group(
1698 info3,
1699 state->request->data.auth.require_membership_of_sid);
1700 if (!NT_STATUS_IS_OK(result)) {
1701 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1702 state->request->data.auth.user,
1703 state->request->data.auth.require_membership_of_sid));
1704 goto done;
1707 result = append_auth_data(state->mem_ctx, state->response,
1708 state->request->flags, info3,
1709 name_domain, name_user);
1710 if (!NT_STATUS_IS_OK(result)) {
1711 goto done;
1714 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1715 && lp_winbind_offline_logon()) {
1717 result = winbindd_store_creds(domain,
1718 state->request->data.auth.user,
1719 state->request->data.auth.pass,
1720 info3);
1723 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1724 struct winbindd_domain *our_domain = find_our_domain();
1726 /* This is not entirely correct I believe, but it is
1727 consistent. Only apply the password policy settings
1728 too warn users for our own domain. Cannot obtain these
1729 from trusted DCs all the time so don't do it at all.
1730 -- jerry */
1732 result = NT_STATUS_NOT_SUPPORTED;
1733 if (our_domain == domain ) {
1734 result = fillup_password_policy(
1735 our_domain, state->response);
1738 if (!NT_STATUS_IS_OK(result)
1739 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1741 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1742 domain->name, nt_errstr(result)));
1743 goto done;
1747 result = NT_STATUS_OK;
1750 done:
1751 /* give us a more useful (more correct?) error code */
1752 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1753 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1754 result = NT_STATUS_NO_LOGON_SERVERS;
1757 set_auth_errors(state->response, result);
1759 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1760 state->request->data.auth.user,
1761 state->response->data.auth.nt_status_string,
1762 state->response->data.auth.pam_error));
1764 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1767 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1768 struct winbindd_cli_state *state)
1770 NTSTATUS result;
1771 struct netr_SamInfo3 *info3 = NULL;
1772 const char *name_user = NULL;
1773 const char *name_domain = NULL;
1774 const char *workstation;
1776 DATA_BLOB lm_resp, nt_resp;
1778 /* This is child-only, so no check for privileged access is needed
1779 anymore */
1781 /* Ensure null termination */
1782 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1783 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1785 name_user = state->request->data.auth_crap.user;
1786 name_domain = state->request->data.auth_crap.domain;
1787 workstation = state->request->data.auth_crap.workstation;
1789 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1790 name_domain, name_user));
1792 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1793 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1794 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1795 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1796 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1797 state->request->data.auth_crap.lm_resp_len,
1798 state->request->data.auth_crap.nt_resp_len));
1799 result = NT_STATUS_INVALID_PARAMETER;
1800 goto done;
1804 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1805 state->request->data.auth_crap.lm_resp_len);
1807 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1808 nt_resp = data_blob_talloc(state->mem_ctx,
1809 state->request->extra_data.data,
1810 state->request->data.auth_crap.nt_resp_len);
1811 } else {
1812 nt_resp = data_blob_talloc(state->mem_ctx,
1813 state->request->data.auth_crap.nt_resp,
1814 state->request->data.auth_crap.nt_resp_len);
1817 if (strequal(name_domain, get_global_sam_name())) {
1818 DATA_BLOB chal_blob = data_blob_const(
1819 state->request->data.auth_crap.chal,
1820 sizeof(state->request->data.auth_crap.chal));
1822 result = winbindd_dual_auth_passdb(
1823 state->mem_ctx, name_domain, name_user,
1824 &chal_blob, &lm_resp, &nt_resp, &info3);
1825 goto process_result;
1828 result = winbind_samlogon_retry_loop(domain,
1829 state->mem_ctx,
1830 state->request->data.auth_crap.logon_parameters,
1831 domain->dcname,
1832 name_user,
1833 name_domain,
1834 /* Bug #3248 - found by Stefan Burkei. */
1835 workstation, /* We carefully set this above so use it... */
1836 state->request->data.auth_crap.chal,
1837 lm_resp,
1838 nt_resp,
1839 &info3);
1840 if (!NT_STATUS_IS_OK(result)) {
1841 goto done;
1844 process_result:
1846 if (NT_STATUS_IS_OK(result)) {
1847 struct dom_sid user_sid;
1849 sid_compose(&user_sid, info3->base.domain_sid,
1850 info3->base.rid);
1851 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1852 &user_sid);
1853 netsamlogon_cache_store(name_user, info3);
1855 /* Check if the user is in the right group */
1857 result = check_info3_in_group(
1858 info3,
1859 state->request->data.auth_crap.require_membership_of_sid);
1860 if (!NT_STATUS_IS_OK(result)) {
1861 DEBUG(3, ("User %s is not in the required group (%s), so "
1862 "crap authentication is rejected\n",
1863 state->request->data.auth_crap.user,
1864 state->request->data.auth_crap.require_membership_of_sid));
1865 goto done;
1868 result = append_auth_data(state->mem_ctx, state->response,
1869 state->request->flags, info3,
1870 name_domain, name_user);
1871 if (!NT_STATUS_IS_OK(result)) {
1872 goto done;
1876 done:
1878 /* give us a more useful (more correct?) error code */
1879 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1880 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1881 result = NT_STATUS_NO_LOGON_SERVERS;
1884 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1885 result = nt_status_squash(result);
1888 set_auth_errors(state->response, result);
1890 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1891 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1892 name_domain,
1893 name_user,
1894 state->response->data.auth.nt_status_string,
1895 state->response->data.auth.pam_error));
1897 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1900 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1901 struct winbindd_cli_state *state)
1903 char *oldpass;
1904 char *newpass = NULL;
1905 struct policy_handle dom_pol;
1906 struct rpc_pipe_client *cli = NULL;
1907 bool got_info = false;
1908 struct samr_DomInfo1 *info = NULL;
1909 struct userPwdChangeFailureInformation *reject = NULL;
1910 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1911 fstring domain, user;
1912 struct dcerpc_binding_handle *b = NULL;
1914 ZERO_STRUCT(dom_pol);
1916 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1917 state->request->data.auth.user));
1919 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1920 goto done;
1923 /* Change password */
1925 oldpass = state->request->data.chauthtok.oldpass;
1926 newpass = state->request->data.chauthtok.newpass;
1928 /* Initialize reject reason */
1929 state->response->data.auth.reject_reason = Undefined;
1931 /* Get sam handle */
1933 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1934 &dom_pol);
1935 if (!NT_STATUS_IS_OK(result)) {
1936 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1937 goto done;
1940 b = cli->binding_handle;
1942 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1943 user,
1944 newpass,
1945 oldpass,
1946 &info,
1947 &reject);
1949 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1951 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1953 fill_in_password_policy(state->response, info);
1955 state->response->data.auth.reject_reason =
1956 reject->extendedFailureReason;
1958 got_info = true;
1961 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1962 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1963 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1964 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1966 /* only fallback when the chgpasswd_user3 call is not supported */
1967 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
1968 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
1969 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
1970 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1972 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1973 nt_errstr(result)));
1975 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1977 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1978 Map to the same status code as Windows 2003. */
1980 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1981 result = NT_STATUS_PASSWORD_RESTRICTION;
1985 done:
1987 if (NT_STATUS_IS_OK(result)
1988 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1989 && lp_winbind_offline_logon()) {
1990 result = winbindd_update_creds_by_name(contact_domain, user,
1991 newpass);
1992 /* Again, this happens when we login from gdm or xdm
1993 * and the password expires, *BUT* cached crendentials
1994 * doesn't exist. winbindd_update_creds_by_name()
1995 * returns NT_STATUS_NO_SUCH_USER.
1996 * This is not a failure.
1997 * --- BoYang
1998 * */
1999 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2000 result = NT_STATUS_OK;
2003 if (!NT_STATUS_IS_OK(result)) {
2004 DEBUG(10, ("Failed to store creds: %s\n",
2005 nt_errstr(result)));
2006 goto process_result;
2010 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2012 NTSTATUS policy_ret;
2014 policy_ret = fillup_password_policy(
2015 contact_domain, state->response);
2017 /* failure of this is non critical, it will just provide no
2018 * additional information to the client why the change has
2019 * failed - Guenther */
2021 if (!NT_STATUS_IS_OK(policy_ret)) {
2022 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2023 goto process_result;
2027 process_result:
2029 if (strequal(contact_domain->name, get_global_sam_name())) {
2030 /* FIXME: internal rpc pipe does not cache handles yet */
2031 if (b) {
2032 if (is_valid_policy_hnd(&dom_pol)) {
2033 NTSTATUS _result;
2034 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2036 TALLOC_FREE(cli);
2040 set_auth_errors(state->response, result);
2042 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2043 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2044 domain,
2045 user,
2046 state->response->data.auth.nt_status_string,
2047 state->response->data.auth.pam_error));
2049 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2052 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2053 struct winbindd_cli_state *state)
2055 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2057 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2058 state->request->data.logoff.user));
2060 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2061 result = NT_STATUS_OK;
2062 goto process_result;
2065 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2066 result = NT_STATUS_OK;
2067 goto process_result;
2070 #ifdef HAVE_KRB5
2072 if (state->request->data.logoff.uid < 0) {
2073 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2074 goto process_result;
2077 /* what we need here is to find the corresponding krb5 ccache name *we*
2078 * created for a given username and destroy it */
2080 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2081 result = NT_STATUS_OK;
2082 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2083 goto process_result;
2086 if (!ccache_entry_identical(state->request->data.logoff.user,
2087 state->request->data.logoff.uid,
2088 state->request->data.logoff.krb5ccname)) {
2089 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2090 goto process_result;
2093 result = remove_ccache(state->request->data.logoff.user);
2094 if (!NT_STATUS_IS_OK(result)) {
2095 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2096 nt_errstr(result)));
2097 goto process_result;
2100 #else
2101 result = NT_STATUS_NOT_SUPPORTED;
2102 #endif
2104 process_result:
2107 set_auth_errors(state->response, result);
2109 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2112 /* Change user password with auth crap*/
2114 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2116 NTSTATUS result;
2117 DATA_BLOB new_nt_password;
2118 DATA_BLOB old_nt_hash_enc;
2119 DATA_BLOB new_lm_password;
2120 DATA_BLOB old_lm_hash_enc;
2121 fstring domain,user;
2122 struct policy_handle dom_pol;
2123 struct winbindd_domain *contact_domain = domainSt;
2124 struct rpc_pipe_client *cli = NULL;
2125 struct dcerpc_binding_handle *b = NULL;
2127 ZERO_STRUCT(dom_pol);
2129 /* Ensure null termination */
2130 state->request->data.chng_pswd_auth_crap.user[
2131 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2132 state->request->data.chng_pswd_auth_crap.domain[
2133 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2134 *domain = 0;
2135 *user = 0;
2137 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2138 (unsigned long)state->pid,
2139 state->request->data.chng_pswd_auth_crap.domain,
2140 state->request->data.chng_pswd_auth_crap.user));
2142 if (lp_winbind_offline_logon()) {
2143 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2144 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2145 result = NT_STATUS_ACCESS_DENIED;
2146 goto done;
2149 if (*state->request->data.chng_pswd_auth_crap.domain) {
2150 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2151 } else {
2152 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2153 domain, user);
2155 if(!*domain) {
2156 DEBUG(3,("no domain specified with username (%s) - "
2157 "failing auth\n",
2158 state->request->data.chng_pswd_auth_crap.user));
2159 result = NT_STATUS_NO_SUCH_USER;
2160 goto done;
2164 if (!*domain && lp_winbind_use_default_domain()) {
2165 fstrcpy(domain,lp_workgroup());
2168 if(!*user) {
2169 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2172 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2173 (unsigned long)state->pid, domain, user));
2175 /* Change password */
2176 new_nt_password = data_blob_const(
2177 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2178 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2180 old_nt_hash_enc = data_blob_const(
2181 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2182 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2184 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2185 new_lm_password = data_blob_const(
2186 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2187 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2189 old_lm_hash_enc = data_blob_const(
2190 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2191 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2192 } else {
2193 new_lm_password.length = 0;
2194 old_lm_hash_enc.length = 0;
2197 /* Get sam handle */
2199 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2200 if (!NT_STATUS_IS_OK(result)) {
2201 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2202 goto done;
2205 b = cli->binding_handle;
2207 result = rpccli_samr_chng_pswd_auth_crap(
2208 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2209 new_lm_password, old_lm_hash_enc);
2211 done:
2213 if (strequal(contact_domain->name, get_global_sam_name())) {
2214 /* FIXME: internal rpc pipe does not cache handles yet */
2215 if (b) {
2216 if (is_valid_policy_hnd(&dom_pol)) {
2217 NTSTATUS _result;
2218 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2220 TALLOC_FREE(cli);
2224 set_auth_errors(state->response, result);
2226 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2227 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2228 domain, user,
2229 state->response->data.auth.nt_status_string,
2230 state->response->data.auth.pam_error));
2232 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;