shadow_copy2: implement disk_free
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob8f2facd447bc4e13cbcd3f21f34810c5d4bc46ee
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"
41 #include "auth/kerberos/pac_utils.h"
42 #include "auth/gensec/gensec.h"
43 #include "librpc/crypto/gse_krb5.h"
45 #undef DBGC_CLASS
46 #define DBGC_CLASS DBGC_WINBIND
48 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
50 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
51 struct winbindd_response *resp,
52 struct netr_SamInfo3 *info3)
54 char *ex;
55 uint32_t i;
57 resp->data.auth.info3.logon_time =
58 nt_time_to_unix(info3->base.logon_time);
59 resp->data.auth.info3.logoff_time =
60 nt_time_to_unix(info3->base.logoff_time);
61 resp->data.auth.info3.kickoff_time =
62 nt_time_to_unix(info3->base.kickoff_time);
63 resp->data.auth.info3.pass_last_set_time =
64 nt_time_to_unix(info3->base.last_password_change);
65 resp->data.auth.info3.pass_can_change_time =
66 nt_time_to_unix(info3->base.allow_password_change);
67 resp->data.auth.info3.pass_must_change_time =
68 nt_time_to_unix(info3->base.force_password_change);
70 resp->data.auth.info3.logon_count = info3->base.logon_count;
71 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
73 resp->data.auth.info3.user_rid = info3->base.rid;
74 resp->data.auth.info3.group_rid = info3->base.primary_gid;
75 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
77 resp->data.auth.info3.num_groups = info3->base.groups.count;
78 resp->data.auth.info3.user_flgs = info3->base.user_flags;
80 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
81 resp->data.auth.info3.num_other_sids = info3->sidcount;
83 fstrcpy(resp->data.auth.info3.user_name,
84 info3->base.account_name.string);
85 fstrcpy(resp->data.auth.info3.full_name,
86 info3->base.full_name.string);
87 fstrcpy(resp->data.auth.info3.logon_script,
88 info3->base.logon_script.string);
89 fstrcpy(resp->data.auth.info3.profile_path,
90 info3->base.profile_path.string);
91 fstrcpy(resp->data.auth.info3.home_dir,
92 info3->base.home_directory.string);
93 fstrcpy(resp->data.auth.info3.dir_drive,
94 info3->base.home_drive.string);
96 fstrcpy(resp->data.auth.info3.logon_srv,
97 info3->base.logon_server.string);
98 fstrcpy(resp->data.auth.info3.logon_dom,
99 info3->base.logon_domain.string);
101 ex = talloc_strdup(mem_ctx, "");
102 NT_STATUS_HAVE_NO_MEMORY(ex);
104 for (i=0; i < info3->base.groups.count; i++) {
105 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
106 info3->base.groups.rids[i].rid,
107 info3->base.groups.rids[i].attributes);
108 NT_STATUS_HAVE_NO_MEMORY(ex);
111 for (i=0; i < info3->sidcount; i++) {
112 char *sid;
114 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
115 NT_STATUS_HAVE_NO_MEMORY(sid);
117 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
118 sid,
119 info3->sids[i].attributes);
120 NT_STATUS_HAVE_NO_MEMORY(ex);
122 talloc_free(sid);
125 resp->extra_data.data = ex;
126 resp->length += talloc_get_size(ex);
128 return NT_STATUS_OK;
131 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
132 struct winbindd_response *resp,
133 struct netr_SamInfo3 *info3)
135 DATA_BLOB blob;
136 enum ndr_err_code ndr_err;
138 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
139 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
140 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
141 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
142 return ndr_map_error2ntstatus(ndr_err);
145 resp->extra_data.data = blob.data;
146 resp->length += blob.length;
148 return NT_STATUS_OK;
151 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
152 struct winbindd_response *resp,
153 const struct netr_SamInfo3 *info3,
154 const char *name_domain,
155 const char *name_user)
157 /* We've been asked to return the unix username, per
158 'winbind use default domain' settings and the like */
160 const char *nt_username, *nt_domain;
162 nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
163 if (!nt_domain) {
164 /* If the server didn't give us one, just use the one
165 * we sent them */
166 nt_domain = name_domain;
169 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
170 if (!nt_username) {
171 /* If the server didn't give us one, just use the one
172 * we sent them */
173 nt_username = name_user;
176 fill_domain_username(resp->data.auth.unix_username,
177 nt_domain, nt_username, true);
179 DEBUG(5, ("Setting unix username to [%s]\n",
180 resp->data.auth.unix_username));
182 return NT_STATUS_OK;
185 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
186 struct winbindd_response *resp,
187 const struct netr_SamInfo3 *info3,
188 const char *name_domain,
189 const char *name_user)
191 char *afsname = NULL;
192 char *cell;
193 char *token;
195 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
196 if (afsname == NULL) {
197 return NT_STATUS_NO_MEMORY;
200 afsname = talloc_string_sub(mem_ctx,
201 lp_afs_username_map(),
202 "%D", name_domain);
203 afsname = talloc_string_sub(mem_ctx, afsname,
204 "%u", name_user);
205 afsname = talloc_string_sub(mem_ctx, afsname,
206 "%U", name_user);
209 struct dom_sid user_sid;
210 fstring sidstr;
212 sid_compose(&user_sid, info3->base.domain_sid,
213 info3->base.rid);
214 sid_to_fstring(sidstr, &user_sid);
215 afsname = talloc_string_sub(mem_ctx, afsname,
216 "%s", sidstr);
219 if (afsname == NULL) {
220 return NT_STATUS_NO_MEMORY;
223 if (!strlower_m(afsname)) {
224 return NT_STATUS_INVALID_PARAMETER;
227 DEBUG(10, ("Generating token for user %s\n", afsname));
229 cell = strchr(afsname, '@');
231 if (cell == NULL) {
232 return NT_STATUS_NO_MEMORY;
235 *cell = '\0';
236 cell += 1;
238 token = afs_createtoken_str(afsname, cell);
239 if (token == NULL) {
240 return NT_STATUS_OK;
242 resp->extra_data.data = talloc_strdup(mem_ctx, token);
243 if (resp->extra_data.data == NULL) {
244 return NT_STATUS_NO_MEMORY;
246 resp->length += strlen((const char *)resp->extra_data.data)+1;
248 return NT_STATUS_OK;
251 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
252 const char *group_sid)
254 * Check whether a user belongs to a group or list of groups.
256 * @param mem_ctx talloc memory context.
257 * @param info3 user information, including group membership info.
258 * @param group_sid One or more groups , separated by commas.
260 * @return NT_STATUS_OK on success,
261 * NT_STATUS_LOGON_FAILURE if the user does not belong,
262 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
265 struct dom_sid *require_membership_of_sid;
266 uint32_t num_require_membership_of_sid;
267 char *req_sid;
268 const char *p;
269 struct dom_sid sid;
270 size_t i;
271 struct security_token *token;
272 TALLOC_CTX *frame = talloc_stackframe();
273 NTSTATUS status;
275 /* Parse the 'required group' SID */
277 if (!group_sid || !group_sid[0]) {
278 /* NO sid supplied, all users may access */
279 TALLOC_FREE(frame);
280 return NT_STATUS_OK;
283 token = talloc_zero(talloc_tos(), struct security_token);
284 if (token == NULL) {
285 DEBUG(0, ("talloc failed\n"));
286 TALLOC_FREE(frame);
287 return NT_STATUS_NO_MEMORY;
290 num_require_membership_of_sid = 0;
291 require_membership_of_sid = NULL;
293 p = group_sid;
295 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
296 if (!string_to_sid(&sid, req_sid)) {
297 DEBUG(0, ("check_info3_in_group: could not parse %s "
298 "as a SID!", req_sid));
299 TALLOC_FREE(frame);
300 return NT_STATUS_INVALID_PARAMETER;
303 status = add_sid_to_array(talloc_tos(), &sid,
304 &require_membership_of_sid,
305 &num_require_membership_of_sid);
306 if (!NT_STATUS_IS_OK(status)) {
307 DEBUG(0, ("add_sid_to_array failed\n"));
308 TALLOC_FREE(frame);
309 return status;
313 status = sid_array_from_info3(talloc_tos(), info3,
314 &token->sids,
315 &token->num_sids,
316 true);
317 if (!NT_STATUS_IS_OK(status)) {
318 TALLOC_FREE(frame);
319 return status;
322 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
323 token))
324 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
325 token))) {
326 DEBUG(3, ("could not add aliases: %s\n",
327 nt_errstr(status)));
328 TALLOC_FREE(frame);
329 return status;
332 security_token_debug(DBGC_CLASS, 10, token);
334 for (i=0; i<num_require_membership_of_sid; i++) {
335 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
336 &require_membership_of_sid[i])));
337 if (nt_token_check_sid(&require_membership_of_sid[i],
338 token)) {
339 DEBUG(10, ("Access ok\n"));
340 TALLOC_FREE(frame);
341 return NT_STATUS_OK;
345 /* Do not distinguish this error from a wrong username/pw */
347 TALLOC_FREE(frame);
348 return NT_STATUS_LOGON_FAILURE;
351 struct winbindd_domain *find_auth_domain(uint8_t flags,
352 const char *domain_name)
354 struct winbindd_domain *domain;
356 if (IS_DC) {
357 domain = find_domain_from_name_noinit(domain_name);
358 if (domain == NULL) {
359 DEBUG(3, ("Authentication for domain [%s] refused "
360 "as it is not a trusted domain\n",
361 domain_name));
363 return domain;
366 if (strequal(domain_name, get_global_sam_name())) {
367 return find_domain_from_name_noinit(domain_name);
370 /* we can auth against trusted domains */
371 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
372 domain = find_domain_from_name_noinit(domain_name);
373 if (domain == NULL) {
374 DEBUG(3, ("Authentication for domain [%s] skipped "
375 "as it is not a trusted domain\n",
376 domain_name));
377 } else {
378 return domain;
382 return find_our_domain();
385 static void fill_in_password_policy(struct winbindd_response *r,
386 const struct samr_DomInfo1 *p)
388 r->data.auth.policy.min_length_password =
389 p->min_password_length;
390 r->data.auth.policy.password_history =
391 p->password_history_length;
392 r->data.auth.policy.password_properties =
393 p->password_properties;
394 r->data.auth.policy.expire =
395 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
396 r->data.auth.policy.min_passwordage =
397 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
400 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
401 struct winbindd_response *response)
403 TALLOC_CTX *frame = talloc_stackframe();
404 struct winbindd_methods *methods;
405 NTSTATUS status;
406 struct samr_DomInfo1 password_policy;
408 if ( !winbindd_can_contact_domain( domain ) ) {
409 DEBUG(5,("fillup_password_policy: No inbound trust to "
410 "contact domain %s\n", domain->name));
411 status = NT_STATUS_NOT_SUPPORTED;
412 goto done;
415 methods = domain->methods;
417 status = methods->password_policy(domain, talloc_tos(), &password_policy);
418 if (NT_STATUS_IS_ERR(status)) {
419 goto done;
422 fill_in_password_policy(response, &password_policy);
424 done:
425 TALLOC_FREE(frame);
426 return NT_STATUS_OK;
429 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
430 TALLOC_CTX *mem_ctx,
431 uint16 *lockout_threshold)
433 struct winbindd_methods *methods;
434 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
435 struct samr_DomInfo12 lockout_policy;
437 *lockout_threshold = 0;
439 methods = domain->methods;
441 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
442 if (NT_STATUS_IS_ERR(status)) {
443 return status;
446 *lockout_threshold = lockout_policy.lockout_threshold;
448 return NT_STATUS_OK;
451 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
452 TALLOC_CTX *mem_ctx,
453 uint32 *password_properties)
455 struct winbindd_methods *methods;
456 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
457 struct samr_DomInfo1 password_policy;
459 *password_properties = 0;
461 methods = domain->methods;
463 status = methods->password_policy(domain, mem_ctx, &password_policy);
464 if (NT_STATUS_IS_ERR(status)) {
465 return status;
468 *password_properties = password_policy.password_properties;
470 return NT_STATUS_OK;
473 #ifdef HAVE_KRB5
475 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
476 const char *type,
477 uid_t uid,
478 const char **user_ccache_file)
480 /* accept FILE and WRFILE as krb5_cc_type from the client and then
481 * build the full ccname string based on the user's uid here -
482 * Guenther*/
484 const char *gen_cc = NULL;
486 if (uid != -1) {
487 if (strequal(type, "FILE")) {
488 gen_cc = talloc_asprintf(
489 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
491 if (strequal(type, "WRFILE")) {
492 gen_cc = talloc_asprintf(
493 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
495 if (strequal(type, "KEYRING")) {
496 gen_cc = talloc_asprintf(
497 mem_ctx, "KEYRING:persistent:%d", uid);
500 if (strnequal(type, "FILE:/", 6) ||
501 strnequal(type, "WRFILE:/", 8) ||
502 strnequal(type, "DIR:/", 5)) {
504 /* we allow only one "%u" substitution */
506 char *p;
508 p = strchr(type, '%');
509 if (p != NULL) {
511 p++;
513 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
514 gen_cc = talloc_asprintf(mem_ctx, type, uid);
520 *user_ccache_file = gen_cc;
522 if (gen_cc == NULL) {
523 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
525 if (gen_cc == NULL) {
526 DEBUG(0,("out of memory\n"));
527 return NULL;
530 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
531 (*user_ccache_file == NULL) ? " (internal)":""));
533 return gen_cc;
536 #endif
538 uid_t get_uid_from_request(struct winbindd_request *request)
540 uid_t uid;
542 uid = request->data.auth.uid;
544 if (uid < 0) {
545 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
546 return -1;
548 return uid;
551 /**********************************************************************
552 Authenticate a user with a clear text password using Kerberos and fill up
553 ccache if required
554 **********************************************************************/
556 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
557 struct winbindd_domain *domain,
558 const char *user,
559 const char *pass,
560 const char *krb5_cc_type,
561 uid_t uid,
562 struct netr_SamInfo3 **info3,
563 fstring krb5ccname)
565 #ifdef HAVE_KRB5
566 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
567 krb5_error_code krb5_ret;
568 const char *cc = NULL;
569 const char *principal_s = NULL;
570 const char *service = NULL;
571 char *realm = NULL;
572 fstring name_domain, name_user;
573 time_t ticket_lifetime = 0;
574 time_t renewal_until = 0;
575 ADS_STRUCT *ads;
576 time_t time_offset = 0;
577 const char *user_ccache_file;
578 struct PAC_LOGON_INFO *logon_info = NULL;
580 *info3 = NULL;
582 /* 1st step:
583 * prepare a krb5_cc_cache string for the user */
585 if (uid == -1) {
586 DEBUG(0,("no valid uid\n"));
589 cc = generate_krb5_ccache(mem_ctx,
590 krb5_cc_type,
591 uid,
592 &user_ccache_file);
593 if (cc == NULL) {
594 return NT_STATUS_NO_MEMORY;
598 /* 2nd step:
599 * get kerberos properties */
601 if (domain->private_data) {
602 ads = (ADS_STRUCT *)domain->private_data;
603 time_offset = ads->auth.time_offset;
607 /* 3rd step:
608 * do kerberos auth and setup ccache as the user */
610 parse_domain_user(user, name_domain, name_user);
612 realm = domain->alt_name;
613 if (!strupper_m(realm)) {
614 return NT_STATUS_INVALID_PARAMETER;
617 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
618 if (principal_s == NULL) {
619 return NT_STATUS_NO_MEMORY;
622 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
623 if (service == NULL) {
624 return NT_STATUS_NO_MEMORY;
627 /* if this is a user ccache, we need to act as the user to let the krb5
628 * library handle the chown, etc. */
630 /************************ ENTERING NON-ROOT **********************/
632 if (user_ccache_file != NULL) {
633 set_effective_uid(uid);
634 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
637 result = kerberos_return_pac(mem_ctx,
638 principal_s,
639 pass,
640 time_offset,
641 &ticket_lifetime,
642 &renewal_until,
644 true,
645 true,
646 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
647 NULL,
648 &logon_info);
649 if (user_ccache_file != NULL) {
650 gain_root_privilege();
653 /************************ RETURNED TO ROOT **********************/
655 if (!NT_STATUS_IS_OK(result)) {
656 goto failed;
659 *info3 = &logon_info->info3;
661 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
662 principal_s));
664 /* if we had a user's ccache then return that string for the pam
665 * environment */
667 if (user_ccache_file != NULL) {
669 fstrcpy(krb5ccname, user_ccache_file);
671 result = add_ccache_to_list(principal_s,
673 service,
674 user,
675 pass,
676 realm,
677 uid,
678 time(NULL),
679 ticket_lifetime,
680 renewal_until,
681 false);
683 if (!NT_STATUS_IS_OK(result)) {
684 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
685 nt_errstr(result)));
687 } else {
689 /* need to delete the memory cred cache, it is not used anymore */
691 krb5_ret = ads_kdestroy(cc);
692 if (krb5_ret) {
693 DEBUG(3,("winbindd_raw_kerberos_login: "
694 "could not destroy krb5 credential cache: "
695 "%s\n", error_message(krb5_ret)));
700 return NT_STATUS_OK;
702 failed:
704 * Do not delete an existing valid credential cache, if the user
705 * e.g. enters a wrong password
707 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
708 && user_ccache_file != NULL) {
709 return result;
712 /* we could have created a new credential cache with a valid tgt in it
713 * but we werent able to get or verify the service ticket for this
714 * local host and therefor didn't get the PAC, we need to remove that
715 * cache entirely now */
717 krb5_ret = ads_kdestroy(cc);
718 if (krb5_ret) {
719 DEBUG(3,("winbindd_raw_kerberos_login: "
720 "could not destroy krb5 credential cache: "
721 "%s\n", error_message(krb5_ret)));
724 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
725 DEBUG(3,("winbindd_raw_kerberos_login: "
726 "could not remove ccache for user %s\n",
727 user));
730 return result;
731 #else
732 return NT_STATUS_NOT_SUPPORTED;
733 #endif /* HAVE_KRB5 */
736 /****************************************************************
737 ****************************************************************/
739 bool check_request_flags(uint32_t flags)
741 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
742 WBFLAG_PAM_INFO3_TEXT |
743 WBFLAG_PAM_INFO3_NDR;
745 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
746 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
747 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
748 !(flags & flags_edata) ) {
749 return true;
752 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
753 flags));
755 return false;
758 /****************************************************************
759 ****************************************************************/
761 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
762 struct winbindd_response *resp,
763 uint32_t request_flags,
764 struct netr_SamInfo3 *info3,
765 const char *name_domain,
766 const char *name_user)
768 NTSTATUS result;
770 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
771 memcpy(resp->data.auth.user_session_key,
772 info3->base.key.key,
773 sizeof(resp->data.auth.user_session_key)
774 /* 16 */);
777 if (request_flags & WBFLAG_PAM_LMKEY) {
778 memcpy(resp->data.auth.first_8_lm_hash,
779 info3->base.LMSessKey.key,
780 sizeof(resp->data.auth.first_8_lm_hash)
781 /* 8 */);
784 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
785 result = append_unix_username(mem_ctx, resp,
786 info3, name_domain, name_user);
787 if (!NT_STATUS_IS_OK(result)) {
788 DEBUG(10,("Failed to append Unix Username: %s\n",
789 nt_errstr(result)));
790 return result;
794 /* currently, anything from here on potentially overwrites extra_data. */
796 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
797 result = append_info3_as_ndr(mem_ctx, resp, info3);
798 if (!NT_STATUS_IS_OK(result)) {
799 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
800 nt_errstr(result)));
801 return result;
805 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
806 result = append_info3_as_txt(mem_ctx, resp, info3);
807 if (!NT_STATUS_IS_OK(result)) {
808 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
809 nt_errstr(result)));
810 return result;
814 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
815 result = append_afs_token(mem_ctx, resp,
816 info3, name_domain, name_user);
817 if (!NT_STATUS_IS_OK(result)) {
818 DEBUG(10,("Failed to append AFS token: %s\n",
819 nt_errstr(result)));
820 return result;
824 return NT_STATUS_OK;
827 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
828 struct winbindd_cli_state *state,
829 struct netr_SamInfo3 **info3)
831 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
832 uint16 max_allowed_bad_attempts;
833 fstring name_domain, name_user;
834 struct dom_sid sid;
835 enum lsa_SidType type;
836 uchar new_nt_pass[NT_HASH_LEN];
837 const uint8 *cached_nt_pass;
838 const uint8 *cached_salt;
839 struct netr_SamInfo3 *my_info3;
840 time_t kickoff_time, must_change_time;
841 bool password_good = false;
842 #ifdef HAVE_KRB5
843 struct winbindd_tdc_domain *tdc_domain = NULL;
844 #endif
846 *info3 = NULL;
848 ZERO_STRUCTP(info3);
850 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
852 /* Parse domain and username */
854 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
857 if (!lookup_cached_name(name_domain,
858 name_user,
859 &sid,
860 &type)) {
861 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
862 return NT_STATUS_NO_SUCH_USER;
865 if (type != SID_NAME_USER) {
866 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
867 return NT_STATUS_LOGON_FAILURE;
870 result = winbindd_get_creds(domain,
871 state->mem_ctx,
872 &sid,
873 &my_info3,
874 &cached_nt_pass,
875 &cached_salt);
876 if (!NT_STATUS_IS_OK(result)) {
877 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
878 return result;
881 *info3 = my_info3;
883 E_md4hash(state->request->data.auth.pass, new_nt_pass);
885 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
886 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
887 if (cached_salt) {
888 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
891 if (cached_salt) {
892 /* In this case we didn't store the nt_hash itself,
893 but the MD5 combination of salt + nt_hash. */
894 uchar salted_hash[NT_HASH_LEN];
895 E_md5hash(cached_salt, new_nt_pass, salted_hash);
897 password_good = (memcmp(cached_nt_pass, salted_hash,
898 NT_HASH_LEN) == 0);
899 } else {
900 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
901 password_good = (memcmp(cached_nt_pass, new_nt_pass,
902 NT_HASH_LEN) == 0);
905 if (password_good) {
907 /* User *DOES* know the password, update logon_time and reset
908 * bad_pw_count */
910 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
912 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
913 return NT_STATUS_ACCOUNT_LOCKED_OUT;
916 if (my_info3->base.acct_flags & ACB_DISABLED) {
917 return NT_STATUS_ACCOUNT_DISABLED;
920 if (my_info3->base.acct_flags & ACB_WSTRUST) {
921 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
924 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
925 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
928 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
929 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
932 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
933 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
934 my_info3->base.acct_flags));
935 return NT_STATUS_LOGON_FAILURE;
938 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
939 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
940 return NT_STATUS_ACCOUNT_EXPIRED;
943 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
944 if (must_change_time != 0 && must_change_time < time(NULL)) {
945 /* we allow grace logons when the password has expired */
946 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
947 /* return NT_STATUS_PASSWORD_EXPIRED; */
948 goto success;
951 #ifdef HAVE_KRB5
952 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
953 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
954 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
955 /* used to cope with the case winbindd starting without network. */
956 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
958 uid_t uid = -1;
959 const char *cc = NULL;
960 char *realm = NULL;
961 const char *principal_s = NULL;
962 const char *service = NULL;
963 const char *user_ccache_file;
965 uid = get_uid_from_request(state->request);
966 if (uid == -1) {
967 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
968 return NT_STATUS_INVALID_PARAMETER;
971 cc = generate_krb5_ccache(state->mem_ctx,
972 state->request->data.auth.krb5_cc_type,
973 state->request->data.auth.uid,
974 &user_ccache_file);
975 if (cc == NULL) {
976 return NT_STATUS_NO_MEMORY;
979 realm = domain->alt_name;
980 if (!strupper_m(realm)) {
981 return NT_STATUS_INVALID_PARAMETER;
984 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
985 if (principal_s == NULL) {
986 return NT_STATUS_NO_MEMORY;
989 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
990 if (service == NULL) {
991 return NT_STATUS_NO_MEMORY;
994 if (user_ccache_file != NULL) {
996 fstrcpy(state->response->data.auth.krb5ccname,
997 user_ccache_file);
999 result = add_ccache_to_list(principal_s,
1001 service,
1002 state->request->data.auth.user,
1003 state->request->data.auth.pass,
1004 domain->alt_name,
1005 uid,
1006 time(NULL),
1007 time(NULL) + lp_winbind_cache_time(),
1008 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1009 true);
1011 if (!NT_STATUS_IS_OK(result)) {
1012 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1013 "to add ccache to list: %s\n",
1014 nt_errstr(result)));
1018 #endif /* HAVE_KRB5 */
1019 success:
1020 /* FIXME: we possibly should handle logon hours as well (does xp when
1021 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1023 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1024 my_info3->base.bad_password_count = 0;
1026 result = winbindd_update_creds_by_info3(domain,
1027 state->request->data.auth.user,
1028 state->request->data.auth.pass,
1029 my_info3);
1030 if (!NT_STATUS_IS_OK(result)) {
1031 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1032 nt_errstr(result)));
1033 return result;
1036 return NT_STATUS_OK;
1040 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1041 if (domain->online == false) {
1042 goto failed;
1045 /* failure of this is not critical */
1046 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1047 if (!NT_STATUS_IS_OK(result)) {
1048 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1049 "Won't be able to honour account lockout policies\n"));
1052 /* increase counter */
1053 my_info3->base.bad_password_count++;
1055 if (max_allowed_bad_attempts == 0) {
1056 goto failed;
1059 /* lockout user */
1060 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1062 uint32 password_properties;
1064 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1065 if (!NT_STATUS_IS_OK(result)) {
1066 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1069 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1070 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1071 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1075 failed:
1076 result = winbindd_update_creds_by_info3(domain,
1077 state->request->data.auth.user,
1078 NULL,
1079 my_info3);
1081 if (!NT_STATUS_IS_OK(result)) {
1082 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1083 nt_errstr(result)));
1086 return NT_STATUS_LOGON_FAILURE;
1089 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1090 struct winbindd_cli_state *state,
1091 struct netr_SamInfo3 **info3)
1093 struct winbindd_domain *contact_domain;
1094 fstring name_domain, name_user;
1095 NTSTATUS result;
1097 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1099 /* Parse domain and username */
1101 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1103 /* what domain should we contact? */
1105 if ( IS_DC ) {
1106 if (!(contact_domain = find_domain_from_name(name_domain))) {
1107 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1108 state->request->data.auth.user, name_domain, name_user, name_domain));
1109 result = NT_STATUS_NO_SUCH_USER;
1110 goto done;
1113 } else {
1114 if (is_myname(name_domain)) {
1115 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1116 result = NT_STATUS_NO_SUCH_USER;
1117 goto done;
1120 contact_domain = find_domain_from_name(name_domain);
1121 if (contact_domain == NULL) {
1122 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1123 state->request->data.auth.user, name_domain, name_user, name_domain));
1125 result = NT_STATUS_NO_SUCH_USER;
1126 goto done;
1130 if (contact_domain->initialized &&
1131 contact_domain->active_directory) {
1132 goto try_login;
1135 if (!contact_domain->initialized) {
1136 init_dc_connection(contact_domain);
1139 if (!contact_domain->active_directory) {
1140 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1141 return NT_STATUS_INVALID_LOGON_TYPE;
1143 try_login:
1144 result = winbindd_raw_kerberos_login(
1145 state->mem_ctx, contact_domain,
1146 state->request->data.auth.user,
1147 state->request->data.auth.pass,
1148 state->request->data.auth.krb5_cc_type,
1149 get_uid_from_request(state->request),
1150 info3, state->response->data.auth.krb5ccname);
1151 done:
1152 return result;
1155 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1156 uint32_t logon_parameters,
1157 const char *domain, const char *user,
1158 const DATA_BLOB *challenge,
1159 const DATA_BLOB *lm_resp,
1160 const DATA_BLOB *nt_resp,
1161 struct netr_SamInfo3 **pinfo3)
1163 struct auth_usersupplied_info *user_info = NULL;
1164 struct tsocket_address *local;
1165 NTSTATUS status;
1166 int rc;
1168 rc = tsocket_address_inet_from_strings(mem_ctx,
1169 "ip",
1170 "127.0.0.1",
1172 &local);
1173 if (rc < 0) {
1174 return NT_STATUS_NO_MEMORY;
1176 status = make_user_info(&user_info, user, user, domain, domain,
1177 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1178 NULL, AUTH_PASSWORD_RESPONSE);
1179 if (!NT_STATUS_IS_OK(status)) {
1180 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1181 return status;
1183 user_info->logon_parameters = logon_parameters;
1185 /* We don't want any more mapping of the username */
1186 user_info->mapped_state = True;
1188 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1189 pinfo3);
1190 free_user_info(&user_info);
1191 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1192 user, nt_errstr(status)));
1193 return status;
1196 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1197 TALLOC_CTX *mem_ctx,
1198 uint32_t logon_parameters,
1199 const char *server,
1200 const char *username,
1201 const char *domainname,
1202 const char *workstation,
1203 const uint8_t chal[8],
1204 DATA_BLOB lm_response,
1205 DATA_BLOB nt_response,
1206 struct netr_SamInfo3 **info3)
1208 int attempts = 0;
1209 int netr_attempts = 0;
1210 bool retry = false;
1211 NTSTATUS result;
1213 do {
1214 struct rpc_pipe_client *netlogon_pipe;
1215 const struct pipe_auth_data *auth;
1216 uint32_t neg_flags = 0;
1218 ZERO_STRUCTP(info3);
1219 retry = false;
1221 result = cm_connect_netlogon(domain, &netlogon_pipe);
1223 if (!NT_STATUS_IS_OK(result)) {
1224 DEBUG(3,("Could not open handle to NETLOGON pipe "
1225 "(error: %s, attempts: %d)\n",
1226 nt_errstr(result), netr_attempts));
1228 /* After the first retry always close the connection */
1229 if (netr_attempts > 0) {
1230 DEBUG(3, ("This is again a problem for this "
1231 "particular call, forcing the close "
1232 "of this connection\n"));
1233 invalidate_cm_connection(&domain->conn);
1236 /* After the second retry failover to the next DC */
1237 if (netr_attempts > 1) {
1239 * If the netlogon server is not reachable then
1240 * it is possible that the DC is rebuilding
1241 * sysvol and shutdown netlogon for that time.
1242 * We should failover to the next dc.
1244 DEBUG(3, ("This is the third problem for this "
1245 "particular call, adding DC to the "
1246 "negative cache list\n"));
1247 add_failed_connection_entry(domain->name,
1248 domain->dcname,
1249 result);
1250 saf_delete(domain->name);
1253 /* Only allow 3 retries */
1254 if (netr_attempts < 3) {
1255 DEBUG(3, ("The connection to netlogon "
1256 "failed, retrying\n"));
1257 netr_attempts++;
1258 retry = true;
1259 continue;
1261 return result;
1263 netr_attempts = 0;
1265 auth = netlogon_pipe->auth;
1266 if (netlogon_pipe->dc) {
1267 neg_flags = netlogon_pipe->dc->negotiate_flags;
1270 /* It is really important to try SamLogonEx here,
1271 * because in a clustered environment, we want to use
1272 * one machine account from multiple physical
1273 * computers.
1275 * With a normal SamLogon call, we must keep the
1276 * credentials chain updated and intact between all
1277 * users of the machine account (which would imply
1278 * cross-node communication for every NTLM logon).
1280 * (The credentials chain is not per NETLOGON pipe
1281 * connection, but globally on the server/client pair
1282 * by machine name).
1284 * When using SamLogonEx, the credentials are not
1285 * supplied, but the session key is implied by the
1286 * wrapping SamLogon context.
1288 * -- abartlet 21 April 2008
1290 * It's also important to use NetlogonValidationSamInfo4 (6),
1291 * because it relies on the rpc transport encryption
1292 * and avoids using the global netlogon schannel
1293 * session key to en/decrypt secret information
1294 * like the user_session_key for network logons.
1296 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1297 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1298 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1299 * are the indication that the server supports
1300 * NetlogonValidationSamInfo4 (6). And it must only
1301 * be used if "SealSecureChannel" is used.
1303 * -- metze 4 February 2011
1306 if (auth == NULL) {
1307 domain->can_do_validation6 = false;
1308 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1309 domain->can_do_validation6 = false;
1310 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1311 domain->can_do_validation6 = false;
1312 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1313 domain->can_do_validation6 = false;
1314 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1315 domain->can_do_validation6 = false;
1318 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1319 result = rpccli_netlogon_sam_network_logon_ex(
1320 netlogon_pipe,
1321 mem_ctx,
1322 logon_parameters,
1323 server, /* server name */
1324 username, /* user name */
1325 domainname, /* target domain */
1326 workstation, /* workstation */
1327 chal,
1329 lm_response,
1330 nt_response,
1331 info3);
1332 } else {
1333 result = rpccli_netlogon_sam_network_logon(
1334 netlogon_pipe,
1335 mem_ctx,
1336 logon_parameters,
1337 server, /* server name */
1338 username, /* user name */
1339 domainname, /* target domain */
1340 workstation, /* workstation */
1341 chal,
1342 domain->can_do_validation6 ? 6 : 3,
1343 lm_response,
1344 nt_response,
1345 info3);
1348 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1351 * It's likely that the server also does not support
1352 * validation level 6
1354 domain->can_do_validation6 = false;
1356 if (domain->can_do_samlogon_ex) {
1357 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1358 "retrying with NetSamLogon\n"));
1359 domain->can_do_samlogon_ex = false;
1360 retry = true;
1361 continue;
1365 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1366 * (no Ex). This happens against old Samba
1367 * DCs. Drop the connection.
1369 invalidate_cm_connection(&domain->conn);
1370 result = NT_STATUS_LOGON_FAILURE;
1371 break;
1374 if (domain->can_do_validation6 &&
1375 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1376 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1377 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1378 DEBUG(3,("Got a DC that can not do validation level 6, "
1379 "retrying with level 3\n"));
1380 domain->can_do_validation6 = false;
1381 retry = true;
1382 continue;
1386 * we increment this after the "feature negotiation"
1387 * for can_do_samlogon_ex and can_do_validation6
1389 attempts += 1;
1391 /* We have to try a second time as cm_connect_netlogon
1392 might not yet have noticed that the DC has killed
1393 our connection. */
1395 if (!rpccli_is_connected(netlogon_pipe)) {
1396 retry = true;
1397 continue;
1400 /* if we get access denied, a possible cause was that we had
1401 and open connection to the DC, but someone changed our
1402 machine account password out from underneath us using 'net
1403 rpc changetrustpw' */
1405 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1406 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1407 "ACCESS_DENIED. Maybe the trust account "
1408 "password was changed and we didn't know it. "
1409 "Killing connections to domain %s\n",
1410 domainname));
1411 invalidate_cm_connection(&domain->conn);
1412 retry = true;
1415 } while ( (attempts < 2) && retry );
1417 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1418 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1419 "returned NT_STATUS_IO_TIMEOUT after the retry."
1420 "Killing connections to domain %s\n",
1421 domainname));
1422 invalidate_cm_connection(&domain->conn);
1424 return result;
1427 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1428 struct winbindd_domain *domain,
1429 const char *user,
1430 const char *pass,
1431 uint32_t request_flags,
1432 struct netr_SamInfo3 **info3)
1435 uchar chal[8];
1436 DATA_BLOB lm_resp;
1437 DATA_BLOB nt_resp;
1438 unsigned char local_nt_response[24];
1439 fstring name_domain, name_user;
1440 NTSTATUS result;
1441 struct netr_SamInfo3 *my_info3 = NULL;
1443 *info3 = NULL;
1445 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1447 /* Parse domain and username */
1449 parse_domain_user(user, name_domain, name_user);
1451 /* do password magic */
1453 generate_random_buffer(chal, sizeof(chal));
1455 if (lp_client_ntlmv2_auth()) {
1456 DATA_BLOB server_chal;
1457 DATA_BLOB names_blob;
1458 server_chal = data_blob_const(chal, 8);
1460 /* note that the 'workgroup' here is for the local
1461 machine. The 'server name' must match the
1462 'workstation' passed to the actual SamLogon call.
1464 names_blob = NTLMv2_generate_names_blob(
1465 mem_ctx, lp_netbios_name(), lp_workgroup());
1467 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1468 pass,
1469 &server_chal,
1470 &names_blob,
1471 &lm_resp, &nt_resp, NULL, NULL)) {
1472 data_blob_free(&names_blob);
1473 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1474 result = NT_STATUS_NO_MEMORY;
1475 goto done;
1477 data_blob_free(&names_blob);
1478 } else {
1479 lm_resp = data_blob_null;
1480 SMBNTencrypt(pass, chal, local_nt_response);
1482 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1483 sizeof(local_nt_response));
1486 if (strequal(name_domain, get_global_sam_name())) {
1487 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1489 result = winbindd_dual_auth_passdb(
1490 mem_ctx, 0, name_domain, name_user,
1491 &chal_blob, &lm_resp, &nt_resp, info3);
1492 goto done;
1495 /* check authentication loop */
1497 result = winbind_samlogon_retry_loop(domain,
1498 mem_ctx,
1500 domain->dcname,
1501 name_user,
1502 name_domain,
1503 lp_netbios_name(),
1504 chal,
1505 lm_resp,
1506 nt_resp,
1507 &my_info3);
1508 if (!NT_STATUS_IS_OK(result)) {
1509 goto done;
1512 /* handle the case where a NT4 DC does not fill in the acct_flags in
1513 * the samlogon reply info3. When accurate info3 is required by the
1514 * caller, we look up the account flags ourselve - gd */
1516 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1517 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1519 struct rpc_pipe_client *samr_pipe;
1520 struct policy_handle samr_domain_handle, user_pol;
1521 union samr_UserInfo *info = NULL;
1522 NTSTATUS status_tmp, result_tmp;
1523 uint32 acct_flags;
1524 struct dcerpc_binding_handle *b;
1526 status_tmp = cm_connect_sam(domain, mem_ctx,
1527 &samr_pipe, &samr_domain_handle);
1529 if (!NT_STATUS_IS_OK(status_tmp)) {
1530 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1531 nt_errstr(status_tmp)));
1532 goto done;
1535 b = samr_pipe->binding_handle;
1537 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1538 &samr_domain_handle,
1539 MAXIMUM_ALLOWED_ACCESS,
1540 my_info3->base.rid,
1541 &user_pol,
1542 &result_tmp);
1544 if (!NT_STATUS_IS_OK(status_tmp)) {
1545 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1546 nt_errstr(status_tmp)));
1547 goto done;
1549 if (!NT_STATUS_IS_OK(result_tmp)) {
1550 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1551 nt_errstr(result_tmp)));
1552 goto done;
1555 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1556 &user_pol,
1558 &info,
1559 &result_tmp);
1561 if (!NT_STATUS_IS_OK(status_tmp)) {
1562 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1563 nt_errstr(status_tmp)));
1564 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1565 goto done;
1567 if (!NT_STATUS_IS_OK(result_tmp)) {
1568 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1569 nt_errstr(result_tmp)));
1570 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1571 goto done;
1574 acct_flags = info->info16.acct_flags;
1576 if (acct_flags == 0) {
1577 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1578 goto done;
1581 my_info3->base.acct_flags = acct_flags;
1583 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1585 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1588 *info3 = my_info3;
1589 done:
1590 return result;
1593 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1594 struct winbindd_cli_state *state)
1596 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1597 NTSTATUS krb5_result = NT_STATUS_OK;
1598 fstring name_domain, name_user;
1599 char *mapped_user;
1600 fstring domain_user;
1601 struct netr_SamInfo3 *info3 = NULL;
1602 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1604 /* Ensure null termination */
1605 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1607 /* Ensure null termination */
1608 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1610 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1611 state->request->data.auth.user));
1613 /* Parse domain and username */
1615 name_map_status = normalize_name_unmap(state->mem_ctx,
1616 state->request->data.auth.user,
1617 &mapped_user);
1619 /* If the name normalization didnt' actually do anything,
1620 just use the original name */
1622 if (!NT_STATUS_IS_OK(name_map_status) &&
1623 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1625 mapped_user = state->request->data.auth.user;
1628 parse_domain_user(mapped_user, name_domain, name_user);
1630 if ( mapped_user != state->request->data.auth.user ) {
1631 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1632 *lp_winbind_separator(),
1633 name_user );
1634 strlcpy( state->request->data.auth.user, domain_user,
1635 sizeof(state->request->data.auth.user));
1638 if (!domain->online) {
1639 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1640 if (domain->startup) {
1641 /* Logons are very important to users. If we're offline and
1642 we get a request within the first 30 seconds of startup,
1643 try very hard to find a DC and go online. */
1645 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1646 "request in startup mode.\n", domain->name ));
1648 winbindd_flush_negative_conn_cache(domain);
1649 result = init_dc_connection(domain);
1653 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1655 /* Check for Kerberos authentication */
1656 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1658 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1659 /* save for later */
1660 krb5_result = result;
1663 if (NT_STATUS_IS_OK(result)) {
1664 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1665 goto process_result;
1666 } else {
1667 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1670 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1671 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1672 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1673 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1674 set_domain_offline( domain );
1675 goto cached_logon;
1678 /* there are quite some NT_STATUS errors where there is no
1679 * point in retrying with a samlogon, we explictly have to take
1680 * care not to increase the bad logon counter on the DC */
1682 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1683 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1684 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1685 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1686 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1687 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1688 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1689 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1690 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1691 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1692 goto done;
1695 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1696 DEBUG(3,("falling back to samlogon\n"));
1697 goto sam_logon;
1698 } else {
1699 goto cached_logon;
1703 sam_logon:
1704 /* Check for Samlogon authentication */
1705 if (domain->online) {
1706 result = winbindd_dual_pam_auth_samlogon(
1707 state->mem_ctx, domain,
1708 state->request->data.auth.user,
1709 state->request->data.auth.pass,
1710 state->request->flags,
1711 &info3);
1713 if (NT_STATUS_IS_OK(result)) {
1714 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1715 /* add the Krb5 err if we have one */
1716 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1717 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1719 goto process_result;
1722 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1723 nt_errstr(result)));
1725 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1726 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1727 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1729 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1730 set_domain_offline( domain );
1731 goto cached_logon;
1734 if (domain->online) {
1735 /* We're still online - fail. */
1736 goto done;
1740 cached_logon:
1741 /* Check for Cached logons */
1742 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1743 lp_winbind_offline_logon()) {
1745 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1747 if (NT_STATUS_IS_OK(result)) {
1748 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1749 goto process_result;
1750 } else {
1751 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1752 goto done;
1756 process_result:
1758 if (NT_STATUS_IS_OK(result)) {
1760 struct dom_sid user_sid;
1762 /* In all codepaths where result == NT_STATUS_OK info3 must have
1763 been initialized. */
1764 if (!info3) {
1765 result = NT_STATUS_INTERNAL_ERROR;
1766 goto done;
1769 sid_compose(&user_sid, info3->base.domain_sid,
1770 info3->base.rid);
1772 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1773 &user_sid);
1774 netsamlogon_cache_store(name_user, info3);
1776 /* save name_to_sid info as early as possible (only if
1777 this is our primary domain so we don't invalidate
1778 the cache entry by storing the seq_num for the wrong
1779 domain). */
1780 if ( domain->primary ) {
1781 cache_name2sid(domain, name_domain, name_user,
1782 SID_NAME_USER, &user_sid);
1785 /* Check if the user is in the right group */
1787 result = check_info3_in_group(
1788 info3,
1789 state->request->data.auth.require_membership_of_sid);
1790 if (!NT_STATUS_IS_OK(result)) {
1791 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1792 state->request->data.auth.user,
1793 state->request->data.auth.require_membership_of_sid));
1794 goto done;
1797 result = append_auth_data(state->mem_ctx, state->response,
1798 state->request->flags, info3,
1799 name_domain, name_user);
1800 if (!NT_STATUS_IS_OK(result)) {
1801 goto done;
1804 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1805 && lp_winbind_offline_logon()) {
1807 result = winbindd_store_creds(domain,
1808 state->request->data.auth.user,
1809 state->request->data.auth.pass,
1810 info3);
1813 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1814 struct winbindd_domain *our_domain = find_our_domain();
1816 /* This is not entirely correct I believe, but it is
1817 consistent. Only apply the password policy settings
1818 too warn users for our own domain. Cannot obtain these
1819 from trusted DCs all the time so don't do it at all.
1820 -- jerry */
1822 result = NT_STATUS_NOT_SUPPORTED;
1823 if (our_domain == domain ) {
1824 result = fillup_password_policy(
1825 our_domain, state->response);
1828 if (!NT_STATUS_IS_OK(result)
1829 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1831 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1832 domain->name, nt_errstr(result)));
1833 goto done;
1837 result = NT_STATUS_OK;
1840 done:
1841 /* give us a more useful (more correct?) error code */
1842 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1843 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1844 result = NT_STATUS_NO_LOGON_SERVERS;
1847 set_auth_errors(state->response, result);
1849 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1850 state->request->data.auth.user,
1851 state->response->data.auth.nt_status_string,
1852 state->response->data.auth.pam_error));
1854 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1857 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1858 struct winbindd_cli_state *state)
1860 NTSTATUS result;
1861 struct netr_SamInfo3 *info3 = NULL;
1862 const char *name_user = NULL;
1863 const char *name_domain = NULL;
1864 const char *workstation;
1866 DATA_BLOB lm_resp, nt_resp;
1868 /* This is child-only, so no check for privileged access is needed
1869 anymore */
1871 /* Ensure null termination */
1872 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1873 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1875 name_user = state->request->data.auth_crap.user;
1876 name_domain = state->request->data.auth_crap.domain;
1877 workstation = state->request->data.auth_crap.workstation;
1879 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1880 name_domain, name_user));
1882 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1883 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1884 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1885 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1886 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1887 state->request->data.auth_crap.lm_resp_len,
1888 state->request->data.auth_crap.nt_resp_len));
1889 result = NT_STATUS_INVALID_PARAMETER;
1890 goto done;
1894 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1895 state->request->data.auth_crap.lm_resp_len);
1897 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1898 nt_resp = data_blob_talloc(state->mem_ctx,
1899 state->request->extra_data.data,
1900 state->request->data.auth_crap.nt_resp_len);
1901 } else {
1902 nt_resp = data_blob_talloc(state->mem_ctx,
1903 state->request->data.auth_crap.nt_resp,
1904 state->request->data.auth_crap.nt_resp_len);
1907 if (strequal(name_domain, get_global_sam_name())) {
1908 DATA_BLOB chal_blob = data_blob_const(
1909 state->request->data.auth_crap.chal,
1910 sizeof(state->request->data.auth_crap.chal));
1912 result = winbindd_dual_auth_passdb(
1913 state->mem_ctx,
1914 state->request->data.auth_crap.logon_parameters,
1915 name_domain, name_user,
1916 &chal_blob, &lm_resp, &nt_resp, &info3);
1917 goto process_result;
1920 result = winbind_samlogon_retry_loop(domain,
1921 state->mem_ctx,
1922 state->request->data.auth_crap.logon_parameters,
1923 domain->dcname,
1924 name_user,
1925 name_domain,
1926 /* Bug #3248 - found by Stefan Burkei. */
1927 workstation, /* We carefully set this above so use it... */
1928 state->request->data.auth_crap.chal,
1929 lm_resp,
1930 nt_resp,
1931 &info3);
1932 if (!NT_STATUS_IS_OK(result)) {
1933 goto done;
1936 process_result:
1938 if (NT_STATUS_IS_OK(result)) {
1939 struct dom_sid user_sid;
1941 sid_compose(&user_sid, info3->base.domain_sid,
1942 info3->base.rid);
1943 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1944 &user_sid);
1945 netsamlogon_cache_store(name_user, info3);
1947 /* Check if the user is in the right group */
1949 result = check_info3_in_group(
1950 info3,
1951 state->request->data.auth_crap.require_membership_of_sid);
1952 if (!NT_STATUS_IS_OK(result)) {
1953 DEBUG(3, ("User %s is not in the required group (%s), so "
1954 "crap authentication is rejected\n",
1955 state->request->data.auth_crap.user,
1956 state->request->data.auth_crap.require_membership_of_sid));
1957 goto done;
1960 result = append_auth_data(state->mem_ctx, state->response,
1961 state->request->flags, info3,
1962 name_domain, name_user);
1963 if (!NT_STATUS_IS_OK(result)) {
1964 goto done;
1968 done:
1970 /* give us a more useful (more correct?) error code */
1971 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1972 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1973 result = NT_STATUS_NO_LOGON_SERVERS;
1976 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1977 result = nt_status_squash(result);
1980 set_auth_errors(state->response, result);
1982 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1983 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1984 name_domain,
1985 name_user,
1986 state->response->data.auth.nt_status_string,
1987 state->response->data.auth.pam_error));
1989 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1992 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1993 struct winbindd_cli_state *state)
1995 char *oldpass;
1996 char *newpass = NULL;
1997 struct policy_handle dom_pol;
1998 struct rpc_pipe_client *cli = NULL;
1999 bool got_info = false;
2000 struct samr_DomInfo1 *info = NULL;
2001 struct userPwdChangeFailureInformation *reject = NULL;
2002 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2003 fstring domain, user;
2004 struct dcerpc_binding_handle *b = NULL;
2006 ZERO_STRUCT(dom_pol);
2008 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2009 state->request->data.auth.user));
2011 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2012 goto done;
2015 /* Change password */
2017 oldpass = state->request->data.chauthtok.oldpass;
2018 newpass = state->request->data.chauthtok.newpass;
2020 /* Initialize reject reason */
2021 state->response->data.auth.reject_reason = Undefined;
2023 /* Get sam handle */
2025 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2026 &dom_pol);
2027 if (!NT_STATUS_IS_OK(result)) {
2028 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2029 goto done;
2032 b = cli->binding_handle;
2034 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2035 user,
2036 newpass,
2037 oldpass,
2038 &info,
2039 &reject);
2041 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2043 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2045 fill_in_password_policy(state->response, info);
2047 state->response->data.auth.reject_reason =
2048 reject->extendedFailureReason;
2050 got_info = true;
2053 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2054 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2055 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2056 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2058 /* only fallback when the chgpasswd_user3 call is not supported */
2059 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2060 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2061 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2062 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2064 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2065 nt_errstr(result)));
2067 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2069 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2070 Map to the same status code as Windows 2003. */
2072 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2073 result = NT_STATUS_PASSWORD_RESTRICTION;
2077 done:
2079 if (NT_STATUS_IS_OK(result)
2080 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2081 && lp_winbind_offline_logon()) {
2082 result = winbindd_update_creds_by_name(contact_domain, user,
2083 newpass);
2084 /* Again, this happens when we login from gdm or xdm
2085 * and the password expires, *BUT* cached crendentials
2086 * doesn't exist. winbindd_update_creds_by_name()
2087 * returns NT_STATUS_NO_SUCH_USER.
2088 * This is not a failure.
2089 * --- BoYang
2090 * */
2091 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2092 result = NT_STATUS_OK;
2095 if (!NT_STATUS_IS_OK(result)) {
2096 DEBUG(10, ("Failed to store creds: %s\n",
2097 nt_errstr(result)));
2098 goto process_result;
2102 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2104 NTSTATUS policy_ret;
2106 policy_ret = fillup_password_policy(
2107 contact_domain, state->response);
2109 /* failure of this is non critical, it will just provide no
2110 * additional information to the client why the change has
2111 * failed - Guenther */
2113 if (!NT_STATUS_IS_OK(policy_ret)) {
2114 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2115 goto process_result;
2119 process_result:
2121 if (strequal(contact_domain->name, get_global_sam_name())) {
2122 /* FIXME: internal rpc pipe does not cache handles yet */
2123 if (b) {
2124 if (is_valid_policy_hnd(&dom_pol)) {
2125 NTSTATUS _result;
2126 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2128 TALLOC_FREE(cli);
2132 set_auth_errors(state->response, result);
2134 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2135 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2136 domain,
2137 user,
2138 state->response->data.auth.nt_status_string,
2139 state->response->data.auth.pam_error));
2141 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2144 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2145 struct winbindd_cli_state *state)
2147 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2149 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2150 state->request->data.logoff.user));
2152 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2153 result = NT_STATUS_OK;
2154 goto process_result;
2157 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2158 result = NT_STATUS_OK;
2159 goto process_result;
2162 #ifdef HAVE_KRB5
2164 if (state->request->data.logoff.uid < 0) {
2165 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2166 goto process_result;
2169 /* what we need here is to find the corresponding krb5 ccache name *we*
2170 * created for a given username and destroy it */
2172 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2173 result = NT_STATUS_OK;
2174 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2175 goto process_result;
2178 if (!ccache_entry_identical(state->request->data.logoff.user,
2179 state->request->data.logoff.uid,
2180 state->request->data.logoff.krb5ccname)) {
2181 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2182 goto process_result;
2185 result = remove_ccache(state->request->data.logoff.user);
2186 if (!NT_STATUS_IS_OK(result)) {
2187 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2188 nt_errstr(result)));
2189 goto process_result;
2193 * Remove any mlock'ed memory creds in the child
2194 * we might be using for krb5 ticket renewal.
2197 winbindd_delete_memory_creds(state->request->data.logoff.user);
2199 #else
2200 result = NT_STATUS_NOT_SUPPORTED;
2201 #endif
2203 process_result:
2206 set_auth_errors(state->response, result);
2208 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2211 /* Change user password with auth crap*/
2213 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2215 NTSTATUS result;
2216 DATA_BLOB new_nt_password;
2217 DATA_BLOB old_nt_hash_enc;
2218 DATA_BLOB new_lm_password;
2219 DATA_BLOB old_lm_hash_enc;
2220 fstring domain,user;
2221 struct policy_handle dom_pol;
2222 struct winbindd_domain *contact_domain = domainSt;
2223 struct rpc_pipe_client *cli = NULL;
2224 struct dcerpc_binding_handle *b = NULL;
2226 ZERO_STRUCT(dom_pol);
2228 /* Ensure null termination */
2229 state->request->data.chng_pswd_auth_crap.user[
2230 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2231 state->request->data.chng_pswd_auth_crap.domain[
2232 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2233 *domain = 0;
2234 *user = 0;
2236 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2237 (unsigned long)state->pid,
2238 state->request->data.chng_pswd_auth_crap.domain,
2239 state->request->data.chng_pswd_auth_crap.user));
2241 if (lp_winbind_offline_logon()) {
2242 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2243 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2244 result = NT_STATUS_ACCESS_DENIED;
2245 goto done;
2248 if (*state->request->data.chng_pswd_auth_crap.domain) {
2249 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2250 } else {
2251 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2252 domain, user);
2254 if(!*domain) {
2255 DEBUG(3,("no domain specified with username (%s) - "
2256 "failing auth\n",
2257 state->request->data.chng_pswd_auth_crap.user));
2258 result = NT_STATUS_NO_SUCH_USER;
2259 goto done;
2263 if (!*domain && lp_winbind_use_default_domain()) {
2264 fstrcpy(domain,lp_workgroup());
2267 if(!*user) {
2268 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2271 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2272 (unsigned long)state->pid, domain, user));
2274 /* Change password */
2275 new_nt_password = data_blob_const(
2276 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2277 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2279 old_nt_hash_enc = data_blob_const(
2280 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2281 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2283 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2284 new_lm_password = data_blob_const(
2285 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2286 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2288 old_lm_hash_enc = data_blob_const(
2289 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2290 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2291 } else {
2292 new_lm_password = data_blob_null;
2293 old_lm_hash_enc = data_blob_null;
2296 /* Get sam handle */
2298 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2299 if (!NT_STATUS_IS_OK(result)) {
2300 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2301 goto done;
2304 b = cli->binding_handle;
2306 result = rpccli_samr_chng_pswd_auth_crap(
2307 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2308 new_lm_password, old_lm_hash_enc);
2310 done:
2312 if (strequal(contact_domain->name, get_global_sam_name())) {
2313 /* FIXME: internal rpc pipe does not cache handles yet */
2314 if (b) {
2315 if (is_valid_policy_hnd(&dom_pol)) {
2316 NTSTATUS _result;
2317 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2319 TALLOC_FREE(cli);
2323 set_auth_errors(state->response, result);
2325 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2326 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2327 domain, user,
2328 state->response->data.auth.nt_status_string,
2329 state->response->data.auth.pam_error));
2331 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2334 #ifdef HAVE_KRB5
2335 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2336 struct PAC_LOGON_INFO **logon_info)
2338 krb5_context krbctx = NULL;
2339 krb5_error_code k5ret;
2340 krb5_keytab keytab;
2341 krb5_kt_cursor cursor;
2342 krb5_keytab_entry entry;
2343 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2345 ZERO_STRUCT(entry);
2346 ZERO_STRUCT(cursor);
2348 k5ret = krb5_init_context(&krbctx);
2349 if (k5ret) {
2350 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2351 error_message(k5ret)));
2352 status = krb5_to_nt_status(k5ret);
2353 goto out;
2356 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2357 if (k5ret) {
2358 DEBUG(1, ("Failed to get keytab: %s\n",
2359 error_message(k5ret)));
2360 status = krb5_to_nt_status(k5ret);
2361 goto out_free;
2364 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2365 if (k5ret) {
2366 DEBUG(1, ("Failed to start seq: %s\n",
2367 error_message(k5ret)));
2368 status = krb5_to_nt_status(k5ret);
2369 goto out_keytab;
2372 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2373 while (k5ret == 0) {
2374 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2375 krbctx, NULL,
2376 KRB5_KT_KEY(&entry), NULL, 0,
2377 logon_info);
2378 if (NT_STATUS_IS_OK(status)) {
2379 break;
2381 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2382 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2385 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2386 if (k5ret) {
2387 DEBUG(1, ("Failed to end seq: %s\n",
2388 error_message(k5ret)));
2390 out_keytab:
2391 k5ret = krb5_kt_close(krbctx, keytab);
2392 if (k5ret) {
2393 DEBUG(1, ("Failed to close keytab: %s\n",
2394 error_message(k5ret)));
2396 out_free:
2397 krb5_free_context(krbctx);
2398 out:
2399 return status;
2402 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2403 struct netr_SamInfo3 **info3)
2405 struct winbindd_request *req = state->request;
2406 DATA_BLOB pac_blob;
2407 struct PAC_LOGON_INFO *logon_info = NULL;
2408 NTSTATUS result;
2410 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2411 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2412 if (!NT_STATUS_IS_OK(result) &&
2413 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2414 DEBUG(1, ("Error during PAC signature verification: %s\n",
2415 nt_errstr(result)));
2416 return result;
2419 if (logon_info) {
2420 /* Signature verification succeeded, trust the PAC */
2421 netsamlogon_cache_store(NULL, &logon_info->info3);
2423 } else {
2424 /* Try without signature verification */
2425 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2426 NULL, NULL, NULL, 0,
2427 &logon_info);
2428 if (!NT_STATUS_IS_OK(result)) {
2429 DEBUG(10, ("Could not extract PAC: %s\n",
2430 nt_errstr(result)));
2431 return result;
2435 *info3 = &logon_info->info3;
2437 return NT_STATUS_OK;
2439 #else /* HAVE_KRB5 */
2440 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2441 struct netr_SamInfo3 **info3)
2443 return NT_STATUS_NO_SUCH_USER;
2445 #endif /* HAVE_KRB5 */