s3-winbind: Do not delete an existing valid credential cache.
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob99794e6aa54b920f3b541fd34ea5d3b8ff42ae6f
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);
497 *user_ccache_file = gen_cc;
499 if (gen_cc == NULL) {
500 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
502 if (gen_cc == NULL) {
503 DEBUG(0,("out of memory\n"));
504 return NULL;
507 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
508 (*user_ccache_file == NULL) ? " (internal)":""));
510 return gen_cc;
513 #endif
515 uid_t get_uid_from_request(struct winbindd_request *request)
517 uid_t uid;
519 uid = request->data.auth.uid;
521 if (uid < 0) {
522 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
523 return -1;
525 return uid;
528 /**********************************************************************
529 Authenticate a user with a clear text password using Kerberos and fill up
530 ccache if required
531 **********************************************************************/
533 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
534 struct winbindd_domain *domain,
535 const char *user,
536 const char *pass,
537 const char *krb5_cc_type,
538 uid_t uid,
539 struct netr_SamInfo3 **info3,
540 fstring krb5ccname)
542 #ifdef HAVE_KRB5
543 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
544 krb5_error_code krb5_ret;
545 const char *cc = NULL;
546 const char *principal_s = NULL;
547 const char *service = NULL;
548 char *realm = NULL;
549 fstring name_domain, name_user;
550 time_t ticket_lifetime = 0;
551 time_t renewal_until = 0;
552 ADS_STRUCT *ads;
553 time_t time_offset = 0;
554 const char *user_ccache_file;
555 struct PAC_LOGON_INFO *logon_info = NULL;
557 *info3 = NULL;
559 /* 1st step:
560 * prepare a krb5_cc_cache string for the user */
562 if (uid == -1) {
563 DEBUG(0,("no valid uid\n"));
566 cc = generate_krb5_ccache(mem_ctx,
567 krb5_cc_type,
568 uid,
569 &user_ccache_file);
570 if (cc == NULL) {
571 return NT_STATUS_NO_MEMORY;
575 /* 2nd step:
576 * get kerberos properties */
578 if (domain->private_data) {
579 ads = (ADS_STRUCT *)domain->private_data;
580 time_offset = ads->auth.time_offset;
584 /* 3rd step:
585 * do kerberos auth and setup ccache as the user */
587 parse_domain_user(user, name_domain, name_user);
589 realm = domain->alt_name;
590 if (!strupper_m(realm)) {
591 return NT_STATUS_INVALID_PARAMETER;
594 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
595 if (principal_s == NULL) {
596 return NT_STATUS_NO_MEMORY;
599 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
600 if (service == NULL) {
601 return NT_STATUS_NO_MEMORY;
604 /* if this is a user ccache, we need to act as the user to let the krb5
605 * library handle the chown, etc. */
607 /************************ ENTERING NON-ROOT **********************/
609 if (user_ccache_file != NULL) {
610 set_effective_uid(uid);
611 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
614 result = kerberos_return_pac(mem_ctx,
615 principal_s,
616 pass,
617 time_offset,
618 &ticket_lifetime,
619 &renewal_until,
621 true,
622 true,
623 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
624 NULL,
625 &logon_info);
626 if (user_ccache_file != NULL) {
627 gain_root_privilege();
630 /************************ RETURNED TO ROOT **********************/
632 if (!NT_STATUS_IS_OK(result)) {
633 goto failed;
636 *info3 = &logon_info->info3;
638 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
639 principal_s));
641 /* if we had a user's ccache then return that string for the pam
642 * environment */
644 if (user_ccache_file != NULL) {
646 fstrcpy(krb5ccname, user_ccache_file);
648 result = add_ccache_to_list(principal_s,
650 service,
651 user,
652 pass,
653 realm,
654 uid,
655 time(NULL),
656 ticket_lifetime,
657 renewal_until,
658 false);
660 if (!NT_STATUS_IS_OK(result)) {
661 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
662 nt_errstr(result)));
664 } else {
666 /* need to delete the memory cred cache, it is not used anymore */
668 krb5_ret = ads_kdestroy(cc);
669 if (krb5_ret) {
670 DEBUG(3,("winbindd_raw_kerberos_login: "
671 "could not destroy krb5 credential cache: "
672 "%s\n", error_message(krb5_ret)));
677 return NT_STATUS_OK;
679 failed:
681 * Do not delete an existing valid credential cache, if the user
682 * e.g. enters a wrong password
684 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
685 && user_ccache_file != NULL) {
686 return result;
689 /* we could have created a new credential cache with a valid tgt in it
690 * but we werent able to get or verify the service ticket for this
691 * local host and therefor didn't get the PAC, we need to remove that
692 * cache entirely now */
694 krb5_ret = ads_kdestroy(cc);
695 if (krb5_ret) {
696 DEBUG(3,("winbindd_raw_kerberos_login: "
697 "could not destroy krb5 credential cache: "
698 "%s\n", error_message(krb5_ret)));
701 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
702 DEBUG(3,("winbindd_raw_kerberos_login: "
703 "could not remove ccache for user %s\n",
704 user));
707 return result;
708 #else
709 return NT_STATUS_NOT_SUPPORTED;
710 #endif /* HAVE_KRB5 */
713 /****************************************************************
714 ****************************************************************/
716 bool check_request_flags(uint32_t flags)
718 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
719 WBFLAG_PAM_INFO3_TEXT |
720 WBFLAG_PAM_INFO3_NDR;
722 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
723 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
724 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
725 !(flags & flags_edata) ) {
726 return true;
729 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
730 flags));
732 return false;
735 /****************************************************************
736 ****************************************************************/
738 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
739 struct winbindd_response *resp,
740 uint32_t request_flags,
741 struct netr_SamInfo3 *info3,
742 const char *name_domain,
743 const char *name_user)
745 NTSTATUS result;
747 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
748 memcpy(resp->data.auth.user_session_key,
749 info3->base.key.key,
750 sizeof(resp->data.auth.user_session_key)
751 /* 16 */);
754 if (request_flags & WBFLAG_PAM_LMKEY) {
755 memcpy(resp->data.auth.first_8_lm_hash,
756 info3->base.LMSessKey.key,
757 sizeof(resp->data.auth.first_8_lm_hash)
758 /* 8 */);
761 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
762 result = append_unix_username(mem_ctx, resp,
763 info3, name_domain, name_user);
764 if (!NT_STATUS_IS_OK(result)) {
765 DEBUG(10,("Failed to append Unix Username: %s\n",
766 nt_errstr(result)));
767 return result;
771 /* currently, anything from here on potentially overwrites extra_data. */
773 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
774 result = append_info3_as_ndr(mem_ctx, resp, info3);
775 if (!NT_STATUS_IS_OK(result)) {
776 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
777 nt_errstr(result)));
778 return result;
782 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
783 result = append_info3_as_txt(mem_ctx, resp, info3);
784 if (!NT_STATUS_IS_OK(result)) {
785 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
786 nt_errstr(result)));
787 return result;
791 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
792 result = append_afs_token(mem_ctx, resp,
793 info3, name_domain, name_user);
794 if (!NT_STATUS_IS_OK(result)) {
795 DEBUG(10,("Failed to append AFS token: %s\n",
796 nt_errstr(result)));
797 return result;
801 return NT_STATUS_OK;
804 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
805 struct winbindd_cli_state *state,
806 struct netr_SamInfo3 **info3)
808 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
809 uint16 max_allowed_bad_attempts;
810 fstring name_domain, name_user;
811 struct dom_sid sid;
812 enum lsa_SidType type;
813 uchar new_nt_pass[NT_HASH_LEN];
814 const uint8 *cached_nt_pass;
815 const uint8 *cached_salt;
816 struct netr_SamInfo3 *my_info3;
817 time_t kickoff_time, must_change_time;
818 bool password_good = false;
819 #ifdef HAVE_KRB5
820 struct winbindd_tdc_domain *tdc_domain = NULL;
821 #endif
823 *info3 = NULL;
825 ZERO_STRUCTP(info3);
827 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
829 /* Parse domain and username */
831 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
834 if (!lookup_cached_name(name_domain,
835 name_user,
836 &sid,
837 &type)) {
838 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
839 return NT_STATUS_NO_SUCH_USER;
842 if (type != SID_NAME_USER) {
843 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
844 return NT_STATUS_LOGON_FAILURE;
847 result = winbindd_get_creds(domain,
848 state->mem_ctx,
849 &sid,
850 &my_info3,
851 &cached_nt_pass,
852 &cached_salt);
853 if (!NT_STATUS_IS_OK(result)) {
854 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
855 return result;
858 *info3 = my_info3;
860 E_md4hash(state->request->data.auth.pass, new_nt_pass);
862 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
863 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
864 if (cached_salt) {
865 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
868 if (cached_salt) {
869 /* In this case we didn't store the nt_hash itself,
870 but the MD5 combination of salt + nt_hash. */
871 uchar salted_hash[NT_HASH_LEN];
872 E_md5hash(cached_salt, new_nt_pass, salted_hash);
874 password_good = (memcmp(cached_nt_pass, salted_hash,
875 NT_HASH_LEN) == 0);
876 } else {
877 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
878 password_good = (memcmp(cached_nt_pass, new_nt_pass,
879 NT_HASH_LEN) == 0);
882 if (password_good) {
884 /* User *DOES* know the password, update logon_time and reset
885 * bad_pw_count */
887 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
889 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
890 return NT_STATUS_ACCOUNT_LOCKED_OUT;
893 if (my_info3->base.acct_flags & ACB_DISABLED) {
894 return NT_STATUS_ACCOUNT_DISABLED;
897 if (my_info3->base.acct_flags & ACB_WSTRUST) {
898 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
901 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
902 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
905 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
906 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
909 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
910 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
911 my_info3->base.acct_flags));
912 return NT_STATUS_LOGON_FAILURE;
915 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
916 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
917 return NT_STATUS_ACCOUNT_EXPIRED;
920 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
921 if (must_change_time != 0 && must_change_time < time(NULL)) {
922 /* we allow grace logons when the password has expired */
923 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
924 /* return NT_STATUS_PASSWORD_EXPIRED; */
925 goto success;
928 #ifdef HAVE_KRB5
929 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
930 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
931 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
932 /* used to cope with the case winbindd starting without network. */
933 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
935 uid_t uid = -1;
936 const char *cc = NULL;
937 char *realm = NULL;
938 const char *principal_s = NULL;
939 const char *service = NULL;
940 const char *user_ccache_file;
942 uid = get_uid_from_request(state->request);
943 if (uid == -1) {
944 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
945 return NT_STATUS_INVALID_PARAMETER;
948 cc = generate_krb5_ccache(state->mem_ctx,
949 state->request->data.auth.krb5_cc_type,
950 state->request->data.auth.uid,
951 &user_ccache_file);
952 if (cc == NULL) {
953 return NT_STATUS_NO_MEMORY;
956 realm = domain->alt_name;
957 if (!strupper_m(realm)) {
958 return NT_STATUS_INVALID_PARAMETER;
961 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
962 if (principal_s == NULL) {
963 return NT_STATUS_NO_MEMORY;
966 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
967 if (service == NULL) {
968 return NT_STATUS_NO_MEMORY;
971 if (user_ccache_file != NULL) {
973 fstrcpy(state->response->data.auth.krb5ccname,
974 user_ccache_file);
976 result = add_ccache_to_list(principal_s,
978 service,
979 state->request->data.auth.user,
980 state->request->data.auth.pass,
981 domain->alt_name,
982 uid,
983 time(NULL),
984 time(NULL) + lp_winbind_cache_time(),
985 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
986 true);
988 if (!NT_STATUS_IS_OK(result)) {
989 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
990 "to add ccache to list: %s\n",
991 nt_errstr(result)));
995 #endif /* HAVE_KRB5 */
996 success:
997 /* FIXME: we possibly should handle logon hours as well (does xp when
998 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1000 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1001 my_info3->base.bad_password_count = 0;
1003 result = winbindd_update_creds_by_info3(domain,
1004 state->request->data.auth.user,
1005 state->request->data.auth.pass,
1006 my_info3);
1007 if (!NT_STATUS_IS_OK(result)) {
1008 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1009 nt_errstr(result)));
1010 return result;
1013 return NT_STATUS_OK;
1017 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1018 if (domain->online == false) {
1019 goto failed;
1022 /* failure of this is not critical */
1023 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1024 if (!NT_STATUS_IS_OK(result)) {
1025 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1026 "Won't be able to honour account lockout policies\n"));
1029 /* increase counter */
1030 my_info3->base.bad_password_count++;
1032 if (max_allowed_bad_attempts == 0) {
1033 goto failed;
1036 /* lockout user */
1037 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1039 uint32 password_properties;
1041 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1042 if (!NT_STATUS_IS_OK(result)) {
1043 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1046 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1047 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1048 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1052 failed:
1053 result = winbindd_update_creds_by_info3(domain,
1054 state->request->data.auth.user,
1055 NULL,
1056 my_info3);
1058 if (!NT_STATUS_IS_OK(result)) {
1059 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1060 nt_errstr(result)));
1063 return NT_STATUS_LOGON_FAILURE;
1066 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1067 struct winbindd_cli_state *state,
1068 struct netr_SamInfo3 **info3)
1070 struct winbindd_domain *contact_domain;
1071 fstring name_domain, name_user;
1072 NTSTATUS result;
1074 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1076 /* Parse domain and username */
1078 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1080 /* what domain should we contact? */
1082 if ( IS_DC ) {
1083 if (!(contact_domain = find_domain_from_name(name_domain))) {
1084 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1085 state->request->data.auth.user, name_domain, name_user, name_domain));
1086 result = NT_STATUS_NO_SUCH_USER;
1087 goto done;
1090 } else {
1091 if (is_myname(name_domain)) {
1092 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1093 result = NT_STATUS_NO_SUCH_USER;
1094 goto done;
1097 contact_domain = find_domain_from_name(name_domain);
1098 if (contact_domain == NULL) {
1099 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1100 state->request->data.auth.user, name_domain, name_user, name_domain));
1102 result = NT_STATUS_NO_SUCH_USER;
1103 goto done;
1107 if (contact_domain->initialized &&
1108 contact_domain->active_directory) {
1109 goto try_login;
1112 if (!contact_domain->initialized) {
1113 init_dc_connection(contact_domain);
1116 if (!contact_domain->active_directory) {
1117 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1118 return NT_STATUS_INVALID_LOGON_TYPE;
1120 try_login:
1121 result = winbindd_raw_kerberos_login(
1122 state->mem_ctx, contact_domain,
1123 state->request->data.auth.user,
1124 state->request->data.auth.pass,
1125 state->request->data.auth.krb5_cc_type,
1126 get_uid_from_request(state->request),
1127 info3, state->response->data.auth.krb5ccname);
1128 done:
1129 return result;
1132 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1133 uint32_t logon_parameters,
1134 const char *domain, const char *user,
1135 const DATA_BLOB *challenge,
1136 const DATA_BLOB *lm_resp,
1137 const DATA_BLOB *nt_resp,
1138 struct netr_SamInfo3 **pinfo3)
1140 struct auth_usersupplied_info *user_info = NULL;
1141 struct tsocket_address *local;
1142 NTSTATUS status;
1143 int rc;
1145 rc = tsocket_address_inet_from_strings(mem_ctx,
1146 "ip",
1147 "127.0.0.1",
1149 &local);
1150 if (rc < 0) {
1151 return NT_STATUS_NO_MEMORY;
1153 status = make_user_info(&user_info, user, user, domain, domain,
1154 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1155 NULL, AUTH_PASSWORD_RESPONSE);
1156 if (!NT_STATUS_IS_OK(status)) {
1157 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1158 return status;
1160 user_info->logon_parameters = logon_parameters;
1162 /* We don't want any more mapping of the username */
1163 user_info->mapped_state = True;
1165 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1166 pinfo3);
1167 free_user_info(&user_info);
1168 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1169 user, nt_errstr(status)));
1170 return status;
1173 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1174 TALLOC_CTX *mem_ctx,
1175 uint32_t logon_parameters,
1176 const char *server,
1177 const char *username,
1178 const char *domainname,
1179 const char *workstation,
1180 const uint8_t chal[8],
1181 DATA_BLOB lm_response,
1182 DATA_BLOB nt_response,
1183 struct netr_SamInfo3 **info3)
1185 int attempts = 0;
1186 int netr_attempts = 0;
1187 bool retry = false;
1188 NTSTATUS result;
1190 do {
1191 struct rpc_pipe_client *netlogon_pipe;
1192 const struct pipe_auth_data *auth;
1193 uint32_t neg_flags = 0;
1195 ZERO_STRUCTP(info3);
1196 retry = false;
1198 result = cm_connect_netlogon(domain, &netlogon_pipe);
1200 if (!NT_STATUS_IS_OK(result)) {
1201 DEBUG(3,("Could not open handle to NETLOGON pipe "
1202 "(error: %s, attempts: %d)\n",
1203 nt_errstr(result), netr_attempts));
1205 /* After the first retry always close the connection */
1206 if (netr_attempts > 0) {
1207 DEBUG(3, ("This is again a problem for this "
1208 "particular call, forcing the close "
1209 "of this connection\n"));
1210 invalidate_cm_connection(&domain->conn);
1213 /* After the second retry failover to the next DC */
1214 if (netr_attempts > 1) {
1216 * If the netlogon server is not reachable then
1217 * it is possible that the DC is rebuilding
1218 * sysvol and shutdown netlogon for that time.
1219 * We should failover to the next dc.
1221 DEBUG(3, ("This is the third problem for this "
1222 "particular call, adding DC to the "
1223 "negative cache list\n"));
1224 add_failed_connection_entry(domain->name,
1225 domain->dcname,
1226 result);
1227 saf_delete(domain->name);
1230 /* Only allow 3 retries */
1231 if (netr_attempts < 3) {
1232 DEBUG(3, ("The connection to netlogon "
1233 "failed, retrying\n"));
1234 netr_attempts++;
1235 retry = true;
1236 continue;
1238 return result;
1240 netr_attempts = 0;
1242 auth = netlogon_pipe->auth;
1243 if (netlogon_pipe->dc) {
1244 neg_flags = netlogon_pipe->dc->negotiate_flags;
1247 /* It is really important to try SamLogonEx here,
1248 * because in a clustered environment, we want to use
1249 * one machine account from multiple physical
1250 * computers.
1252 * With a normal SamLogon call, we must keep the
1253 * credentials chain updated and intact between all
1254 * users of the machine account (which would imply
1255 * cross-node communication for every NTLM logon).
1257 * (The credentials chain is not per NETLOGON pipe
1258 * connection, but globally on the server/client pair
1259 * by machine name).
1261 * When using SamLogonEx, the credentials are not
1262 * supplied, but the session key is implied by the
1263 * wrapping SamLogon context.
1265 * -- abartlet 21 April 2008
1267 * It's also important to use NetlogonValidationSamInfo4 (6),
1268 * because it relies on the rpc transport encryption
1269 * and avoids using the global netlogon schannel
1270 * session key to en/decrypt secret information
1271 * like the user_session_key for network logons.
1273 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1274 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1275 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1276 * are the indication that the server supports
1277 * NetlogonValidationSamInfo4 (6). And it must only
1278 * be used if "SealSecureChannel" is used.
1280 * -- metze 4 February 2011
1283 if (auth == NULL) {
1284 domain->can_do_validation6 = false;
1285 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1286 domain->can_do_validation6 = false;
1287 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1288 domain->can_do_validation6 = false;
1289 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1290 domain->can_do_validation6 = false;
1291 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1292 domain->can_do_validation6 = false;
1295 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1296 result = rpccli_netlogon_sam_network_logon_ex(
1297 netlogon_pipe,
1298 mem_ctx,
1299 logon_parameters,
1300 server, /* server name */
1301 username, /* user name */
1302 domainname, /* target domain */
1303 workstation, /* workstation */
1304 chal,
1306 lm_response,
1307 nt_response,
1308 info3);
1309 } else {
1310 result = rpccli_netlogon_sam_network_logon(
1311 netlogon_pipe,
1312 mem_ctx,
1313 logon_parameters,
1314 server, /* server name */
1315 username, /* user name */
1316 domainname, /* target domain */
1317 workstation, /* workstation */
1318 chal,
1319 domain->can_do_validation6 ? 6 : 3,
1320 lm_response,
1321 nt_response,
1322 info3);
1325 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1328 * It's likely that the server also does not support
1329 * validation level 6
1331 domain->can_do_validation6 = false;
1333 if (domain->can_do_samlogon_ex) {
1334 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1335 "retrying with NetSamLogon\n"));
1336 domain->can_do_samlogon_ex = false;
1337 retry = true;
1338 continue;
1342 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1343 * (no Ex). This happens against old Samba
1344 * DCs. Drop the connection.
1346 invalidate_cm_connection(&domain->conn);
1347 result = NT_STATUS_LOGON_FAILURE;
1348 break;
1351 if (domain->can_do_validation6 &&
1352 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1353 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1354 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1355 DEBUG(3,("Got a DC that can not do validation level 6, "
1356 "retrying with level 3\n"));
1357 domain->can_do_validation6 = false;
1358 retry = true;
1359 continue;
1363 * we increment this after the "feature negotiation"
1364 * for can_do_samlogon_ex and can_do_validation6
1366 attempts += 1;
1368 /* We have to try a second time as cm_connect_netlogon
1369 might not yet have noticed that the DC has killed
1370 our connection. */
1372 if (!rpccli_is_connected(netlogon_pipe)) {
1373 retry = true;
1374 continue;
1377 /* if we get access denied, a possible cause was that we had
1378 and open connection to the DC, but someone changed our
1379 machine account password out from underneath us using 'net
1380 rpc changetrustpw' */
1382 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1383 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1384 "ACCESS_DENIED. Maybe the trust account "
1385 "password was changed and we didn't know it. "
1386 "Killing connections to domain %s\n",
1387 domainname));
1388 invalidate_cm_connection(&domain->conn);
1389 retry = true;
1392 } while ( (attempts < 2) && retry );
1394 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1395 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1396 "returned NT_STATUS_IO_TIMEOUT after the retry."
1397 "Killing connections to domain %s\n",
1398 domainname));
1399 invalidate_cm_connection(&domain->conn);
1401 return result;
1404 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1405 struct winbindd_domain *domain,
1406 const char *user,
1407 const char *pass,
1408 uint32_t request_flags,
1409 struct netr_SamInfo3 **info3)
1412 uchar chal[8];
1413 DATA_BLOB lm_resp;
1414 DATA_BLOB nt_resp;
1415 unsigned char local_nt_response[24];
1416 fstring name_domain, name_user;
1417 NTSTATUS result;
1418 struct netr_SamInfo3 *my_info3 = NULL;
1420 *info3 = NULL;
1422 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1424 /* Parse domain and username */
1426 parse_domain_user(user, name_domain, name_user);
1428 /* do password magic */
1430 generate_random_buffer(chal, sizeof(chal));
1432 if (lp_client_ntlmv2_auth()) {
1433 DATA_BLOB server_chal;
1434 DATA_BLOB names_blob;
1435 server_chal = data_blob_const(chal, 8);
1437 /* note that the 'workgroup' here is for the local
1438 machine. The 'server name' must match the
1439 'workstation' passed to the actual SamLogon call.
1441 names_blob = NTLMv2_generate_names_blob(
1442 mem_ctx, lp_netbios_name(), lp_workgroup());
1444 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1445 pass,
1446 &server_chal,
1447 &names_blob,
1448 &lm_resp, &nt_resp, NULL, NULL)) {
1449 data_blob_free(&names_blob);
1450 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1451 result = NT_STATUS_NO_MEMORY;
1452 goto done;
1454 data_blob_free(&names_blob);
1455 } else {
1456 lm_resp = data_blob_null;
1457 SMBNTencrypt(pass, chal, local_nt_response);
1459 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1460 sizeof(local_nt_response));
1463 if (strequal(name_domain, get_global_sam_name())) {
1464 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1466 result = winbindd_dual_auth_passdb(
1467 mem_ctx, 0, name_domain, name_user,
1468 &chal_blob, &lm_resp, &nt_resp, info3);
1469 goto done;
1472 /* check authentication loop */
1474 result = winbind_samlogon_retry_loop(domain,
1475 mem_ctx,
1477 domain->dcname,
1478 name_user,
1479 name_domain,
1480 lp_netbios_name(),
1481 chal,
1482 lm_resp,
1483 nt_resp,
1484 &my_info3);
1485 if (!NT_STATUS_IS_OK(result)) {
1486 goto done;
1489 /* handle the case where a NT4 DC does not fill in the acct_flags in
1490 * the samlogon reply info3. When accurate info3 is required by the
1491 * caller, we look up the account flags ourselve - gd */
1493 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1494 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1496 struct rpc_pipe_client *samr_pipe;
1497 struct policy_handle samr_domain_handle, user_pol;
1498 union samr_UserInfo *info = NULL;
1499 NTSTATUS status_tmp, result_tmp;
1500 uint32 acct_flags;
1501 struct dcerpc_binding_handle *b;
1503 status_tmp = cm_connect_sam(domain, mem_ctx,
1504 &samr_pipe, &samr_domain_handle);
1506 if (!NT_STATUS_IS_OK(status_tmp)) {
1507 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1508 nt_errstr(status_tmp)));
1509 goto done;
1512 b = samr_pipe->binding_handle;
1514 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1515 &samr_domain_handle,
1516 MAXIMUM_ALLOWED_ACCESS,
1517 my_info3->base.rid,
1518 &user_pol,
1519 &result_tmp);
1521 if (!NT_STATUS_IS_OK(status_tmp)) {
1522 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1523 nt_errstr(status_tmp)));
1524 goto done;
1526 if (!NT_STATUS_IS_OK(result_tmp)) {
1527 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1528 nt_errstr(result_tmp)));
1529 goto done;
1532 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1533 &user_pol,
1535 &info,
1536 &result_tmp);
1538 if (!NT_STATUS_IS_OK(status_tmp)) {
1539 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1540 nt_errstr(status_tmp)));
1541 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1542 goto done;
1544 if (!NT_STATUS_IS_OK(result_tmp)) {
1545 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1546 nt_errstr(result_tmp)));
1547 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1548 goto done;
1551 acct_flags = info->info16.acct_flags;
1553 if (acct_flags == 0) {
1554 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1555 goto done;
1558 my_info3->base.acct_flags = acct_flags;
1560 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1562 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1565 *info3 = my_info3;
1566 done:
1567 return result;
1570 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1571 struct winbindd_cli_state *state)
1573 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1574 NTSTATUS krb5_result = NT_STATUS_OK;
1575 fstring name_domain, name_user;
1576 char *mapped_user;
1577 fstring domain_user;
1578 struct netr_SamInfo3 *info3 = NULL;
1579 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1581 /* Ensure null termination */
1582 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1584 /* Ensure null termination */
1585 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1587 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1588 state->request->data.auth.user));
1590 /* Parse domain and username */
1592 name_map_status = normalize_name_unmap(state->mem_ctx,
1593 state->request->data.auth.user,
1594 &mapped_user);
1596 /* If the name normalization didnt' actually do anything,
1597 just use the original name */
1599 if (!NT_STATUS_IS_OK(name_map_status) &&
1600 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1602 mapped_user = state->request->data.auth.user;
1605 parse_domain_user(mapped_user, name_domain, name_user);
1607 if ( mapped_user != state->request->data.auth.user ) {
1608 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1609 *lp_winbind_separator(),
1610 name_user );
1611 strlcpy( state->request->data.auth.user, domain_user,
1612 sizeof(state->request->data.auth.user));
1615 if (!domain->online) {
1616 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1617 if (domain->startup) {
1618 /* Logons are very important to users. If we're offline and
1619 we get a request within the first 30 seconds of startup,
1620 try very hard to find a DC and go online. */
1622 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1623 "request in startup mode.\n", domain->name ));
1625 winbindd_flush_negative_conn_cache(domain);
1626 result = init_dc_connection(domain);
1630 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1632 /* Check for Kerberos authentication */
1633 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1635 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1636 /* save for later */
1637 krb5_result = result;
1640 if (NT_STATUS_IS_OK(result)) {
1641 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1642 goto process_result;
1643 } else {
1644 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1647 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1648 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1649 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1650 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1651 set_domain_offline( domain );
1652 goto cached_logon;
1655 /* there are quite some NT_STATUS errors where there is no
1656 * point in retrying with a samlogon, we explictly have to take
1657 * care not to increase the bad logon counter on the DC */
1659 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1660 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1661 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1662 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1663 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1664 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1665 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1666 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1667 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1668 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1669 goto done;
1672 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1673 DEBUG(3,("falling back to samlogon\n"));
1674 goto sam_logon;
1675 } else {
1676 goto cached_logon;
1680 sam_logon:
1681 /* Check for Samlogon authentication */
1682 if (domain->online) {
1683 result = winbindd_dual_pam_auth_samlogon(
1684 state->mem_ctx, domain,
1685 state->request->data.auth.user,
1686 state->request->data.auth.pass,
1687 state->request->flags,
1688 &info3);
1690 if (NT_STATUS_IS_OK(result)) {
1691 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1692 /* add the Krb5 err if we have one */
1693 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1694 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1696 goto process_result;
1699 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1700 nt_errstr(result)));
1702 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1703 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1704 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1706 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1707 set_domain_offline( domain );
1708 goto cached_logon;
1711 if (domain->online) {
1712 /* We're still online - fail. */
1713 goto done;
1717 cached_logon:
1718 /* Check for Cached logons */
1719 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1720 lp_winbind_offline_logon()) {
1722 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1724 if (NT_STATUS_IS_OK(result)) {
1725 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1726 goto process_result;
1727 } else {
1728 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1729 goto done;
1733 process_result:
1735 if (NT_STATUS_IS_OK(result)) {
1737 struct dom_sid user_sid;
1739 /* In all codepaths where result == NT_STATUS_OK info3 must have
1740 been initialized. */
1741 if (!info3) {
1742 result = NT_STATUS_INTERNAL_ERROR;
1743 goto done;
1746 sid_compose(&user_sid, info3->base.domain_sid,
1747 info3->base.rid);
1749 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1750 &user_sid);
1751 netsamlogon_cache_store(name_user, info3);
1753 /* save name_to_sid info as early as possible (only if
1754 this is our primary domain so we don't invalidate
1755 the cache entry by storing the seq_num for the wrong
1756 domain). */
1757 if ( domain->primary ) {
1758 cache_name2sid(domain, name_domain, name_user,
1759 SID_NAME_USER, &user_sid);
1762 /* Check if the user is in the right group */
1764 result = check_info3_in_group(
1765 info3,
1766 state->request->data.auth.require_membership_of_sid);
1767 if (!NT_STATUS_IS_OK(result)) {
1768 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1769 state->request->data.auth.user,
1770 state->request->data.auth.require_membership_of_sid));
1771 goto done;
1774 result = append_auth_data(state->mem_ctx, state->response,
1775 state->request->flags, info3,
1776 name_domain, name_user);
1777 if (!NT_STATUS_IS_OK(result)) {
1778 goto done;
1781 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1782 && lp_winbind_offline_logon()) {
1784 result = winbindd_store_creds(domain,
1785 state->request->data.auth.user,
1786 state->request->data.auth.pass,
1787 info3);
1790 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1791 struct winbindd_domain *our_domain = find_our_domain();
1793 /* This is not entirely correct I believe, but it is
1794 consistent. Only apply the password policy settings
1795 too warn users for our own domain. Cannot obtain these
1796 from trusted DCs all the time so don't do it at all.
1797 -- jerry */
1799 result = NT_STATUS_NOT_SUPPORTED;
1800 if (our_domain == domain ) {
1801 result = fillup_password_policy(
1802 our_domain, state->response);
1805 if (!NT_STATUS_IS_OK(result)
1806 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1808 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1809 domain->name, nt_errstr(result)));
1810 goto done;
1814 result = NT_STATUS_OK;
1817 done:
1818 /* give us a more useful (more correct?) error code */
1819 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1820 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1821 result = NT_STATUS_NO_LOGON_SERVERS;
1824 set_auth_errors(state->response, result);
1826 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1827 state->request->data.auth.user,
1828 state->response->data.auth.nt_status_string,
1829 state->response->data.auth.pam_error));
1831 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1834 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1835 struct winbindd_cli_state *state)
1837 NTSTATUS result;
1838 struct netr_SamInfo3 *info3 = NULL;
1839 const char *name_user = NULL;
1840 const char *name_domain = NULL;
1841 const char *workstation;
1843 DATA_BLOB lm_resp, nt_resp;
1845 /* This is child-only, so no check for privileged access is needed
1846 anymore */
1848 /* Ensure null termination */
1849 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1850 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1852 name_user = state->request->data.auth_crap.user;
1853 name_domain = state->request->data.auth_crap.domain;
1854 workstation = state->request->data.auth_crap.workstation;
1856 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1857 name_domain, name_user));
1859 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1860 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1861 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1862 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1863 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1864 state->request->data.auth_crap.lm_resp_len,
1865 state->request->data.auth_crap.nt_resp_len));
1866 result = NT_STATUS_INVALID_PARAMETER;
1867 goto done;
1871 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1872 state->request->data.auth_crap.lm_resp_len);
1874 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1875 nt_resp = data_blob_talloc(state->mem_ctx,
1876 state->request->extra_data.data,
1877 state->request->data.auth_crap.nt_resp_len);
1878 } else {
1879 nt_resp = data_blob_talloc(state->mem_ctx,
1880 state->request->data.auth_crap.nt_resp,
1881 state->request->data.auth_crap.nt_resp_len);
1884 if (strequal(name_domain, get_global_sam_name())) {
1885 DATA_BLOB chal_blob = data_blob_const(
1886 state->request->data.auth_crap.chal,
1887 sizeof(state->request->data.auth_crap.chal));
1889 result = winbindd_dual_auth_passdb(
1890 state->mem_ctx,
1891 state->request->data.auth_crap.logon_parameters,
1892 name_domain, name_user,
1893 &chal_blob, &lm_resp, &nt_resp, &info3);
1894 goto process_result;
1897 result = winbind_samlogon_retry_loop(domain,
1898 state->mem_ctx,
1899 state->request->data.auth_crap.logon_parameters,
1900 domain->dcname,
1901 name_user,
1902 name_domain,
1903 /* Bug #3248 - found by Stefan Burkei. */
1904 workstation, /* We carefully set this above so use it... */
1905 state->request->data.auth_crap.chal,
1906 lm_resp,
1907 nt_resp,
1908 &info3);
1909 if (!NT_STATUS_IS_OK(result)) {
1910 goto done;
1913 process_result:
1915 if (NT_STATUS_IS_OK(result)) {
1916 struct dom_sid user_sid;
1918 sid_compose(&user_sid, info3->base.domain_sid,
1919 info3->base.rid);
1920 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1921 &user_sid);
1922 netsamlogon_cache_store(name_user, info3);
1924 /* Check if the user is in the right group */
1926 result = check_info3_in_group(
1927 info3,
1928 state->request->data.auth_crap.require_membership_of_sid);
1929 if (!NT_STATUS_IS_OK(result)) {
1930 DEBUG(3, ("User %s is not in the required group (%s), so "
1931 "crap authentication is rejected\n",
1932 state->request->data.auth_crap.user,
1933 state->request->data.auth_crap.require_membership_of_sid));
1934 goto done;
1937 result = append_auth_data(state->mem_ctx, state->response,
1938 state->request->flags, info3,
1939 name_domain, name_user);
1940 if (!NT_STATUS_IS_OK(result)) {
1941 goto done;
1945 done:
1947 /* give us a more useful (more correct?) error code */
1948 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1949 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1950 result = NT_STATUS_NO_LOGON_SERVERS;
1953 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1954 result = nt_status_squash(result);
1957 set_auth_errors(state->response, result);
1959 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1960 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1961 name_domain,
1962 name_user,
1963 state->response->data.auth.nt_status_string,
1964 state->response->data.auth.pam_error));
1966 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1969 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1970 struct winbindd_cli_state *state)
1972 char *oldpass;
1973 char *newpass = NULL;
1974 struct policy_handle dom_pol;
1975 struct rpc_pipe_client *cli = NULL;
1976 bool got_info = false;
1977 struct samr_DomInfo1 *info = NULL;
1978 struct userPwdChangeFailureInformation *reject = NULL;
1979 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1980 fstring domain, user;
1981 struct dcerpc_binding_handle *b = NULL;
1983 ZERO_STRUCT(dom_pol);
1985 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1986 state->request->data.auth.user));
1988 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1989 goto done;
1992 /* Change password */
1994 oldpass = state->request->data.chauthtok.oldpass;
1995 newpass = state->request->data.chauthtok.newpass;
1997 /* Initialize reject reason */
1998 state->response->data.auth.reject_reason = Undefined;
2000 /* Get sam handle */
2002 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2003 &dom_pol);
2004 if (!NT_STATUS_IS_OK(result)) {
2005 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2006 goto done;
2009 b = cli->binding_handle;
2011 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2012 user,
2013 newpass,
2014 oldpass,
2015 &info,
2016 &reject);
2018 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2020 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2022 fill_in_password_policy(state->response, info);
2024 state->response->data.auth.reject_reason =
2025 reject->extendedFailureReason;
2027 got_info = true;
2030 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2031 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2032 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2033 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2035 /* only fallback when the chgpasswd_user3 call is not supported */
2036 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2037 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2038 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2039 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2041 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2042 nt_errstr(result)));
2044 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2046 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2047 Map to the same status code as Windows 2003. */
2049 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2050 result = NT_STATUS_PASSWORD_RESTRICTION;
2054 done:
2056 if (NT_STATUS_IS_OK(result)
2057 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2058 && lp_winbind_offline_logon()) {
2059 result = winbindd_update_creds_by_name(contact_domain, user,
2060 newpass);
2061 /* Again, this happens when we login from gdm or xdm
2062 * and the password expires, *BUT* cached crendentials
2063 * doesn't exist. winbindd_update_creds_by_name()
2064 * returns NT_STATUS_NO_SUCH_USER.
2065 * This is not a failure.
2066 * --- BoYang
2067 * */
2068 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2069 result = NT_STATUS_OK;
2072 if (!NT_STATUS_IS_OK(result)) {
2073 DEBUG(10, ("Failed to store creds: %s\n",
2074 nt_errstr(result)));
2075 goto process_result;
2079 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2081 NTSTATUS policy_ret;
2083 policy_ret = fillup_password_policy(
2084 contact_domain, state->response);
2086 /* failure of this is non critical, it will just provide no
2087 * additional information to the client why the change has
2088 * failed - Guenther */
2090 if (!NT_STATUS_IS_OK(policy_ret)) {
2091 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2092 goto process_result;
2096 process_result:
2098 if (strequal(contact_domain->name, get_global_sam_name())) {
2099 /* FIXME: internal rpc pipe does not cache handles yet */
2100 if (b) {
2101 if (is_valid_policy_hnd(&dom_pol)) {
2102 NTSTATUS _result;
2103 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2105 TALLOC_FREE(cli);
2109 set_auth_errors(state->response, result);
2111 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2112 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2113 domain,
2114 user,
2115 state->response->data.auth.nt_status_string,
2116 state->response->data.auth.pam_error));
2118 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2121 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2122 struct winbindd_cli_state *state)
2124 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2126 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2127 state->request->data.logoff.user));
2129 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2130 result = NT_STATUS_OK;
2131 goto process_result;
2134 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2135 result = NT_STATUS_OK;
2136 goto process_result;
2139 #ifdef HAVE_KRB5
2141 if (state->request->data.logoff.uid < 0) {
2142 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2143 goto process_result;
2146 /* what we need here is to find the corresponding krb5 ccache name *we*
2147 * created for a given username and destroy it */
2149 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2150 result = NT_STATUS_OK;
2151 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2152 goto process_result;
2155 if (!ccache_entry_identical(state->request->data.logoff.user,
2156 state->request->data.logoff.uid,
2157 state->request->data.logoff.krb5ccname)) {
2158 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2159 goto process_result;
2162 result = remove_ccache(state->request->data.logoff.user);
2163 if (!NT_STATUS_IS_OK(result)) {
2164 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2165 nt_errstr(result)));
2166 goto process_result;
2170 * Remove any mlock'ed memory creds in the child
2171 * we might be using for krb5 ticket renewal.
2174 winbindd_delete_memory_creds(state->request->data.logoff.user);
2176 #else
2177 result = NT_STATUS_NOT_SUPPORTED;
2178 #endif
2180 process_result:
2183 set_auth_errors(state->response, result);
2185 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2188 /* Change user password with auth crap*/
2190 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2192 NTSTATUS result;
2193 DATA_BLOB new_nt_password;
2194 DATA_BLOB old_nt_hash_enc;
2195 DATA_BLOB new_lm_password;
2196 DATA_BLOB old_lm_hash_enc;
2197 fstring domain,user;
2198 struct policy_handle dom_pol;
2199 struct winbindd_domain *contact_domain = domainSt;
2200 struct rpc_pipe_client *cli = NULL;
2201 struct dcerpc_binding_handle *b = NULL;
2203 ZERO_STRUCT(dom_pol);
2205 /* Ensure null termination */
2206 state->request->data.chng_pswd_auth_crap.user[
2207 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2208 state->request->data.chng_pswd_auth_crap.domain[
2209 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2210 *domain = 0;
2211 *user = 0;
2213 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2214 (unsigned long)state->pid,
2215 state->request->data.chng_pswd_auth_crap.domain,
2216 state->request->data.chng_pswd_auth_crap.user));
2218 if (lp_winbind_offline_logon()) {
2219 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2220 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2221 result = NT_STATUS_ACCESS_DENIED;
2222 goto done;
2225 if (*state->request->data.chng_pswd_auth_crap.domain) {
2226 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2227 } else {
2228 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2229 domain, user);
2231 if(!*domain) {
2232 DEBUG(3,("no domain specified with username (%s) - "
2233 "failing auth\n",
2234 state->request->data.chng_pswd_auth_crap.user));
2235 result = NT_STATUS_NO_SUCH_USER;
2236 goto done;
2240 if (!*domain && lp_winbind_use_default_domain()) {
2241 fstrcpy(domain,lp_workgroup());
2244 if(!*user) {
2245 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2248 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2249 (unsigned long)state->pid, domain, user));
2251 /* Change password */
2252 new_nt_password = data_blob_const(
2253 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2254 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2256 old_nt_hash_enc = data_blob_const(
2257 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2258 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2260 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2261 new_lm_password = data_blob_const(
2262 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2263 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2265 old_lm_hash_enc = data_blob_const(
2266 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2267 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2268 } else {
2269 new_lm_password = data_blob_null;
2270 old_lm_hash_enc = data_blob_null;
2273 /* Get sam handle */
2275 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2276 if (!NT_STATUS_IS_OK(result)) {
2277 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2278 goto done;
2281 b = cli->binding_handle;
2283 result = rpccli_samr_chng_pswd_auth_crap(
2284 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2285 new_lm_password, old_lm_hash_enc);
2287 done:
2289 if (strequal(contact_domain->name, get_global_sam_name())) {
2290 /* FIXME: internal rpc pipe does not cache handles yet */
2291 if (b) {
2292 if (is_valid_policy_hnd(&dom_pol)) {
2293 NTSTATUS _result;
2294 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2296 TALLOC_FREE(cli);
2300 set_auth_errors(state->response, result);
2302 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2303 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2304 domain, user,
2305 state->response->data.auth.nt_status_string,
2306 state->response->data.auth.pam_error));
2308 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2311 #ifdef HAVE_KRB5
2312 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2313 struct PAC_LOGON_INFO **logon_info)
2315 krb5_context krbctx = NULL;
2316 krb5_error_code k5ret;
2317 krb5_keytab keytab;
2318 krb5_kt_cursor cursor;
2319 krb5_keytab_entry entry;
2320 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2322 ZERO_STRUCT(entry);
2323 ZERO_STRUCT(cursor);
2325 k5ret = krb5_init_context(&krbctx);
2326 if (k5ret) {
2327 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2328 error_message(k5ret)));
2329 status = krb5_to_nt_status(k5ret);
2330 goto out;
2333 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2334 if (k5ret) {
2335 DEBUG(1, ("Failed to get keytab: %s\n",
2336 error_message(k5ret)));
2337 status = krb5_to_nt_status(k5ret);
2338 goto out_free;
2341 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2342 if (k5ret) {
2343 DEBUG(1, ("Failed to start seq: %s\n",
2344 error_message(k5ret)));
2345 status = krb5_to_nt_status(k5ret);
2346 goto out_keytab;
2349 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2350 while (k5ret == 0) {
2351 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2352 krbctx, NULL,
2353 KRB5_KT_KEY(&entry), NULL, 0,
2354 logon_info);
2355 if (NT_STATUS_IS_OK(status)) {
2356 break;
2358 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2359 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2362 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2363 if (k5ret) {
2364 DEBUG(1, ("Failed to end seq: %s\n",
2365 error_message(k5ret)));
2367 out_keytab:
2368 k5ret = krb5_kt_close(krbctx, keytab);
2369 if (k5ret) {
2370 DEBUG(1, ("Failed to close keytab: %s\n",
2371 error_message(k5ret)));
2373 out_free:
2374 krb5_free_context(krbctx);
2375 out:
2376 return status;
2379 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2380 struct netr_SamInfo3 **info3)
2382 struct winbindd_request *req = state->request;
2383 DATA_BLOB pac_blob;
2384 struct PAC_LOGON_INFO *logon_info = NULL;
2385 NTSTATUS result;
2387 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2388 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2389 if (!NT_STATUS_IS_OK(result) &&
2390 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2391 DEBUG(1, ("Error during PAC signature verification: %s\n",
2392 nt_errstr(result)));
2393 return result;
2396 if (logon_info) {
2397 /* Signature verification succeeded, trust the PAC */
2398 netsamlogon_cache_store(NULL, &logon_info->info3);
2400 } else {
2401 /* Try without signature verification */
2402 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2403 NULL, NULL, NULL, 0,
2404 &logon_info);
2405 if (!NT_STATUS_IS_OK(result)) {
2406 DEBUG(10, ("Could not extract PAC: %s\n",
2407 nt_errstr(result)));
2408 return result;
2412 *info3 = &logon_info->info3;
2414 return NT_STATUS_OK;
2416 #else /* HAVE_KRB5 */
2417 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2418 struct netr_SamInfo3 **info3)
2420 return NT_STATUS_NO_SUCH_USER;
2422 #endif /* HAVE_KRB5 */