s3-winbind: Don't set a default directory for DIR.
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob9b6764bef702e49abf25a29539c3b69b3030b61d
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);
496 if (strnequal(type, "FILE:/", 6) ||
497 strnequal(type, "WRFILE:/", 8) ||
498 strnequal(type, "DIR:/", 5)) {
500 /* we allow only one "%u" substitution */
502 char *p;
504 p = strchr(type, '%');
505 if (p != NULL) {
507 p++;
509 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
510 gen_cc = talloc_asprintf(mem_ctx, type, uid);
516 *user_ccache_file = gen_cc;
518 if (gen_cc == NULL) {
519 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
521 if (gen_cc == NULL) {
522 DEBUG(0,("out of memory\n"));
523 return NULL;
526 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
527 (*user_ccache_file == NULL) ? " (internal)":""));
529 return gen_cc;
532 #endif
534 uid_t get_uid_from_request(struct winbindd_request *request)
536 uid_t uid;
538 uid = request->data.auth.uid;
540 if (uid < 0) {
541 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
542 return -1;
544 return uid;
547 /**********************************************************************
548 Authenticate a user with a clear text password using Kerberos and fill up
549 ccache if required
550 **********************************************************************/
552 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
553 struct winbindd_domain *domain,
554 const char *user,
555 const char *pass,
556 const char *krb5_cc_type,
557 uid_t uid,
558 struct netr_SamInfo3 **info3,
559 fstring krb5ccname)
561 #ifdef HAVE_KRB5
562 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
563 krb5_error_code krb5_ret;
564 const char *cc = NULL;
565 const char *principal_s = NULL;
566 const char *service = NULL;
567 char *realm = NULL;
568 fstring name_domain, name_user;
569 time_t ticket_lifetime = 0;
570 time_t renewal_until = 0;
571 ADS_STRUCT *ads;
572 time_t time_offset = 0;
573 const char *user_ccache_file;
574 struct PAC_LOGON_INFO *logon_info = NULL;
576 *info3 = NULL;
578 /* 1st step:
579 * prepare a krb5_cc_cache string for the user */
581 if (uid == -1) {
582 DEBUG(0,("no valid uid\n"));
585 cc = generate_krb5_ccache(mem_ctx,
586 krb5_cc_type,
587 uid,
588 &user_ccache_file);
589 if (cc == NULL) {
590 return NT_STATUS_NO_MEMORY;
594 /* 2nd step:
595 * get kerberos properties */
597 if (domain->private_data) {
598 ads = (ADS_STRUCT *)domain->private_data;
599 time_offset = ads->auth.time_offset;
603 /* 3rd step:
604 * do kerberos auth and setup ccache as the user */
606 parse_domain_user(user, name_domain, name_user);
608 realm = domain->alt_name;
609 if (!strupper_m(realm)) {
610 return NT_STATUS_INVALID_PARAMETER;
613 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
614 if (principal_s == NULL) {
615 return NT_STATUS_NO_MEMORY;
618 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
619 if (service == NULL) {
620 return NT_STATUS_NO_MEMORY;
623 /* if this is a user ccache, we need to act as the user to let the krb5
624 * library handle the chown, etc. */
626 /************************ ENTERING NON-ROOT **********************/
628 if (user_ccache_file != NULL) {
629 set_effective_uid(uid);
630 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
633 result = kerberos_return_pac(mem_ctx,
634 principal_s,
635 pass,
636 time_offset,
637 &ticket_lifetime,
638 &renewal_until,
640 true,
641 true,
642 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
643 NULL,
644 &logon_info);
645 if (user_ccache_file != NULL) {
646 gain_root_privilege();
649 /************************ RETURNED TO ROOT **********************/
651 if (!NT_STATUS_IS_OK(result)) {
652 goto failed;
655 *info3 = &logon_info->info3;
657 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
658 principal_s));
660 /* if we had a user's ccache then return that string for the pam
661 * environment */
663 if (user_ccache_file != NULL) {
665 fstrcpy(krb5ccname, user_ccache_file);
667 result = add_ccache_to_list(principal_s,
669 service,
670 user,
671 pass,
672 realm,
673 uid,
674 time(NULL),
675 ticket_lifetime,
676 renewal_until,
677 false);
679 if (!NT_STATUS_IS_OK(result)) {
680 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
681 nt_errstr(result)));
683 } else {
685 /* need to delete the memory cred cache, it is not used anymore */
687 krb5_ret = ads_kdestroy(cc);
688 if (krb5_ret) {
689 DEBUG(3,("winbindd_raw_kerberos_login: "
690 "could not destroy krb5 credential cache: "
691 "%s\n", error_message(krb5_ret)));
696 return NT_STATUS_OK;
698 failed:
700 * Do not delete an existing valid credential cache, if the user
701 * e.g. enters a wrong password
703 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
704 && user_ccache_file != NULL) {
705 return result;
708 /* we could have created a new credential cache with a valid tgt in it
709 * but we werent able to get or verify the service ticket for this
710 * local host and therefor didn't get the PAC, we need to remove that
711 * cache entirely now */
713 krb5_ret = ads_kdestroy(cc);
714 if (krb5_ret) {
715 DEBUG(3,("winbindd_raw_kerberos_login: "
716 "could not destroy krb5 credential cache: "
717 "%s\n", error_message(krb5_ret)));
720 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
721 DEBUG(3,("winbindd_raw_kerberos_login: "
722 "could not remove ccache for user %s\n",
723 user));
726 return result;
727 #else
728 return NT_STATUS_NOT_SUPPORTED;
729 #endif /* HAVE_KRB5 */
732 /****************************************************************
733 ****************************************************************/
735 bool check_request_flags(uint32_t flags)
737 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
738 WBFLAG_PAM_INFO3_TEXT |
739 WBFLAG_PAM_INFO3_NDR;
741 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
742 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
743 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
744 !(flags & flags_edata) ) {
745 return true;
748 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
749 flags));
751 return false;
754 /****************************************************************
755 ****************************************************************/
757 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
758 struct winbindd_response *resp,
759 uint32_t request_flags,
760 struct netr_SamInfo3 *info3,
761 const char *name_domain,
762 const char *name_user)
764 NTSTATUS result;
766 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
767 memcpy(resp->data.auth.user_session_key,
768 info3->base.key.key,
769 sizeof(resp->data.auth.user_session_key)
770 /* 16 */);
773 if (request_flags & WBFLAG_PAM_LMKEY) {
774 memcpy(resp->data.auth.first_8_lm_hash,
775 info3->base.LMSessKey.key,
776 sizeof(resp->data.auth.first_8_lm_hash)
777 /* 8 */);
780 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
781 result = append_unix_username(mem_ctx, resp,
782 info3, name_domain, name_user);
783 if (!NT_STATUS_IS_OK(result)) {
784 DEBUG(10,("Failed to append Unix Username: %s\n",
785 nt_errstr(result)));
786 return result;
790 /* currently, anything from here on potentially overwrites extra_data. */
792 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
793 result = append_info3_as_ndr(mem_ctx, resp, info3);
794 if (!NT_STATUS_IS_OK(result)) {
795 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
796 nt_errstr(result)));
797 return result;
801 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
802 result = append_info3_as_txt(mem_ctx, resp, info3);
803 if (!NT_STATUS_IS_OK(result)) {
804 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
805 nt_errstr(result)));
806 return result;
810 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
811 result = append_afs_token(mem_ctx, resp,
812 info3, name_domain, name_user);
813 if (!NT_STATUS_IS_OK(result)) {
814 DEBUG(10,("Failed to append AFS token: %s\n",
815 nt_errstr(result)));
816 return result;
820 return NT_STATUS_OK;
823 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
824 struct winbindd_cli_state *state,
825 struct netr_SamInfo3 **info3)
827 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
828 uint16 max_allowed_bad_attempts;
829 fstring name_domain, name_user;
830 struct dom_sid sid;
831 enum lsa_SidType type;
832 uchar new_nt_pass[NT_HASH_LEN];
833 const uint8 *cached_nt_pass;
834 const uint8 *cached_salt;
835 struct netr_SamInfo3 *my_info3;
836 time_t kickoff_time, must_change_time;
837 bool password_good = false;
838 #ifdef HAVE_KRB5
839 struct winbindd_tdc_domain *tdc_domain = NULL;
840 #endif
842 *info3 = NULL;
844 ZERO_STRUCTP(info3);
846 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
848 /* Parse domain and username */
850 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
853 if (!lookup_cached_name(name_domain,
854 name_user,
855 &sid,
856 &type)) {
857 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
858 return NT_STATUS_NO_SUCH_USER;
861 if (type != SID_NAME_USER) {
862 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
863 return NT_STATUS_LOGON_FAILURE;
866 result = winbindd_get_creds(domain,
867 state->mem_ctx,
868 &sid,
869 &my_info3,
870 &cached_nt_pass,
871 &cached_salt);
872 if (!NT_STATUS_IS_OK(result)) {
873 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
874 return result;
877 *info3 = my_info3;
879 E_md4hash(state->request->data.auth.pass, new_nt_pass);
881 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
882 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
883 if (cached_salt) {
884 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
887 if (cached_salt) {
888 /* In this case we didn't store the nt_hash itself,
889 but the MD5 combination of salt + nt_hash. */
890 uchar salted_hash[NT_HASH_LEN];
891 E_md5hash(cached_salt, new_nt_pass, salted_hash);
893 password_good = (memcmp(cached_nt_pass, salted_hash,
894 NT_HASH_LEN) == 0);
895 } else {
896 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
897 password_good = (memcmp(cached_nt_pass, new_nt_pass,
898 NT_HASH_LEN) == 0);
901 if (password_good) {
903 /* User *DOES* know the password, update logon_time and reset
904 * bad_pw_count */
906 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
908 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
909 return NT_STATUS_ACCOUNT_LOCKED_OUT;
912 if (my_info3->base.acct_flags & ACB_DISABLED) {
913 return NT_STATUS_ACCOUNT_DISABLED;
916 if (my_info3->base.acct_flags & ACB_WSTRUST) {
917 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
920 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
921 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
924 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
925 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
928 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
929 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
930 my_info3->base.acct_flags));
931 return NT_STATUS_LOGON_FAILURE;
934 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
935 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
936 return NT_STATUS_ACCOUNT_EXPIRED;
939 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
940 if (must_change_time != 0 && must_change_time < time(NULL)) {
941 /* we allow grace logons when the password has expired */
942 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
943 /* return NT_STATUS_PASSWORD_EXPIRED; */
944 goto success;
947 #ifdef HAVE_KRB5
948 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
949 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
950 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
951 /* used to cope with the case winbindd starting without network. */
952 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
954 uid_t uid = -1;
955 const char *cc = NULL;
956 char *realm = NULL;
957 const char *principal_s = NULL;
958 const char *service = NULL;
959 const char *user_ccache_file;
961 uid = get_uid_from_request(state->request);
962 if (uid == -1) {
963 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
964 return NT_STATUS_INVALID_PARAMETER;
967 cc = generate_krb5_ccache(state->mem_ctx,
968 state->request->data.auth.krb5_cc_type,
969 state->request->data.auth.uid,
970 &user_ccache_file);
971 if (cc == NULL) {
972 return NT_STATUS_NO_MEMORY;
975 realm = domain->alt_name;
976 if (!strupper_m(realm)) {
977 return NT_STATUS_INVALID_PARAMETER;
980 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
981 if (principal_s == NULL) {
982 return NT_STATUS_NO_MEMORY;
985 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
986 if (service == NULL) {
987 return NT_STATUS_NO_MEMORY;
990 if (user_ccache_file != NULL) {
992 fstrcpy(state->response->data.auth.krb5ccname,
993 user_ccache_file);
995 result = add_ccache_to_list(principal_s,
997 service,
998 state->request->data.auth.user,
999 state->request->data.auth.pass,
1000 domain->alt_name,
1001 uid,
1002 time(NULL),
1003 time(NULL) + lp_winbind_cache_time(),
1004 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1005 true);
1007 if (!NT_STATUS_IS_OK(result)) {
1008 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1009 "to add ccache to list: %s\n",
1010 nt_errstr(result)));
1014 #endif /* HAVE_KRB5 */
1015 success:
1016 /* FIXME: we possibly should handle logon hours as well (does xp when
1017 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1019 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1020 my_info3->base.bad_password_count = 0;
1022 result = winbindd_update_creds_by_info3(domain,
1023 state->request->data.auth.user,
1024 state->request->data.auth.pass,
1025 my_info3);
1026 if (!NT_STATUS_IS_OK(result)) {
1027 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1028 nt_errstr(result)));
1029 return result;
1032 return NT_STATUS_OK;
1036 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1037 if (domain->online == false) {
1038 goto failed;
1041 /* failure of this is not critical */
1042 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1043 if (!NT_STATUS_IS_OK(result)) {
1044 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1045 "Won't be able to honour account lockout policies\n"));
1048 /* increase counter */
1049 my_info3->base.bad_password_count++;
1051 if (max_allowed_bad_attempts == 0) {
1052 goto failed;
1055 /* lockout user */
1056 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1058 uint32 password_properties;
1060 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1061 if (!NT_STATUS_IS_OK(result)) {
1062 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1065 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1066 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1067 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1071 failed:
1072 result = winbindd_update_creds_by_info3(domain,
1073 state->request->data.auth.user,
1074 NULL,
1075 my_info3);
1077 if (!NT_STATUS_IS_OK(result)) {
1078 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1079 nt_errstr(result)));
1082 return NT_STATUS_LOGON_FAILURE;
1085 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1086 struct winbindd_cli_state *state,
1087 struct netr_SamInfo3 **info3)
1089 struct winbindd_domain *contact_domain;
1090 fstring name_domain, name_user;
1091 NTSTATUS result;
1093 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1095 /* Parse domain and username */
1097 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1099 /* what domain should we contact? */
1101 if ( IS_DC ) {
1102 if (!(contact_domain = find_domain_from_name(name_domain))) {
1103 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1104 state->request->data.auth.user, name_domain, name_user, name_domain));
1105 result = NT_STATUS_NO_SUCH_USER;
1106 goto done;
1109 } else {
1110 if (is_myname(name_domain)) {
1111 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1112 result = NT_STATUS_NO_SUCH_USER;
1113 goto done;
1116 contact_domain = find_domain_from_name(name_domain);
1117 if (contact_domain == NULL) {
1118 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1119 state->request->data.auth.user, name_domain, name_user, name_domain));
1121 result = NT_STATUS_NO_SUCH_USER;
1122 goto done;
1126 if (contact_domain->initialized &&
1127 contact_domain->active_directory) {
1128 goto try_login;
1131 if (!contact_domain->initialized) {
1132 init_dc_connection(contact_domain);
1135 if (!contact_domain->active_directory) {
1136 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1137 return NT_STATUS_INVALID_LOGON_TYPE;
1139 try_login:
1140 result = winbindd_raw_kerberos_login(
1141 state->mem_ctx, contact_domain,
1142 state->request->data.auth.user,
1143 state->request->data.auth.pass,
1144 state->request->data.auth.krb5_cc_type,
1145 get_uid_from_request(state->request),
1146 info3, state->response->data.auth.krb5ccname);
1147 done:
1148 return result;
1151 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1152 uint32_t logon_parameters,
1153 const char *domain, const char *user,
1154 const DATA_BLOB *challenge,
1155 const DATA_BLOB *lm_resp,
1156 const DATA_BLOB *nt_resp,
1157 struct netr_SamInfo3 **pinfo3)
1159 struct auth_usersupplied_info *user_info = NULL;
1160 struct tsocket_address *local;
1161 NTSTATUS status;
1162 int rc;
1164 rc = tsocket_address_inet_from_strings(mem_ctx,
1165 "ip",
1166 "127.0.0.1",
1168 &local);
1169 if (rc < 0) {
1170 return NT_STATUS_NO_MEMORY;
1172 status = make_user_info(&user_info, user, user, domain, domain,
1173 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1174 NULL, AUTH_PASSWORD_RESPONSE);
1175 if (!NT_STATUS_IS_OK(status)) {
1176 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1177 return status;
1179 user_info->logon_parameters = logon_parameters;
1181 /* We don't want any more mapping of the username */
1182 user_info->mapped_state = True;
1184 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1185 pinfo3);
1186 free_user_info(&user_info);
1187 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1188 user, nt_errstr(status)));
1189 return status;
1192 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1193 TALLOC_CTX *mem_ctx,
1194 uint32_t logon_parameters,
1195 const char *server,
1196 const char *username,
1197 const char *domainname,
1198 const char *workstation,
1199 const uint8_t chal[8],
1200 DATA_BLOB lm_response,
1201 DATA_BLOB nt_response,
1202 struct netr_SamInfo3 **info3)
1204 int attempts = 0;
1205 int netr_attempts = 0;
1206 bool retry = false;
1207 NTSTATUS result;
1209 do {
1210 struct rpc_pipe_client *netlogon_pipe;
1211 const struct pipe_auth_data *auth;
1212 uint32_t neg_flags = 0;
1214 ZERO_STRUCTP(info3);
1215 retry = false;
1217 result = cm_connect_netlogon(domain, &netlogon_pipe);
1219 if (!NT_STATUS_IS_OK(result)) {
1220 DEBUG(3,("Could not open handle to NETLOGON pipe "
1221 "(error: %s, attempts: %d)\n",
1222 nt_errstr(result), netr_attempts));
1224 /* After the first retry always close the connection */
1225 if (netr_attempts > 0) {
1226 DEBUG(3, ("This is again a problem for this "
1227 "particular call, forcing the close "
1228 "of this connection\n"));
1229 invalidate_cm_connection(&domain->conn);
1232 /* After the second retry failover to the next DC */
1233 if (netr_attempts > 1) {
1235 * If the netlogon server is not reachable then
1236 * it is possible that the DC is rebuilding
1237 * sysvol and shutdown netlogon for that time.
1238 * We should failover to the next dc.
1240 DEBUG(3, ("This is the third problem for this "
1241 "particular call, adding DC to the "
1242 "negative cache list\n"));
1243 add_failed_connection_entry(domain->name,
1244 domain->dcname,
1245 result);
1246 saf_delete(domain->name);
1249 /* Only allow 3 retries */
1250 if (netr_attempts < 3) {
1251 DEBUG(3, ("The connection to netlogon "
1252 "failed, retrying\n"));
1253 netr_attempts++;
1254 retry = true;
1255 continue;
1257 return result;
1259 netr_attempts = 0;
1261 auth = netlogon_pipe->auth;
1262 if (netlogon_pipe->dc) {
1263 neg_flags = netlogon_pipe->dc->negotiate_flags;
1266 /* It is really important to try SamLogonEx here,
1267 * because in a clustered environment, we want to use
1268 * one machine account from multiple physical
1269 * computers.
1271 * With a normal SamLogon call, we must keep the
1272 * credentials chain updated and intact between all
1273 * users of the machine account (which would imply
1274 * cross-node communication for every NTLM logon).
1276 * (The credentials chain is not per NETLOGON pipe
1277 * connection, but globally on the server/client pair
1278 * by machine name).
1280 * When using SamLogonEx, the credentials are not
1281 * supplied, but the session key is implied by the
1282 * wrapping SamLogon context.
1284 * -- abartlet 21 April 2008
1286 * It's also important to use NetlogonValidationSamInfo4 (6),
1287 * because it relies on the rpc transport encryption
1288 * and avoids using the global netlogon schannel
1289 * session key to en/decrypt secret information
1290 * like the user_session_key for network logons.
1292 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1293 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1294 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1295 * are the indication that the server supports
1296 * NetlogonValidationSamInfo4 (6). And it must only
1297 * be used if "SealSecureChannel" is used.
1299 * -- metze 4 February 2011
1302 if (auth == NULL) {
1303 domain->can_do_validation6 = false;
1304 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1305 domain->can_do_validation6 = false;
1306 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1307 domain->can_do_validation6 = false;
1308 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1309 domain->can_do_validation6 = false;
1310 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1311 domain->can_do_validation6 = false;
1314 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1315 result = rpccli_netlogon_sam_network_logon_ex(
1316 netlogon_pipe,
1317 mem_ctx,
1318 logon_parameters,
1319 server, /* server name */
1320 username, /* user name */
1321 domainname, /* target domain */
1322 workstation, /* workstation */
1323 chal,
1325 lm_response,
1326 nt_response,
1327 info3);
1328 } else {
1329 result = rpccli_netlogon_sam_network_logon(
1330 netlogon_pipe,
1331 mem_ctx,
1332 logon_parameters,
1333 server, /* server name */
1334 username, /* user name */
1335 domainname, /* target domain */
1336 workstation, /* workstation */
1337 chal,
1338 domain->can_do_validation6 ? 6 : 3,
1339 lm_response,
1340 nt_response,
1341 info3);
1344 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1347 * It's likely that the server also does not support
1348 * validation level 6
1350 domain->can_do_validation6 = false;
1352 if (domain->can_do_samlogon_ex) {
1353 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1354 "retrying with NetSamLogon\n"));
1355 domain->can_do_samlogon_ex = false;
1356 retry = true;
1357 continue;
1361 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1362 * (no Ex). This happens against old Samba
1363 * DCs. Drop the connection.
1365 invalidate_cm_connection(&domain->conn);
1366 result = NT_STATUS_LOGON_FAILURE;
1367 break;
1370 if (domain->can_do_validation6 &&
1371 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1372 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1373 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1374 DEBUG(3,("Got a DC that can not do validation level 6, "
1375 "retrying with level 3\n"));
1376 domain->can_do_validation6 = false;
1377 retry = true;
1378 continue;
1382 * we increment this after the "feature negotiation"
1383 * for can_do_samlogon_ex and can_do_validation6
1385 attempts += 1;
1387 /* We have to try a second time as cm_connect_netlogon
1388 might not yet have noticed that the DC has killed
1389 our connection. */
1391 if (!rpccli_is_connected(netlogon_pipe)) {
1392 retry = true;
1393 continue;
1396 /* if we get access denied, a possible cause was that we had
1397 and open connection to the DC, but someone changed our
1398 machine account password out from underneath us using 'net
1399 rpc changetrustpw' */
1401 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1402 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1403 "ACCESS_DENIED. Maybe the trust account "
1404 "password was changed and we didn't know it. "
1405 "Killing connections to domain %s\n",
1406 domainname));
1407 invalidate_cm_connection(&domain->conn);
1408 retry = true;
1411 } while ( (attempts < 2) && retry );
1413 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1414 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1415 "returned NT_STATUS_IO_TIMEOUT after the retry."
1416 "Killing connections to domain %s\n",
1417 domainname));
1418 invalidate_cm_connection(&domain->conn);
1420 return result;
1423 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1424 struct winbindd_domain *domain,
1425 const char *user,
1426 const char *pass,
1427 uint32_t request_flags,
1428 struct netr_SamInfo3 **info3)
1431 uchar chal[8];
1432 DATA_BLOB lm_resp;
1433 DATA_BLOB nt_resp;
1434 unsigned char local_nt_response[24];
1435 fstring name_domain, name_user;
1436 NTSTATUS result;
1437 struct netr_SamInfo3 *my_info3 = NULL;
1439 *info3 = NULL;
1441 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1443 /* Parse domain and username */
1445 parse_domain_user(user, name_domain, name_user);
1447 /* do password magic */
1449 generate_random_buffer(chal, sizeof(chal));
1451 if (lp_client_ntlmv2_auth()) {
1452 DATA_BLOB server_chal;
1453 DATA_BLOB names_blob;
1454 server_chal = data_blob_const(chal, 8);
1456 /* note that the 'workgroup' here is for the local
1457 machine. The 'server name' must match the
1458 'workstation' passed to the actual SamLogon call.
1460 names_blob = NTLMv2_generate_names_blob(
1461 mem_ctx, lp_netbios_name(), lp_workgroup());
1463 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1464 pass,
1465 &server_chal,
1466 &names_blob,
1467 &lm_resp, &nt_resp, NULL, NULL)) {
1468 data_blob_free(&names_blob);
1469 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1470 result = NT_STATUS_NO_MEMORY;
1471 goto done;
1473 data_blob_free(&names_blob);
1474 } else {
1475 lm_resp = data_blob_null;
1476 SMBNTencrypt(pass, chal, local_nt_response);
1478 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1479 sizeof(local_nt_response));
1482 if (strequal(name_domain, get_global_sam_name())) {
1483 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1485 result = winbindd_dual_auth_passdb(
1486 mem_ctx, 0, name_domain, name_user,
1487 &chal_blob, &lm_resp, &nt_resp, info3);
1488 goto done;
1491 /* check authentication loop */
1493 result = winbind_samlogon_retry_loop(domain,
1494 mem_ctx,
1496 domain->dcname,
1497 name_user,
1498 name_domain,
1499 lp_netbios_name(),
1500 chal,
1501 lm_resp,
1502 nt_resp,
1503 &my_info3);
1504 if (!NT_STATUS_IS_OK(result)) {
1505 goto done;
1508 /* handle the case where a NT4 DC does not fill in the acct_flags in
1509 * the samlogon reply info3. When accurate info3 is required by the
1510 * caller, we look up the account flags ourselve - gd */
1512 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1513 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1515 struct rpc_pipe_client *samr_pipe;
1516 struct policy_handle samr_domain_handle, user_pol;
1517 union samr_UserInfo *info = NULL;
1518 NTSTATUS status_tmp, result_tmp;
1519 uint32 acct_flags;
1520 struct dcerpc_binding_handle *b;
1522 status_tmp = cm_connect_sam(domain, mem_ctx,
1523 &samr_pipe, &samr_domain_handle);
1525 if (!NT_STATUS_IS_OK(status_tmp)) {
1526 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1527 nt_errstr(status_tmp)));
1528 goto done;
1531 b = samr_pipe->binding_handle;
1533 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1534 &samr_domain_handle,
1535 MAXIMUM_ALLOWED_ACCESS,
1536 my_info3->base.rid,
1537 &user_pol,
1538 &result_tmp);
1540 if (!NT_STATUS_IS_OK(status_tmp)) {
1541 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1542 nt_errstr(status_tmp)));
1543 goto done;
1545 if (!NT_STATUS_IS_OK(result_tmp)) {
1546 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1547 nt_errstr(result_tmp)));
1548 goto done;
1551 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1552 &user_pol,
1554 &info,
1555 &result_tmp);
1557 if (!NT_STATUS_IS_OK(status_tmp)) {
1558 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1559 nt_errstr(status_tmp)));
1560 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1561 goto done;
1563 if (!NT_STATUS_IS_OK(result_tmp)) {
1564 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1565 nt_errstr(result_tmp)));
1566 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1567 goto done;
1570 acct_flags = info->info16.acct_flags;
1572 if (acct_flags == 0) {
1573 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1574 goto done;
1577 my_info3->base.acct_flags = acct_flags;
1579 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1581 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1584 *info3 = my_info3;
1585 done:
1586 return result;
1589 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1590 struct winbindd_cli_state *state)
1592 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1593 NTSTATUS krb5_result = NT_STATUS_OK;
1594 fstring name_domain, name_user;
1595 char *mapped_user;
1596 fstring domain_user;
1597 struct netr_SamInfo3 *info3 = NULL;
1598 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1600 /* Ensure null termination */
1601 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1603 /* Ensure null termination */
1604 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1606 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1607 state->request->data.auth.user));
1609 /* Parse domain and username */
1611 name_map_status = normalize_name_unmap(state->mem_ctx,
1612 state->request->data.auth.user,
1613 &mapped_user);
1615 /* If the name normalization didnt' actually do anything,
1616 just use the original name */
1618 if (!NT_STATUS_IS_OK(name_map_status) &&
1619 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1621 mapped_user = state->request->data.auth.user;
1624 parse_domain_user(mapped_user, name_domain, name_user);
1626 if ( mapped_user != state->request->data.auth.user ) {
1627 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1628 *lp_winbind_separator(),
1629 name_user );
1630 strlcpy( state->request->data.auth.user, domain_user,
1631 sizeof(state->request->data.auth.user));
1634 if (!domain->online) {
1635 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1636 if (domain->startup) {
1637 /* Logons are very important to users. If we're offline and
1638 we get a request within the first 30 seconds of startup,
1639 try very hard to find a DC and go online. */
1641 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1642 "request in startup mode.\n", domain->name ));
1644 winbindd_flush_negative_conn_cache(domain);
1645 result = init_dc_connection(domain);
1649 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1651 /* Check for Kerberos authentication */
1652 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1654 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1655 /* save for later */
1656 krb5_result = result;
1659 if (NT_STATUS_IS_OK(result)) {
1660 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1661 goto process_result;
1662 } else {
1663 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1666 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1667 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1668 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1669 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1670 set_domain_offline( domain );
1671 goto cached_logon;
1674 /* there are quite some NT_STATUS errors where there is no
1675 * point in retrying with a samlogon, we explictly have to take
1676 * care not to increase the bad logon counter on the DC */
1678 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1679 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1680 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1681 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1682 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1683 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1684 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1685 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1686 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1687 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1688 goto done;
1691 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1692 DEBUG(3,("falling back to samlogon\n"));
1693 goto sam_logon;
1694 } else {
1695 goto cached_logon;
1699 sam_logon:
1700 /* Check for Samlogon authentication */
1701 if (domain->online) {
1702 result = winbindd_dual_pam_auth_samlogon(
1703 state->mem_ctx, domain,
1704 state->request->data.auth.user,
1705 state->request->data.auth.pass,
1706 state->request->flags,
1707 &info3);
1709 if (NT_STATUS_IS_OK(result)) {
1710 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1711 /* add the Krb5 err if we have one */
1712 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1713 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1715 goto process_result;
1718 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1719 nt_errstr(result)));
1721 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1722 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1723 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1725 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1726 set_domain_offline( domain );
1727 goto cached_logon;
1730 if (domain->online) {
1731 /* We're still online - fail. */
1732 goto done;
1736 cached_logon:
1737 /* Check for Cached logons */
1738 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1739 lp_winbind_offline_logon()) {
1741 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1743 if (NT_STATUS_IS_OK(result)) {
1744 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1745 goto process_result;
1746 } else {
1747 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1748 goto done;
1752 process_result:
1754 if (NT_STATUS_IS_OK(result)) {
1756 struct dom_sid user_sid;
1758 /* In all codepaths where result == NT_STATUS_OK info3 must have
1759 been initialized. */
1760 if (!info3) {
1761 result = NT_STATUS_INTERNAL_ERROR;
1762 goto done;
1765 sid_compose(&user_sid, info3->base.domain_sid,
1766 info3->base.rid);
1768 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1769 &user_sid);
1770 netsamlogon_cache_store(name_user, info3);
1772 /* save name_to_sid info as early as possible (only if
1773 this is our primary domain so we don't invalidate
1774 the cache entry by storing the seq_num for the wrong
1775 domain). */
1776 if ( domain->primary ) {
1777 cache_name2sid(domain, name_domain, name_user,
1778 SID_NAME_USER, &user_sid);
1781 /* Check if the user is in the right group */
1783 result = check_info3_in_group(
1784 info3,
1785 state->request->data.auth.require_membership_of_sid);
1786 if (!NT_STATUS_IS_OK(result)) {
1787 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1788 state->request->data.auth.user,
1789 state->request->data.auth.require_membership_of_sid));
1790 goto done;
1793 result = append_auth_data(state->mem_ctx, state->response,
1794 state->request->flags, info3,
1795 name_domain, name_user);
1796 if (!NT_STATUS_IS_OK(result)) {
1797 goto done;
1800 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1801 && lp_winbind_offline_logon()) {
1803 result = winbindd_store_creds(domain,
1804 state->request->data.auth.user,
1805 state->request->data.auth.pass,
1806 info3);
1809 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1810 struct winbindd_domain *our_domain = find_our_domain();
1812 /* This is not entirely correct I believe, but it is
1813 consistent. Only apply the password policy settings
1814 too warn users for our own domain. Cannot obtain these
1815 from trusted DCs all the time so don't do it at all.
1816 -- jerry */
1818 result = NT_STATUS_NOT_SUPPORTED;
1819 if (our_domain == domain ) {
1820 result = fillup_password_policy(
1821 our_domain, state->response);
1824 if (!NT_STATUS_IS_OK(result)
1825 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1827 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1828 domain->name, nt_errstr(result)));
1829 goto done;
1833 result = NT_STATUS_OK;
1836 done:
1837 /* give us a more useful (more correct?) error code */
1838 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1839 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1840 result = NT_STATUS_NO_LOGON_SERVERS;
1843 set_auth_errors(state->response, result);
1845 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1846 state->request->data.auth.user,
1847 state->response->data.auth.nt_status_string,
1848 state->response->data.auth.pam_error));
1850 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1853 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1854 struct winbindd_cli_state *state)
1856 NTSTATUS result;
1857 struct netr_SamInfo3 *info3 = NULL;
1858 const char *name_user = NULL;
1859 const char *name_domain = NULL;
1860 const char *workstation;
1862 DATA_BLOB lm_resp, nt_resp;
1864 /* This is child-only, so no check for privileged access is needed
1865 anymore */
1867 /* Ensure null termination */
1868 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1869 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1871 name_user = state->request->data.auth_crap.user;
1872 name_domain = state->request->data.auth_crap.domain;
1873 workstation = state->request->data.auth_crap.workstation;
1875 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1876 name_domain, name_user));
1878 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1879 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1880 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1881 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1882 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1883 state->request->data.auth_crap.lm_resp_len,
1884 state->request->data.auth_crap.nt_resp_len));
1885 result = NT_STATUS_INVALID_PARAMETER;
1886 goto done;
1890 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1891 state->request->data.auth_crap.lm_resp_len);
1893 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1894 nt_resp = data_blob_talloc(state->mem_ctx,
1895 state->request->extra_data.data,
1896 state->request->data.auth_crap.nt_resp_len);
1897 } else {
1898 nt_resp = data_blob_talloc(state->mem_ctx,
1899 state->request->data.auth_crap.nt_resp,
1900 state->request->data.auth_crap.nt_resp_len);
1903 if (strequal(name_domain, get_global_sam_name())) {
1904 DATA_BLOB chal_blob = data_blob_const(
1905 state->request->data.auth_crap.chal,
1906 sizeof(state->request->data.auth_crap.chal));
1908 result = winbindd_dual_auth_passdb(
1909 state->mem_ctx,
1910 state->request->data.auth_crap.logon_parameters,
1911 name_domain, name_user,
1912 &chal_blob, &lm_resp, &nt_resp, &info3);
1913 goto process_result;
1916 result = winbind_samlogon_retry_loop(domain,
1917 state->mem_ctx,
1918 state->request->data.auth_crap.logon_parameters,
1919 domain->dcname,
1920 name_user,
1921 name_domain,
1922 /* Bug #3248 - found by Stefan Burkei. */
1923 workstation, /* We carefully set this above so use it... */
1924 state->request->data.auth_crap.chal,
1925 lm_resp,
1926 nt_resp,
1927 &info3);
1928 if (!NT_STATUS_IS_OK(result)) {
1929 goto done;
1932 process_result:
1934 if (NT_STATUS_IS_OK(result)) {
1935 struct dom_sid user_sid;
1937 sid_compose(&user_sid, info3->base.domain_sid,
1938 info3->base.rid);
1939 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1940 &user_sid);
1941 netsamlogon_cache_store(name_user, info3);
1943 /* Check if the user is in the right group */
1945 result = check_info3_in_group(
1946 info3,
1947 state->request->data.auth_crap.require_membership_of_sid);
1948 if (!NT_STATUS_IS_OK(result)) {
1949 DEBUG(3, ("User %s is not in the required group (%s), so "
1950 "crap authentication is rejected\n",
1951 state->request->data.auth_crap.user,
1952 state->request->data.auth_crap.require_membership_of_sid));
1953 goto done;
1956 result = append_auth_data(state->mem_ctx, state->response,
1957 state->request->flags, info3,
1958 name_domain, name_user);
1959 if (!NT_STATUS_IS_OK(result)) {
1960 goto done;
1964 done:
1966 /* give us a more useful (more correct?) error code */
1967 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1968 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1969 result = NT_STATUS_NO_LOGON_SERVERS;
1972 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1973 result = nt_status_squash(result);
1976 set_auth_errors(state->response, result);
1978 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1979 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1980 name_domain,
1981 name_user,
1982 state->response->data.auth.nt_status_string,
1983 state->response->data.auth.pam_error));
1985 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1988 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1989 struct winbindd_cli_state *state)
1991 char *oldpass;
1992 char *newpass = NULL;
1993 struct policy_handle dom_pol;
1994 struct rpc_pipe_client *cli = NULL;
1995 bool got_info = false;
1996 struct samr_DomInfo1 *info = NULL;
1997 struct userPwdChangeFailureInformation *reject = NULL;
1998 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1999 fstring domain, user;
2000 struct dcerpc_binding_handle *b = NULL;
2002 ZERO_STRUCT(dom_pol);
2004 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2005 state->request->data.auth.user));
2007 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2008 goto done;
2011 /* Change password */
2013 oldpass = state->request->data.chauthtok.oldpass;
2014 newpass = state->request->data.chauthtok.newpass;
2016 /* Initialize reject reason */
2017 state->response->data.auth.reject_reason = Undefined;
2019 /* Get sam handle */
2021 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2022 &dom_pol);
2023 if (!NT_STATUS_IS_OK(result)) {
2024 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2025 goto done;
2028 b = cli->binding_handle;
2030 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2031 user,
2032 newpass,
2033 oldpass,
2034 &info,
2035 &reject);
2037 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2039 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2041 fill_in_password_policy(state->response, info);
2043 state->response->data.auth.reject_reason =
2044 reject->extendedFailureReason;
2046 got_info = true;
2049 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2050 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2051 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2052 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2054 /* only fallback when the chgpasswd_user3 call is not supported */
2055 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2056 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2057 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2058 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2060 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2061 nt_errstr(result)));
2063 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2065 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2066 Map to the same status code as Windows 2003. */
2068 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2069 result = NT_STATUS_PASSWORD_RESTRICTION;
2073 done:
2075 if (NT_STATUS_IS_OK(result)
2076 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2077 && lp_winbind_offline_logon()) {
2078 result = winbindd_update_creds_by_name(contact_domain, user,
2079 newpass);
2080 /* Again, this happens when we login from gdm or xdm
2081 * and the password expires, *BUT* cached crendentials
2082 * doesn't exist. winbindd_update_creds_by_name()
2083 * returns NT_STATUS_NO_SUCH_USER.
2084 * This is not a failure.
2085 * --- BoYang
2086 * */
2087 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2088 result = NT_STATUS_OK;
2091 if (!NT_STATUS_IS_OK(result)) {
2092 DEBUG(10, ("Failed to store creds: %s\n",
2093 nt_errstr(result)));
2094 goto process_result;
2098 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2100 NTSTATUS policy_ret;
2102 policy_ret = fillup_password_policy(
2103 contact_domain, state->response);
2105 /* failure of this is non critical, it will just provide no
2106 * additional information to the client why the change has
2107 * failed - Guenther */
2109 if (!NT_STATUS_IS_OK(policy_ret)) {
2110 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2111 goto process_result;
2115 process_result:
2117 if (strequal(contact_domain->name, get_global_sam_name())) {
2118 /* FIXME: internal rpc pipe does not cache handles yet */
2119 if (b) {
2120 if (is_valid_policy_hnd(&dom_pol)) {
2121 NTSTATUS _result;
2122 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2124 TALLOC_FREE(cli);
2128 set_auth_errors(state->response, result);
2130 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2131 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2132 domain,
2133 user,
2134 state->response->data.auth.nt_status_string,
2135 state->response->data.auth.pam_error));
2137 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2140 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2141 struct winbindd_cli_state *state)
2143 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2145 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2146 state->request->data.logoff.user));
2148 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2149 result = NT_STATUS_OK;
2150 goto process_result;
2153 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2154 result = NT_STATUS_OK;
2155 goto process_result;
2158 #ifdef HAVE_KRB5
2160 if (state->request->data.logoff.uid < 0) {
2161 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2162 goto process_result;
2165 /* what we need here is to find the corresponding krb5 ccache name *we*
2166 * created for a given username and destroy it */
2168 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2169 result = NT_STATUS_OK;
2170 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2171 goto process_result;
2174 if (!ccache_entry_identical(state->request->data.logoff.user,
2175 state->request->data.logoff.uid,
2176 state->request->data.logoff.krb5ccname)) {
2177 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2178 goto process_result;
2181 result = remove_ccache(state->request->data.logoff.user);
2182 if (!NT_STATUS_IS_OK(result)) {
2183 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2184 nt_errstr(result)));
2185 goto process_result;
2189 * Remove any mlock'ed memory creds in the child
2190 * we might be using for krb5 ticket renewal.
2193 winbindd_delete_memory_creds(state->request->data.logoff.user);
2195 #else
2196 result = NT_STATUS_NOT_SUPPORTED;
2197 #endif
2199 process_result:
2202 set_auth_errors(state->response, result);
2204 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2207 /* Change user password with auth crap*/
2209 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2211 NTSTATUS result;
2212 DATA_BLOB new_nt_password;
2213 DATA_BLOB old_nt_hash_enc;
2214 DATA_BLOB new_lm_password;
2215 DATA_BLOB old_lm_hash_enc;
2216 fstring domain,user;
2217 struct policy_handle dom_pol;
2218 struct winbindd_domain *contact_domain = domainSt;
2219 struct rpc_pipe_client *cli = NULL;
2220 struct dcerpc_binding_handle *b = NULL;
2222 ZERO_STRUCT(dom_pol);
2224 /* Ensure null termination */
2225 state->request->data.chng_pswd_auth_crap.user[
2226 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2227 state->request->data.chng_pswd_auth_crap.domain[
2228 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2229 *domain = 0;
2230 *user = 0;
2232 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2233 (unsigned long)state->pid,
2234 state->request->data.chng_pswd_auth_crap.domain,
2235 state->request->data.chng_pswd_auth_crap.user));
2237 if (lp_winbind_offline_logon()) {
2238 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2239 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2240 result = NT_STATUS_ACCESS_DENIED;
2241 goto done;
2244 if (*state->request->data.chng_pswd_auth_crap.domain) {
2245 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2246 } else {
2247 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2248 domain, user);
2250 if(!*domain) {
2251 DEBUG(3,("no domain specified with username (%s) - "
2252 "failing auth\n",
2253 state->request->data.chng_pswd_auth_crap.user));
2254 result = NT_STATUS_NO_SUCH_USER;
2255 goto done;
2259 if (!*domain && lp_winbind_use_default_domain()) {
2260 fstrcpy(domain,lp_workgroup());
2263 if(!*user) {
2264 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2267 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2268 (unsigned long)state->pid, domain, user));
2270 /* Change password */
2271 new_nt_password = data_blob_const(
2272 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2273 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2275 old_nt_hash_enc = data_blob_const(
2276 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2277 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2279 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2280 new_lm_password = data_blob_const(
2281 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2282 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2284 old_lm_hash_enc = data_blob_const(
2285 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2286 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2287 } else {
2288 new_lm_password = data_blob_null;
2289 old_lm_hash_enc = data_blob_null;
2292 /* Get sam handle */
2294 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2295 if (!NT_STATUS_IS_OK(result)) {
2296 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2297 goto done;
2300 b = cli->binding_handle;
2302 result = rpccli_samr_chng_pswd_auth_crap(
2303 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2304 new_lm_password, old_lm_hash_enc);
2306 done:
2308 if (strequal(contact_domain->name, get_global_sam_name())) {
2309 /* FIXME: internal rpc pipe does not cache handles yet */
2310 if (b) {
2311 if (is_valid_policy_hnd(&dom_pol)) {
2312 NTSTATUS _result;
2313 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2315 TALLOC_FREE(cli);
2319 set_auth_errors(state->response, result);
2321 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2322 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2323 domain, user,
2324 state->response->data.auth.nt_status_string,
2325 state->response->data.auth.pam_error));
2327 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2330 #ifdef HAVE_KRB5
2331 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2332 struct PAC_LOGON_INFO **logon_info)
2334 krb5_context krbctx = NULL;
2335 krb5_error_code k5ret;
2336 krb5_keytab keytab;
2337 krb5_kt_cursor cursor;
2338 krb5_keytab_entry entry;
2339 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2341 ZERO_STRUCT(entry);
2342 ZERO_STRUCT(cursor);
2344 k5ret = krb5_init_context(&krbctx);
2345 if (k5ret) {
2346 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2347 error_message(k5ret)));
2348 status = krb5_to_nt_status(k5ret);
2349 goto out;
2352 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2353 if (k5ret) {
2354 DEBUG(1, ("Failed to get keytab: %s\n",
2355 error_message(k5ret)));
2356 status = krb5_to_nt_status(k5ret);
2357 goto out_free;
2360 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2361 if (k5ret) {
2362 DEBUG(1, ("Failed to start seq: %s\n",
2363 error_message(k5ret)));
2364 status = krb5_to_nt_status(k5ret);
2365 goto out_keytab;
2368 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2369 while (k5ret == 0) {
2370 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2371 krbctx, NULL,
2372 KRB5_KT_KEY(&entry), NULL, 0,
2373 logon_info);
2374 if (NT_STATUS_IS_OK(status)) {
2375 break;
2377 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2378 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2381 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2382 if (k5ret) {
2383 DEBUG(1, ("Failed to end seq: %s\n",
2384 error_message(k5ret)));
2386 out_keytab:
2387 k5ret = krb5_kt_close(krbctx, keytab);
2388 if (k5ret) {
2389 DEBUG(1, ("Failed to close keytab: %s\n",
2390 error_message(k5ret)));
2392 out_free:
2393 krb5_free_context(krbctx);
2394 out:
2395 return status;
2398 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2399 struct netr_SamInfo3 **info3)
2401 struct winbindd_request *req = state->request;
2402 DATA_BLOB pac_blob;
2403 struct PAC_LOGON_INFO *logon_info = NULL;
2404 NTSTATUS result;
2406 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2407 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2408 if (!NT_STATUS_IS_OK(result) &&
2409 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2410 DEBUG(1, ("Error during PAC signature verification: %s\n",
2411 nt_errstr(result)));
2412 return result;
2415 if (logon_info) {
2416 /* Signature verification succeeded, trust the PAC */
2417 netsamlogon_cache_store(NULL, &logon_info->info3);
2419 } else {
2420 /* Try without signature verification */
2421 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2422 NULL, NULL, NULL, 0,
2423 &logon_info);
2424 if (!NT_STATUS_IS_OK(result)) {
2425 DEBUG(10, ("Could not extract PAC: %s\n",
2426 nt_errstr(result)));
2427 return result;
2431 *info3 = &logon_info->info3;
2433 return NT_STATUS_OK;
2435 #else /* HAVE_KRB5 */
2436 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2437 struct netr_SamInfo3 **info3)
2439 return NT_STATUS_NO_SUCH_USER;
2441 #endif /* HAVE_KRB5 */