s3:winbindd: make use of rpccli_netlogon_network_logon()
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob3f3ec7090f27927f701ff5eb71489965bace6bfd
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 if (domain->alt_name == NULL) {
583 return NT_STATUS_INVALID_PARAMETER;
586 /* 1st step:
587 * prepare a krb5_cc_cache string for the user */
589 if (uid == -1) {
590 DEBUG(0,("no valid uid\n"));
593 cc = generate_krb5_ccache(mem_ctx,
594 krb5_cc_type,
595 uid,
596 &user_ccache_file);
597 if (cc == NULL) {
598 return NT_STATUS_NO_MEMORY;
602 /* 2nd step:
603 * get kerberos properties */
605 if (domain->private_data) {
606 ads = (ADS_STRUCT *)domain->private_data;
607 time_offset = ads->auth.time_offset;
611 /* 3rd step:
612 * do kerberos auth and setup ccache as the user */
614 parse_domain_user(user, name_domain, name_user);
616 realm = talloc_strdup(mem_ctx, domain->alt_name);
617 if (realm == NULL) {
618 return NT_STATUS_NO_MEMORY;
621 if (!strupper_m(realm)) {
622 return NT_STATUS_INVALID_PARAMETER;
625 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
626 if (principal_s == NULL) {
627 return NT_STATUS_NO_MEMORY;
630 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
631 if (service == NULL) {
632 return NT_STATUS_NO_MEMORY;
635 /* if this is a user ccache, we need to act as the user to let the krb5
636 * library handle the chown, etc. */
638 /************************ ENTERING NON-ROOT **********************/
640 if (user_ccache_file != NULL) {
641 set_effective_uid(uid);
642 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
645 result = kerberos_return_pac(mem_ctx,
646 principal_s,
647 pass,
648 time_offset,
649 &ticket_lifetime,
650 &renewal_until,
652 true,
653 true,
654 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
655 NULL,
656 &logon_info);
657 if (user_ccache_file != NULL) {
658 gain_root_privilege();
661 /************************ RETURNED TO ROOT **********************/
663 if (!NT_STATUS_IS_OK(result)) {
664 goto failed;
667 *info3 = &logon_info->info3;
669 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
670 principal_s));
672 /* if we had a user's ccache then return that string for the pam
673 * environment */
675 if (user_ccache_file != NULL) {
677 fstrcpy(krb5ccname, user_ccache_file);
679 result = add_ccache_to_list(principal_s,
681 service,
682 user,
683 pass,
684 realm,
685 uid,
686 time(NULL),
687 ticket_lifetime,
688 renewal_until,
689 false);
691 if (!NT_STATUS_IS_OK(result)) {
692 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
693 nt_errstr(result)));
695 } else {
697 /* need to delete the memory cred cache, it is not used anymore */
699 krb5_ret = ads_kdestroy(cc);
700 if (krb5_ret) {
701 DEBUG(3,("winbindd_raw_kerberos_login: "
702 "could not destroy krb5 credential cache: "
703 "%s\n", error_message(krb5_ret)));
708 return NT_STATUS_OK;
710 failed:
712 * Do not delete an existing valid credential cache, if the user
713 * e.g. enters a wrong password
715 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
716 && user_ccache_file != NULL) {
717 return result;
720 /* we could have created a new credential cache with a valid tgt in it
721 * but we werent able to get or verify the service ticket for this
722 * local host and therefor didn't get the PAC, we need to remove that
723 * cache entirely now */
725 krb5_ret = ads_kdestroy(cc);
726 if (krb5_ret) {
727 DEBUG(3,("winbindd_raw_kerberos_login: "
728 "could not destroy krb5 credential cache: "
729 "%s\n", error_message(krb5_ret)));
732 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
733 DEBUG(3,("winbindd_raw_kerberos_login: "
734 "could not remove ccache for user %s\n",
735 user));
738 return result;
739 #else
740 return NT_STATUS_NOT_SUPPORTED;
741 #endif /* HAVE_KRB5 */
744 /****************************************************************
745 ****************************************************************/
747 bool check_request_flags(uint32_t flags)
749 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
750 WBFLAG_PAM_INFO3_TEXT |
751 WBFLAG_PAM_INFO3_NDR;
753 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
754 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
755 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
756 !(flags & flags_edata) ) {
757 return true;
760 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
761 flags));
763 return false;
766 /****************************************************************
767 ****************************************************************/
769 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
770 struct winbindd_response *resp,
771 uint32_t request_flags,
772 struct netr_SamInfo3 *info3,
773 const char *name_domain,
774 const char *name_user)
776 NTSTATUS result;
778 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
779 memcpy(resp->data.auth.user_session_key,
780 info3->base.key.key,
781 sizeof(resp->data.auth.user_session_key)
782 /* 16 */);
785 if (request_flags & WBFLAG_PAM_LMKEY) {
786 memcpy(resp->data.auth.first_8_lm_hash,
787 info3->base.LMSessKey.key,
788 sizeof(resp->data.auth.first_8_lm_hash)
789 /* 8 */);
792 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
793 result = append_unix_username(mem_ctx, resp,
794 info3, name_domain, name_user);
795 if (!NT_STATUS_IS_OK(result)) {
796 DEBUG(10,("Failed to append Unix Username: %s\n",
797 nt_errstr(result)));
798 return result;
802 /* currently, anything from here on potentially overwrites extra_data. */
804 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
805 result = append_info3_as_ndr(mem_ctx, resp, info3);
806 if (!NT_STATUS_IS_OK(result)) {
807 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
808 nt_errstr(result)));
809 return result;
813 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
814 result = append_info3_as_txt(mem_ctx, resp, info3);
815 if (!NT_STATUS_IS_OK(result)) {
816 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
817 nt_errstr(result)));
818 return result;
822 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
823 result = append_afs_token(mem_ctx, resp,
824 info3, name_domain, name_user);
825 if (!NT_STATUS_IS_OK(result)) {
826 DEBUG(10,("Failed to append AFS token: %s\n",
827 nt_errstr(result)));
828 return result;
832 return NT_STATUS_OK;
835 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
836 struct winbindd_cli_state *state,
837 struct netr_SamInfo3 **info3)
839 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
840 uint16 max_allowed_bad_attempts;
841 fstring name_domain, name_user;
842 struct dom_sid sid;
843 enum lsa_SidType type;
844 uchar new_nt_pass[NT_HASH_LEN];
845 const uint8 *cached_nt_pass;
846 const uint8 *cached_salt;
847 struct netr_SamInfo3 *my_info3;
848 time_t kickoff_time, must_change_time;
849 bool password_good = false;
850 #ifdef HAVE_KRB5
851 struct winbindd_tdc_domain *tdc_domain = NULL;
852 #endif
854 *info3 = NULL;
856 ZERO_STRUCTP(info3);
858 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
860 /* Parse domain and username */
862 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
865 if (!lookup_cached_name(name_domain,
866 name_user,
867 &sid,
868 &type)) {
869 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
870 return NT_STATUS_NO_SUCH_USER;
873 if (type != SID_NAME_USER) {
874 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
875 return NT_STATUS_LOGON_FAILURE;
878 result = winbindd_get_creds(domain,
879 state->mem_ctx,
880 &sid,
881 &my_info3,
882 &cached_nt_pass,
883 &cached_salt);
884 if (!NT_STATUS_IS_OK(result)) {
885 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
886 return result;
889 *info3 = my_info3;
891 E_md4hash(state->request->data.auth.pass, new_nt_pass);
893 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
894 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
895 if (cached_salt) {
896 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
899 if (cached_salt) {
900 /* In this case we didn't store the nt_hash itself,
901 but the MD5 combination of salt + nt_hash. */
902 uchar salted_hash[NT_HASH_LEN];
903 E_md5hash(cached_salt, new_nt_pass, salted_hash);
905 password_good = (memcmp(cached_nt_pass, salted_hash,
906 NT_HASH_LEN) == 0);
907 } else {
908 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
909 password_good = (memcmp(cached_nt_pass, new_nt_pass,
910 NT_HASH_LEN) == 0);
913 if (password_good) {
915 /* User *DOES* know the password, update logon_time and reset
916 * bad_pw_count */
918 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
920 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
921 return NT_STATUS_ACCOUNT_LOCKED_OUT;
924 if (my_info3->base.acct_flags & ACB_DISABLED) {
925 return NT_STATUS_ACCOUNT_DISABLED;
928 if (my_info3->base.acct_flags & ACB_WSTRUST) {
929 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
932 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
933 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
936 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
937 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
940 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
941 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
942 my_info3->base.acct_flags));
943 return NT_STATUS_LOGON_FAILURE;
946 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
947 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
948 return NT_STATUS_ACCOUNT_EXPIRED;
951 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
952 if (must_change_time != 0 && must_change_time < time(NULL)) {
953 /* we allow grace logons when the password has expired */
954 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
955 /* return NT_STATUS_PASSWORD_EXPIRED; */
956 goto success;
959 #ifdef HAVE_KRB5
960 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
961 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
962 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
963 /* used to cope with the case winbindd starting without network. */
964 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
966 uid_t uid = -1;
967 const char *cc = NULL;
968 char *realm = NULL;
969 const char *principal_s = NULL;
970 const char *service = NULL;
971 const char *user_ccache_file;
973 if (domain->alt_name == NULL) {
974 return NT_STATUS_INVALID_PARAMETER;
977 uid = get_uid_from_request(state->request);
978 if (uid == -1) {
979 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
980 return NT_STATUS_INVALID_PARAMETER;
983 cc = generate_krb5_ccache(state->mem_ctx,
984 state->request->data.auth.krb5_cc_type,
985 state->request->data.auth.uid,
986 &user_ccache_file);
987 if (cc == NULL) {
988 return NT_STATUS_NO_MEMORY;
991 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
992 if (realm == NULL) {
993 return NT_STATUS_NO_MEMORY;
996 if (!strupper_m(realm)) {
997 return NT_STATUS_INVALID_PARAMETER;
1000 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1001 if (principal_s == NULL) {
1002 return NT_STATUS_NO_MEMORY;
1005 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1006 if (service == NULL) {
1007 return NT_STATUS_NO_MEMORY;
1010 if (user_ccache_file != NULL) {
1012 fstrcpy(state->response->data.auth.krb5ccname,
1013 user_ccache_file);
1015 result = add_ccache_to_list(principal_s,
1017 service,
1018 state->request->data.auth.user,
1019 state->request->data.auth.pass,
1020 realm,
1021 uid,
1022 time(NULL),
1023 time(NULL) + lp_winbind_cache_time(),
1024 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1025 true);
1027 if (!NT_STATUS_IS_OK(result)) {
1028 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1029 "to add ccache to list: %s\n",
1030 nt_errstr(result)));
1034 #endif /* HAVE_KRB5 */
1035 success:
1036 /* FIXME: we possibly should handle logon hours as well (does xp when
1037 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1039 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1040 my_info3->base.bad_password_count = 0;
1042 result = winbindd_update_creds_by_info3(domain,
1043 state->request->data.auth.user,
1044 state->request->data.auth.pass,
1045 my_info3);
1046 if (!NT_STATUS_IS_OK(result)) {
1047 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1048 nt_errstr(result)));
1049 return result;
1052 return NT_STATUS_OK;
1056 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1057 if (domain->online == false) {
1058 goto failed;
1061 /* failure of this is not critical */
1062 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1063 if (!NT_STATUS_IS_OK(result)) {
1064 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1065 "Won't be able to honour account lockout policies\n"));
1068 /* increase counter */
1069 my_info3->base.bad_password_count++;
1071 if (max_allowed_bad_attempts == 0) {
1072 goto failed;
1075 /* lockout user */
1076 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1078 uint32 password_properties;
1080 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1081 if (!NT_STATUS_IS_OK(result)) {
1082 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1085 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1086 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1087 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1091 failed:
1092 result = winbindd_update_creds_by_info3(domain,
1093 state->request->data.auth.user,
1094 NULL,
1095 my_info3);
1097 if (!NT_STATUS_IS_OK(result)) {
1098 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1099 nt_errstr(result)));
1102 return NT_STATUS_LOGON_FAILURE;
1105 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1106 struct winbindd_cli_state *state,
1107 struct netr_SamInfo3 **info3)
1109 struct winbindd_domain *contact_domain;
1110 fstring name_domain, name_user;
1111 NTSTATUS result;
1113 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1115 /* Parse domain and username */
1117 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1119 /* what domain should we contact? */
1121 if ( IS_DC ) {
1122 if (!(contact_domain = find_domain_from_name(name_domain))) {
1123 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1124 state->request->data.auth.user, name_domain, name_user, name_domain));
1125 result = NT_STATUS_NO_SUCH_USER;
1126 goto done;
1129 } else {
1130 if (is_myname(name_domain)) {
1131 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1132 result = NT_STATUS_NO_SUCH_USER;
1133 goto done;
1136 contact_domain = find_domain_from_name(name_domain);
1137 if (contact_domain == NULL) {
1138 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1139 state->request->data.auth.user, name_domain, name_user, name_domain));
1141 result = NT_STATUS_NO_SUCH_USER;
1142 goto done;
1146 if (contact_domain->initialized &&
1147 contact_domain->active_directory) {
1148 goto try_login;
1151 if (!contact_domain->initialized) {
1152 init_dc_connection(contact_domain);
1155 if (!contact_domain->active_directory) {
1156 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1157 return NT_STATUS_INVALID_LOGON_TYPE;
1159 try_login:
1160 result = winbindd_raw_kerberos_login(
1161 state->mem_ctx, contact_domain,
1162 state->request->data.auth.user,
1163 state->request->data.auth.pass,
1164 state->request->data.auth.krb5_cc_type,
1165 get_uid_from_request(state->request),
1166 info3, state->response->data.auth.krb5ccname);
1167 done:
1168 return result;
1171 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1172 uint32_t logon_parameters,
1173 const char *domain, const char *user,
1174 const DATA_BLOB *challenge,
1175 const DATA_BLOB *lm_resp,
1176 const DATA_BLOB *nt_resp,
1177 struct netr_SamInfo3 **pinfo3)
1179 struct auth_usersupplied_info *user_info = NULL;
1180 struct tsocket_address *local;
1181 NTSTATUS status;
1182 int rc;
1184 rc = tsocket_address_inet_from_strings(mem_ctx,
1185 "ip",
1186 "127.0.0.1",
1188 &local);
1189 if (rc < 0) {
1190 return NT_STATUS_NO_MEMORY;
1192 status = make_user_info(&user_info, user, user, domain, domain,
1193 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1194 NULL, AUTH_PASSWORD_RESPONSE);
1195 if (!NT_STATUS_IS_OK(status)) {
1196 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1197 return status;
1199 user_info->logon_parameters = logon_parameters;
1201 /* We don't want any more mapping of the username */
1202 user_info->mapped_state = True;
1204 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1205 pinfo3);
1206 free_user_info(&user_info);
1207 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1208 user, nt_errstr(status)));
1209 return status;
1212 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1213 TALLOC_CTX *mem_ctx,
1214 uint32_t logon_parameters,
1215 const char *server,
1216 const char *username,
1217 const char *domainname,
1218 const char *workstation,
1219 const uint8_t chal[8],
1220 DATA_BLOB lm_response,
1221 DATA_BLOB nt_response,
1222 struct netr_SamInfo3 **info3)
1224 int attempts = 0;
1225 int netr_attempts = 0;
1226 bool retry = false;
1227 NTSTATUS result;
1229 do {
1230 struct rpc_pipe_client *netlogon_pipe;
1231 uint8_t authoritative = 0;
1232 uint32_t flags = 0;
1234 ZERO_STRUCTP(info3);
1235 retry = false;
1237 result = cm_connect_netlogon(domain, &netlogon_pipe);
1239 if (!NT_STATUS_IS_OK(result)) {
1240 DEBUG(3,("Could not open handle to NETLOGON pipe "
1241 "(error: %s, attempts: %d)\n",
1242 nt_errstr(result), netr_attempts));
1244 /* After the first retry always close the connection */
1245 if (netr_attempts > 0) {
1246 DEBUG(3, ("This is again a problem for this "
1247 "particular call, forcing the close "
1248 "of this connection\n"));
1249 invalidate_cm_connection(&domain->conn);
1252 /* After the second retry failover to the next DC */
1253 if (netr_attempts > 1) {
1255 * If the netlogon server is not reachable then
1256 * it is possible that the DC is rebuilding
1257 * sysvol and shutdown netlogon for that time.
1258 * We should failover to the next dc.
1260 DEBUG(3, ("This is the third problem for this "
1261 "particular call, adding DC to the "
1262 "negative cache list\n"));
1263 add_failed_connection_entry(domain->name,
1264 domain->dcname,
1265 result);
1266 saf_delete(domain->name);
1269 /* Only allow 3 retries */
1270 if (netr_attempts < 3) {
1271 DEBUG(3, ("The connection to netlogon "
1272 "failed, retrying\n"));
1273 netr_attempts++;
1274 retry = true;
1275 continue;
1277 return result;
1279 netr_attempts = 0;
1281 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1282 netlogon_pipe->binding_handle,
1283 mem_ctx,
1284 logon_parameters,
1285 username,
1286 domainname,
1287 workstation,
1288 chal,
1289 lm_response,
1290 nt_response,
1291 &authoritative,
1292 &flags,
1293 info3);
1296 * we increment this after the "feature negotiation"
1297 * for can_do_samlogon_ex and can_do_validation6
1299 attempts += 1;
1301 /* We have to try a second time as cm_connect_netlogon
1302 might not yet have noticed that the DC has killed
1303 our connection. */
1305 if (!rpccli_is_connected(netlogon_pipe)) {
1306 retry = true;
1307 continue;
1310 /* if we get access denied, a possible cause was that we had
1311 and open connection to the DC, but someone changed our
1312 machine account password out from underneath us using 'net
1313 rpc changetrustpw' */
1315 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1316 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1317 "ACCESS_DENIED. Maybe the trust account "
1318 "password was changed and we didn't know it. "
1319 "Killing connections to domain %s\n",
1320 domainname));
1321 invalidate_cm_connection(&domain->conn);
1322 retry = true;
1325 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1327 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1328 * (no Ex). This happens against old Samba
1329 * DCs, if LogonSamLogonEx() fails with an error
1330 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1332 * The server will log something like this:
1333 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1335 * This sets the whole connection into a fault_state mode
1336 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1338 * This also happens to our retry with LogonSamLogonWithFlags()
1339 * and LogonSamLogon().
1341 * In order to recover from this situation, we need to
1342 * drop the connection.
1344 invalidate_cm_connection(&domain->conn);
1345 result = NT_STATUS_LOGON_FAILURE;
1346 break;
1349 } while ( (attempts < 2) && retry );
1351 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1352 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1353 "returned NT_STATUS_IO_TIMEOUT after the retry."
1354 "Killing connections to domain %s\n",
1355 domainname));
1356 invalidate_cm_connection(&domain->conn);
1358 return result;
1361 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1362 struct winbindd_domain *domain,
1363 const char *user,
1364 const char *pass,
1365 uint32_t request_flags,
1366 struct netr_SamInfo3 **info3)
1369 uchar chal[8];
1370 DATA_BLOB lm_resp;
1371 DATA_BLOB nt_resp;
1372 unsigned char local_nt_response[24];
1373 fstring name_domain, name_user;
1374 NTSTATUS result;
1375 struct netr_SamInfo3 *my_info3 = NULL;
1377 *info3 = NULL;
1379 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1381 /* Parse domain and username */
1383 parse_domain_user(user, name_domain, name_user);
1385 /* do password magic */
1387 generate_random_buffer(chal, sizeof(chal));
1389 if (lp_client_ntlmv2_auth()) {
1390 DATA_BLOB server_chal;
1391 DATA_BLOB names_blob;
1392 server_chal = data_blob_const(chal, 8);
1394 /* note that the 'workgroup' here is for the local
1395 machine. The 'server name' must match the
1396 'workstation' passed to the actual SamLogon call.
1398 names_blob = NTLMv2_generate_names_blob(
1399 mem_ctx, lp_netbios_name(), lp_workgroup());
1401 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1402 pass,
1403 &server_chal,
1404 &names_blob,
1405 &lm_resp, &nt_resp, NULL, NULL)) {
1406 data_blob_free(&names_blob);
1407 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1408 result = NT_STATUS_NO_MEMORY;
1409 goto done;
1411 data_blob_free(&names_blob);
1412 } else {
1413 lm_resp = data_blob_null;
1414 SMBNTencrypt(pass, chal, local_nt_response);
1416 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1417 sizeof(local_nt_response));
1420 if (strequal(name_domain, get_global_sam_name())) {
1421 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1423 result = winbindd_dual_auth_passdb(
1424 mem_ctx, 0, name_domain, name_user,
1425 &chal_blob, &lm_resp, &nt_resp, info3);
1426 goto done;
1429 /* check authentication loop */
1431 result = winbind_samlogon_retry_loop(domain,
1432 mem_ctx,
1434 domain->dcname,
1435 name_user,
1436 name_domain,
1437 lp_netbios_name(),
1438 chal,
1439 lm_resp,
1440 nt_resp,
1441 &my_info3);
1442 if (!NT_STATUS_IS_OK(result)) {
1443 goto done;
1446 /* handle the case where a NT4 DC does not fill in the acct_flags in
1447 * the samlogon reply info3. When accurate info3 is required by the
1448 * caller, we look up the account flags ourselve - gd */
1450 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1451 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1453 struct rpc_pipe_client *samr_pipe;
1454 struct policy_handle samr_domain_handle, user_pol;
1455 union samr_UserInfo *info = NULL;
1456 NTSTATUS status_tmp, result_tmp;
1457 uint32 acct_flags;
1458 struct dcerpc_binding_handle *b;
1460 status_tmp = cm_connect_sam(domain, mem_ctx,
1461 &samr_pipe, &samr_domain_handle);
1463 if (!NT_STATUS_IS_OK(status_tmp)) {
1464 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1465 nt_errstr(status_tmp)));
1466 goto done;
1469 b = samr_pipe->binding_handle;
1471 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1472 &samr_domain_handle,
1473 MAXIMUM_ALLOWED_ACCESS,
1474 my_info3->base.rid,
1475 &user_pol,
1476 &result_tmp);
1478 if (!NT_STATUS_IS_OK(status_tmp)) {
1479 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1480 nt_errstr(status_tmp)));
1481 goto done;
1483 if (!NT_STATUS_IS_OK(result_tmp)) {
1484 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1485 nt_errstr(result_tmp)));
1486 goto done;
1489 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1490 &user_pol,
1492 &info,
1493 &result_tmp);
1495 if (!NT_STATUS_IS_OK(status_tmp)) {
1496 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1497 nt_errstr(status_tmp)));
1498 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1499 goto done;
1501 if (!NT_STATUS_IS_OK(result_tmp)) {
1502 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1503 nt_errstr(result_tmp)));
1504 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1505 goto done;
1508 acct_flags = info->info16.acct_flags;
1510 if (acct_flags == 0) {
1511 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1512 goto done;
1515 my_info3->base.acct_flags = acct_flags;
1517 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1519 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1522 *info3 = my_info3;
1523 done:
1524 return result;
1527 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1528 struct winbindd_cli_state *state)
1530 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1531 NTSTATUS krb5_result = NT_STATUS_OK;
1532 fstring name_domain, name_user;
1533 char *mapped_user;
1534 fstring domain_user;
1535 struct netr_SamInfo3 *info3 = NULL;
1536 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1538 /* Ensure null termination */
1539 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1541 /* Ensure null termination */
1542 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1544 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1545 state->request->data.auth.user));
1547 /* Parse domain and username */
1549 name_map_status = normalize_name_unmap(state->mem_ctx,
1550 state->request->data.auth.user,
1551 &mapped_user);
1553 /* If the name normalization didnt' actually do anything,
1554 just use the original name */
1556 if (!NT_STATUS_IS_OK(name_map_status) &&
1557 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1559 mapped_user = state->request->data.auth.user;
1562 parse_domain_user(mapped_user, name_domain, name_user);
1564 if ( mapped_user != state->request->data.auth.user ) {
1565 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1566 *lp_winbind_separator(),
1567 name_user );
1568 strlcpy( state->request->data.auth.user, domain_user,
1569 sizeof(state->request->data.auth.user));
1572 if (!domain->online) {
1573 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1574 if (domain->startup) {
1575 /* Logons are very important to users. If we're offline and
1576 we get a request within the first 30 seconds of startup,
1577 try very hard to find a DC and go online. */
1579 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1580 "request in startup mode.\n", domain->name ));
1582 winbindd_flush_negative_conn_cache(domain);
1583 result = init_dc_connection(domain);
1587 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1589 /* Check for Kerberos authentication */
1590 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1592 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1593 /* save for later */
1594 krb5_result = result;
1597 if (NT_STATUS_IS_OK(result)) {
1598 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1599 goto process_result;
1600 } else {
1601 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1604 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1605 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1606 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1607 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1608 set_domain_offline( domain );
1609 goto cached_logon;
1612 /* there are quite some NT_STATUS errors where there is no
1613 * point in retrying with a samlogon, we explictly have to take
1614 * care not to increase the bad logon counter on the DC */
1616 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1617 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1618 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1619 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1620 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1621 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1622 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1623 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1624 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1625 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1626 goto done;
1629 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1630 DEBUG(3,("falling back to samlogon\n"));
1631 goto sam_logon;
1632 } else {
1633 goto cached_logon;
1637 sam_logon:
1638 /* Check for Samlogon authentication */
1639 if (domain->online) {
1640 result = winbindd_dual_pam_auth_samlogon(
1641 state->mem_ctx, domain,
1642 state->request->data.auth.user,
1643 state->request->data.auth.pass,
1644 state->request->flags,
1645 &info3);
1647 if (NT_STATUS_IS_OK(result)) {
1648 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1649 /* add the Krb5 err if we have one */
1650 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1651 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1653 goto process_result;
1656 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1657 nt_errstr(result)));
1659 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1660 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1661 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1663 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1664 set_domain_offline( domain );
1665 goto cached_logon;
1668 if (domain->online) {
1669 /* We're still online - fail. */
1670 goto done;
1674 cached_logon:
1675 /* Check for Cached logons */
1676 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1677 lp_winbind_offline_logon()) {
1679 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1681 if (NT_STATUS_IS_OK(result)) {
1682 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1683 goto process_result;
1684 } else {
1685 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1686 goto done;
1690 process_result:
1692 if (NT_STATUS_IS_OK(result)) {
1694 struct dom_sid user_sid;
1696 /* In all codepaths where result == NT_STATUS_OK info3 must have
1697 been initialized. */
1698 if (!info3) {
1699 result = NT_STATUS_INTERNAL_ERROR;
1700 goto done;
1703 sid_compose(&user_sid, info3->base.domain_sid,
1704 info3->base.rid);
1706 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1707 &user_sid);
1708 netsamlogon_cache_store(name_user, info3);
1710 /* save name_to_sid info as early as possible (only if
1711 this is our primary domain so we don't invalidate
1712 the cache entry by storing the seq_num for the wrong
1713 domain). */
1714 if ( domain->primary ) {
1715 cache_name2sid(domain, name_domain, name_user,
1716 SID_NAME_USER, &user_sid);
1719 /* Check if the user is in the right group */
1721 result = check_info3_in_group(
1722 info3,
1723 state->request->data.auth.require_membership_of_sid);
1724 if (!NT_STATUS_IS_OK(result)) {
1725 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1726 state->request->data.auth.user,
1727 state->request->data.auth.require_membership_of_sid));
1728 goto done;
1731 result = append_auth_data(state->mem_ctx, state->response,
1732 state->request->flags, info3,
1733 name_domain, name_user);
1734 if (!NT_STATUS_IS_OK(result)) {
1735 goto done;
1738 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1739 && lp_winbind_offline_logon()) {
1741 result = winbindd_store_creds(domain,
1742 state->request->data.auth.user,
1743 state->request->data.auth.pass,
1744 info3);
1747 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1748 struct winbindd_domain *our_domain = find_our_domain();
1750 /* This is not entirely correct I believe, but it is
1751 consistent. Only apply the password policy settings
1752 too warn users for our own domain. Cannot obtain these
1753 from trusted DCs all the time so don't do it at all.
1754 -- jerry */
1756 result = NT_STATUS_NOT_SUPPORTED;
1757 if (our_domain == domain ) {
1758 result = fillup_password_policy(
1759 our_domain, state->response);
1762 if (!NT_STATUS_IS_OK(result)
1763 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1765 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1766 domain->name, nt_errstr(result)));
1767 goto done;
1771 result = NT_STATUS_OK;
1774 done:
1775 /* give us a more useful (more correct?) error code */
1776 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1777 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1778 result = NT_STATUS_NO_LOGON_SERVERS;
1781 set_auth_errors(state->response, result);
1783 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1784 state->request->data.auth.user,
1785 state->response->data.auth.nt_status_string,
1786 state->response->data.auth.pam_error));
1788 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1791 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1792 struct winbindd_cli_state *state)
1794 NTSTATUS result;
1795 struct netr_SamInfo3 *info3 = NULL;
1796 const char *name_user = NULL;
1797 const char *name_domain = NULL;
1798 const char *workstation;
1800 DATA_BLOB lm_resp, nt_resp;
1802 /* This is child-only, so no check for privileged access is needed
1803 anymore */
1805 /* Ensure null termination */
1806 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1807 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1809 name_user = state->request->data.auth_crap.user;
1810 name_domain = state->request->data.auth_crap.domain;
1811 workstation = state->request->data.auth_crap.workstation;
1813 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1814 name_domain, name_user));
1816 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1817 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1818 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1819 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1820 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1821 state->request->data.auth_crap.lm_resp_len,
1822 state->request->data.auth_crap.nt_resp_len));
1823 result = NT_STATUS_INVALID_PARAMETER;
1824 goto done;
1828 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1829 state->request->data.auth_crap.lm_resp_len);
1831 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1832 nt_resp = data_blob_talloc(state->mem_ctx,
1833 state->request->extra_data.data,
1834 state->request->data.auth_crap.nt_resp_len);
1835 } else {
1836 nt_resp = data_blob_talloc(state->mem_ctx,
1837 state->request->data.auth_crap.nt_resp,
1838 state->request->data.auth_crap.nt_resp_len);
1841 if (strequal(name_domain, get_global_sam_name())) {
1842 DATA_BLOB chal_blob = data_blob_const(
1843 state->request->data.auth_crap.chal,
1844 sizeof(state->request->data.auth_crap.chal));
1846 result = winbindd_dual_auth_passdb(
1847 state->mem_ctx,
1848 state->request->data.auth_crap.logon_parameters,
1849 name_domain, name_user,
1850 &chal_blob, &lm_resp, &nt_resp, &info3);
1851 goto process_result;
1854 result = winbind_samlogon_retry_loop(domain,
1855 state->mem_ctx,
1856 state->request->data.auth_crap.logon_parameters,
1857 domain->dcname,
1858 name_user,
1859 name_domain,
1860 /* Bug #3248 - found by Stefan Burkei. */
1861 workstation, /* We carefully set this above so use it... */
1862 state->request->data.auth_crap.chal,
1863 lm_resp,
1864 nt_resp,
1865 &info3);
1866 if (!NT_STATUS_IS_OK(result)) {
1867 goto done;
1870 process_result:
1872 if (NT_STATUS_IS_OK(result)) {
1873 struct dom_sid user_sid;
1875 sid_compose(&user_sid, info3->base.domain_sid,
1876 info3->base.rid);
1877 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1878 &user_sid);
1879 netsamlogon_cache_store(name_user, info3);
1881 /* Check if the user is in the right group */
1883 result = check_info3_in_group(
1884 info3,
1885 state->request->data.auth_crap.require_membership_of_sid);
1886 if (!NT_STATUS_IS_OK(result)) {
1887 DEBUG(3, ("User %s is not in the required group (%s), so "
1888 "crap authentication is rejected\n",
1889 state->request->data.auth_crap.user,
1890 state->request->data.auth_crap.require_membership_of_sid));
1891 goto done;
1894 result = append_auth_data(state->mem_ctx, state->response,
1895 state->request->flags, info3,
1896 name_domain, name_user);
1897 if (!NT_STATUS_IS_OK(result)) {
1898 goto done;
1902 done:
1904 /* give us a more useful (more correct?) error code */
1905 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1906 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1907 result = NT_STATUS_NO_LOGON_SERVERS;
1910 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1911 result = nt_status_squash(result);
1914 set_auth_errors(state->response, result);
1916 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1917 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1918 name_domain,
1919 name_user,
1920 state->response->data.auth.nt_status_string,
1921 state->response->data.auth.pam_error));
1923 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1926 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1927 struct winbindd_cli_state *state)
1929 char *oldpass;
1930 char *newpass = NULL;
1931 struct policy_handle dom_pol;
1932 struct rpc_pipe_client *cli = NULL;
1933 bool got_info = false;
1934 struct samr_DomInfo1 *info = NULL;
1935 struct userPwdChangeFailureInformation *reject = NULL;
1936 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1937 fstring domain, user;
1938 struct dcerpc_binding_handle *b = NULL;
1940 ZERO_STRUCT(dom_pol);
1942 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1943 state->request->data.auth.user));
1945 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1946 goto done;
1949 /* Change password */
1951 oldpass = state->request->data.chauthtok.oldpass;
1952 newpass = state->request->data.chauthtok.newpass;
1954 /* Initialize reject reason */
1955 state->response->data.auth.reject_reason = Undefined;
1957 /* Get sam handle */
1959 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1960 &dom_pol);
1961 if (!NT_STATUS_IS_OK(result)) {
1962 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1963 goto done;
1966 b = cli->binding_handle;
1968 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1969 user,
1970 newpass,
1971 oldpass,
1972 &info,
1973 &reject);
1975 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1977 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1979 fill_in_password_policy(state->response, info);
1981 state->response->data.auth.reject_reason =
1982 reject->extendedFailureReason;
1984 got_info = true;
1987 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1988 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1989 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1990 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1992 /* only fallback when the chgpasswd_user3 call is not supported */
1993 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
1994 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
1995 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
1996 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1998 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1999 nt_errstr(result)));
2001 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2003 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2004 Map to the same status code as Windows 2003. */
2006 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2007 result = NT_STATUS_PASSWORD_RESTRICTION;
2011 done:
2013 if (NT_STATUS_IS_OK(result)
2014 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2015 && lp_winbind_offline_logon()) {
2016 result = winbindd_update_creds_by_name(contact_domain, user,
2017 newpass);
2018 /* Again, this happens when we login from gdm or xdm
2019 * and the password expires, *BUT* cached crendentials
2020 * doesn't exist. winbindd_update_creds_by_name()
2021 * returns NT_STATUS_NO_SUCH_USER.
2022 * This is not a failure.
2023 * --- BoYang
2024 * */
2025 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2026 result = NT_STATUS_OK;
2029 if (!NT_STATUS_IS_OK(result)) {
2030 DEBUG(10, ("Failed to store creds: %s\n",
2031 nt_errstr(result)));
2032 goto process_result;
2036 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2038 NTSTATUS policy_ret;
2040 policy_ret = fillup_password_policy(
2041 contact_domain, state->response);
2043 /* failure of this is non critical, it will just provide no
2044 * additional information to the client why the change has
2045 * failed - Guenther */
2047 if (!NT_STATUS_IS_OK(policy_ret)) {
2048 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2049 goto process_result;
2053 process_result:
2055 if (strequal(contact_domain->name, get_global_sam_name())) {
2056 /* FIXME: internal rpc pipe does not cache handles yet */
2057 if (b) {
2058 if (is_valid_policy_hnd(&dom_pol)) {
2059 NTSTATUS _result;
2060 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2062 TALLOC_FREE(cli);
2066 set_auth_errors(state->response, result);
2068 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2069 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2070 domain,
2071 user,
2072 state->response->data.auth.nt_status_string,
2073 state->response->data.auth.pam_error));
2075 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2078 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2079 struct winbindd_cli_state *state)
2081 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2083 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2084 state->request->data.logoff.user));
2086 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2087 result = NT_STATUS_OK;
2088 goto process_result;
2091 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2092 result = NT_STATUS_OK;
2093 goto process_result;
2096 #ifdef HAVE_KRB5
2098 if (state->request->data.logoff.uid < 0) {
2099 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2100 goto process_result;
2103 /* what we need here is to find the corresponding krb5 ccache name *we*
2104 * created for a given username and destroy it */
2106 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2107 result = NT_STATUS_OK;
2108 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2109 goto process_result;
2112 if (!ccache_entry_identical(state->request->data.logoff.user,
2113 state->request->data.logoff.uid,
2114 state->request->data.logoff.krb5ccname)) {
2115 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2116 goto process_result;
2119 result = remove_ccache(state->request->data.logoff.user);
2120 if (!NT_STATUS_IS_OK(result)) {
2121 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2122 nt_errstr(result)));
2123 goto process_result;
2127 * Remove any mlock'ed memory creds in the child
2128 * we might be using for krb5 ticket renewal.
2131 winbindd_delete_memory_creds(state->request->data.logoff.user);
2133 #else
2134 result = NT_STATUS_NOT_SUPPORTED;
2135 #endif
2137 process_result:
2140 set_auth_errors(state->response, result);
2142 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2145 /* Change user password with auth crap*/
2147 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2149 NTSTATUS result;
2150 DATA_BLOB new_nt_password;
2151 DATA_BLOB old_nt_hash_enc;
2152 DATA_BLOB new_lm_password;
2153 DATA_BLOB old_lm_hash_enc;
2154 fstring domain,user;
2155 struct policy_handle dom_pol;
2156 struct winbindd_domain *contact_domain = domainSt;
2157 struct rpc_pipe_client *cli = NULL;
2158 struct dcerpc_binding_handle *b = NULL;
2160 ZERO_STRUCT(dom_pol);
2162 /* Ensure null termination */
2163 state->request->data.chng_pswd_auth_crap.user[
2164 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2165 state->request->data.chng_pswd_auth_crap.domain[
2166 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2167 *domain = 0;
2168 *user = 0;
2170 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2171 (unsigned long)state->pid,
2172 state->request->data.chng_pswd_auth_crap.domain,
2173 state->request->data.chng_pswd_auth_crap.user));
2175 if (lp_winbind_offline_logon()) {
2176 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2177 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2178 result = NT_STATUS_ACCESS_DENIED;
2179 goto done;
2182 if (*state->request->data.chng_pswd_auth_crap.domain) {
2183 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2184 } else {
2185 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2186 domain, user);
2188 if(!*domain) {
2189 DEBUG(3,("no domain specified with username (%s) - "
2190 "failing auth\n",
2191 state->request->data.chng_pswd_auth_crap.user));
2192 result = NT_STATUS_NO_SUCH_USER;
2193 goto done;
2197 if (!*domain && lp_winbind_use_default_domain()) {
2198 fstrcpy(domain,lp_workgroup());
2201 if(!*user) {
2202 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2205 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2206 (unsigned long)state->pid, domain, user));
2208 /* Change password */
2209 new_nt_password = data_blob_const(
2210 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2211 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2213 old_nt_hash_enc = data_blob_const(
2214 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2215 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2217 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2218 new_lm_password = data_blob_const(
2219 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2220 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2222 old_lm_hash_enc = data_blob_const(
2223 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2224 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2225 } else {
2226 new_lm_password = data_blob_null;
2227 old_lm_hash_enc = data_blob_null;
2230 /* Get sam handle */
2232 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2233 if (!NT_STATUS_IS_OK(result)) {
2234 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2235 goto done;
2238 b = cli->binding_handle;
2240 result = rpccli_samr_chng_pswd_auth_crap(
2241 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2242 new_lm_password, old_lm_hash_enc);
2244 done:
2246 if (strequal(contact_domain->name, get_global_sam_name())) {
2247 /* FIXME: internal rpc pipe does not cache handles yet */
2248 if (b) {
2249 if (is_valid_policy_hnd(&dom_pol)) {
2250 NTSTATUS _result;
2251 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2253 TALLOC_FREE(cli);
2257 set_auth_errors(state->response, result);
2259 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2260 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2261 domain, user,
2262 state->response->data.auth.nt_status_string,
2263 state->response->data.auth.pam_error));
2265 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2268 #ifdef HAVE_KRB5
2269 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2270 struct PAC_LOGON_INFO **logon_info)
2272 krb5_context krbctx = NULL;
2273 krb5_error_code k5ret;
2274 krb5_keytab keytab;
2275 krb5_kt_cursor cursor;
2276 krb5_keytab_entry entry;
2277 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2279 ZERO_STRUCT(entry);
2280 ZERO_STRUCT(cursor);
2282 k5ret = krb5_init_context(&krbctx);
2283 if (k5ret) {
2284 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2285 error_message(k5ret)));
2286 status = krb5_to_nt_status(k5ret);
2287 goto out;
2290 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2291 if (k5ret) {
2292 DEBUG(1, ("Failed to get keytab: %s\n",
2293 error_message(k5ret)));
2294 status = krb5_to_nt_status(k5ret);
2295 goto out_free;
2298 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2299 if (k5ret) {
2300 DEBUG(1, ("Failed to start seq: %s\n",
2301 error_message(k5ret)));
2302 status = krb5_to_nt_status(k5ret);
2303 goto out_keytab;
2306 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2307 while (k5ret == 0) {
2308 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2309 krbctx, NULL,
2310 KRB5_KT_KEY(&entry), NULL, 0,
2311 logon_info);
2312 if (NT_STATUS_IS_OK(status)) {
2313 break;
2315 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2316 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2319 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2320 if (k5ret) {
2321 DEBUG(1, ("Failed to end seq: %s\n",
2322 error_message(k5ret)));
2324 out_keytab:
2325 k5ret = krb5_kt_close(krbctx, keytab);
2326 if (k5ret) {
2327 DEBUG(1, ("Failed to close keytab: %s\n",
2328 error_message(k5ret)));
2330 out_free:
2331 krb5_free_context(krbctx);
2332 out:
2333 return status;
2336 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2337 struct netr_SamInfo3 **info3)
2339 struct winbindd_request *req = state->request;
2340 DATA_BLOB pac_blob;
2341 struct PAC_LOGON_INFO *logon_info = NULL;
2342 NTSTATUS result;
2344 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2345 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2346 if (!NT_STATUS_IS_OK(result) &&
2347 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2348 DEBUG(1, ("Error during PAC signature verification: %s\n",
2349 nt_errstr(result)));
2350 return result;
2353 if (logon_info) {
2354 /* Signature verification succeeded, trust the PAC */
2355 netsamlogon_cache_store(NULL, &logon_info->info3);
2357 } else {
2358 /* Try without signature verification */
2359 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2360 NULL, NULL, NULL, 0,
2361 &logon_info);
2362 if (!NT_STATUS_IS_OK(result)) {
2363 DEBUG(10, ("Could not extract PAC: %s\n",
2364 nt_errstr(result)));
2365 return result;
2369 *info3 = &logon_info->info3;
2371 return NT_STATUS_OK;
2373 #else /* HAVE_KRB5 */
2374 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2375 struct netr_SamInfo3 **info3)
2377 return NT_STATUS_NO_SUCH_USER;
2379 #endif /* HAVE_KRB5 */