gensec: recv_handler can't be NULL at that point.
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob158a7c431d2487a09a1202eea0085fd0a47e1765
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 if (domain->alt_name == NULL) {
560 return NT_STATUS_INVALID_PARAMETER;
563 /* 1st step:
564 * prepare a krb5_cc_cache string for the user */
566 if (uid == -1) {
567 DEBUG(0,("no valid uid\n"));
570 cc = generate_krb5_ccache(mem_ctx,
571 krb5_cc_type,
572 uid,
573 &user_ccache_file);
574 if (cc == NULL) {
575 return NT_STATUS_NO_MEMORY;
579 /* 2nd step:
580 * get kerberos properties */
582 if (domain->private_data) {
583 ads = (ADS_STRUCT *)domain->private_data;
584 time_offset = ads->auth.time_offset;
588 /* 3rd step:
589 * do kerberos auth and setup ccache as the user */
591 parse_domain_user(user, name_domain, name_user);
593 realm = talloc_strdup(mem_ctx, domain->alt_name);
594 if (realm == NULL) {
595 return NT_STATUS_NO_MEMORY;
598 if (!strupper_m(realm)) {
599 return NT_STATUS_INVALID_PARAMETER;
602 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
603 if (principal_s == NULL) {
604 return NT_STATUS_NO_MEMORY;
607 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
608 if (service == NULL) {
609 return NT_STATUS_NO_MEMORY;
612 /* if this is a user ccache, we need to act as the user to let the krb5
613 * library handle the chown, etc. */
615 /************************ ENTERING NON-ROOT **********************/
617 if (user_ccache_file != NULL) {
618 set_effective_uid(uid);
619 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
622 result = kerberos_return_pac(mem_ctx,
623 principal_s,
624 pass,
625 time_offset,
626 &ticket_lifetime,
627 &renewal_until,
629 true,
630 true,
631 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
632 NULL,
633 &logon_info);
634 if (user_ccache_file != NULL) {
635 gain_root_privilege();
638 /************************ RETURNED TO ROOT **********************/
640 if (!NT_STATUS_IS_OK(result)) {
641 goto failed;
644 *info3 = &logon_info->info3;
646 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
647 principal_s));
649 /* if we had a user's ccache then return that string for the pam
650 * environment */
652 if (user_ccache_file != NULL) {
654 fstrcpy(krb5ccname, user_ccache_file);
656 result = add_ccache_to_list(principal_s,
658 service,
659 user,
660 pass,
661 realm,
662 uid,
663 time(NULL),
664 ticket_lifetime,
665 renewal_until,
666 false);
668 if (!NT_STATUS_IS_OK(result)) {
669 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
670 nt_errstr(result)));
672 } else {
674 /* need to delete the memory cred cache, it is not used anymore */
676 krb5_ret = ads_kdestroy(cc);
677 if (krb5_ret) {
678 DEBUG(3,("winbindd_raw_kerberos_login: "
679 "could not destroy krb5 credential cache: "
680 "%s\n", error_message(krb5_ret)));
685 return NT_STATUS_OK;
687 failed:
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 if (domain->alt_name == NULL) {
943 return NT_STATUS_INVALID_PARAMETER;
946 uid = get_uid_from_request(state->request);
947 if (uid == -1) {
948 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
949 return NT_STATUS_INVALID_PARAMETER;
952 cc = generate_krb5_ccache(state->mem_ctx,
953 state->request->data.auth.krb5_cc_type,
954 state->request->data.auth.uid,
955 &user_ccache_file);
956 if (cc == NULL) {
957 return NT_STATUS_NO_MEMORY;
960 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
961 if (realm == NULL) {
962 return NT_STATUS_NO_MEMORY;
965 if (!strupper_m(realm)) {
966 return NT_STATUS_INVALID_PARAMETER;
969 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
970 if (principal_s == NULL) {
971 return NT_STATUS_NO_MEMORY;
974 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
975 if (service == NULL) {
976 return NT_STATUS_NO_MEMORY;
979 if (user_ccache_file != NULL) {
981 fstrcpy(state->response->data.auth.krb5ccname,
982 user_ccache_file);
984 result = add_ccache_to_list(principal_s,
986 service,
987 state->request->data.auth.user,
988 state->request->data.auth.pass,
989 realm,
990 uid,
991 time(NULL),
992 time(NULL) + lp_winbind_cache_time(),
993 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
994 true);
996 if (!NT_STATUS_IS_OK(result)) {
997 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
998 "to add ccache to list: %s\n",
999 nt_errstr(result)));
1003 #endif /* HAVE_KRB5 */
1004 success:
1005 /* FIXME: we possibly should handle logon hours as well (does xp when
1006 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1008 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1009 my_info3->base.bad_password_count = 0;
1011 result = winbindd_update_creds_by_info3(domain,
1012 state->request->data.auth.user,
1013 state->request->data.auth.pass,
1014 my_info3);
1015 if (!NT_STATUS_IS_OK(result)) {
1016 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1017 nt_errstr(result)));
1018 return result;
1021 return NT_STATUS_OK;
1025 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1026 if (domain->online == false) {
1027 goto failed;
1030 /* failure of this is not critical */
1031 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1032 if (!NT_STATUS_IS_OK(result)) {
1033 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1034 "Won't be able to honour account lockout policies\n"));
1037 /* increase counter */
1038 my_info3->base.bad_password_count++;
1040 if (max_allowed_bad_attempts == 0) {
1041 goto failed;
1044 /* lockout user */
1045 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1047 uint32 password_properties;
1049 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1050 if (!NT_STATUS_IS_OK(result)) {
1051 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1054 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1055 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1056 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1060 failed:
1061 result = winbindd_update_creds_by_info3(domain,
1062 state->request->data.auth.user,
1063 NULL,
1064 my_info3);
1066 if (!NT_STATUS_IS_OK(result)) {
1067 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1068 nt_errstr(result)));
1071 return NT_STATUS_LOGON_FAILURE;
1074 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1075 struct winbindd_cli_state *state,
1076 struct netr_SamInfo3 **info3)
1078 struct winbindd_domain *contact_domain;
1079 fstring name_domain, name_user;
1080 NTSTATUS result;
1082 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1084 /* Parse domain and username */
1086 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1088 /* what domain should we contact? */
1090 if ( IS_DC ) {
1091 if (!(contact_domain = find_domain_from_name(name_domain))) {
1092 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1093 state->request->data.auth.user, name_domain, name_user, name_domain));
1094 result = NT_STATUS_NO_SUCH_USER;
1095 goto done;
1098 } else {
1099 if (is_myname(name_domain)) {
1100 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1101 result = NT_STATUS_NO_SUCH_USER;
1102 goto done;
1105 contact_domain = find_domain_from_name(name_domain);
1106 if (contact_domain == NULL) {
1107 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1108 state->request->data.auth.user, name_domain, name_user, name_domain));
1110 result = NT_STATUS_NO_SUCH_USER;
1111 goto done;
1115 if (contact_domain->initialized &&
1116 contact_domain->active_directory) {
1117 goto try_login;
1120 if (!contact_domain->initialized) {
1121 init_dc_connection(contact_domain);
1124 if (!contact_domain->active_directory) {
1125 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1126 return NT_STATUS_INVALID_LOGON_TYPE;
1128 try_login:
1129 result = winbindd_raw_kerberos_login(
1130 state->mem_ctx, contact_domain,
1131 state->request->data.auth.user,
1132 state->request->data.auth.pass,
1133 state->request->data.auth.krb5_cc_type,
1134 get_uid_from_request(state->request),
1135 info3, state->response->data.auth.krb5ccname);
1136 done:
1137 return result;
1140 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1141 uint32_t logon_parameters,
1142 const char *domain, const char *user,
1143 const DATA_BLOB *challenge,
1144 const DATA_BLOB *lm_resp,
1145 const DATA_BLOB *nt_resp,
1146 struct netr_SamInfo3 **pinfo3)
1148 struct auth_usersupplied_info *user_info = NULL;
1149 struct tsocket_address *local;
1150 NTSTATUS status;
1151 int rc;
1153 rc = tsocket_address_inet_from_strings(mem_ctx,
1154 "ip",
1155 "127.0.0.1",
1157 &local);
1158 if (rc < 0) {
1159 return NT_STATUS_NO_MEMORY;
1161 status = make_user_info(&user_info, user, user, domain, domain,
1162 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1163 NULL, AUTH_PASSWORD_RESPONSE);
1164 if (!NT_STATUS_IS_OK(status)) {
1165 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1166 return status;
1168 user_info->logon_parameters = logon_parameters;
1170 /* We don't want any more mapping of the username */
1171 user_info->mapped_state = True;
1173 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1174 pinfo3);
1175 free_user_info(&user_info);
1176 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1177 user, nt_errstr(status)));
1178 return status;
1181 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1182 TALLOC_CTX *mem_ctx,
1183 uint32_t logon_parameters,
1184 const char *server,
1185 const char *username,
1186 const char *domainname,
1187 const char *workstation,
1188 const uint8_t chal[8],
1189 DATA_BLOB lm_response,
1190 DATA_BLOB nt_response,
1191 struct netr_SamInfo3 **info3)
1193 int attempts = 0;
1194 int netr_attempts = 0;
1195 bool retry = false;
1196 NTSTATUS result;
1198 do {
1199 struct rpc_pipe_client *netlogon_pipe;
1200 const struct pipe_auth_data *auth;
1201 uint32_t neg_flags = 0;
1203 ZERO_STRUCTP(info3);
1204 retry = false;
1206 result = cm_connect_netlogon(domain, &netlogon_pipe);
1208 if (!NT_STATUS_IS_OK(result)) {
1209 DEBUG(3,("Could not open handle to NETLOGON pipe "
1210 "(error: %s, attempts: %d)\n",
1211 nt_errstr(result), netr_attempts));
1213 /* After the first retry always close the connection */
1214 if (netr_attempts > 0) {
1215 DEBUG(3, ("This is again a problem for this "
1216 "particular call, forcing the close "
1217 "of this connection\n"));
1218 invalidate_cm_connection(&domain->conn);
1221 /* After the second retry failover to the next DC */
1222 if (netr_attempts > 1) {
1224 * If the netlogon server is not reachable then
1225 * it is possible that the DC is rebuilding
1226 * sysvol and shutdown netlogon for that time.
1227 * We should failover to the next dc.
1229 DEBUG(3, ("This is the third problem for this "
1230 "particular call, adding DC to the "
1231 "negative cache list\n"));
1232 add_failed_connection_entry(domain->name,
1233 domain->dcname,
1234 result);
1235 saf_delete(domain->name);
1238 /* Only allow 3 retries */
1239 if (netr_attempts < 3) {
1240 DEBUG(3, ("The connection to netlogon "
1241 "failed, retrying\n"));
1242 netr_attempts++;
1243 retry = true;
1244 continue;
1246 return result;
1248 netr_attempts = 0;
1250 auth = netlogon_pipe->auth;
1251 if (netlogon_pipe->dc) {
1252 neg_flags = netlogon_pipe->dc->negotiate_flags;
1255 /* It is really important to try SamLogonEx here,
1256 * because in a clustered environment, we want to use
1257 * one machine account from multiple physical
1258 * computers.
1260 * With a normal SamLogon call, we must keep the
1261 * credentials chain updated and intact between all
1262 * users of the machine account (which would imply
1263 * cross-node communication for every NTLM logon).
1265 * (The credentials chain is not per NETLOGON pipe
1266 * connection, but globally on the server/client pair
1267 * by machine name).
1269 * When using SamLogonEx, the credentials are not
1270 * supplied, but the session key is implied by the
1271 * wrapping SamLogon context.
1273 * -- abartlet 21 April 2008
1275 * It's also important to use NetlogonValidationSamInfo4 (6),
1276 * because it relies on the rpc transport encryption
1277 * and avoids using the global netlogon schannel
1278 * session key to en/decrypt secret information
1279 * like the user_session_key for network logons.
1281 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1282 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1283 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1284 * are the indication that the server supports
1285 * NetlogonValidationSamInfo4 (6). And it must only
1286 * be used if "SealSecureChannel" is used.
1288 * -- metze 4 February 2011
1291 if (auth == NULL) {
1292 domain->can_do_validation6 = false;
1293 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1294 domain->can_do_validation6 = false;
1295 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1296 domain->can_do_validation6 = false;
1297 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1298 domain->can_do_validation6 = false;
1299 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1300 domain->can_do_validation6 = false;
1303 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1304 result = rpccli_netlogon_sam_network_logon_ex(
1305 netlogon_pipe,
1306 mem_ctx,
1307 logon_parameters,
1308 server, /* server name */
1309 username, /* user name */
1310 domainname, /* target domain */
1311 workstation, /* workstation */
1312 chal,
1314 lm_response,
1315 nt_response,
1316 info3);
1317 } else {
1318 result = rpccli_netlogon_sam_network_logon(
1319 netlogon_pipe,
1320 mem_ctx,
1321 logon_parameters,
1322 server, /* server name */
1323 username, /* user name */
1324 domainname, /* target domain */
1325 workstation, /* workstation */
1326 chal,
1327 domain->can_do_validation6 ? 6 : 3,
1328 lm_response,
1329 nt_response,
1330 info3);
1333 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1336 * It's likely that the server also does not support
1337 * validation level 6
1339 domain->can_do_validation6 = false;
1341 if (domain->can_do_samlogon_ex) {
1342 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1343 "retrying with NetSamLogon\n"));
1344 domain->can_do_samlogon_ex = false;
1345 retry = true;
1346 continue;
1350 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1351 * (no Ex). This happens against old Samba
1352 * DCs. Drop the connection.
1354 invalidate_cm_connection(&domain->conn);
1355 result = NT_STATUS_LOGON_FAILURE;
1356 break;
1359 if (domain->can_do_validation6 &&
1360 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1361 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1362 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1363 DEBUG(3,("Got a DC that can not do validation level 6, "
1364 "retrying with level 3\n"));
1365 domain->can_do_validation6 = false;
1366 retry = true;
1367 continue;
1371 * we increment this after the "feature negotiation"
1372 * for can_do_samlogon_ex and can_do_validation6
1374 attempts += 1;
1376 /* We have to try a second time as cm_connect_netlogon
1377 might not yet have noticed that the DC has killed
1378 our connection. */
1380 if (!rpccli_is_connected(netlogon_pipe)) {
1381 retry = true;
1382 continue;
1385 /* if we get access denied, a possible cause was that we had
1386 and open connection to the DC, but someone changed our
1387 machine account password out from underneath us using 'net
1388 rpc changetrustpw' */
1390 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1391 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1392 "ACCESS_DENIED. Maybe the trust account "
1393 "password was changed and we didn't know it. "
1394 "Killing connections to domain %s\n",
1395 domainname));
1396 invalidate_cm_connection(&domain->conn);
1397 retry = true;
1400 } while ( (attempts < 2) && retry );
1402 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1403 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1404 "returned NT_STATUS_IO_TIMEOUT after the retry."
1405 "Killing connections to domain %s\n",
1406 domainname));
1407 invalidate_cm_connection(&domain->conn);
1409 return result;
1412 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1413 struct winbindd_domain *domain,
1414 const char *user,
1415 const char *pass,
1416 uint32_t request_flags,
1417 struct netr_SamInfo3 **info3)
1420 uchar chal[8];
1421 DATA_BLOB lm_resp;
1422 DATA_BLOB nt_resp;
1423 unsigned char local_nt_response[24];
1424 fstring name_domain, name_user;
1425 NTSTATUS result;
1426 struct netr_SamInfo3 *my_info3 = NULL;
1428 *info3 = NULL;
1430 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1432 /* Parse domain and username */
1434 parse_domain_user(user, name_domain, name_user);
1436 /* do password magic */
1438 generate_random_buffer(chal, sizeof(chal));
1440 if (lp_client_ntlmv2_auth()) {
1441 DATA_BLOB server_chal;
1442 DATA_BLOB names_blob;
1443 server_chal = data_blob_const(chal, 8);
1445 /* note that the 'workgroup' here is for the local
1446 machine. The 'server name' must match the
1447 'workstation' passed to the actual SamLogon call.
1449 names_blob = NTLMv2_generate_names_blob(
1450 mem_ctx, lp_netbios_name(), lp_workgroup());
1452 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1453 pass,
1454 &server_chal,
1455 &names_blob,
1456 &lm_resp, &nt_resp, NULL, NULL)) {
1457 data_blob_free(&names_blob);
1458 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1459 result = NT_STATUS_NO_MEMORY;
1460 goto done;
1462 data_blob_free(&names_blob);
1463 } else {
1464 lm_resp = data_blob_null;
1465 SMBNTencrypt(pass, chal, local_nt_response);
1467 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1468 sizeof(local_nt_response));
1471 if (strequal(name_domain, get_global_sam_name())) {
1472 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1474 result = winbindd_dual_auth_passdb(
1475 mem_ctx, 0, name_domain, name_user,
1476 &chal_blob, &lm_resp, &nt_resp, info3);
1477 goto done;
1480 /* check authentication loop */
1482 result = winbind_samlogon_retry_loop(domain,
1483 mem_ctx,
1485 domain->dcname,
1486 name_user,
1487 name_domain,
1488 lp_netbios_name(),
1489 chal,
1490 lm_resp,
1491 nt_resp,
1492 &my_info3);
1493 if (!NT_STATUS_IS_OK(result)) {
1494 goto done;
1497 /* handle the case where a NT4 DC does not fill in the acct_flags in
1498 * the samlogon reply info3. When accurate info3 is required by the
1499 * caller, we look up the account flags ourselve - gd */
1501 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1502 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1504 struct rpc_pipe_client *samr_pipe;
1505 struct policy_handle samr_domain_handle, user_pol;
1506 union samr_UserInfo *info = NULL;
1507 NTSTATUS status_tmp, result_tmp;
1508 uint32 acct_flags;
1509 struct dcerpc_binding_handle *b;
1511 status_tmp = cm_connect_sam(domain, mem_ctx,
1512 &samr_pipe, &samr_domain_handle);
1514 if (!NT_STATUS_IS_OK(status_tmp)) {
1515 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1516 nt_errstr(status_tmp)));
1517 goto done;
1520 b = samr_pipe->binding_handle;
1522 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1523 &samr_domain_handle,
1524 MAXIMUM_ALLOWED_ACCESS,
1525 my_info3->base.rid,
1526 &user_pol,
1527 &result_tmp);
1529 if (!NT_STATUS_IS_OK(status_tmp)) {
1530 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1531 nt_errstr(status_tmp)));
1532 goto done;
1534 if (!NT_STATUS_IS_OK(result_tmp)) {
1535 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1536 nt_errstr(result_tmp)));
1537 goto done;
1540 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1541 &user_pol,
1543 &info,
1544 &result_tmp);
1546 if (!NT_STATUS_IS_OK(status_tmp)) {
1547 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1548 nt_errstr(status_tmp)));
1549 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1550 goto done;
1552 if (!NT_STATUS_IS_OK(result_tmp)) {
1553 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1554 nt_errstr(result_tmp)));
1555 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1556 goto done;
1559 acct_flags = info->info16.acct_flags;
1561 if (acct_flags == 0) {
1562 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1563 goto done;
1566 my_info3->base.acct_flags = acct_flags;
1568 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1570 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1573 *info3 = my_info3;
1574 done:
1575 return result;
1578 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1579 struct winbindd_cli_state *state)
1581 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1582 NTSTATUS krb5_result = NT_STATUS_OK;
1583 fstring name_domain, name_user;
1584 char *mapped_user;
1585 fstring domain_user;
1586 struct netr_SamInfo3 *info3 = NULL;
1587 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1589 /* Ensure null termination */
1590 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1592 /* Ensure null termination */
1593 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1595 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1596 state->request->data.auth.user));
1598 /* Parse domain and username */
1600 name_map_status = normalize_name_unmap(state->mem_ctx,
1601 state->request->data.auth.user,
1602 &mapped_user);
1604 /* If the name normalization didnt' actually do anything,
1605 just use the original name */
1607 if (!NT_STATUS_IS_OK(name_map_status) &&
1608 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1610 mapped_user = state->request->data.auth.user;
1613 parse_domain_user(mapped_user, name_domain, name_user);
1615 if ( mapped_user != state->request->data.auth.user ) {
1616 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1617 *lp_winbind_separator(),
1618 name_user );
1619 strlcpy( state->request->data.auth.user, domain_user,
1620 sizeof(state->request->data.auth.user));
1623 if (!domain->online) {
1624 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1625 if (domain->startup) {
1626 /* Logons are very important to users. If we're offline and
1627 we get a request within the first 30 seconds of startup,
1628 try very hard to find a DC and go online. */
1630 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1631 "request in startup mode.\n", domain->name ));
1633 winbindd_flush_negative_conn_cache(domain);
1634 result = init_dc_connection(domain);
1638 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1640 /* Check for Kerberos authentication */
1641 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1643 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1644 /* save for later */
1645 krb5_result = result;
1648 if (NT_STATUS_IS_OK(result)) {
1649 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1650 goto process_result;
1651 } else {
1652 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1655 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1656 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1657 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1658 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1659 set_domain_offline( domain );
1660 goto cached_logon;
1663 /* there are quite some NT_STATUS errors where there is no
1664 * point in retrying with a samlogon, we explictly have to take
1665 * care not to increase the bad logon counter on the DC */
1667 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1668 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1669 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1670 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1671 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1672 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1673 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1674 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1675 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1676 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1677 goto done;
1680 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1681 DEBUG(3,("falling back to samlogon\n"));
1682 goto sam_logon;
1683 } else {
1684 goto cached_logon;
1688 sam_logon:
1689 /* Check for Samlogon authentication */
1690 if (domain->online) {
1691 result = winbindd_dual_pam_auth_samlogon(
1692 state->mem_ctx, domain,
1693 state->request->data.auth.user,
1694 state->request->data.auth.pass,
1695 state->request->flags,
1696 &info3);
1698 if (NT_STATUS_IS_OK(result)) {
1699 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1700 /* add the Krb5 err if we have one */
1701 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1702 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1704 goto process_result;
1707 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1708 nt_errstr(result)));
1710 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1711 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1712 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1714 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1715 set_domain_offline( domain );
1716 goto cached_logon;
1719 if (domain->online) {
1720 /* We're still online - fail. */
1721 goto done;
1725 cached_logon:
1726 /* Check for Cached logons */
1727 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1728 lp_winbind_offline_logon()) {
1730 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1732 if (NT_STATUS_IS_OK(result)) {
1733 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1734 goto process_result;
1735 } else {
1736 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1737 goto done;
1741 process_result:
1743 if (NT_STATUS_IS_OK(result)) {
1745 struct dom_sid user_sid;
1747 /* In all codepaths where result == NT_STATUS_OK info3 must have
1748 been initialized. */
1749 if (!info3) {
1750 result = NT_STATUS_INTERNAL_ERROR;
1751 goto done;
1754 sid_compose(&user_sid, info3->base.domain_sid,
1755 info3->base.rid);
1757 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1758 &user_sid);
1759 netsamlogon_cache_store(name_user, info3);
1761 /* save name_to_sid info as early as possible (only if
1762 this is our primary domain so we don't invalidate
1763 the cache entry by storing the seq_num for the wrong
1764 domain). */
1765 if ( domain->primary ) {
1766 cache_name2sid(domain, name_domain, name_user,
1767 SID_NAME_USER, &user_sid);
1770 /* Check if the user is in the right group */
1772 result = check_info3_in_group(
1773 info3,
1774 state->request->data.auth.require_membership_of_sid);
1775 if (!NT_STATUS_IS_OK(result)) {
1776 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1777 state->request->data.auth.user,
1778 state->request->data.auth.require_membership_of_sid));
1779 goto done;
1782 result = append_auth_data(state->mem_ctx, state->response,
1783 state->request->flags, info3,
1784 name_domain, name_user);
1785 if (!NT_STATUS_IS_OK(result)) {
1786 goto done;
1789 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1790 && lp_winbind_offline_logon()) {
1792 result = winbindd_store_creds(domain,
1793 state->request->data.auth.user,
1794 state->request->data.auth.pass,
1795 info3);
1798 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1799 struct winbindd_domain *our_domain = find_our_domain();
1801 /* This is not entirely correct I believe, but it is
1802 consistent. Only apply the password policy settings
1803 too warn users for our own domain. Cannot obtain these
1804 from trusted DCs all the time so don't do it at all.
1805 -- jerry */
1807 result = NT_STATUS_NOT_SUPPORTED;
1808 if (our_domain == domain ) {
1809 result = fillup_password_policy(
1810 our_domain, state->response);
1813 if (!NT_STATUS_IS_OK(result)
1814 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1816 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1817 domain->name, nt_errstr(result)));
1818 goto done;
1822 result = NT_STATUS_OK;
1825 done:
1826 /* give us a more useful (more correct?) error code */
1827 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1828 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1829 result = NT_STATUS_NO_LOGON_SERVERS;
1832 set_auth_errors(state->response, result);
1834 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1835 state->request->data.auth.user,
1836 state->response->data.auth.nt_status_string,
1837 state->response->data.auth.pam_error));
1839 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1842 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1843 struct winbindd_cli_state *state)
1845 NTSTATUS result;
1846 struct netr_SamInfo3 *info3 = NULL;
1847 const char *name_user = NULL;
1848 const char *name_domain = NULL;
1849 const char *workstation;
1851 DATA_BLOB lm_resp, nt_resp;
1853 /* This is child-only, so no check for privileged access is needed
1854 anymore */
1856 /* Ensure null termination */
1857 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1858 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1860 name_user = state->request->data.auth_crap.user;
1861 name_domain = state->request->data.auth_crap.domain;
1862 workstation = state->request->data.auth_crap.workstation;
1864 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1865 name_domain, name_user));
1867 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1868 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1869 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1870 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1871 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1872 state->request->data.auth_crap.lm_resp_len,
1873 state->request->data.auth_crap.nt_resp_len));
1874 result = NT_STATUS_INVALID_PARAMETER;
1875 goto done;
1879 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1880 state->request->data.auth_crap.lm_resp_len);
1882 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1883 nt_resp = data_blob_talloc(state->mem_ctx,
1884 state->request->extra_data.data,
1885 state->request->data.auth_crap.nt_resp_len);
1886 } else {
1887 nt_resp = data_blob_talloc(state->mem_ctx,
1888 state->request->data.auth_crap.nt_resp,
1889 state->request->data.auth_crap.nt_resp_len);
1892 if (strequal(name_domain, get_global_sam_name())) {
1893 DATA_BLOB chal_blob = data_blob_const(
1894 state->request->data.auth_crap.chal,
1895 sizeof(state->request->data.auth_crap.chal));
1897 result = winbindd_dual_auth_passdb(
1898 state->mem_ctx,
1899 state->request->data.auth_crap.logon_parameters,
1900 name_domain, name_user,
1901 &chal_blob, &lm_resp, &nt_resp, &info3);
1902 goto process_result;
1905 result = winbind_samlogon_retry_loop(domain,
1906 state->mem_ctx,
1907 state->request->data.auth_crap.logon_parameters,
1908 domain->dcname,
1909 name_user,
1910 name_domain,
1911 /* Bug #3248 - found by Stefan Burkei. */
1912 workstation, /* We carefully set this above so use it... */
1913 state->request->data.auth_crap.chal,
1914 lm_resp,
1915 nt_resp,
1916 &info3);
1917 if (!NT_STATUS_IS_OK(result)) {
1918 goto done;
1921 process_result:
1923 if (NT_STATUS_IS_OK(result)) {
1924 struct dom_sid user_sid;
1926 sid_compose(&user_sid, info3->base.domain_sid,
1927 info3->base.rid);
1928 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1929 &user_sid);
1930 netsamlogon_cache_store(name_user, info3);
1932 /* Check if the user is in the right group */
1934 result = check_info3_in_group(
1935 info3,
1936 state->request->data.auth_crap.require_membership_of_sid);
1937 if (!NT_STATUS_IS_OK(result)) {
1938 DEBUG(3, ("User %s is not in the required group (%s), so "
1939 "crap authentication is rejected\n",
1940 state->request->data.auth_crap.user,
1941 state->request->data.auth_crap.require_membership_of_sid));
1942 goto done;
1945 result = append_auth_data(state->mem_ctx, state->response,
1946 state->request->flags, info3,
1947 name_domain, name_user);
1948 if (!NT_STATUS_IS_OK(result)) {
1949 goto done;
1953 done:
1955 /* give us a more useful (more correct?) error code */
1956 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1957 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1958 result = NT_STATUS_NO_LOGON_SERVERS;
1961 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1962 result = nt_status_squash(result);
1965 set_auth_errors(state->response, result);
1967 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1968 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1969 name_domain,
1970 name_user,
1971 state->response->data.auth.nt_status_string,
1972 state->response->data.auth.pam_error));
1974 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1977 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1978 struct winbindd_cli_state *state)
1980 char *oldpass;
1981 char *newpass = NULL;
1982 struct policy_handle dom_pol;
1983 struct rpc_pipe_client *cli = NULL;
1984 bool got_info = false;
1985 struct samr_DomInfo1 *info = NULL;
1986 struct userPwdChangeFailureInformation *reject = NULL;
1987 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1988 fstring domain, user;
1989 struct dcerpc_binding_handle *b = NULL;
1991 ZERO_STRUCT(dom_pol);
1993 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1994 state->request->data.auth.user));
1996 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1997 goto done;
2000 /* Change password */
2002 oldpass = state->request->data.chauthtok.oldpass;
2003 newpass = state->request->data.chauthtok.newpass;
2005 /* Initialize reject reason */
2006 state->response->data.auth.reject_reason = Undefined;
2008 /* Get sam handle */
2010 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2011 &dom_pol);
2012 if (!NT_STATUS_IS_OK(result)) {
2013 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2014 goto done;
2017 b = cli->binding_handle;
2019 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2020 user,
2021 newpass,
2022 oldpass,
2023 &info,
2024 &reject);
2026 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2028 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2030 fill_in_password_policy(state->response, info);
2032 state->response->data.auth.reject_reason =
2033 reject->extendedFailureReason;
2035 got_info = true;
2038 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2039 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2040 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2041 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2043 /* only fallback when the chgpasswd_user3 call is not supported */
2044 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2045 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2046 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2047 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2049 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2050 nt_errstr(result)));
2052 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2054 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2055 Map to the same status code as Windows 2003. */
2057 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2058 result = NT_STATUS_PASSWORD_RESTRICTION;
2062 done:
2064 if (NT_STATUS_IS_OK(result)
2065 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2066 && lp_winbind_offline_logon()) {
2067 result = winbindd_update_creds_by_name(contact_domain, user,
2068 newpass);
2069 /* Again, this happens when we login from gdm or xdm
2070 * and the password expires, *BUT* cached crendentials
2071 * doesn't exist. winbindd_update_creds_by_name()
2072 * returns NT_STATUS_NO_SUCH_USER.
2073 * This is not a failure.
2074 * --- BoYang
2075 * */
2076 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2077 result = NT_STATUS_OK;
2080 if (!NT_STATUS_IS_OK(result)) {
2081 DEBUG(10, ("Failed to store creds: %s\n",
2082 nt_errstr(result)));
2083 goto process_result;
2087 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2089 NTSTATUS policy_ret;
2091 policy_ret = fillup_password_policy(
2092 contact_domain, state->response);
2094 /* failure of this is non critical, it will just provide no
2095 * additional information to the client why the change has
2096 * failed - Guenther */
2098 if (!NT_STATUS_IS_OK(policy_ret)) {
2099 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2100 goto process_result;
2104 process_result:
2106 if (strequal(contact_domain->name, get_global_sam_name())) {
2107 /* FIXME: internal rpc pipe does not cache handles yet */
2108 if (b) {
2109 if (is_valid_policy_hnd(&dom_pol)) {
2110 NTSTATUS _result;
2111 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2113 TALLOC_FREE(cli);
2117 set_auth_errors(state->response, result);
2119 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2120 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2121 domain,
2122 user,
2123 state->response->data.auth.nt_status_string,
2124 state->response->data.auth.pam_error));
2126 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2129 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2130 struct winbindd_cli_state *state)
2132 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2134 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2135 state->request->data.logoff.user));
2137 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2138 result = NT_STATUS_OK;
2139 goto process_result;
2142 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2143 result = NT_STATUS_OK;
2144 goto process_result;
2147 #ifdef HAVE_KRB5
2149 if (state->request->data.logoff.uid < 0) {
2150 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2151 goto process_result;
2154 /* what we need here is to find the corresponding krb5 ccache name *we*
2155 * created for a given username and destroy it */
2157 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2158 result = NT_STATUS_OK;
2159 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2160 goto process_result;
2163 if (!ccache_entry_identical(state->request->data.logoff.user,
2164 state->request->data.logoff.uid,
2165 state->request->data.logoff.krb5ccname)) {
2166 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2167 goto process_result;
2170 result = remove_ccache(state->request->data.logoff.user);
2171 if (!NT_STATUS_IS_OK(result)) {
2172 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2173 nt_errstr(result)));
2174 goto process_result;
2178 * Remove any mlock'ed memory creds in the child
2179 * we might be using for krb5 ticket renewal.
2182 winbindd_delete_memory_creds(state->request->data.logoff.user);
2184 #else
2185 result = NT_STATUS_NOT_SUPPORTED;
2186 #endif
2188 process_result:
2191 set_auth_errors(state->response, result);
2193 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2196 /* Change user password with auth crap*/
2198 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2200 NTSTATUS result;
2201 DATA_BLOB new_nt_password;
2202 DATA_BLOB old_nt_hash_enc;
2203 DATA_BLOB new_lm_password;
2204 DATA_BLOB old_lm_hash_enc;
2205 fstring domain,user;
2206 struct policy_handle dom_pol;
2207 struct winbindd_domain *contact_domain = domainSt;
2208 struct rpc_pipe_client *cli = NULL;
2209 struct dcerpc_binding_handle *b = NULL;
2211 ZERO_STRUCT(dom_pol);
2213 /* Ensure null termination */
2214 state->request->data.chng_pswd_auth_crap.user[
2215 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2216 state->request->data.chng_pswd_auth_crap.domain[
2217 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2218 *domain = 0;
2219 *user = 0;
2221 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2222 (unsigned long)state->pid,
2223 state->request->data.chng_pswd_auth_crap.domain,
2224 state->request->data.chng_pswd_auth_crap.user));
2226 if (lp_winbind_offline_logon()) {
2227 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2228 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2229 result = NT_STATUS_ACCESS_DENIED;
2230 goto done;
2233 if (*state->request->data.chng_pswd_auth_crap.domain) {
2234 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2235 } else {
2236 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2237 domain, user);
2239 if(!*domain) {
2240 DEBUG(3,("no domain specified with username (%s) - "
2241 "failing auth\n",
2242 state->request->data.chng_pswd_auth_crap.user));
2243 result = NT_STATUS_NO_SUCH_USER;
2244 goto done;
2248 if (!*domain && lp_winbind_use_default_domain()) {
2249 fstrcpy(domain,lp_workgroup());
2252 if(!*user) {
2253 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2256 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2257 (unsigned long)state->pid, domain, user));
2259 /* Change password */
2260 new_nt_password = data_blob_const(
2261 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2262 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2264 old_nt_hash_enc = data_blob_const(
2265 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2266 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2268 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2269 new_lm_password = data_blob_const(
2270 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2271 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2273 old_lm_hash_enc = data_blob_const(
2274 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2275 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2276 } else {
2277 new_lm_password = data_blob_null;
2278 old_lm_hash_enc = data_blob_null;
2281 /* Get sam handle */
2283 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2284 if (!NT_STATUS_IS_OK(result)) {
2285 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2286 goto done;
2289 b = cli->binding_handle;
2291 result = rpccli_samr_chng_pswd_auth_crap(
2292 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2293 new_lm_password, old_lm_hash_enc);
2295 done:
2297 if (strequal(contact_domain->name, get_global_sam_name())) {
2298 /* FIXME: internal rpc pipe does not cache handles yet */
2299 if (b) {
2300 if (is_valid_policy_hnd(&dom_pol)) {
2301 NTSTATUS _result;
2302 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2304 TALLOC_FREE(cli);
2308 set_auth_errors(state->response, result);
2310 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2311 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2312 domain, user,
2313 state->response->data.auth.nt_status_string,
2314 state->response->data.auth.pam_error));
2316 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2319 #ifdef HAVE_KRB5
2320 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2321 struct PAC_LOGON_INFO **logon_info)
2323 krb5_context krbctx = NULL;
2324 krb5_error_code k5ret;
2325 krb5_keytab keytab;
2326 krb5_kt_cursor cursor;
2327 krb5_keytab_entry entry;
2328 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2330 ZERO_STRUCT(entry);
2331 ZERO_STRUCT(cursor);
2333 k5ret = krb5_init_context(&krbctx);
2334 if (k5ret) {
2335 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2336 error_message(k5ret)));
2337 status = krb5_to_nt_status(k5ret);
2338 goto out;
2341 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2342 if (k5ret) {
2343 DEBUG(1, ("Failed to get keytab: %s\n",
2344 error_message(k5ret)));
2345 status = krb5_to_nt_status(k5ret);
2346 goto out_free;
2349 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2350 if (k5ret) {
2351 DEBUG(1, ("Failed to start seq: %s\n",
2352 error_message(k5ret)));
2353 status = krb5_to_nt_status(k5ret);
2354 goto out_keytab;
2357 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2358 while (k5ret == 0) {
2359 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2360 krbctx, NULL,
2361 KRB5_KT_KEY(&entry), NULL, 0,
2362 logon_info);
2363 if (NT_STATUS_IS_OK(status)) {
2364 break;
2366 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2367 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2370 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2371 if (k5ret) {
2372 DEBUG(1, ("Failed to end seq: %s\n",
2373 error_message(k5ret)));
2375 out_keytab:
2376 k5ret = krb5_kt_close(krbctx, keytab);
2377 if (k5ret) {
2378 DEBUG(1, ("Failed to close keytab: %s\n",
2379 error_message(k5ret)));
2381 out_free:
2382 krb5_free_context(krbctx);
2383 out:
2384 return status;
2387 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2388 struct netr_SamInfo3 **info3)
2390 struct winbindd_request *req = state->request;
2391 DATA_BLOB pac_blob;
2392 struct PAC_LOGON_INFO *logon_info = NULL;
2393 NTSTATUS result;
2395 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2396 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2397 if (!NT_STATUS_IS_OK(result) &&
2398 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2399 DEBUG(1, ("Error during PAC signature verification: %s\n",
2400 nt_errstr(result)));
2401 return result;
2404 if (logon_info) {
2405 /* Signature verification succeeded, trust the PAC */
2406 netsamlogon_cache_store(NULL, &logon_info->info3);
2408 } else {
2409 /* Try without signature verification */
2410 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2411 NULL, NULL, NULL, 0,
2412 &logon_info);
2413 if (!NT_STATUS_IS_OK(result)) {
2414 DEBUG(10, ("Could not extract PAC: %s\n",
2415 nt_errstr(result)));
2416 return result;
2420 *info3 = &logon_info->info3;
2422 return NT_STATUS_OK;
2424 #else /* HAVE_KRB5 */
2425 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2426 struct netr_SamInfo3 **info3)
2428 return NT_STATUS_NO_SUCH_USER;
2430 #endif /* HAVE_KRB5 */