s3: Add an async smbsock_connect
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob44861dfd011e2131ed1567f554bacbc4cb33d926
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 "smb_krb5.h"
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
32 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
34 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
35 struct winbindd_cli_state *state,
36 struct netr_SamInfo3 *info3)
38 char *ex;
39 size_t size;
40 uint32_t i;
42 state->response.data.auth.info3.logon_time =
43 nt_time_to_unix(info3->base.last_logon);
44 state->response.data.auth.info3.logoff_time =
45 nt_time_to_unix(info3->base.last_logoff);
46 state->response.data.auth.info3.kickoff_time =
47 nt_time_to_unix(info3->base.acct_expiry);
48 state->response.data.auth.info3.pass_last_set_time =
49 nt_time_to_unix(info3->base.last_password_change);
50 state->response.data.auth.info3.pass_can_change_time =
51 nt_time_to_unix(info3->base.allow_password_change);
52 state->response.data.auth.info3.pass_must_change_time =
53 nt_time_to_unix(info3->base.force_password_change);
55 state->response.data.auth.info3.logon_count = info3->base.logon_count;
56 state->response.data.auth.info3.bad_pw_count = info3->base.bad_password_count;
58 state->response.data.auth.info3.user_rid = info3->base.rid;
59 state->response.data.auth.info3.group_rid = info3->base.primary_gid;
60 sid_to_fstring(state->response.data.auth.info3.dom_sid, info3->base.domain_sid);
62 state->response.data.auth.info3.num_groups = info3->base.groups.count;
63 state->response.data.auth.info3.user_flgs = info3->base.user_flags;
65 state->response.data.auth.info3.acct_flags = info3->base.acct_flags;
66 state->response.data.auth.info3.num_other_sids = info3->sidcount;
68 fstrcpy(state->response.data.auth.info3.user_name,
69 info3->base.account_name.string);
70 fstrcpy(state->response.data.auth.info3.full_name,
71 info3->base.full_name.string);
72 fstrcpy(state->response.data.auth.info3.logon_script,
73 info3->base.logon_script.string);
74 fstrcpy(state->response.data.auth.info3.profile_path,
75 info3->base.profile_path.string);
76 fstrcpy(state->response.data.auth.info3.home_dir,
77 info3->base.home_directory.string);
78 fstrcpy(state->response.data.auth.info3.dir_drive,
79 info3->base.home_drive.string);
81 fstrcpy(state->response.data.auth.info3.logon_srv,
82 info3->base.logon_server.string);
83 fstrcpy(state->response.data.auth.info3.logon_dom,
84 info3->base.domain.string);
86 ex = talloc_strdup(mem_ctx, "");
87 NT_STATUS_HAVE_NO_MEMORY(ex);
89 for (i=0; i < info3->base.groups.count; i++) {
90 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
91 info3->base.groups.rids[i].rid,
92 info3->base.groups.rids[i].attributes);
93 NT_STATUS_HAVE_NO_MEMORY(ex);
96 for (i=0; i < info3->sidcount; i++) {
97 char *sid;
99 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
100 NT_STATUS_HAVE_NO_MEMORY(sid);
102 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
103 sid,
104 info3->sids[i].attributes);
105 NT_STATUS_HAVE_NO_MEMORY(ex);
107 talloc_free(sid);
110 size = talloc_get_size(ex);
112 SAFE_FREE(state->response.extra_data.data);
113 state->response.extra_data.data = SMB_MALLOC(size);
114 if (!state->response.extra_data.data) {
115 return NT_STATUS_NO_MEMORY;
117 memcpy(state->response.extra_data.data, ex, size);
118 talloc_free(ex);
120 state->response.length += size;
122 return NT_STATUS_OK;
125 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
126 struct winbindd_cli_state *state,
127 struct netr_SamInfo3 *info3)
129 DATA_BLOB blob;
130 enum ndr_err_code ndr_err;
132 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
133 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
134 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
135 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
136 return ndr_map_error2ntstatus(ndr_err);
139 SAFE_FREE(state->response.extra_data.data);
140 state->response.extra_data.data = SMB_MALLOC(blob.length);
141 if (!state->response.extra_data.data) {
142 data_blob_free(&blob);
143 return NT_STATUS_NO_MEMORY;
146 memset(state->response.extra_data.data, '\0', blob.length);
147 memcpy(state->response.extra_data.data, blob.data, blob.length);
148 state->response.length += blob.length;
150 data_blob_free(&blob);
152 return NT_STATUS_OK;
155 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
156 struct winbindd_cli_state *state,
157 const struct netr_SamInfo3 *info3,
158 const char *name_domain,
159 const char *name_user)
161 /* We've been asked to return the unix username, per
162 'winbind use default domain' settings and the like */
164 const char *nt_username, *nt_domain;
166 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
167 if (!nt_domain) {
168 /* If the server didn't give us one, just use the one
169 * we sent them */
170 nt_domain = name_domain;
173 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
174 if (!nt_username) {
175 /* If the server didn't give us one, just use the one
176 * we sent them */
177 nt_username = name_user;
180 fill_domain_username(state->response.data.auth.unix_username,
181 nt_domain, nt_username, true);
183 DEBUG(5,("Setting unix username to [%s]\n",
184 state->response.data.auth.unix_username));
186 return NT_STATUS_OK;
189 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
190 struct winbindd_cli_state *state,
191 const struct netr_SamInfo3 *info3,
192 const char *name_domain,
193 const char *name_user)
195 char *afsname = NULL;
196 char *cell;
198 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
199 if (afsname == NULL) {
200 return NT_STATUS_NO_MEMORY;
203 afsname = talloc_string_sub(mem_ctx,
204 lp_afs_username_map(),
205 "%D", name_domain);
206 afsname = talloc_string_sub(mem_ctx, afsname,
207 "%u", name_user);
208 afsname = talloc_string_sub(mem_ctx, afsname,
209 "%U", name_user);
212 DOM_SID user_sid;
213 fstring sidstr;
215 sid_copy(&user_sid, info3->base.domain_sid);
216 sid_append_rid(&user_sid, info3->base.rid);
217 sid_to_fstring(sidstr, &user_sid);
218 afsname = talloc_string_sub(mem_ctx, afsname,
219 "%s", sidstr);
222 if (afsname == NULL) {
223 return NT_STATUS_NO_MEMORY;
226 strlower_m(afsname);
228 DEBUG(10, ("Generating token for user %s\n", afsname));
230 cell = strchr(afsname, '@');
232 if (cell == NULL) {
233 return NT_STATUS_NO_MEMORY;
236 *cell = '\0';
237 cell += 1;
239 /* Append an AFS token string */
240 SAFE_FREE(state->response.extra_data.data);
241 state->response.extra_data.data =
242 afs_createtoken_str(afsname, cell);
244 if (state->response.extra_data.data != NULL) {
245 state->response.length +=
246 strlen((const char *)state->response.extra_data.data)+1;
249 return NT_STATUS_OK;
252 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
253 struct netr_SamInfo3 *info3,
254 const char *group_sid)
256 * Check whether a user belongs to a group or list of groups.
258 * @param mem_ctx talloc memory context.
259 * @param info3 user information, including group membership info.
260 * @param group_sid One or more groups , separated by commas.
262 * @return NT_STATUS_OK on success,
263 * NT_STATUS_LOGON_FAILURE if the user does not belong,
264 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
267 DOM_SID *require_membership_of_sid;
268 size_t num_require_membership_of_sid;
269 char *req_sid;
270 const char *p;
271 DOM_SID sid;
272 size_t i;
273 struct nt_user_token *token;
274 TALLOC_CTX *frame = NULL;
275 NTSTATUS status;
277 /* Parse the 'required group' SID */
279 if (!group_sid || !group_sid[0]) {
280 /* NO sid supplied, all users may access */
281 return NT_STATUS_OK;
284 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
285 DEBUG(0, ("talloc failed\n"));
286 return NT_STATUS_NO_MEMORY;
289 num_require_membership_of_sid = 0;
290 require_membership_of_sid = NULL;
292 p = group_sid;
294 frame = talloc_stackframe();
295 while (next_token_talloc(frame, &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(mem_ctx, &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 TALLOC_FREE(frame);
315 status = sid_array_from_info3(mem_ctx, info3,
316 &token->user_sids,
317 &token->num_sids,
318 true, false);
319 if (!NT_STATUS_IS_OK(status)) {
320 return status;
323 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
324 token))
325 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
326 token))) {
327 DEBUG(3, ("could not add aliases: %s\n",
328 nt_errstr(status)));
329 return status;
332 debug_nt_user_token(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 return NT_STATUS_OK;
344 /* Do not distinguish this error from a wrong username/pw */
346 return NT_STATUS_LOGON_FAILURE;
349 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
350 const char *domain_name)
352 struct winbindd_domain *domain;
354 if (IS_DC) {
355 domain = find_domain_from_name_noinit(domain_name);
356 if (domain == NULL) {
357 DEBUG(3, ("Authentication for domain [%s] refused "
358 "as it is not a trusted domain\n",
359 domain_name));
361 return domain;
364 if (is_myname(domain_name)) {
365 DEBUG(3, ("Authentication for domain %s (local domain "
366 "to this server) not supported at this "
367 "stage\n", domain_name));
368 return NULL;
371 /* we can auth against trusted domains */
372 if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
373 domain = find_domain_from_name_noinit(domain_name);
374 if (domain == NULL) {
375 DEBUG(3, ("Authentication for domain [%s] skipped "
376 "as it is not a trusted domain\n",
377 domain_name));
378 } else {
379 return domain;
383 return find_our_domain();
386 static void fill_in_password_policy(struct winbindd_response *r,
387 const struct samr_DomInfo1 *p)
389 r->data.auth.policy.min_length_password =
390 p->min_password_length;
391 r->data.auth.policy.password_history =
392 p->password_history_length;
393 r->data.auth.policy.password_properties =
394 p->password_properties;
395 r->data.auth.policy.expire =
396 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
397 r->data.auth.policy.min_passwordage =
398 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
401 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
402 struct winbindd_cli_state *state)
404 struct winbindd_methods *methods;
405 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
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 return NT_STATUS_NOT_SUPPORTED;
414 methods = domain->methods;
416 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
417 if (NT_STATUS_IS_ERR(status)) {
418 return status;
421 fill_in_password_policy(&state->response, &password_policy);
423 return NT_STATUS_OK;
426 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
427 TALLOC_CTX *mem_ctx,
428 uint16 *lockout_threshold)
430 struct winbindd_methods *methods;
431 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
432 struct samr_DomInfo12 lockout_policy;
434 *lockout_threshold = 0;
436 methods = domain->methods;
438 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
439 if (NT_STATUS_IS_ERR(status)) {
440 return status;
443 *lockout_threshold = lockout_policy.lockout_threshold;
445 return NT_STATUS_OK;
448 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
449 TALLOC_CTX *mem_ctx,
450 uint32 *password_properties)
452 struct winbindd_methods *methods;
453 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
454 struct samr_DomInfo1 password_policy;
456 *password_properties = 0;
458 methods = domain->methods;
460 status = methods->password_policy(domain, mem_ctx, &password_policy);
461 if (NT_STATUS_IS_ERR(status)) {
462 return status;
465 *password_properties = password_policy.password_properties;
467 return NT_STATUS_OK;
470 #ifdef HAVE_KRB5
472 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
473 const char *type,
474 uid_t uid,
475 bool *internal_ccache)
477 /* accept FILE and WRFILE as krb5_cc_type from the client and then
478 * build the full ccname string based on the user's uid here -
479 * Guenther*/
481 const char *gen_cc = NULL;
483 *internal_ccache = true;
485 if (uid == -1) {
486 goto memory_ccache;
489 if (!type || type[0] == '\0') {
490 goto memory_ccache;
493 if (strequal(type, "FILE")) {
494 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
495 } else if (strequal(type, "WRFILE")) {
496 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
497 } else {
498 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
499 goto memory_ccache;
502 *internal_ccache = false;
503 goto done;
505 memory_ccache:
506 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
508 done:
509 if (gen_cc == NULL) {
510 DEBUG(0,("out of memory\n"));
511 return NULL;
514 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
516 return gen_cc;
519 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
521 const char *type = state->request.data.auth.krb5_cc_type;
523 state->response.data.auth.krb5ccname[0] = '\0';
525 if (type[0] == '\0') {
526 return;
529 if (!strequal(type, "FILE") &&
530 !strequal(type, "WRFILE")) {
531 DEBUG(10,("won't return krbccname for a %s type ccache\n",
532 type));
533 return;
536 fstrcpy(state->response.data.auth.krb5ccname, cc);
539 #endif
541 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
543 uid_t uid = -1;
545 uid = state->request.data.auth.uid;
547 if (uid < 0) {
548 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
549 return -1;
551 return uid;
554 /**********************************************************************
555 Authenticate a user with a clear text password using Kerberos and fill up
556 ccache if required
557 **********************************************************************/
559 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
560 struct winbindd_cli_state *state,
561 struct netr_SamInfo3 **info3)
563 #ifdef HAVE_KRB5
564 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
565 krb5_error_code krb5_ret;
566 const char *cc = NULL;
567 const char *principal_s = NULL;
568 const char *service = NULL;
569 char *realm = NULL;
570 fstring name_domain, name_user;
571 time_t ticket_lifetime = 0;
572 time_t renewal_until = 0;
573 uid_t uid = -1;
574 ADS_STRUCT *ads;
575 time_t time_offset = 0;
576 bool internal_ccache = true;
578 ZERO_STRUCTP(info3);
580 *info3 = NULL;
582 /* 1st step:
583 * prepare a krb5_cc_cache string for the user */
585 uid = get_uid_from_state(state);
586 if (uid == -1) {
587 DEBUG(0,("no valid uid\n"));
590 cc = generate_krb5_ccache(state->mem_ctx,
591 state->request.data.auth.krb5_cc_type,
592 state->request.data.auth.uid,
593 &internal_ccache);
594 if (cc == NULL) {
595 return NT_STATUS_NO_MEMORY;
599 /* 2nd step:
600 * get kerberos properties */
602 if (domain->private_data) {
603 ads = (ADS_STRUCT *)domain->private_data;
604 time_offset = ads->auth.time_offset;
608 /* 3rd step:
609 * do kerberos auth and setup ccache as the user */
611 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
613 realm = domain->alt_name;
614 strupper_m(realm);
616 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
617 if (principal_s == NULL) {
618 return NT_STATUS_NO_MEMORY;
621 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
622 if (service == NULL) {
623 return NT_STATUS_NO_MEMORY;
626 /* if this is a user ccache, we need to act as the user to let the krb5
627 * library handle the chown, etc. */
629 /************************ ENTERING NON-ROOT **********************/
631 if (!internal_ccache) {
632 set_effective_uid(uid);
633 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
636 result = kerberos_return_info3_from_pac(state->mem_ctx,
637 principal_s,
638 state->request.data.auth.pass,
639 time_offset,
640 &ticket_lifetime,
641 &renewal_until,
643 true,
644 true,
645 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
646 info3);
647 if (!internal_ccache) {
648 gain_root_privilege();
651 /************************ RETURNED TO ROOT **********************/
653 if (!NT_STATUS_IS_OK(result)) {
654 goto failed;
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 (!internal_ccache) {
665 setup_return_cc_name(state, cc);
667 result = add_ccache_to_list(principal_s,
669 service,
670 state->request.data.auth.user,
671 realm,
672 uid,
673 time(NULL),
674 ticket_lifetime,
675 renewal_until,
676 false);
678 if (!NT_STATUS_IS_OK(result)) {
679 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
680 nt_errstr(result)));
682 } else {
684 /* need to delete the memory cred cache, it is not used anymore */
686 krb5_ret = ads_kdestroy(cc);
687 if (krb5_ret) {
688 DEBUG(3,("winbindd_raw_kerberos_login: "
689 "could not destroy krb5 credential cache: "
690 "%s\n", error_message(krb5_ret)));
695 return NT_STATUS_OK;
697 failed:
699 /* we could have created a new credential cache with a valid tgt in it
700 * but we werent able to get or verify the service ticket for this
701 * local host and therefor didn't get the PAC, we need to remove that
702 * cache entirely now */
704 krb5_ret = ads_kdestroy(cc);
705 if (krb5_ret) {
706 DEBUG(3,("winbindd_raw_kerberos_login: "
707 "could not destroy krb5 credential cache: "
708 "%s\n", error_message(krb5_ret)));
711 if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
712 DEBUG(3,("winbindd_raw_kerberos_login: "
713 "could not remove ccache for user %s\n",
714 state->request.data.auth.user));
717 return result;
718 #else
719 return NT_STATUS_NOT_SUPPORTED;
720 #endif /* HAVE_KRB5 */
723 /****************************************************************
724 ****************************************************************/
726 static bool check_request_flags(uint32_t flags)
728 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
729 WBFLAG_PAM_INFO3_TEXT |
730 WBFLAG_PAM_INFO3_NDR;
732 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
733 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
734 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
735 !(flags & flags_edata) ) {
736 return true;
739 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags));
741 return false;
744 /****************************************************************
745 ****************************************************************/
747 static NTSTATUS append_data(struct winbindd_cli_state *state,
748 struct netr_SamInfo3 *info3,
749 const char *name_domain,
750 const char *name_user)
752 NTSTATUS result;
753 uint32_t flags = state->request.flags;
755 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
756 memcpy(state->response.data.auth.user_session_key,
757 info3->base.key.key,
758 sizeof(state->response.data.auth.user_session_key)
759 /* 16 */);
762 if (flags & WBFLAG_PAM_LMKEY) {
763 memcpy(state->response.data.auth.first_8_lm_hash,
764 info3->base.LMSessKey.key,
765 sizeof(state->response.data.auth.first_8_lm_hash)
766 /* 8 */);
769 if (flags & WBFLAG_PAM_INFO3_TEXT) {
770 result = append_info3_as_txt(state->mem_ctx, state, info3);
771 if (!NT_STATUS_IS_OK(result)) {
772 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
773 nt_errstr(result)));
774 return result;
778 /* currently, anything from here on potentially overwrites extra_data. */
780 if (flags & WBFLAG_PAM_INFO3_NDR) {
781 result = append_info3_as_ndr(state->mem_ctx, state, info3);
782 if (!NT_STATUS_IS_OK(result)) {
783 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
784 nt_errstr(result)));
785 return result;
789 if (flags & WBFLAG_PAM_UNIX_NAME) {
790 result = append_unix_username(state->mem_ctx, state, info3,
791 name_domain, name_user);
792 if (!NT_STATUS_IS_OK(result)) {
793 DEBUG(10,("Failed to append Unix Username: %s\n",
794 nt_errstr(result)));
795 return result;
799 if (flags & WBFLAG_PAM_AFS_TOKEN) {
800 result = append_afs_token(state->mem_ctx, state, info3,
801 name_domain, name_user);
802 if (!NT_STATUS_IS_OK(result)) {
803 DEBUG(10,("Failed to append AFS token: %s\n",
804 nt_errstr(result)));
805 return result;
809 return NT_STATUS_OK;
812 void winbindd_pam_auth(struct winbindd_cli_state *state)
814 struct winbindd_domain *domain;
815 fstring name_domain, name_user, mapped_user;
816 char *mapped = NULL;
817 NTSTATUS result;
818 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
820 /* Ensure null termination */
821 state->request.data.auth.user
822 [sizeof(state->request.data.auth.user)-1]='\0';
824 /* Ensure null termination */
825 state->request.data.auth.pass
826 [sizeof(state->request.data.auth.pass)-1]='\0';
828 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
829 state->request.data.auth.user));
831 if (!check_request_flags(state->request.flags)) {
832 result = NT_STATUS_INVALID_PARAMETER_MIX;
833 goto done;
836 /* Parse domain and username */
838 name_map_status = normalize_name_unmap(state->mem_ctx,
839 state->request.data.auth.user,
840 &mapped);
842 /* If the name normalization didnt' actually do anything,
843 just use the original name */
845 if (NT_STATUS_IS_OK(name_map_status)
846 ||NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
847 fstrcpy(mapped_user, mapped);
848 } else {
849 fstrcpy(mapped_user, state->request.data.auth.user);
852 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
853 result = NT_STATUS_NO_SUCH_USER;
854 goto done;
857 domain = find_auth_domain(state, name_domain);
859 if (domain == NULL) {
860 result = NT_STATUS_NO_SUCH_USER;
861 goto done;
864 sendto_domain(state, domain);
865 return;
866 done:
867 set_auth_errors(&state->response, result);
868 DEBUG(5, ("Plain text authentication for %s returned %s "
869 "(PAM: %d)\n",
870 state->request.data.auth.user,
871 state->response.data.auth.nt_status_string,
872 state->response.data.auth.pam_error));
873 request_error(state);
876 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
877 struct winbindd_cli_state *state,
878 struct netr_SamInfo3 **info3)
880 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
881 uint16 max_allowed_bad_attempts;
882 fstring name_domain, name_user;
883 DOM_SID sid;
884 enum lsa_SidType type;
885 uchar new_nt_pass[NT_HASH_LEN];
886 const uint8 *cached_nt_pass;
887 const uint8 *cached_salt;
888 struct netr_SamInfo3 *my_info3;
889 time_t kickoff_time, must_change_time;
890 bool password_good = false;
891 #ifdef HAVE_KRB5
892 struct winbindd_tdc_domain *tdc_domain = NULL;
893 #endif
895 *info3 = NULL;
897 ZERO_STRUCTP(info3);
899 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
901 /* Parse domain and username */
903 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
906 if (!lookup_cached_name(state->mem_ctx,
907 name_domain,
908 name_user,
909 &sid,
910 &type)) {
911 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
912 return NT_STATUS_NO_SUCH_USER;
915 if (type != SID_NAME_USER) {
916 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
917 return NT_STATUS_LOGON_FAILURE;
920 result = winbindd_get_creds(domain,
921 state->mem_ctx,
922 &sid,
923 &my_info3,
924 &cached_nt_pass,
925 &cached_salt);
926 if (!NT_STATUS_IS_OK(result)) {
927 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
928 return result;
931 *info3 = my_info3;
933 E_md4hash(state->request.data.auth.pass, new_nt_pass);
935 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
936 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
937 if (cached_salt) {
938 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
941 if (cached_salt) {
942 /* In this case we didn't store the nt_hash itself,
943 but the MD5 combination of salt + nt_hash. */
944 uchar salted_hash[NT_HASH_LEN];
945 E_md5hash(cached_salt, new_nt_pass, salted_hash);
947 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
948 true : false;
949 } else {
950 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
951 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
952 true : false;
955 if (password_good) {
957 /* User *DOES* know the password, update logon_time and reset
958 * bad_pw_count */
960 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
962 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
963 return NT_STATUS_ACCOUNT_LOCKED_OUT;
966 if (my_info3->base.acct_flags & ACB_DISABLED) {
967 return NT_STATUS_ACCOUNT_DISABLED;
970 if (my_info3->base.acct_flags & ACB_WSTRUST) {
971 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
974 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
975 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
978 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
979 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
982 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
983 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
984 my_info3->base.acct_flags));
985 return NT_STATUS_LOGON_FAILURE;
988 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
989 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
990 return NT_STATUS_ACCOUNT_EXPIRED;
993 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
994 if (must_change_time != 0 && must_change_time < time(NULL)) {
995 /* we allow grace logons when the password has expired */
996 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
997 /* return NT_STATUS_PASSWORD_EXPIRED; */
998 goto success;
1001 #ifdef HAVE_KRB5
1002 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
1003 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
1004 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
1006 uid_t uid = -1;
1007 const char *cc = NULL;
1008 char *realm = NULL;
1009 const char *principal_s = NULL;
1010 const char *service = NULL;
1011 bool internal_ccache = false;
1013 uid = get_uid_from_state(state);
1014 if (uid == -1) {
1015 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1016 return NT_STATUS_INVALID_PARAMETER;
1019 cc = generate_krb5_ccache(state->mem_ctx,
1020 state->request.data.auth.krb5_cc_type,
1021 state->request.data.auth.uid,
1022 &internal_ccache);
1023 if (cc == NULL) {
1024 return NT_STATUS_NO_MEMORY;
1027 realm = domain->alt_name;
1028 strupper_m(realm);
1030 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1031 if (principal_s == NULL) {
1032 return NT_STATUS_NO_MEMORY;
1035 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1036 if (service == NULL) {
1037 return NT_STATUS_NO_MEMORY;
1040 if (!internal_ccache) {
1042 setup_return_cc_name(state, cc);
1044 result = add_ccache_to_list(principal_s,
1046 service,
1047 state->request.data.auth.user,
1048 domain->alt_name,
1049 uid,
1050 time(NULL),
1051 time(NULL) + lp_winbind_cache_time(),
1052 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1053 true);
1055 if (!NT_STATUS_IS_OK(result)) {
1056 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1057 "to add ccache to list: %s\n",
1058 nt_errstr(result)));
1062 #endif /* HAVE_KRB5 */
1063 success:
1064 /* FIXME: we possibly should handle logon hours as well (does xp when
1065 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1067 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1068 my_info3->base.bad_password_count = 0;
1070 result = winbindd_update_creds_by_info3(domain,
1071 state->mem_ctx,
1072 state->request.data.auth.user,
1073 state->request.data.auth.pass,
1074 my_info3);
1075 if (!NT_STATUS_IS_OK(result)) {
1076 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1077 nt_errstr(result)));
1078 return result;
1081 return NT_STATUS_OK;
1085 /* User does *NOT* know the correct password, modify info3 accordingly */
1087 /* failure of this is not critical */
1088 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1089 if (!NT_STATUS_IS_OK(result)) {
1090 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1091 "Won't be able to honour account lockout policies\n"));
1094 /* increase counter */
1095 my_info3->base.bad_password_count++;
1097 if (max_allowed_bad_attempts == 0) {
1098 goto failed;
1101 /* lockout user */
1102 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1104 uint32 password_properties;
1106 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1107 if (!NT_STATUS_IS_OK(result)) {
1108 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1111 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1112 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1113 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1117 failed:
1118 result = winbindd_update_creds_by_info3(domain,
1119 state->mem_ctx,
1120 state->request.data.auth.user,
1121 NULL,
1122 my_info3);
1124 if (!NT_STATUS_IS_OK(result)) {
1125 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1126 nt_errstr(result)));
1129 return NT_STATUS_LOGON_FAILURE;
1132 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1133 struct winbindd_cli_state *state,
1134 struct netr_SamInfo3 **info3)
1136 struct winbindd_domain *contact_domain;
1137 fstring name_domain, name_user;
1138 NTSTATUS result;
1140 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1142 /* Parse domain and username */
1144 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1146 /* what domain should we contact? */
1148 if ( IS_DC ) {
1149 if (!(contact_domain = find_domain_from_name(name_domain))) {
1150 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1151 state->request.data.auth.user, name_domain, name_user, name_domain));
1152 result = NT_STATUS_NO_SUCH_USER;
1153 goto done;
1156 } else {
1157 if (is_myname(name_domain)) {
1158 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1159 result = NT_STATUS_NO_SUCH_USER;
1160 goto done;
1163 contact_domain = find_domain_from_name(name_domain);
1164 if (contact_domain == NULL) {
1165 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1166 state->request.data.auth.user, name_domain, name_user, name_domain));
1168 contact_domain = find_our_domain();
1172 if (contact_domain->initialized &&
1173 contact_domain->active_directory) {
1174 goto try_login;
1177 if (!contact_domain->initialized) {
1178 init_dc_connection(contact_domain);
1181 if (!contact_domain->active_directory) {
1182 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1183 return NT_STATUS_INVALID_LOGON_TYPE;
1185 try_login:
1186 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1187 done:
1188 return result;
1191 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1192 TALLOC_CTX *mem_ctx,
1193 uint32 logon_parameters,
1194 const char *server,
1195 const char *username,
1196 const char *domain,
1197 const char *workstation,
1198 const uint8 chal[8],
1199 DATA_BLOB lm_response,
1200 DATA_BLOB nt_response,
1201 struct netr_SamInfo3 **info3);
1203 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1204 struct winbindd_cli_state *state,
1205 struct netr_SamInfo3 **info3)
1208 struct rpc_pipe_client *netlogon_pipe;
1209 uchar chal[8];
1210 DATA_BLOB lm_resp;
1211 DATA_BLOB nt_resp;
1212 int attempts = 0;
1213 unsigned char local_lm_response[24];
1214 unsigned char local_nt_response[24];
1215 struct winbindd_domain *contact_domain;
1216 fstring name_domain, name_user;
1217 bool retry;
1218 NTSTATUS result;
1219 struct netr_SamInfo3 *my_info3 = NULL;
1221 *info3 = NULL;
1223 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1225 /* Parse domain and username */
1227 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1229 /* do password magic */
1232 generate_random_buffer(chal, 8);
1233 if (lp_client_ntlmv2_auth()) {
1234 DATA_BLOB server_chal;
1235 DATA_BLOB names_blob;
1236 DATA_BLOB nt_response;
1237 DATA_BLOB lm_response;
1238 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1240 /* note that the 'workgroup' here is a best guess - we don't know
1241 the server's domain at this point. The 'server name' is also
1242 dodgy...
1244 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1246 if (!SMBNTLMv2encrypt(name_user, name_domain,
1247 state->request.data.auth.pass,
1248 &server_chal,
1249 &names_blob,
1250 &lm_response, &nt_response, NULL)) {
1251 data_blob_free(&names_blob);
1252 data_blob_free(&server_chal);
1253 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1254 result = NT_STATUS_NO_MEMORY;
1255 goto done;
1257 data_blob_free(&names_blob);
1258 data_blob_free(&server_chal);
1259 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1260 lm_response.length);
1261 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1262 nt_response.length);
1263 data_blob_free(&lm_response);
1264 data_blob_free(&nt_response);
1266 } else {
1267 if (lp_client_lanman_auth()
1268 && SMBencrypt(state->request.data.auth.pass,
1269 chal,
1270 local_lm_response)) {
1271 lm_resp = data_blob_talloc(state->mem_ctx,
1272 local_lm_response,
1273 sizeof(local_lm_response));
1274 } else {
1275 lm_resp = data_blob_null;
1277 SMBNTencrypt(state->request.data.auth.pass,
1278 chal,
1279 local_nt_response);
1281 nt_resp = data_blob_talloc(state->mem_ctx,
1282 local_nt_response,
1283 sizeof(local_nt_response));
1286 /* what domain should we contact? */
1288 if ( IS_DC ) {
1289 if (!(contact_domain = find_domain_from_name(name_domain))) {
1290 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1291 state->request.data.auth.user, name_domain, name_user, name_domain));
1292 result = NT_STATUS_NO_SUCH_USER;
1293 goto done;
1296 } else {
1297 if (is_myname(name_domain)) {
1298 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1299 result = NT_STATUS_NO_SUCH_USER;
1300 goto done;
1303 contact_domain = find_our_domain();
1306 /* check authentication loop */
1308 do {
1309 netlogon_fn_t logon_fn;
1311 ZERO_STRUCTP(my_info3);
1312 retry = false;
1314 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1316 if (!NT_STATUS_IS_OK(result)) {
1317 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1318 goto done;
1321 /* It is really important to try SamLogonEx here,
1322 * because in a clustered environment, we want to use
1323 * one machine account from multiple physical
1324 * computers.
1326 * With a normal SamLogon call, we must keep the
1327 * credentials chain updated and intact between all
1328 * users of the machine account (which would imply
1329 * cross-node communication for every NTLM logon).
1331 * (The credentials chain is not per NETLOGON pipe
1332 * connection, but globally on the server/client pair
1333 * by machine name).
1335 * When using SamLogonEx, the credentials are not
1336 * supplied, but the session key is implied by the
1337 * wrapping SamLogon context.
1339 * -- abartlet 21 April 2008
1342 logon_fn = contact_domain->can_do_samlogon_ex
1343 ? rpccli_netlogon_sam_network_logon_ex
1344 : rpccli_netlogon_sam_network_logon;
1346 result = logon_fn(netlogon_pipe,
1347 state->mem_ctx,
1349 contact_domain->dcname, /* server name */
1350 name_user, /* user name */
1351 name_domain, /* target domain */
1352 global_myname(), /* workstation */
1353 chal,
1354 lm_resp,
1355 nt_resp,
1356 &my_info3);
1357 attempts += 1;
1359 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1360 && contact_domain->can_do_samlogon_ex) {
1361 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1362 "retrying with NetSamLogon\n"));
1363 contact_domain->can_do_samlogon_ex = false;
1364 retry = true;
1365 continue;
1368 /* We have to try a second time as cm_connect_netlogon
1369 might not yet have noticed that the DC has killed
1370 our connection. */
1372 if (!rpccli_is_connected(netlogon_pipe)) {
1373 retry = true;
1374 continue;
1377 /* if we get access denied, a possible cause was that we had
1378 and open connection to the DC, but someone changed our
1379 machine account password out from underneath us using 'net
1380 rpc changetrustpw' */
1382 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1383 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1384 "ACCESS_DENIED. Maybe the trust account "
1385 "password was changed and we didn't know it. "
1386 "Killing connections to domain %s\n",
1387 name_domain));
1388 invalidate_cm_connection(&contact_domain->conn);
1389 retry = true;
1392 } while ( (attempts < 2) && retry );
1394 /* handle the case where a NT4 DC does not fill in the acct_flags in
1395 * the samlogon reply info3. When accurate info3 is required by the
1396 * caller, we look up the account flags ourselve - gd */
1398 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1399 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1401 struct rpc_pipe_client *samr_pipe;
1402 struct policy_handle samr_domain_handle, user_pol;
1403 union samr_UserInfo *info = NULL;
1404 NTSTATUS status_tmp;
1405 uint32 acct_flags;
1407 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1408 &samr_pipe, &samr_domain_handle);
1410 if (!NT_STATUS_IS_OK(status_tmp)) {
1411 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1412 nt_errstr(status_tmp)));
1413 goto done;
1416 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1417 &samr_domain_handle,
1418 MAXIMUM_ALLOWED_ACCESS,
1419 my_info3->base.rid,
1420 &user_pol);
1422 if (!NT_STATUS_IS_OK(status_tmp)) {
1423 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1424 nt_errstr(status_tmp)));
1425 goto done;
1428 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1429 &user_pol,
1431 &info);
1433 if (!NT_STATUS_IS_OK(status_tmp)) {
1434 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1435 nt_errstr(status_tmp)));
1436 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1437 goto done;
1440 acct_flags = info->info16.acct_flags;
1442 if (acct_flags == 0) {
1443 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1444 goto done;
1447 my_info3->base.acct_flags = acct_flags;
1449 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1451 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1454 *info3 = my_info3;
1455 done:
1456 return result;
1459 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1460 struct winbindd_cli_state *state)
1462 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1463 NTSTATUS krb5_result = NT_STATUS_OK;
1464 fstring name_domain, name_user;
1465 char *mapped_user;
1466 fstring domain_user;
1467 struct netr_SamInfo3 *info3 = NULL;
1468 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1470 /* Ensure null termination */
1471 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1473 /* Ensure null termination */
1474 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1476 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1477 state->request.data.auth.user));
1479 if (!check_request_flags(state->request.flags)) {
1480 result = NT_STATUS_INVALID_PARAMETER_MIX;
1481 goto done;
1484 /* Parse domain and username */
1486 name_map_status = normalize_name_unmap(state->mem_ctx,
1487 state->request.data.auth.user,
1488 &mapped_user);
1490 /* If the name normalization didnt' actually do anything,
1491 just use the original name */
1493 if (!NT_STATUS_IS_OK(name_map_status) &&
1494 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1496 mapped_user = state->request.data.auth.user;
1499 parse_domain_user(mapped_user, name_domain, name_user);
1501 if ( mapped_user != state->request.data.auth.user ) {
1502 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1503 safe_strcpy( state->request.data.auth.user, domain_user,
1504 sizeof(state->request.data.auth.user)-1 );
1507 if (domain->online == false) {
1508 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1509 if (domain->startup) {
1510 /* Logons are very important to users. If we're offline and
1511 we get a request within the first 30 seconds of startup,
1512 try very hard to find a DC and go online. */
1514 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1515 "request in startup mode.\n", domain->name ));
1517 winbindd_flush_negative_conn_cache(domain);
1518 result = init_dc_connection(domain);
1522 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1524 /* Check for Kerberos authentication */
1525 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1527 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1528 /* save for later */
1529 krb5_result = result;
1532 if (NT_STATUS_IS_OK(result)) {
1533 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1534 goto process_result;
1535 } else {
1536 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1539 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1541 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1542 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1543 set_domain_offline( domain );
1544 goto cached_logon;
1547 /* there are quite some NT_STATUS errors where there is no
1548 * point in retrying with a samlogon, we explictly have to take
1549 * care not to increase the bad logon counter on the DC */
1551 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1552 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1553 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1554 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1555 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1556 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1557 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1558 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1559 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1560 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1561 goto process_result;
1564 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1565 DEBUG(3,("falling back to samlogon\n"));
1566 goto sam_logon;
1567 } else {
1568 goto cached_logon;
1572 sam_logon:
1573 /* Check for Samlogon authentication */
1574 if (domain->online) {
1575 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1577 if (NT_STATUS_IS_OK(result)) {
1578 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1579 /* add the Krb5 err if we have one */
1580 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1581 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1583 goto process_result;
1586 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1587 nt_errstr(result)));
1589 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1590 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1591 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1593 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1594 set_domain_offline( domain );
1595 goto cached_logon;
1598 if (domain->online) {
1599 /* We're still online - fail. */
1600 goto done;
1604 cached_logon:
1605 /* Check for Cached logons */
1606 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1607 lp_winbind_offline_logon()) {
1609 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1611 if (NT_STATUS_IS_OK(result)) {
1612 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1613 goto process_result;
1614 } else {
1615 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1616 goto done;
1620 process_result:
1622 if (NT_STATUS_IS_OK(result)) {
1624 DOM_SID user_sid;
1626 /* In all codepaths where result == NT_STATUS_OK info3 must have
1627 been initialized. */
1628 if (!info3) {
1629 result = NT_STATUS_INTERNAL_ERROR;
1630 goto done;
1633 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1634 netsamlogon_cache_store(name_user, info3);
1636 /* save name_to_sid info as early as possible (only if
1637 this is our primary domain so we don't invalidate
1638 the cache entry by storing the seq_num for the wrong
1639 domain). */
1640 if ( domain->primary ) {
1641 sid_compose(&user_sid, info3->base.domain_sid,
1642 info3->base.rid);
1643 cache_name2sid(domain, name_domain, name_user,
1644 SID_NAME_USER, &user_sid);
1647 /* Check if the user is in the right group */
1649 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1650 state->request.data.auth.require_membership_of_sid))) {
1651 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1652 state->request.data.auth.user,
1653 state->request.data.auth.require_membership_of_sid));
1654 goto done;
1657 result = append_data(state, info3, name_domain, name_user);
1658 if (!NT_STATUS_IS_OK(result)) {
1659 goto done;
1662 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1664 /* Store in-memory creds for single-signon using ntlm_auth. */
1665 result = winbindd_add_memory_creds(state->request.data.auth.user,
1666 get_uid_from_state(state),
1667 state->request.data.auth.pass);
1669 if (!NT_STATUS_IS_OK(result)) {
1670 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1671 goto done;
1674 if (lp_winbind_offline_logon()) {
1675 result = winbindd_store_creds(domain,
1676 state->mem_ctx,
1677 state->request.data.auth.user,
1678 state->request.data.auth.pass,
1679 info3, NULL);
1680 if (!NT_STATUS_IS_OK(result)) {
1682 /* Release refcount. */
1683 winbindd_delete_memory_creds(state->request.data.auth.user);
1685 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1686 goto done;
1692 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1693 struct winbindd_domain *our_domain = find_our_domain();
1695 /* This is not entirely correct I believe, but it is
1696 consistent. Only apply the password policy settings
1697 too warn users for our own domain. Cannot obtain these
1698 from trusted DCs all the time so don't do it at all.
1699 -- jerry */
1701 result = NT_STATUS_NOT_SUPPORTED;
1702 if (our_domain == domain ) {
1703 result = fillup_password_policy(our_domain, state);
1706 if (!NT_STATUS_IS_OK(result)
1707 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1709 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1710 domain->name, nt_errstr(result)));
1711 goto done;
1715 result = NT_STATUS_OK;
1718 done:
1719 /* give us a more useful (more correct?) error code */
1720 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1721 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1722 result = NT_STATUS_NO_LOGON_SERVERS;
1725 set_auth_errors(&state->response, result);
1727 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1728 state->request.data.auth.user,
1729 state->response.data.auth.nt_status_string,
1730 state->response.data.auth.pam_error));
1732 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1736 /**********************************************************************
1737 Challenge Response Authentication Protocol
1738 **********************************************************************/
1740 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1742 struct winbindd_domain *domain = NULL;
1743 const char *domain_name = NULL;
1744 NTSTATUS result;
1746 if (!check_request_flags(state->request.flags)) {
1747 result = NT_STATUS_INVALID_PARAMETER_MIX;
1748 goto done;
1751 if (!state->privileged) {
1752 char *error_string = NULL;
1753 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1754 "denied. !\n"));
1755 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1756 "on %s are set correctly.\n",
1757 get_winbind_priv_pipe_dir()));
1758 /* send a better message than ACCESS_DENIED */
1759 error_string = talloc_asprintf(state->mem_ctx,
1760 "winbind client not authorized "
1761 "to use winbindd_pam_auth_crap."
1762 " Ensure permissions on %s "
1763 "are set correctly.",
1764 get_winbind_priv_pipe_dir());
1765 fstrcpy(state->response.data.auth.error_string, error_string);
1766 result = NT_STATUS_ACCESS_DENIED;
1767 goto done;
1770 /* Ensure null termination */
1771 state->request.data.auth_crap.user
1772 [sizeof(state->request.data.auth_crap.user)-1]=0;
1773 state->request.data.auth_crap.domain
1774 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1776 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1777 (unsigned long)state->pid,
1778 state->request.data.auth_crap.domain,
1779 state->request.data.auth_crap.user));
1781 if (*state->request.data.auth_crap.domain != '\0') {
1782 domain_name = state->request.data.auth_crap.domain;
1783 } else if (lp_winbind_use_default_domain()) {
1784 domain_name = lp_workgroup();
1787 if (domain_name != NULL)
1788 domain = find_auth_domain(state, domain_name);
1790 if (domain != NULL) {
1791 sendto_domain(state, domain);
1792 return;
1795 result = NT_STATUS_NO_SUCH_USER;
1797 done:
1798 set_auth_errors(&state->response, result);
1799 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1800 state->request.data.auth_crap.domain,
1801 state->request.data.auth_crap.user,
1802 state->response.data.auth.nt_status_string,
1803 state->response.data.auth.pam_error));
1804 request_error(state);
1805 return;
1809 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1810 struct winbindd_cli_state *state)
1812 NTSTATUS result;
1813 struct netr_SamInfo3 *info3 = NULL;
1814 struct rpc_pipe_client *netlogon_pipe;
1815 const char *name_user = NULL;
1816 const char *name_domain = NULL;
1817 const char *workstation;
1818 struct winbindd_domain *contact_domain;
1819 int attempts = 0;
1820 bool retry;
1822 DATA_BLOB lm_resp, nt_resp;
1824 /* This is child-only, so no check for privileged access is needed
1825 anymore */
1827 /* Ensure null termination */
1828 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1829 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1831 if (!check_request_flags(state->request.flags)) {
1832 result = NT_STATUS_INVALID_PARAMETER_MIX;
1833 goto done;
1836 name_user = state->request.data.auth_crap.user;
1838 if (*state->request.data.auth_crap.domain) {
1839 name_domain = state->request.data.auth_crap.domain;
1840 } else if (lp_winbind_use_default_domain()) {
1841 name_domain = lp_workgroup();
1842 } else {
1843 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1844 name_user));
1845 result = NT_STATUS_NO_SUCH_USER;
1846 goto done;
1849 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1850 name_domain, name_user));
1852 if (*state->request.data.auth_crap.workstation) {
1853 workstation = state->request.data.auth_crap.workstation;
1854 } else {
1855 workstation = global_myname();
1858 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1859 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1860 if (!(state->request.flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1861 state->request.extra_len != state->request.data.auth_crap.nt_resp_len) {
1862 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1863 state->request.data.auth_crap.lm_resp_len,
1864 state->request.data.auth_crap.nt_resp_len));
1865 result = NT_STATUS_INVALID_PARAMETER;
1866 goto done;
1870 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1871 state->request.data.auth_crap.lm_resp_len);
1873 if (state->request.flags & WBFLAG_BIG_NTLMV2_BLOB) {
1874 nt_resp = data_blob_talloc(state->mem_ctx,
1875 state->request.extra_data.data,
1876 state->request.data.auth_crap.nt_resp_len);
1877 } else {
1878 nt_resp = data_blob_talloc(state->mem_ctx,
1879 state->request.data.auth_crap.nt_resp,
1880 state->request.data.auth_crap.nt_resp_len);
1883 /* what domain should we contact? */
1885 if ( IS_DC ) {
1886 if (!(contact_domain = find_domain_from_name(name_domain))) {
1887 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1888 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1889 result = NT_STATUS_NO_SUCH_USER;
1890 goto done;
1892 } else {
1893 if (is_myname(name_domain)) {
1894 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1895 result = NT_STATUS_NO_SUCH_USER;
1896 goto done;
1898 contact_domain = find_our_domain();
1901 do {
1902 netlogon_fn_t logon_fn;
1904 retry = false;
1906 netlogon_pipe = NULL;
1907 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1909 if (!NT_STATUS_IS_OK(result)) {
1910 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1911 nt_errstr(result)));
1912 goto done;
1915 logon_fn = contact_domain->can_do_samlogon_ex
1916 ? rpccli_netlogon_sam_network_logon_ex
1917 : rpccli_netlogon_sam_network_logon;
1919 result = logon_fn(netlogon_pipe,
1920 state->mem_ctx,
1921 state->request.data.auth_crap.logon_parameters,
1922 contact_domain->dcname,
1923 name_user,
1924 name_domain,
1925 /* Bug #3248 - found by Stefan Burkei. */
1926 workstation, /* We carefully set this above so use it... */
1927 state->request.data.auth_crap.chal,
1928 lm_resp,
1929 nt_resp,
1930 &info3);
1932 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1933 && contact_domain->can_do_samlogon_ex) {
1934 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1935 "retrying with NetSamLogon\n"));
1936 contact_domain->can_do_samlogon_ex = false;
1937 retry = true;
1938 continue;
1941 attempts += 1;
1943 /* We have to try a second time as cm_connect_netlogon
1944 might not yet have noticed that the DC has killed
1945 our connection. */
1947 if (!rpccli_is_connected(netlogon_pipe)) {
1948 retry = true;
1949 continue;
1952 /* if we get access denied, a possible cause was that we had and open
1953 connection to the DC, but someone changed our machine account password
1954 out from underneath us using 'net rpc changetrustpw' */
1956 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1957 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1958 "ACCESS_DENIED. Maybe the trust account "
1959 "password was changed and we didn't know it. "
1960 "Killing connections to domain %s\n",
1961 name_domain));
1962 invalidate_cm_connection(&contact_domain->conn);
1963 retry = true;
1966 } while ( (attempts < 2) && retry );
1968 if (NT_STATUS_IS_OK(result)) {
1970 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1971 netsamlogon_cache_store(name_user, info3);
1973 /* Check if the user is in the right group */
1975 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1976 state->request.data.auth_crap.require_membership_of_sid))) {
1977 DEBUG(3, ("User %s is not in the required group (%s), so "
1978 "crap authentication is rejected\n",
1979 state->request.data.auth_crap.user,
1980 state->request.data.auth_crap.require_membership_of_sid));
1981 goto done;
1984 result = append_data(state, info3, name_domain, name_user);
1985 if (!NT_STATUS_IS_OK(result)) {
1986 goto done;
1990 done:
1992 /* give us a more useful (more correct?) error code */
1993 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1994 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1995 result = NT_STATUS_NO_LOGON_SERVERS;
1998 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1999 result = nt_status_squash(result);
2002 set_auth_errors(&state->response, result);
2004 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2005 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2006 name_domain,
2007 name_user,
2008 state->response.data.auth.nt_status_string,
2009 state->response.data.auth.pam_error));
2011 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2014 /* Change a user password */
2016 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2018 fstring domain, user;
2019 char *mapped_user;
2020 struct winbindd_domain *contact_domain;
2021 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2023 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2024 state->request.data.chauthtok.user));
2026 /* Setup crap */
2028 nt_status = normalize_name_unmap(state->mem_ctx,
2029 state->request.data.chauthtok.user,
2030 &mapped_user);
2032 /* Update the chauthtok name if we did any mapping */
2034 if (NT_STATUS_IS_OK(nt_status) ||
2035 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2037 fstrcpy(state->request.data.chauthtok.user, mapped_user);
2040 /* Must pass in state->...chauthtok.user because
2041 canonicalize_username() assumes an fstring(). Since
2042 we have already copied it (if necessary), this is ok. */
2044 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
2045 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2046 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2047 "(PAM: %d)\n",
2048 state->request.data.auth.user,
2049 state->response.data.auth.nt_status_string,
2050 state->response.data.auth.pam_error));
2051 request_error(state);
2052 return;
2055 contact_domain = find_domain_from_name(domain);
2056 if (!contact_domain) {
2057 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2058 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2059 state->request.data.chauthtok.user, domain, user, domain));
2060 request_error(state);
2061 return;
2064 sendto_domain(state, contact_domain);
2067 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2068 struct winbindd_cli_state *state)
2070 char *oldpass;
2071 char *newpass = NULL;
2072 struct policy_handle dom_pol;
2073 struct rpc_pipe_client *cli;
2074 bool got_info = false;
2075 struct samr_DomInfo1 *info = NULL;
2076 struct samr_ChangeReject *reject = NULL;
2077 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2078 fstring domain, user;
2080 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2081 state->request.data.auth.user));
2083 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2084 goto done;
2087 /* Change password */
2089 oldpass = state->request.data.chauthtok.oldpass;
2090 newpass = state->request.data.chauthtok.newpass;
2092 /* Initialize reject reason */
2093 state->response.data.auth.reject_reason = Undefined;
2095 /* Get sam handle */
2097 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2098 &dom_pol);
2099 if (!NT_STATUS_IS_OK(result)) {
2100 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2101 goto done;
2104 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2105 user,
2106 newpass,
2107 oldpass,
2108 &info,
2109 &reject);
2111 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2113 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2115 fill_in_password_policy(&state->response, info);
2117 state->response.data.auth.reject_reason =
2118 reject->reason;
2120 got_info = true;
2123 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2124 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2125 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2126 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2128 /* only fallback when the chgpasswd_user3 call is not supported */
2129 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2130 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2131 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2132 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2134 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2135 nt_errstr(result)));
2137 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2139 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2140 Map to the same status code as Windows 2003. */
2142 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2143 result = NT_STATUS_PASSWORD_RESTRICTION;
2147 done:
2149 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2151 /* Update the single sign-on memory creds. */
2152 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2153 newpass);
2155 /* When we login from gdm or xdm and password expires,
2156 * we change password, but there are no memory crendentials
2157 * So, winbindd_replace_memory_creds() returns
2158 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2159 * --- BoYang
2160 * */
2161 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2162 result = NT_STATUS_OK;
2165 if (!NT_STATUS_IS_OK(result)) {
2166 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2167 goto process_result;
2170 if (lp_winbind_offline_logon()) {
2171 result = winbindd_update_creds_by_name(contact_domain,
2172 state->mem_ctx, user,
2173 newpass);
2174 /* Again, this happens when we login from gdm or xdm
2175 * and the password expires, *BUT* cached crendentials
2176 * doesn't exist. winbindd_update_creds_by_name()
2177 * returns NT_STATUS_NO_SUCH_USER.
2178 * This is not a failure.
2179 * --- BoYang
2180 * */
2181 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2182 result = NT_STATUS_OK;
2185 if (!NT_STATUS_IS_OK(result)) {
2186 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2187 goto process_result;
2192 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2194 NTSTATUS policy_ret;
2196 policy_ret = fillup_password_policy(contact_domain, state);
2198 /* failure of this is non critical, it will just provide no
2199 * additional information to the client why the change has
2200 * failed - Guenther */
2202 if (!NT_STATUS_IS_OK(policy_ret)) {
2203 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2204 goto process_result;
2208 process_result:
2210 set_auth_errors(&state->response, result);
2212 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2213 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2214 domain,
2215 user,
2216 state->response.data.auth.nt_status_string,
2217 state->response.data.auth.pam_error));
2219 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2222 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2224 struct winbindd_domain *domain;
2225 fstring name_domain, user;
2226 uid_t caller_uid = (uid_t)-1;
2227 uid_t request_uid = state->request.data.logoff.uid;
2229 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2230 state->request.data.logoff.user));
2232 /* Ensure null termination */
2233 state->request.data.logoff.user
2234 [sizeof(state->request.data.logoff.user)-1]='\0';
2236 state->request.data.logoff.krb5ccname
2237 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2239 if (request_uid == (gid_t)-1) {
2240 goto failed;
2243 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2244 goto failed;
2247 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2248 goto failed;
2251 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2252 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2253 strerror(errno)));
2254 goto failed;
2257 switch (caller_uid) {
2258 case -1:
2259 goto failed;
2260 case 0:
2261 /* root must be able to logoff any user - gd */
2262 state->request.data.logoff.uid = request_uid;
2263 break;
2264 default:
2265 if (caller_uid != request_uid) {
2266 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2267 goto failed;
2269 state->request.data.logoff.uid = caller_uid;
2270 break;
2273 sendto_domain(state, domain);
2274 return;
2276 failed:
2277 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2278 DEBUG(5, ("Pam Logoff for %s returned %s "
2279 "(PAM: %d)\n",
2280 state->request.data.logoff.user,
2281 state->response.data.auth.nt_status_string,
2282 state->response.data.auth.pam_error));
2283 request_error(state);
2284 return;
2287 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2288 struct winbindd_cli_state *state)
2290 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2292 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2293 state->request.data.logoff.user));
2295 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2296 result = NT_STATUS_OK;
2297 goto process_result;
2300 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2301 result = NT_STATUS_OK;
2302 goto process_result;
2305 #ifdef HAVE_KRB5
2307 if (state->request.data.logoff.uid < 0) {
2308 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2309 goto process_result;
2312 /* what we need here is to find the corresponding krb5 ccache name *we*
2313 * created for a given username and destroy it */
2315 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2316 result = NT_STATUS_OK;
2317 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2318 goto process_result;
2321 if (!ccache_entry_identical(state->request.data.logoff.user,
2322 state->request.data.logoff.uid,
2323 state->request.data.logoff.krb5ccname)) {
2324 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2325 goto process_result;
2328 result = remove_ccache(state->request.data.logoff.user);
2329 if (!NT_STATUS_IS_OK(result)) {
2330 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2331 nt_errstr(result)));
2332 goto process_result;
2335 #else
2336 result = NT_STATUS_NOT_SUPPORTED;
2337 #endif
2339 process_result:
2341 winbindd_delete_memory_creds(state->request.data.logoff.user);
2343 set_auth_errors(&state->response, result);
2345 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2348 /* Change user password with auth crap*/
2350 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2352 struct winbindd_domain *domain = NULL;
2353 const char *domain_name = NULL;
2355 /* Ensure null termination */
2356 state->request.data.chng_pswd_auth_crap.user[
2357 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2358 state->request.data.chng_pswd_auth_crap.domain[
2359 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2361 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2362 (unsigned long)state->pid,
2363 state->request.data.chng_pswd_auth_crap.domain,
2364 state->request.data.chng_pswd_auth_crap.user));
2366 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2367 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2368 } else if (lp_winbind_use_default_domain()) {
2369 domain_name = lp_workgroup();
2372 if (domain_name != NULL)
2373 domain = find_domain_from_name(domain_name);
2375 if (domain != NULL) {
2376 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2377 "%s\n", (unsigned long)state->pid,domain->name));
2378 sendto_domain(state, domain);
2379 return;
2382 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2383 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2384 state->request.data.chng_pswd_auth_crap.domain,
2385 state->request.data.chng_pswd_auth_crap.user,
2386 state->response.data.auth.nt_status_string,
2387 state->response.data.auth.pam_error));
2388 request_error(state);
2389 return;
2392 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2394 NTSTATUS result;
2395 DATA_BLOB new_nt_password;
2396 DATA_BLOB old_nt_hash_enc;
2397 DATA_BLOB new_lm_password;
2398 DATA_BLOB old_lm_hash_enc;
2399 fstring domain,user;
2400 struct policy_handle dom_pol;
2401 struct winbindd_domain *contact_domain = domainSt;
2402 struct rpc_pipe_client *cli;
2404 /* Ensure null termination */
2405 state->request.data.chng_pswd_auth_crap.user[
2406 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2407 state->request.data.chng_pswd_auth_crap.domain[
2408 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2409 *domain = 0;
2410 *user = 0;
2412 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2413 (unsigned long)state->pid,
2414 state->request.data.chng_pswd_auth_crap.domain,
2415 state->request.data.chng_pswd_auth_crap.user));
2417 if (lp_winbind_offline_logon()) {
2418 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2419 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2420 result = NT_STATUS_ACCESS_DENIED;
2421 goto done;
2424 if (*state->request.data.chng_pswd_auth_crap.domain) {
2425 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2426 } else {
2427 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2428 domain, user);
2430 if(!*domain) {
2431 DEBUG(3,("no domain specified with username (%s) - "
2432 "failing auth\n",
2433 state->request.data.chng_pswd_auth_crap.user));
2434 result = NT_STATUS_NO_SUCH_USER;
2435 goto done;
2439 if (!*domain && lp_winbind_use_default_domain()) {
2440 fstrcpy(domain,(char *)lp_workgroup());
2443 if(!*user) {
2444 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2447 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2448 (unsigned long)state->pid, domain, user));
2450 /* Change password */
2451 new_nt_password = data_blob_talloc(
2452 state->mem_ctx,
2453 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2454 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2456 old_nt_hash_enc = data_blob_talloc(
2457 state->mem_ctx,
2458 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2459 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2461 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2462 new_lm_password = data_blob_talloc(
2463 state->mem_ctx,
2464 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2465 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2467 old_lm_hash_enc = data_blob_talloc(
2468 state->mem_ctx,
2469 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2470 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2471 } else {
2472 new_lm_password.length = 0;
2473 old_lm_hash_enc.length = 0;
2476 /* Get sam handle */
2478 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2479 if (!NT_STATUS_IS_OK(result)) {
2480 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2481 goto done;
2484 result = rpccli_samr_chng_pswd_auth_crap(
2485 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2486 new_lm_password, old_lm_hash_enc);
2488 done:
2490 set_auth_errors(&state->response, result);
2492 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2493 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2494 domain, user,
2495 state->response.data.auth.nt_status_string,
2496 state->response.data.auth.pam_error));
2498 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;