s4:upgradeprovision Rework script, and reset machine account pw
[Samba/cd1.git] / source3 / winbindd / winbindd_pam.c
blob3117533f316c0bacdbd32ca96e447cdcbd71dec5
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/cli_samr.h"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
35 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
36 struct winbindd_cli_state *state,
37 struct netr_SamInfo3 *info3)
39 char *ex;
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(state->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 state->response->extra_data.data = ex;
111 state->response->length += talloc_get_size(ex);
113 return NT_STATUS_OK;
116 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
117 struct winbindd_cli_state *state,
118 struct netr_SamInfo3 *info3)
120 DATA_BLOB blob;
121 enum ndr_err_code ndr_err;
123 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
124 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
125 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
126 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
127 return ndr_map_error2ntstatus(ndr_err);
130 state->response->extra_data.data = blob.data;
131 state->response->length += blob.length;
133 return NT_STATUS_OK;
136 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
137 struct winbindd_cli_state *state,
138 const struct netr_SamInfo3 *info3,
139 const char *name_domain,
140 const char *name_user)
142 /* We've been asked to return the unix username, per
143 'winbind use default domain' settings and the like */
145 const char *nt_username, *nt_domain;
147 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
148 if (!nt_domain) {
149 /* If the server didn't give us one, just use the one
150 * we sent them */
151 nt_domain = name_domain;
154 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
155 if (!nt_username) {
156 /* If the server didn't give us one, just use the one
157 * we sent them */
158 nt_username = name_user;
161 fill_domain_username(state->response->data.auth.unix_username,
162 nt_domain, nt_username, true);
164 DEBUG(5,("Setting unix username to [%s]\n",
165 state->response->data.auth.unix_username));
167 return NT_STATUS_OK;
170 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
171 struct winbindd_cli_state *state,
172 const struct netr_SamInfo3 *info3,
173 const char *name_domain,
174 const char *name_user)
176 char *afsname = NULL;
177 char *cell;
178 char *token;
180 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
181 if (afsname == NULL) {
182 return NT_STATUS_NO_MEMORY;
185 afsname = talloc_string_sub(mem_ctx,
186 lp_afs_username_map(),
187 "%D", name_domain);
188 afsname = talloc_string_sub(mem_ctx, afsname,
189 "%u", name_user);
190 afsname = talloc_string_sub(mem_ctx, afsname,
191 "%U", name_user);
194 DOM_SID user_sid;
195 fstring sidstr;
197 sid_copy(&user_sid, info3->base.domain_sid);
198 sid_append_rid(&user_sid, info3->base.rid);
199 sid_to_fstring(sidstr, &user_sid);
200 afsname = talloc_string_sub(mem_ctx, afsname,
201 "%s", sidstr);
204 if (afsname == NULL) {
205 return NT_STATUS_NO_MEMORY;
208 strlower_m(afsname);
210 DEBUG(10, ("Generating token for user %s\n", afsname));
212 cell = strchr(afsname, '@');
214 if (cell == NULL) {
215 return NT_STATUS_NO_MEMORY;
218 *cell = '\0';
219 cell += 1;
221 token = afs_createtoken_str(afsname, cell);
222 if (token == NULL) {
223 return NT_STATUS_OK;
225 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
226 token);
227 if (state->response->extra_data.data == NULL) {
228 return NT_STATUS_NO_MEMORY;
230 state->response->length +=
231 strlen((const char *)state->response->extra_data.data)+1;
233 return NT_STATUS_OK;
236 NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
237 const char *group_sid)
239 * Check whether a user belongs to a group or list of groups.
241 * @param mem_ctx talloc memory context.
242 * @param info3 user information, including group membership info.
243 * @param group_sid One or more groups , separated by commas.
245 * @return NT_STATUS_OK on success,
246 * NT_STATUS_LOGON_FAILURE if the user does not belong,
247 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
250 DOM_SID *require_membership_of_sid;
251 size_t num_require_membership_of_sid;
252 char *req_sid;
253 const char *p;
254 DOM_SID sid;
255 size_t i;
256 struct nt_user_token *token;
257 TALLOC_CTX *frame = talloc_stackframe();
258 NTSTATUS status;
260 /* Parse the 'required group' SID */
262 if (!group_sid || !group_sid[0]) {
263 /* NO sid supplied, all users may access */
264 return NT_STATUS_OK;
267 token = talloc_zero(talloc_tos(), struct nt_user_token);
268 if (token == NULL) {
269 DEBUG(0, ("talloc failed\n"));
270 TALLOC_FREE(frame);
271 return NT_STATUS_NO_MEMORY;
274 num_require_membership_of_sid = 0;
275 require_membership_of_sid = NULL;
277 p = group_sid;
279 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
280 if (!string_to_sid(&sid, req_sid)) {
281 DEBUG(0, ("check_info3_in_group: could not parse %s "
282 "as a SID!", req_sid));
283 TALLOC_FREE(frame);
284 return NT_STATUS_INVALID_PARAMETER;
287 status = add_sid_to_array(talloc_tos(), &sid,
288 &require_membership_of_sid,
289 &num_require_membership_of_sid);
290 if (!NT_STATUS_IS_OK(status)) {
291 DEBUG(0, ("add_sid_to_array failed\n"));
292 TALLOC_FREE(frame);
293 return status;
297 status = sid_array_from_info3(talloc_tos(), info3,
298 &token->user_sids,
299 &token->num_sids,
300 true, false);
301 if (!NT_STATUS_IS_OK(status)) {
302 TALLOC_FREE(frame);
303 return status;
306 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
307 token))
308 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
309 token))) {
310 DEBUG(3, ("could not add aliases: %s\n",
311 nt_errstr(status)));
312 TALLOC_FREE(frame);
313 return status;
316 debug_nt_user_token(DBGC_CLASS, 10, token);
318 for (i=0; i<num_require_membership_of_sid; i++) {
319 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
320 &require_membership_of_sid[i])));
321 if (nt_token_check_sid(&require_membership_of_sid[i],
322 token)) {
323 DEBUG(10, ("Access ok\n"));
324 TALLOC_FREE(frame);
325 return NT_STATUS_OK;
329 /* Do not distinguish this error from a wrong username/pw */
331 TALLOC_FREE(frame);
332 return NT_STATUS_LOGON_FAILURE;
335 struct winbindd_domain *find_auth_domain(uint8_t flags,
336 const char *domain_name)
338 struct winbindd_domain *domain;
340 if (IS_DC) {
341 domain = find_domain_from_name_noinit(domain_name);
342 if (domain == NULL) {
343 DEBUG(3, ("Authentication for domain [%s] refused "
344 "as it is not a trusted domain\n",
345 domain_name));
347 return domain;
350 if (is_myname(domain_name)) {
351 DEBUG(3, ("Authentication for domain %s (local domain "
352 "to this server) not supported at this "
353 "stage\n", domain_name));
354 return NULL;
357 /* we can auth against trusted domains */
358 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
359 domain = find_domain_from_name_noinit(domain_name);
360 if (domain == NULL) {
361 DEBUG(3, ("Authentication for domain [%s] skipped "
362 "as it is not a trusted domain\n",
363 domain_name));
364 } else {
365 return domain;
369 return find_our_domain();
372 static void fill_in_password_policy(struct winbindd_response *r,
373 const struct samr_DomInfo1 *p)
375 r->data.auth.policy.min_length_password =
376 p->min_password_length;
377 r->data.auth.policy.password_history =
378 p->password_history_length;
379 r->data.auth.policy.password_properties =
380 p->password_properties;
381 r->data.auth.policy.expire =
382 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
383 r->data.auth.policy.min_passwordage =
384 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
387 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
388 struct winbindd_cli_state *state)
390 struct winbindd_methods *methods;
391 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
392 struct samr_DomInfo1 password_policy;
394 if ( !winbindd_can_contact_domain( domain ) ) {
395 DEBUG(5,("fillup_password_policy: No inbound trust to "
396 "contact domain %s\n", domain->name));
397 return NT_STATUS_NOT_SUPPORTED;
400 methods = domain->methods;
402 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
403 if (NT_STATUS_IS_ERR(status)) {
404 return status;
407 fill_in_password_policy(state->response, &password_policy);
409 return NT_STATUS_OK;
412 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
413 TALLOC_CTX *mem_ctx,
414 uint16 *lockout_threshold)
416 struct winbindd_methods *methods;
417 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
418 struct samr_DomInfo12 lockout_policy;
420 *lockout_threshold = 0;
422 methods = domain->methods;
424 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
425 if (NT_STATUS_IS_ERR(status)) {
426 return status;
429 *lockout_threshold = lockout_policy.lockout_threshold;
431 return NT_STATUS_OK;
434 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
435 TALLOC_CTX *mem_ctx,
436 uint32 *password_properties)
438 struct winbindd_methods *methods;
439 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
440 struct samr_DomInfo1 password_policy;
442 *password_properties = 0;
444 methods = domain->methods;
446 status = methods->password_policy(domain, mem_ctx, &password_policy);
447 if (NT_STATUS_IS_ERR(status)) {
448 return status;
451 *password_properties = password_policy.password_properties;
453 return NT_STATUS_OK;
456 #ifdef HAVE_KRB5
458 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
459 const char *type,
460 uid_t uid,
461 bool *internal_ccache)
463 /* accept FILE and WRFILE as krb5_cc_type from the client and then
464 * build the full ccname string based on the user's uid here -
465 * Guenther*/
467 const char *gen_cc = NULL;
469 *internal_ccache = true;
471 if (uid == -1) {
472 goto memory_ccache;
475 if (!type || type[0] == '\0') {
476 goto memory_ccache;
479 if (strequal(type, "FILE")) {
480 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
481 } else if (strequal(type, "WRFILE")) {
482 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
483 } else {
484 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
485 goto memory_ccache;
488 *internal_ccache = false;
489 goto done;
491 memory_ccache:
492 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
494 done:
495 if (gen_cc == NULL) {
496 DEBUG(0,("out of memory\n"));
497 return NULL;
500 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
502 return gen_cc;
505 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
507 const char *type = state->request->data.auth.krb5_cc_type;
509 state->response->data.auth.krb5ccname[0] = '\0';
511 if (type[0] == '\0') {
512 return;
515 if (!strequal(type, "FILE") &&
516 !strequal(type, "WRFILE")) {
517 DEBUG(10,("won't return krbccname for a %s type ccache\n",
518 type));
519 return;
522 fstrcpy(state->response->data.auth.krb5ccname, cc);
525 #endif
527 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
529 uid_t uid;
531 uid = state->request->data.auth.uid;
533 if (uid < 0) {
534 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
535 return -1;
537 return uid;
540 /**********************************************************************
541 Authenticate a user with a clear text password using Kerberos and fill up
542 ccache if required
543 **********************************************************************/
545 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
546 struct winbindd_cli_state *state,
547 struct netr_SamInfo3 **info3)
549 #ifdef HAVE_KRB5
550 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
551 krb5_error_code krb5_ret;
552 const char *cc = NULL;
553 const char *principal_s = NULL;
554 const char *service = NULL;
555 char *realm = NULL;
556 fstring name_domain, name_user;
557 time_t ticket_lifetime = 0;
558 time_t renewal_until = 0;
559 uid_t uid = -1;
560 ADS_STRUCT *ads;
561 time_t time_offset = 0;
562 bool internal_ccache = true;
564 ZERO_STRUCTP(info3);
566 *info3 = NULL;
568 /* 1st step:
569 * prepare a krb5_cc_cache string for the user */
571 uid = get_uid_from_state(state);
572 if (uid == -1) {
573 DEBUG(0,("no valid uid\n"));
576 cc = generate_krb5_ccache(state->mem_ctx,
577 state->request->data.auth.krb5_cc_type,
578 state->request->data.auth.uid,
579 &internal_ccache);
580 if (cc == NULL) {
581 return NT_STATUS_NO_MEMORY;
585 /* 2nd step:
586 * get kerberos properties */
588 if (domain->private_data) {
589 ads = (ADS_STRUCT *)domain->private_data;
590 time_offset = ads->auth.time_offset;
594 /* 3rd step:
595 * do kerberos auth and setup ccache as the user */
597 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
599 realm = domain->alt_name;
600 strupper_m(realm);
602 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
603 if (principal_s == NULL) {
604 return NT_STATUS_NO_MEMORY;
607 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
608 if (service == NULL) {
609 return NT_STATUS_NO_MEMORY;
612 /* if this is a user ccache, we need to act as the user to let the krb5
613 * library handle the chown, etc. */
615 /************************ ENTERING NON-ROOT **********************/
617 if (!internal_ccache) {
618 set_effective_uid(uid);
619 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
622 result = kerberos_return_info3_from_pac(state->mem_ctx,
623 principal_s,
624 state->request->data.auth.pass,
625 time_offset,
626 &ticket_lifetime,
627 &renewal_until,
629 true,
630 true,
631 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
632 NULL,
633 info3);
634 if (!internal_ccache) {
635 gain_root_privilege();
638 /************************ RETURNED TO ROOT **********************/
640 if (!NT_STATUS_IS_OK(result)) {
641 goto failed;
644 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
645 principal_s));
647 /* if we had a user's ccache then return that string for the pam
648 * environment */
650 if (!internal_ccache) {
652 setup_return_cc_name(state, cc);
654 result = add_ccache_to_list(principal_s,
656 service,
657 state->request->data.auth.user,
658 realm,
659 uid,
660 time(NULL),
661 ticket_lifetime,
662 renewal_until,
663 false);
665 if (!NT_STATUS_IS_OK(result)) {
666 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
667 nt_errstr(result)));
669 } else {
671 /* need to delete the memory cred cache, it is not used anymore */
673 krb5_ret = ads_kdestroy(cc);
674 if (krb5_ret) {
675 DEBUG(3,("winbindd_raw_kerberos_login: "
676 "could not destroy krb5 credential cache: "
677 "%s\n", error_message(krb5_ret)));
682 return NT_STATUS_OK;
684 failed:
686 /* we could have created a new credential cache with a valid tgt in it
687 * but we werent able to get or verify the service ticket for this
688 * local host and therefor didn't get the PAC, we need to remove that
689 * cache entirely now */
691 krb5_ret = ads_kdestroy(cc);
692 if (krb5_ret) {
693 DEBUG(3,("winbindd_raw_kerberos_login: "
694 "could not destroy krb5 credential cache: "
695 "%s\n", error_message(krb5_ret)));
698 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
699 DEBUG(3,("winbindd_raw_kerberos_login: "
700 "could not remove ccache for user %s\n",
701 state->request->data.auth.user));
704 return result;
705 #else
706 return NT_STATUS_NOT_SUPPORTED;
707 #endif /* HAVE_KRB5 */
710 /****************************************************************
711 ****************************************************************/
713 bool check_request_flags(uint32_t flags)
715 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
716 WBFLAG_PAM_INFO3_TEXT |
717 WBFLAG_PAM_INFO3_NDR;
719 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
720 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
721 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
722 !(flags & flags_edata) ) {
723 return true;
726 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
727 flags));
729 return false;
732 /****************************************************************
733 ****************************************************************/
735 NTSTATUS append_auth_data(struct winbindd_cli_state *state,
736 struct netr_SamInfo3 *info3,
737 const char *name_domain,
738 const char *name_user)
740 NTSTATUS result;
741 uint32_t flags = state->request->flags;
743 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
744 memcpy(state->response->data.auth.user_session_key,
745 info3->base.key.key,
746 sizeof(state->response->data.auth.user_session_key)
747 /* 16 */);
750 if (flags & WBFLAG_PAM_LMKEY) {
751 memcpy(state->response->data.auth.first_8_lm_hash,
752 info3->base.LMSessKey.key,
753 sizeof(state->response->data.auth.first_8_lm_hash)
754 /* 8 */);
757 if (flags & WBFLAG_PAM_INFO3_TEXT) {
758 result = append_info3_as_txt(state->mem_ctx, state, info3);
759 if (!NT_STATUS_IS_OK(result)) {
760 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
761 nt_errstr(result)));
762 return result;
766 /* currently, anything from here on potentially overwrites extra_data. */
768 if (flags & WBFLAG_PAM_INFO3_NDR) {
769 result = append_info3_as_ndr(state->mem_ctx, state, info3);
770 if (!NT_STATUS_IS_OK(result)) {
771 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
772 nt_errstr(result)));
773 return result;
777 if (flags & WBFLAG_PAM_UNIX_NAME) {
778 result = append_unix_username(state->mem_ctx, state, info3,
779 name_domain, name_user);
780 if (!NT_STATUS_IS_OK(result)) {
781 DEBUG(10,("Failed to append Unix Username: %s\n",
782 nt_errstr(result)));
783 return result;
787 if (flags & WBFLAG_PAM_AFS_TOKEN) {
788 result = append_afs_token(state->mem_ctx, state, info3,
789 name_domain, name_user);
790 if (!NT_STATUS_IS_OK(result)) {
791 DEBUG(10,("Failed to append AFS token: %s\n",
792 nt_errstr(result)));
793 return result;
797 return NT_STATUS_OK;
800 void winbindd_pam_auth(struct winbindd_cli_state *state)
802 struct winbindd_domain *domain;
803 fstring name_domain, name_user, mapped_user;
804 char *mapped = NULL;
805 NTSTATUS result;
806 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
808 /* Ensure null termination */
809 state->request->data.auth.user
810 [sizeof(state->request->data.auth.user)-1]='\0';
812 /* Ensure null termination */
813 state->request->data.auth.pass
814 [sizeof(state->request->data.auth.pass)-1]='\0';
816 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
817 state->request->data.auth.user));
819 if (!check_request_flags(state->request->flags)) {
820 result = NT_STATUS_INVALID_PARAMETER_MIX;
821 goto done;
824 /* Parse domain and username */
826 name_map_status = normalize_name_unmap(state->mem_ctx,
827 state->request->data.auth.user,
828 &mapped);
830 /* If the name normalization didnt' actually do anything,
831 just use the original name */
833 if (NT_STATUS_IS_OK(name_map_status)
834 ||NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
835 fstrcpy(mapped_user, mapped);
836 } else {
837 fstrcpy(mapped_user, state->request->data.auth.user);
840 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
841 result = NT_STATUS_NO_SUCH_USER;
842 goto done;
845 domain = find_auth_domain(state->request->flags, name_domain);
847 if (domain == NULL) {
848 result = NT_STATUS_NO_SUCH_USER;
849 goto done;
852 sendto_domain(state, domain);
853 return;
854 done:
855 set_auth_errors(state->response, result);
856 DEBUG(5, ("Plain text authentication for %s returned %s "
857 "(PAM: %d)\n",
858 state->request->data.auth.user,
859 state->response->data.auth.nt_status_string,
860 state->response->data.auth.pam_error));
861 request_error(state);
864 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
865 struct winbindd_cli_state *state,
866 struct netr_SamInfo3 **info3)
868 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
869 uint16 max_allowed_bad_attempts;
870 fstring name_domain, name_user;
871 DOM_SID sid;
872 enum lsa_SidType type;
873 uchar new_nt_pass[NT_HASH_LEN];
874 const uint8 *cached_nt_pass;
875 const uint8 *cached_salt;
876 struct netr_SamInfo3 *my_info3;
877 time_t kickoff_time, must_change_time;
878 bool password_good = false;
879 #ifdef HAVE_KRB5
880 struct winbindd_tdc_domain *tdc_domain = NULL;
881 #endif
883 *info3 = NULL;
885 ZERO_STRUCTP(info3);
887 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
889 /* Parse domain and username */
891 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
894 if (!lookup_cached_name(state->mem_ctx,
895 name_domain,
896 name_user,
897 &sid,
898 &type)) {
899 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
900 return NT_STATUS_NO_SUCH_USER;
903 if (type != SID_NAME_USER) {
904 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
905 return NT_STATUS_LOGON_FAILURE;
908 result = winbindd_get_creds(domain,
909 state->mem_ctx,
910 &sid,
911 &my_info3,
912 &cached_nt_pass,
913 &cached_salt);
914 if (!NT_STATUS_IS_OK(result)) {
915 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
916 return result;
919 *info3 = my_info3;
921 E_md4hash(state->request->data.auth.pass, new_nt_pass);
923 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
924 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
925 if (cached_salt) {
926 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
929 if (cached_salt) {
930 /* In this case we didn't store the nt_hash itself,
931 but the MD5 combination of salt + nt_hash. */
932 uchar salted_hash[NT_HASH_LEN];
933 E_md5hash(cached_salt, new_nt_pass, salted_hash);
935 password_good = (memcmp(cached_nt_pass, salted_hash,
936 NT_HASH_LEN) == 0);
937 } else {
938 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
939 password_good = (memcmp(cached_nt_pass, new_nt_pass,
940 NT_HASH_LEN) == 0);
943 if (password_good) {
945 /* User *DOES* know the password, update logon_time and reset
946 * bad_pw_count */
948 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
950 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
951 return NT_STATUS_ACCOUNT_LOCKED_OUT;
954 if (my_info3->base.acct_flags & ACB_DISABLED) {
955 return NT_STATUS_ACCOUNT_DISABLED;
958 if (my_info3->base.acct_flags & ACB_WSTRUST) {
959 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
962 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
963 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
966 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
967 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
970 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
971 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
972 my_info3->base.acct_flags));
973 return NT_STATUS_LOGON_FAILURE;
976 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
977 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
978 return NT_STATUS_ACCOUNT_EXPIRED;
981 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
982 if (must_change_time != 0 && must_change_time < time(NULL)) {
983 /* we allow grace logons when the password has expired */
984 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
985 /* return NT_STATUS_PASSWORD_EXPIRED; */
986 goto success;
989 #ifdef HAVE_KRB5
990 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
991 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
992 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
993 /* used to cope with the case winbindd starting without network. */
994 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
996 uid_t uid = -1;
997 const char *cc = NULL;
998 char *realm = NULL;
999 const char *principal_s = NULL;
1000 const char *service = NULL;
1001 bool internal_ccache = false;
1003 uid = get_uid_from_state(state);
1004 if (uid == -1) {
1005 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1006 return NT_STATUS_INVALID_PARAMETER;
1009 cc = generate_krb5_ccache(state->mem_ctx,
1010 state->request->data.auth.krb5_cc_type,
1011 state->request->data.auth.uid,
1012 &internal_ccache);
1013 if (cc == NULL) {
1014 return NT_STATUS_NO_MEMORY;
1017 realm = domain->alt_name;
1018 strupper_m(realm);
1020 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1021 if (principal_s == NULL) {
1022 return NT_STATUS_NO_MEMORY;
1025 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1026 if (service == NULL) {
1027 return NT_STATUS_NO_MEMORY;
1030 if (!internal_ccache) {
1032 setup_return_cc_name(state, cc);
1034 result = add_ccache_to_list(principal_s,
1036 service,
1037 state->request->data.auth.user,
1038 domain->alt_name,
1039 uid,
1040 time(NULL),
1041 time(NULL) + lp_winbind_cache_time(),
1042 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1043 true);
1045 if (!NT_STATUS_IS_OK(result)) {
1046 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1047 "to add ccache to list: %s\n",
1048 nt_errstr(result)));
1052 #endif /* HAVE_KRB5 */
1053 success:
1054 /* FIXME: we possibly should handle logon hours as well (does xp when
1055 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1057 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1058 my_info3->base.bad_password_count = 0;
1060 result = winbindd_update_creds_by_info3(domain,
1061 state->mem_ctx,
1062 state->request->data.auth.user,
1063 state->request->data.auth.pass,
1064 my_info3);
1065 if (!NT_STATUS_IS_OK(result)) {
1066 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1067 nt_errstr(result)));
1068 return result;
1071 return NT_STATUS_OK;
1075 /* User does *NOT* know the correct password, modify info3 accordingly */
1077 /* failure of this is not critical */
1078 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1079 if (!NT_STATUS_IS_OK(result)) {
1080 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1081 "Won't be able to honour account lockout policies\n"));
1084 /* increase counter */
1085 my_info3->base.bad_password_count++;
1087 if (max_allowed_bad_attempts == 0) {
1088 goto failed;
1091 /* lockout user */
1092 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1094 uint32 password_properties;
1096 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1097 if (!NT_STATUS_IS_OK(result)) {
1098 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1101 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1102 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1103 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1107 failed:
1108 result = winbindd_update_creds_by_info3(domain,
1109 state->mem_ctx,
1110 state->request->data.auth.user,
1111 NULL,
1112 my_info3);
1114 if (!NT_STATUS_IS_OK(result)) {
1115 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1116 nt_errstr(result)));
1119 return NT_STATUS_LOGON_FAILURE;
1122 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1123 struct winbindd_cli_state *state,
1124 struct netr_SamInfo3 **info3)
1126 struct winbindd_domain *contact_domain;
1127 fstring name_domain, name_user;
1128 NTSTATUS result;
1130 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1132 /* Parse domain and username */
1134 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1136 /* what domain should we contact? */
1138 if ( IS_DC ) {
1139 if (!(contact_domain = find_domain_from_name(name_domain))) {
1140 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1141 state->request->data.auth.user, name_domain, name_user, name_domain));
1142 result = NT_STATUS_NO_SUCH_USER;
1143 goto done;
1146 } else {
1147 if (is_myname(name_domain)) {
1148 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1149 result = NT_STATUS_NO_SUCH_USER;
1150 goto done;
1153 contact_domain = find_domain_from_name(name_domain);
1154 if (contact_domain == NULL) {
1155 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1156 state->request->data.auth.user, name_domain, name_user, name_domain));
1158 contact_domain = find_our_domain();
1162 if (contact_domain->initialized &&
1163 contact_domain->active_directory) {
1164 goto try_login;
1167 if (!contact_domain->initialized) {
1168 init_dc_connection(contact_domain);
1171 if (!contact_domain->active_directory) {
1172 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1173 return NT_STATUS_INVALID_LOGON_TYPE;
1175 try_login:
1176 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1177 done:
1178 return result;
1181 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1182 TALLOC_CTX *mem_ctx,
1183 uint32 logon_parameters,
1184 const char *server,
1185 const char *username,
1186 const char *domain,
1187 const char *workstation,
1188 const uint8 chal[8],
1189 DATA_BLOB lm_response,
1190 DATA_BLOB nt_response,
1191 struct netr_SamInfo3 **info3);
1193 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1194 struct winbindd_cli_state *state,
1195 struct netr_SamInfo3 **info3)
1198 struct rpc_pipe_client *netlogon_pipe;
1199 uchar chal[8];
1200 DATA_BLOB lm_resp;
1201 DATA_BLOB nt_resp;
1202 int attempts = 0;
1203 unsigned char local_lm_response[24];
1204 unsigned char local_nt_response[24];
1205 struct winbindd_domain *contact_domain;
1206 fstring name_domain, name_user;
1207 bool retry;
1208 NTSTATUS result;
1209 struct netr_SamInfo3 *my_info3 = NULL;
1211 *info3 = NULL;
1213 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1215 /* Parse domain and username */
1217 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1219 /* do password magic */
1222 generate_random_buffer(chal, 8);
1223 if (lp_client_ntlmv2_auth()) {
1224 DATA_BLOB server_chal;
1225 DATA_BLOB names_blob;
1226 DATA_BLOB nt_response;
1227 DATA_BLOB lm_response;
1228 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1230 /* note that the 'workgroup' here is a best guess - we don't know
1231 the server's domain at this point. The 'server name' is also
1232 dodgy...
1234 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1236 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1237 state->request->data.auth.pass,
1238 &server_chal,
1239 &names_blob,
1240 &lm_response, &nt_response, NULL, NULL)) {
1241 data_blob_free(&names_blob);
1242 data_blob_free(&server_chal);
1243 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1244 result = NT_STATUS_NO_MEMORY;
1245 goto done;
1247 data_blob_free(&names_blob);
1248 data_blob_free(&server_chal);
1249 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1250 lm_response.length);
1251 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1252 nt_response.length);
1253 data_blob_free(&lm_response);
1254 data_blob_free(&nt_response);
1256 } else {
1257 if (lp_client_lanman_auth()
1258 && SMBencrypt(state->request->data.auth.pass,
1259 chal,
1260 local_lm_response)) {
1261 lm_resp = data_blob_talloc(state->mem_ctx,
1262 local_lm_response,
1263 sizeof(local_lm_response));
1264 } else {
1265 lm_resp = data_blob_null;
1267 SMBNTencrypt(state->request->data.auth.pass,
1268 chal,
1269 local_nt_response);
1271 nt_resp = data_blob_talloc(state->mem_ctx,
1272 local_nt_response,
1273 sizeof(local_nt_response));
1276 /* what domain should we contact? */
1278 if ( IS_DC ) {
1279 if (!(contact_domain = find_domain_from_name(name_domain))) {
1280 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1281 state->request->data.auth.user, name_domain, name_user, name_domain));
1282 result = NT_STATUS_NO_SUCH_USER;
1283 goto done;
1286 } else {
1287 if (is_myname(name_domain)) {
1288 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1289 result = NT_STATUS_NO_SUCH_USER;
1290 goto done;
1293 contact_domain = find_our_domain();
1296 /* check authentication loop */
1298 do {
1299 netlogon_fn_t logon_fn;
1301 ZERO_STRUCTP(my_info3);
1302 retry = false;
1304 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1306 if (!NT_STATUS_IS_OK(result)) {
1307 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1308 goto done;
1311 /* It is really important to try SamLogonEx here,
1312 * because in a clustered environment, we want to use
1313 * one machine account from multiple physical
1314 * computers.
1316 * With a normal SamLogon call, we must keep the
1317 * credentials chain updated and intact between all
1318 * users of the machine account (which would imply
1319 * cross-node communication for every NTLM logon).
1321 * (The credentials chain is not per NETLOGON pipe
1322 * connection, but globally on the server/client pair
1323 * by machine name).
1325 * When using SamLogonEx, the credentials are not
1326 * supplied, but the session key is implied by the
1327 * wrapping SamLogon context.
1329 * -- abartlet 21 April 2008
1332 logon_fn = contact_domain->can_do_samlogon_ex
1333 ? rpccli_netlogon_sam_network_logon_ex
1334 : rpccli_netlogon_sam_network_logon;
1336 result = logon_fn(netlogon_pipe,
1337 state->mem_ctx,
1339 contact_domain->dcname, /* server name */
1340 name_user, /* user name */
1341 name_domain, /* target domain */
1342 global_myname(), /* workstation */
1343 chal,
1344 lm_resp,
1345 nt_resp,
1346 &my_info3);
1347 attempts += 1;
1349 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1350 && contact_domain->can_do_samlogon_ex) {
1351 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1352 "retrying with NetSamLogon\n"));
1353 contact_domain->can_do_samlogon_ex = false;
1354 retry = true;
1355 continue;
1358 /* We have to try a second time as cm_connect_netlogon
1359 might not yet have noticed that the DC has killed
1360 our connection. */
1362 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1363 retry = true;
1364 continue;
1367 /* if we get access denied, a possible cause was that we had
1368 and open connection to the DC, but someone changed our
1369 machine account password out from underneath us using 'net
1370 rpc changetrustpw' */
1372 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1373 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1374 "ACCESS_DENIED. Maybe the trust account "
1375 "password was changed and we didn't know it. "
1376 "Killing connections to domain %s\n",
1377 name_domain));
1378 invalidate_cm_connection(&contact_domain->conn);
1379 retry = true;
1382 } while ( (attempts < 2) && retry );
1384 /* handle the case where a NT4 DC does not fill in the acct_flags in
1385 * the samlogon reply info3. When accurate info3 is required by the
1386 * caller, we look up the account flags ourselve - gd */
1388 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1389 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1391 struct rpc_pipe_client *samr_pipe;
1392 struct policy_handle samr_domain_handle, user_pol;
1393 union samr_UserInfo *info = NULL;
1394 NTSTATUS status_tmp;
1395 uint32 acct_flags;
1397 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1398 &samr_pipe, &samr_domain_handle);
1400 if (!NT_STATUS_IS_OK(status_tmp)) {
1401 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1402 nt_errstr(status_tmp)));
1403 goto done;
1406 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1407 &samr_domain_handle,
1408 MAXIMUM_ALLOWED_ACCESS,
1409 my_info3->base.rid,
1410 &user_pol);
1412 if (!NT_STATUS_IS_OK(status_tmp)) {
1413 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1414 nt_errstr(status_tmp)));
1415 goto done;
1418 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1419 &user_pol,
1421 &info);
1423 if (!NT_STATUS_IS_OK(status_tmp)) {
1424 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1425 nt_errstr(status_tmp)));
1426 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1427 goto done;
1430 acct_flags = info->info16.acct_flags;
1432 if (acct_flags == 0) {
1433 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1434 goto done;
1437 my_info3->base.acct_flags = acct_flags;
1439 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1441 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1444 *info3 = my_info3;
1445 done:
1446 return result;
1449 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1450 struct winbindd_cli_state *state)
1452 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1453 NTSTATUS krb5_result = NT_STATUS_OK;
1454 fstring name_domain, name_user;
1455 char *mapped_user;
1456 fstring domain_user;
1457 struct netr_SamInfo3 *info3 = NULL;
1458 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1460 /* Ensure null termination */
1461 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1463 /* Ensure null termination */
1464 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1466 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1467 state->request->data.auth.user));
1469 if (!check_request_flags(state->request->flags)) {
1470 result = NT_STATUS_INVALID_PARAMETER_MIX;
1471 goto done;
1474 /* Parse domain and username */
1476 name_map_status = normalize_name_unmap(state->mem_ctx,
1477 state->request->data.auth.user,
1478 &mapped_user);
1480 /* If the name normalization didnt' actually do anything,
1481 just use the original name */
1483 if (!NT_STATUS_IS_OK(name_map_status) &&
1484 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1486 mapped_user = state->request->data.auth.user;
1489 parse_domain_user(mapped_user, name_domain, name_user);
1491 if ( mapped_user != state->request->data.auth.user ) {
1492 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1493 safe_strcpy( state->request->data.auth.user, domain_user,
1494 sizeof(state->request->data.auth.user)-1 );
1497 if (domain->online == false) {
1498 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1499 if (domain->startup) {
1500 /* Logons are very important to users. If we're offline and
1501 we get a request within the first 30 seconds of startup,
1502 try very hard to find a DC and go online. */
1504 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1505 "request in startup mode.\n", domain->name ));
1507 winbindd_flush_negative_conn_cache(domain);
1508 result = init_dc_connection(domain);
1512 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1514 /* Check for Kerberos authentication */
1515 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1517 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1518 /* save for later */
1519 krb5_result = result;
1522 if (NT_STATUS_IS_OK(result)) {
1523 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1524 goto process_result;
1525 } else {
1526 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1529 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1530 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1531 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1532 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1533 set_domain_offline( domain );
1534 goto cached_logon;
1537 /* there are quite some NT_STATUS errors where there is no
1538 * point in retrying with a samlogon, we explictly have to take
1539 * care not to increase the bad logon counter on the DC */
1541 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1542 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1543 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1544 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1545 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1546 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1547 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1548 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1549 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1550 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1551 goto process_result;
1554 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1555 DEBUG(3,("falling back to samlogon\n"));
1556 goto sam_logon;
1557 } else {
1558 goto cached_logon;
1562 sam_logon:
1563 /* Check for Samlogon authentication */
1564 if (domain->online) {
1565 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1567 if (NT_STATUS_IS_OK(result)) {
1568 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1569 /* add the Krb5 err if we have one */
1570 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1571 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1573 goto process_result;
1576 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1577 nt_errstr(result)));
1579 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1580 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1581 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1583 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1584 set_domain_offline( domain );
1585 goto cached_logon;
1588 if (domain->online) {
1589 /* We're still online - fail. */
1590 goto done;
1594 cached_logon:
1595 /* Check for Cached logons */
1596 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1597 lp_winbind_offline_logon()) {
1599 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1601 if (NT_STATUS_IS_OK(result)) {
1602 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1603 goto process_result;
1604 } else {
1605 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1606 goto done;
1610 process_result:
1612 if (NT_STATUS_IS_OK(result)) {
1614 DOM_SID user_sid;
1616 /* In all codepaths where result == NT_STATUS_OK info3 must have
1617 been initialized. */
1618 if (!info3) {
1619 result = NT_STATUS_INTERNAL_ERROR;
1620 goto done;
1623 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1624 netsamlogon_cache_store(name_user, info3);
1626 /* save name_to_sid info as early as possible (only if
1627 this is our primary domain so we don't invalidate
1628 the cache entry by storing the seq_num for the wrong
1629 domain). */
1630 if ( domain->primary ) {
1631 sid_compose(&user_sid, info3->base.domain_sid,
1632 info3->base.rid);
1633 cache_name2sid(domain, name_domain, name_user,
1634 SID_NAME_USER, &user_sid);
1637 /* Check if the user is in the right group */
1639 result = check_info3_in_group(
1640 info3,
1641 state->request->data.auth.require_membership_of_sid);
1642 if (!NT_STATUS_IS_OK(result)) {
1643 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1644 state->request->data.auth.user,
1645 state->request->data.auth.require_membership_of_sid));
1646 goto done;
1649 result = append_auth_data(state, info3, name_domain,
1650 name_user);
1651 if (!NT_STATUS_IS_OK(result)) {
1652 goto done;
1655 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1657 /* Store in-memory creds for single-signon using ntlm_auth. */
1658 result = winbindd_add_memory_creds(state->request->data.auth.user,
1659 get_uid_from_state(state),
1660 state->request->data.auth.pass);
1662 if (!NT_STATUS_IS_OK(result)) {
1663 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1664 goto done;
1667 if (lp_winbind_offline_logon()) {
1668 result = winbindd_store_creds(domain,
1669 state->mem_ctx,
1670 state->request->data.auth.user,
1671 state->request->data.auth.pass,
1672 info3, NULL);
1673 if (!NT_STATUS_IS_OK(result)) {
1675 /* Release refcount. */
1676 winbindd_delete_memory_creds(state->request->data.auth.user);
1678 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1679 goto done;
1685 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1686 struct winbindd_domain *our_domain = find_our_domain();
1688 /* This is not entirely correct I believe, but it is
1689 consistent. Only apply the password policy settings
1690 too warn users for our own domain. Cannot obtain these
1691 from trusted DCs all the time so don't do it at all.
1692 -- jerry */
1694 result = NT_STATUS_NOT_SUPPORTED;
1695 if (our_domain == domain ) {
1696 result = fillup_password_policy(our_domain, state);
1699 if (!NT_STATUS_IS_OK(result)
1700 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1702 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1703 domain->name, nt_errstr(result)));
1704 goto done;
1708 result = NT_STATUS_OK;
1711 done:
1712 /* give us a more useful (more correct?) error code */
1713 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1714 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1715 result = NT_STATUS_NO_LOGON_SERVERS;
1718 set_auth_errors(state->response, result);
1720 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1721 state->request->data.auth.user,
1722 state->response->data.auth.nt_status_string,
1723 state->response->data.auth.pam_error));
1725 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1729 /**********************************************************************
1730 Challenge Response Authentication Protocol
1731 **********************************************************************/
1733 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1735 struct winbindd_domain *domain = NULL;
1736 const char *domain_name = NULL;
1737 NTSTATUS result;
1739 if (!check_request_flags(state->request->flags)) {
1740 result = NT_STATUS_INVALID_PARAMETER_MIX;
1741 goto done;
1744 if (!state->privileged) {
1745 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1746 "denied. !\n"));
1747 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1748 "on %s are set correctly.\n",
1749 get_winbind_priv_pipe_dir()));
1750 /* send a better message than ACCESS_DENIED */
1751 fstr_sprintf(state->response->data.auth.error_string,
1752 "winbind client not authorized to use "
1753 "winbindd_pam_auth_crap. Ensure permissions on "
1754 "%s are set correctly.",
1755 get_winbind_priv_pipe_dir());
1756 result = NT_STATUS_ACCESS_DENIED;
1757 goto done;
1760 /* Ensure null termination */
1761 state->request->data.auth_crap.user
1762 [sizeof(state->request->data.auth_crap.user)-1]=0;
1763 state->request->data.auth_crap.domain
1764 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1766 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1767 (unsigned long)state->pid,
1768 state->request->data.auth_crap.domain,
1769 state->request->data.auth_crap.user));
1771 if (*state->request->data.auth_crap.domain != '\0') {
1772 domain_name = state->request->data.auth_crap.domain;
1773 } else if (lp_winbind_use_default_domain()) {
1774 domain_name = lp_workgroup();
1777 if (domain_name != NULL)
1778 domain = find_auth_domain(state->request->flags, domain_name);
1780 if (domain != NULL) {
1781 sendto_domain(state, domain);
1782 return;
1785 result = NT_STATUS_NO_SUCH_USER;
1787 done:
1788 set_auth_errors(state->response, result);
1789 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1790 state->request->data.auth_crap.domain,
1791 state->request->data.auth_crap.user,
1792 state->response->data.auth.nt_status_string,
1793 state->response->data.auth.pam_error));
1794 request_error(state);
1795 return;
1799 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1800 struct winbindd_cli_state *state)
1802 NTSTATUS result;
1803 struct netr_SamInfo3 *info3 = NULL;
1804 struct rpc_pipe_client *netlogon_pipe;
1805 const char *name_user = NULL;
1806 const char *name_domain = NULL;
1807 const char *workstation;
1808 struct winbindd_domain *contact_domain;
1809 int attempts = 0;
1810 bool retry;
1812 DATA_BLOB lm_resp, nt_resp;
1814 /* This is child-only, so no check for privileged access is needed
1815 anymore */
1817 /* Ensure null termination */
1818 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1819 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1821 if (!check_request_flags(state->request->flags)) {
1822 result = NT_STATUS_INVALID_PARAMETER_MIX;
1823 goto done;
1826 name_user = state->request->data.auth_crap.user;
1828 if (*state->request->data.auth_crap.domain) {
1829 name_domain = state->request->data.auth_crap.domain;
1830 } else if (lp_winbind_use_default_domain()) {
1831 name_domain = lp_workgroup();
1832 } else {
1833 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1834 name_user));
1835 result = NT_STATUS_NO_SUCH_USER;
1836 goto done;
1839 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1840 name_domain, name_user));
1842 if (*state->request->data.auth_crap.workstation) {
1843 workstation = state->request->data.auth_crap.workstation;
1844 } else {
1845 workstation = global_myname();
1848 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1849 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1850 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1851 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1852 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1853 state->request->data.auth_crap.lm_resp_len,
1854 state->request->data.auth_crap.nt_resp_len));
1855 result = NT_STATUS_INVALID_PARAMETER;
1856 goto done;
1860 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1861 state->request->data.auth_crap.lm_resp_len);
1863 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1864 nt_resp = data_blob_talloc(state->mem_ctx,
1865 state->request->extra_data.data,
1866 state->request->data.auth_crap.nt_resp_len);
1867 } else {
1868 nt_resp = data_blob_talloc(state->mem_ctx,
1869 state->request->data.auth_crap.nt_resp,
1870 state->request->data.auth_crap.nt_resp_len);
1873 /* what domain should we contact? */
1875 if ( IS_DC ) {
1876 if (!(contact_domain = find_domain_from_name(name_domain))) {
1877 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1878 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1879 result = NT_STATUS_NO_SUCH_USER;
1880 goto done;
1882 } else {
1883 if (is_myname(name_domain)) {
1884 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1885 result = NT_STATUS_NO_SUCH_USER;
1886 goto done;
1888 contact_domain = find_our_domain();
1891 do {
1892 netlogon_fn_t logon_fn;
1894 retry = false;
1896 netlogon_pipe = NULL;
1897 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1899 if (!NT_STATUS_IS_OK(result)) {
1900 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1901 nt_errstr(result)));
1902 goto done;
1905 logon_fn = contact_domain->can_do_samlogon_ex
1906 ? rpccli_netlogon_sam_network_logon_ex
1907 : rpccli_netlogon_sam_network_logon;
1909 result = logon_fn(netlogon_pipe,
1910 state->mem_ctx,
1911 state->request->data.auth_crap.logon_parameters,
1912 contact_domain->dcname,
1913 name_user,
1914 name_domain,
1915 /* Bug #3248 - found by Stefan Burkei. */
1916 workstation, /* We carefully set this above so use it... */
1917 state->request->data.auth_crap.chal,
1918 lm_resp,
1919 nt_resp,
1920 &info3);
1922 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1923 && contact_domain->can_do_samlogon_ex) {
1924 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1925 "retrying with NetSamLogon\n"));
1926 contact_domain->can_do_samlogon_ex = false;
1927 retry = true;
1928 continue;
1931 attempts += 1;
1933 /* We have to try a second time as cm_connect_netlogon
1934 might not yet have noticed that the DC has killed
1935 our connection. */
1937 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1938 retry = true;
1939 continue;
1942 /* if we get access denied, a possible cause was that we had and open
1943 connection to the DC, but someone changed our machine account password
1944 out from underneath us using 'net rpc changetrustpw' */
1946 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1947 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1948 "ACCESS_DENIED. Maybe the trust account "
1949 "password was changed and we didn't know it. "
1950 "Killing connections to domain %s\n",
1951 name_domain));
1952 invalidate_cm_connection(&contact_domain->conn);
1953 retry = true;
1956 } while ( (attempts < 2) && retry );
1958 if (NT_STATUS_IS_OK(result)) {
1960 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1961 netsamlogon_cache_store(name_user, info3);
1963 /* Check if the user is in the right group */
1965 result = check_info3_in_group(
1966 info3,
1967 state->request->data.auth_crap.require_membership_of_sid);
1968 if (!NT_STATUS_IS_OK(result)) {
1969 DEBUG(3, ("User %s is not in the required group (%s), so "
1970 "crap authentication is rejected\n",
1971 state->request->data.auth_crap.user,
1972 state->request->data.auth_crap.require_membership_of_sid));
1973 goto done;
1976 result = append_auth_data(state, info3, name_domain,
1977 name_user);
1978 if (!NT_STATUS_IS_OK(result)) {
1979 goto done;
1983 done:
1985 /* give us a more useful (more correct?) error code */
1986 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1987 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1988 result = NT_STATUS_NO_LOGON_SERVERS;
1991 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1992 result = nt_status_squash(result);
1995 set_auth_errors(state->response, result);
1997 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1998 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1999 name_domain,
2000 name_user,
2001 state->response->data.auth.nt_status_string,
2002 state->response->data.auth.pam_error));
2004 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2007 /* Change a user password */
2009 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2011 fstring domain, user;
2012 char *mapped_user;
2013 struct winbindd_domain *contact_domain;
2014 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2016 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2017 state->request->data.chauthtok.user));
2019 /* Setup crap */
2021 nt_status = normalize_name_unmap(state->mem_ctx,
2022 state->request->data.chauthtok.user,
2023 &mapped_user);
2025 /* Update the chauthtok name if we did any mapping */
2027 if (NT_STATUS_IS_OK(nt_status) ||
2028 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2030 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2033 /* Must pass in state->...chauthtok.user because
2034 canonicalize_username() assumes an fstring(). Since
2035 we have already copied it (if necessary), this is ok. */
2037 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2038 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2039 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2040 "(PAM: %d)\n",
2041 state->request->data.auth.user,
2042 state->response->data.auth.nt_status_string,
2043 state->response->data.auth.pam_error));
2044 request_error(state);
2045 return;
2048 contact_domain = find_domain_from_name(domain);
2049 if (!contact_domain) {
2050 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2051 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2052 state->request->data.chauthtok.user, domain, user, domain));
2053 request_error(state);
2054 return;
2057 sendto_domain(state, contact_domain);
2060 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2061 struct winbindd_cli_state *state)
2063 char *oldpass;
2064 char *newpass = NULL;
2065 struct policy_handle dom_pol;
2066 struct rpc_pipe_client *cli;
2067 bool got_info = false;
2068 struct samr_DomInfo1 *info = NULL;
2069 struct userPwdChangeFailureInformation *reject = NULL;
2070 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2071 fstring domain, user;
2073 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2074 state->request->data.auth.user));
2076 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2077 goto done;
2080 /* Change password */
2082 oldpass = state->request->data.chauthtok.oldpass;
2083 newpass = state->request->data.chauthtok.newpass;
2085 /* Initialize reject reason */
2086 state->response->data.auth.reject_reason = Undefined;
2088 /* Get sam handle */
2090 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2091 &dom_pol);
2092 if (!NT_STATUS_IS_OK(result)) {
2093 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2094 goto done;
2097 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2098 user,
2099 newpass,
2100 oldpass,
2101 &info,
2102 &reject);
2104 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2106 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2108 fill_in_password_policy(state->response, info);
2110 state->response->data.auth.reject_reason =
2111 reject->extendedFailureReason;
2113 got_info = true;
2116 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2117 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2118 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2119 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2121 /* only fallback when the chgpasswd_user3 call is not supported */
2122 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2123 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2124 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2125 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2127 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2128 nt_errstr(result)));
2130 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2132 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2133 Map to the same status code as Windows 2003. */
2135 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2136 result = NT_STATUS_PASSWORD_RESTRICTION;
2140 done:
2142 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2144 /* Update the single sign-on memory creds. */
2145 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2146 newpass);
2148 /* When we login from gdm or xdm and password expires,
2149 * we change password, but there are no memory crendentials
2150 * So, winbindd_replace_memory_creds() returns
2151 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2152 * --- BoYang
2153 * */
2154 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2155 result = NT_STATUS_OK;
2158 if (!NT_STATUS_IS_OK(result)) {
2159 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2160 goto process_result;
2163 if (lp_winbind_offline_logon()) {
2164 result = winbindd_update_creds_by_name(contact_domain,
2165 state->mem_ctx, user,
2166 newpass);
2167 /* Again, this happens when we login from gdm or xdm
2168 * and the password expires, *BUT* cached crendentials
2169 * doesn't exist. winbindd_update_creds_by_name()
2170 * returns NT_STATUS_NO_SUCH_USER.
2171 * This is not a failure.
2172 * --- BoYang
2173 * */
2174 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2175 result = NT_STATUS_OK;
2178 if (!NT_STATUS_IS_OK(result)) {
2179 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2180 goto process_result;
2185 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2187 NTSTATUS policy_ret;
2189 policy_ret = fillup_password_policy(contact_domain, state);
2191 /* failure of this is non critical, it will just provide no
2192 * additional information to the client why the change has
2193 * failed - Guenther */
2195 if (!NT_STATUS_IS_OK(policy_ret)) {
2196 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2197 goto process_result;
2201 process_result:
2203 set_auth_errors(state->response, result);
2205 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2206 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2207 domain,
2208 user,
2209 state->response->data.auth.nt_status_string,
2210 state->response->data.auth.pam_error));
2212 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2215 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2217 struct winbindd_domain *domain;
2218 fstring name_domain, user;
2219 uid_t caller_uid = (uid_t)-1;
2220 uid_t request_uid = state->request->data.logoff.uid;
2222 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2223 state->request->data.logoff.user));
2225 /* Ensure null termination */
2226 state->request->data.logoff.user
2227 [sizeof(state->request->data.logoff.user)-1]='\0';
2229 state->request->data.logoff.krb5ccname
2230 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2232 if (request_uid == (gid_t)-1) {
2233 goto failed;
2236 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2237 goto failed;
2240 if ((domain = find_auth_domain(state->request->flags,
2241 name_domain)) == NULL) {
2242 goto failed;
2245 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2246 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2247 strerror(errno)));
2248 goto failed;
2251 switch (caller_uid) {
2252 case -1:
2253 goto failed;
2254 case 0:
2255 /* root must be able to logoff any user - gd */
2256 state->request->data.logoff.uid = request_uid;
2257 break;
2258 default:
2259 if (caller_uid != request_uid) {
2260 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2261 goto failed;
2263 state->request->data.logoff.uid = caller_uid;
2264 break;
2267 sendto_domain(state, domain);
2268 return;
2270 failed:
2271 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2272 DEBUG(5, ("Pam Logoff for %s returned %s "
2273 "(PAM: %d)\n",
2274 state->request->data.logoff.user,
2275 state->response->data.auth.nt_status_string,
2276 state->response->data.auth.pam_error));
2277 request_error(state);
2278 return;
2281 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2282 struct winbindd_cli_state *state)
2284 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2286 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2287 state->request->data.logoff.user));
2289 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2290 result = NT_STATUS_OK;
2291 goto process_result;
2294 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2295 result = NT_STATUS_OK;
2296 goto process_result;
2299 #ifdef HAVE_KRB5
2301 if (state->request->data.logoff.uid < 0) {
2302 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2303 goto process_result;
2306 /* what we need here is to find the corresponding krb5 ccache name *we*
2307 * created for a given username and destroy it */
2309 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2310 result = NT_STATUS_OK;
2311 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2312 goto process_result;
2315 if (!ccache_entry_identical(state->request->data.logoff.user,
2316 state->request->data.logoff.uid,
2317 state->request->data.logoff.krb5ccname)) {
2318 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2319 goto process_result;
2322 result = remove_ccache(state->request->data.logoff.user);
2323 if (!NT_STATUS_IS_OK(result)) {
2324 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2325 nt_errstr(result)));
2326 goto process_result;
2329 #else
2330 result = NT_STATUS_NOT_SUPPORTED;
2331 #endif
2333 process_result:
2335 winbindd_delete_memory_creds(state->request->data.logoff.user);
2337 set_auth_errors(state->response, result);
2339 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2342 /* Change user password with auth crap*/
2344 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2346 struct winbindd_domain *domain = NULL;
2347 const char *domain_name = NULL;
2349 /* Ensure null termination */
2350 state->request->data.chng_pswd_auth_crap.user[
2351 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2352 state->request->data.chng_pswd_auth_crap.domain[
2353 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2355 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2356 (unsigned long)state->pid,
2357 state->request->data.chng_pswd_auth_crap.domain,
2358 state->request->data.chng_pswd_auth_crap.user));
2360 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2361 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2362 } else if (lp_winbind_use_default_domain()) {
2363 domain_name = lp_workgroup();
2366 if (domain_name != NULL)
2367 domain = find_domain_from_name(domain_name);
2369 if (domain != NULL) {
2370 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2371 "%s\n", (unsigned long)state->pid,domain->name));
2372 sendto_domain(state, domain);
2373 return;
2376 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2377 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2378 state->request->data.chng_pswd_auth_crap.domain,
2379 state->request->data.chng_pswd_auth_crap.user,
2380 state->response->data.auth.nt_status_string,
2381 state->response->data.auth.pam_error));
2382 request_error(state);
2383 return;
2386 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2388 NTSTATUS result;
2389 DATA_BLOB new_nt_password;
2390 DATA_BLOB old_nt_hash_enc;
2391 DATA_BLOB new_lm_password;
2392 DATA_BLOB old_lm_hash_enc;
2393 fstring domain,user;
2394 struct policy_handle dom_pol;
2395 struct winbindd_domain *contact_domain = domainSt;
2396 struct rpc_pipe_client *cli;
2398 /* Ensure null termination */
2399 state->request->data.chng_pswd_auth_crap.user[
2400 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2401 state->request->data.chng_pswd_auth_crap.domain[
2402 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2403 *domain = 0;
2404 *user = 0;
2406 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2407 (unsigned long)state->pid,
2408 state->request->data.chng_pswd_auth_crap.domain,
2409 state->request->data.chng_pswd_auth_crap.user));
2411 if (lp_winbind_offline_logon()) {
2412 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2413 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2414 result = NT_STATUS_ACCESS_DENIED;
2415 goto done;
2418 if (*state->request->data.chng_pswd_auth_crap.domain) {
2419 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2420 } else {
2421 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2422 domain, user);
2424 if(!*domain) {
2425 DEBUG(3,("no domain specified with username (%s) - "
2426 "failing auth\n",
2427 state->request->data.chng_pswd_auth_crap.user));
2428 result = NT_STATUS_NO_SUCH_USER;
2429 goto done;
2433 if (!*domain && lp_winbind_use_default_domain()) {
2434 fstrcpy(domain,(char *)lp_workgroup());
2437 if(!*user) {
2438 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2441 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2442 (unsigned long)state->pid, domain, user));
2444 /* Change password */
2445 new_nt_password = data_blob_talloc(
2446 state->mem_ctx,
2447 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2448 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2450 old_nt_hash_enc = data_blob_talloc(
2451 state->mem_ctx,
2452 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2453 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2455 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2456 new_lm_password = data_blob_talloc(
2457 state->mem_ctx,
2458 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2459 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2461 old_lm_hash_enc = data_blob_talloc(
2462 state->mem_ctx,
2463 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2464 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2465 } else {
2466 new_lm_password.length = 0;
2467 old_lm_hash_enc.length = 0;
2470 /* Get sam handle */
2472 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2473 if (!NT_STATUS_IS_OK(result)) {
2474 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2475 goto done;
2478 result = rpccli_samr_chng_pswd_auth_crap(
2479 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2480 new_lm_password, old_lm_hash_enc);
2482 done:
2484 set_auth_errors(state->response, result);
2486 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2487 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2488 domain, user,
2489 state->response->data.auth.nt_status_string,
2490 state->response->data.auth.pam_error));
2492 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;