s3: Convert WINBINDD_PAM_AUTH to the new async API
[Samba/ekacnet.git] / source3 / winbindd / winbindd_pam.c
blob2e1bc204e6793a66680ab62085258a6a1e2e7cff
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"
29 #include "smb_krb5.h"
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
34 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
36 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
37 struct winbindd_cli_state *state,
38 struct netr_SamInfo3 *info3)
40 char *ex;
41 uint32_t i;
43 state->response->data.auth.info3.logon_time =
44 nt_time_to_unix(info3->base.last_logon);
45 state->response->data.auth.info3.logoff_time =
46 nt_time_to_unix(info3->base.last_logoff);
47 state->response->data.auth.info3.kickoff_time =
48 nt_time_to_unix(info3->base.acct_expiry);
49 state->response->data.auth.info3.pass_last_set_time =
50 nt_time_to_unix(info3->base.last_password_change);
51 state->response->data.auth.info3.pass_can_change_time =
52 nt_time_to_unix(info3->base.allow_password_change);
53 state->response->data.auth.info3.pass_must_change_time =
54 nt_time_to_unix(info3->base.force_password_change);
56 state->response->data.auth.info3.logon_count = info3->base.logon_count;
57 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
59 state->response->data.auth.info3.user_rid = info3->base.rid;
60 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
61 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
63 state->response->data.auth.info3.num_groups = info3->base.groups.count;
64 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
66 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
67 state->response->data.auth.info3.num_other_sids = info3->sidcount;
69 fstrcpy(state->response->data.auth.info3.user_name,
70 info3->base.account_name.string);
71 fstrcpy(state->response->data.auth.info3.full_name,
72 info3->base.full_name.string);
73 fstrcpy(state->response->data.auth.info3.logon_script,
74 info3->base.logon_script.string);
75 fstrcpy(state->response->data.auth.info3.profile_path,
76 info3->base.profile_path.string);
77 fstrcpy(state->response->data.auth.info3.home_dir,
78 info3->base.home_directory.string);
79 fstrcpy(state->response->data.auth.info3.dir_drive,
80 info3->base.home_drive.string);
82 fstrcpy(state->response->data.auth.info3.logon_srv,
83 info3->base.logon_server.string);
84 fstrcpy(state->response->data.auth.info3.logon_dom,
85 info3->base.domain.string);
87 ex = talloc_strdup(state->mem_ctx, "");
88 NT_STATUS_HAVE_NO_MEMORY(ex);
90 for (i=0; i < info3->base.groups.count; i++) {
91 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
92 info3->base.groups.rids[i].rid,
93 info3->base.groups.rids[i].attributes);
94 NT_STATUS_HAVE_NO_MEMORY(ex);
97 for (i=0; i < info3->sidcount; i++) {
98 char *sid;
100 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
101 NT_STATUS_HAVE_NO_MEMORY(sid);
103 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
104 sid,
105 info3->sids[i].attributes);
106 NT_STATUS_HAVE_NO_MEMORY(ex);
108 talloc_free(sid);
111 state->response->extra_data.data = ex;
112 state->response->length += talloc_get_size(ex);
114 return NT_STATUS_OK;
117 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
118 struct winbindd_cli_state *state,
119 struct netr_SamInfo3 *info3)
121 DATA_BLOB blob;
122 enum ndr_err_code ndr_err;
124 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
125 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
126 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
127 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
128 return ndr_map_error2ntstatus(ndr_err);
131 state->response->extra_data.data = blob.data;
132 state->response->length += blob.length;
134 return NT_STATUS_OK;
137 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
138 struct winbindd_cli_state *state,
139 const struct netr_SamInfo3 *info3,
140 const char *name_domain,
141 const char *name_user)
143 /* We've been asked to return the unix username, per
144 'winbind use default domain' settings and the like */
146 const char *nt_username, *nt_domain;
148 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
149 if (!nt_domain) {
150 /* If the server didn't give us one, just use the one
151 * we sent them */
152 nt_domain = name_domain;
155 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
156 if (!nt_username) {
157 /* If the server didn't give us one, just use the one
158 * we sent them */
159 nt_username = name_user;
162 fill_domain_username(state->response->data.auth.unix_username,
163 nt_domain, nt_username, true);
165 DEBUG(5,("Setting unix username to [%s]\n",
166 state->response->data.auth.unix_username));
168 return NT_STATUS_OK;
171 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
172 struct winbindd_cli_state *state,
173 const struct netr_SamInfo3 *info3,
174 const char *name_domain,
175 const char *name_user)
177 char *afsname = NULL;
178 char *cell;
179 char *token;
181 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
182 if (afsname == NULL) {
183 return NT_STATUS_NO_MEMORY;
186 afsname = talloc_string_sub(mem_ctx,
187 lp_afs_username_map(),
188 "%D", name_domain);
189 afsname = talloc_string_sub(mem_ctx, afsname,
190 "%u", name_user);
191 afsname = talloc_string_sub(mem_ctx, afsname,
192 "%U", name_user);
195 DOM_SID user_sid;
196 fstring sidstr;
198 sid_compose(&user_sid, info3->base.domain_sid,
199 info3->base.rid);
200 sid_to_fstring(sidstr, &user_sid);
201 afsname = talloc_string_sub(mem_ctx, afsname,
202 "%s", sidstr);
205 if (afsname == NULL) {
206 return NT_STATUS_NO_MEMORY;
209 strlower_m(afsname);
211 DEBUG(10, ("Generating token for user %s\n", afsname));
213 cell = strchr(afsname, '@');
215 if (cell == NULL) {
216 return NT_STATUS_NO_MEMORY;
219 *cell = '\0';
220 cell += 1;
222 token = afs_createtoken_str(afsname, cell);
223 if (token == NULL) {
224 return NT_STATUS_OK;
226 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
227 token);
228 if (state->response->extra_data.data == NULL) {
229 return NT_STATUS_NO_MEMORY;
231 state->response->length +=
232 strlen((const char *)state->response->extra_data.data)+1;
234 return NT_STATUS_OK;
237 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
238 const char *group_sid)
240 * Check whether a user belongs to a group or list of groups.
242 * @param mem_ctx talloc memory context.
243 * @param info3 user information, including group membership info.
244 * @param group_sid One or more groups , separated by commas.
246 * @return NT_STATUS_OK on success,
247 * NT_STATUS_LOGON_FAILURE if the user does not belong,
248 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
251 DOM_SID *require_membership_of_sid;
252 size_t num_require_membership_of_sid;
253 char *req_sid;
254 const char *p;
255 DOM_SID sid;
256 size_t i;
257 struct nt_user_token *token;
258 TALLOC_CTX *frame = talloc_stackframe();
259 NTSTATUS status;
261 /* Parse the 'required group' SID */
263 if (!group_sid || !group_sid[0]) {
264 /* NO sid supplied, all users may access */
265 return NT_STATUS_OK;
268 token = talloc_zero(talloc_tos(), struct nt_user_token);
269 if (token == NULL) {
270 DEBUG(0, ("talloc failed\n"));
271 TALLOC_FREE(frame);
272 return NT_STATUS_NO_MEMORY;
275 num_require_membership_of_sid = 0;
276 require_membership_of_sid = NULL;
278 p = group_sid;
280 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
281 if (!string_to_sid(&sid, req_sid)) {
282 DEBUG(0, ("check_info3_in_group: could not parse %s "
283 "as a SID!", req_sid));
284 TALLOC_FREE(frame);
285 return NT_STATUS_INVALID_PARAMETER;
288 status = add_sid_to_array(talloc_tos(), &sid,
289 &require_membership_of_sid,
290 &num_require_membership_of_sid);
291 if (!NT_STATUS_IS_OK(status)) {
292 DEBUG(0, ("add_sid_to_array failed\n"));
293 TALLOC_FREE(frame);
294 return status;
298 status = sid_array_from_info3(talloc_tos(), info3,
299 &token->user_sids,
300 &token->num_sids,
301 true, false);
302 if (!NT_STATUS_IS_OK(status)) {
303 TALLOC_FREE(frame);
304 return status;
307 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
308 token))
309 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
310 token))) {
311 DEBUG(3, ("could not add aliases: %s\n",
312 nt_errstr(status)));
313 TALLOC_FREE(frame);
314 return status;
317 debug_nt_user_token(DBGC_CLASS, 10, token);
319 for (i=0; i<num_require_membership_of_sid; i++) {
320 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
321 &require_membership_of_sid[i])));
322 if (nt_token_check_sid(&require_membership_of_sid[i],
323 token)) {
324 DEBUG(10, ("Access ok\n"));
325 TALLOC_FREE(frame);
326 return NT_STATUS_OK;
330 /* Do not distinguish this error from a wrong username/pw */
332 TALLOC_FREE(frame);
333 return NT_STATUS_LOGON_FAILURE;
336 struct winbindd_domain *find_auth_domain(uint8_t flags,
337 const char *domain_name)
339 struct winbindd_domain *domain;
341 if (IS_DC) {
342 domain = find_domain_from_name_noinit(domain_name);
343 if (domain == NULL) {
344 DEBUG(3, ("Authentication for domain [%s] refused "
345 "as it is not a trusted domain\n",
346 domain_name));
348 return domain;
351 if (strequal(domain_name, get_global_sam_name())) {
352 return find_domain_from_name_noinit(domain_name);
355 /* we can auth against trusted domains */
356 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
357 domain = find_domain_from_name_noinit(domain_name);
358 if (domain == NULL) {
359 DEBUG(3, ("Authentication for domain [%s] skipped "
360 "as it is not a trusted domain\n",
361 domain_name));
362 } else {
363 return domain;
367 return find_our_domain();
370 static void fill_in_password_policy(struct winbindd_response *r,
371 const struct samr_DomInfo1 *p)
373 r->data.auth.policy.min_length_password =
374 p->min_password_length;
375 r->data.auth.policy.password_history =
376 p->password_history_length;
377 r->data.auth.policy.password_properties =
378 p->password_properties;
379 r->data.auth.policy.expire =
380 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
381 r->data.auth.policy.min_passwordage =
382 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
385 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
386 struct winbindd_cli_state *state)
388 struct winbindd_methods *methods;
389 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
390 struct samr_DomInfo1 password_policy;
392 if ( !winbindd_can_contact_domain( domain ) ) {
393 DEBUG(5,("fillup_password_policy: No inbound trust to "
394 "contact domain %s\n", domain->name));
395 return NT_STATUS_NOT_SUPPORTED;
398 methods = domain->methods;
400 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
401 if (NT_STATUS_IS_ERR(status)) {
402 return status;
405 fill_in_password_policy(state->response, &password_policy);
407 return NT_STATUS_OK;
410 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
411 TALLOC_CTX *mem_ctx,
412 uint16 *lockout_threshold)
414 struct winbindd_methods *methods;
415 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
416 struct samr_DomInfo12 lockout_policy;
418 *lockout_threshold = 0;
420 methods = domain->methods;
422 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
423 if (NT_STATUS_IS_ERR(status)) {
424 return status;
427 *lockout_threshold = lockout_policy.lockout_threshold;
429 return NT_STATUS_OK;
432 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
433 TALLOC_CTX *mem_ctx,
434 uint32 *password_properties)
436 struct winbindd_methods *methods;
437 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
438 struct samr_DomInfo1 password_policy;
440 *password_properties = 0;
442 methods = domain->methods;
444 status = methods->password_policy(domain, mem_ctx, &password_policy);
445 if (NT_STATUS_IS_ERR(status)) {
446 return status;
449 *password_properties = password_policy.password_properties;
451 return NT_STATUS_OK;
454 #ifdef HAVE_KRB5
456 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
457 const char *type,
458 uid_t uid,
459 bool *internal_ccache)
461 /* accept FILE and WRFILE as krb5_cc_type from the client and then
462 * build the full ccname string based on the user's uid here -
463 * Guenther*/
465 const char *gen_cc = NULL;
467 *internal_ccache = true;
469 if (uid == -1) {
470 goto memory_ccache;
473 if (!type || type[0] == '\0') {
474 goto memory_ccache;
477 if (strequal(type, "FILE")) {
478 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
479 } else if (strequal(type, "WRFILE")) {
480 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
481 } else {
482 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
483 goto memory_ccache;
486 *internal_ccache = false;
487 goto done;
489 memory_ccache:
490 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
492 done:
493 if (gen_cc == NULL) {
494 DEBUG(0,("out of memory\n"));
495 return NULL;
498 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
500 return gen_cc;
503 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
505 const char *type = state->request->data.auth.krb5_cc_type;
507 state->response->data.auth.krb5ccname[0] = '\0';
509 if (type[0] == '\0') {
510 return;
513 if (!strequal(type, "FILE") &&
514 !strequal(type, "WRFILE")) {
515 DEBUG(10,("won't return krbccname for a %s type ccache\n",
516 type));
517 return;
520 fstrcpy(state->response->data.auth.krb5ccname, cc);
523 #endif
525 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
527 uid_t uid;
529 uid = state->request->data.auth.uid;
531 if (uid < 0) {
532 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
533 return -1;
535 return uid;
538 /**********************************************************************
539 Authenticate a user with a clear text password using Kerberos and fill up
540 ccache if required
541 **********************************************************************/
543 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
544 struct winbindd_cli_state *state,
545 struct netr_SamInfo3 **info3)
547 #ifdef HAVE_KRB5
548 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
549 krb5_error_code krb5_ret;
550 const char *cc = NULL;
551 const char *principal_s = NULL;
552 const char *service = NULL;
553 char *realm = NULL;
554 fstring name_domain, name_user;
555 time_t ticket_lifetime = 0;
556 time_t renewal_until = 0;
557 uid_t uid = -1;
558 ADS_STRUCT *ads;
559 time_t time_offset = 0;
560 bool internal_ccache = true;
562 ZERO_STRUCTP(info3);
564 *info3 = NULL;
566 /* 1st step:
567 * prepare a krb5_cc_cache string for the user */
569 uid = get_uid_from_state(state);
570 if (uid == -1) {
571 DEBUG(0,("no valid uid\n"));
574 cc = generate_krb5_ccache(state->mem_ctx,
575 state->request->data.auth.krb5_cc_type,
576 state->request->data.auth.uid,
577 &internal_ccache);
578 if (cc == NULL) {
579 return NT_STATUS_NO_MEMORY;
583 /* 2nd step:
584 * get kerberos properties */
586 if (domain->private_data) {
587 ads = (ADS_STRUCT *)domain->private_data;
588 time_offset = ads->auth.time_offset;
592 /* 3rd step:
593 * do kerberos auth and setup ccache as the user */
595 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
597 realm = domain->alt_name;
598 strupper_m(realm);
600 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
601 if (principal_s == NULL) {
602 return NT_STATUS_NO_MEMORY;
605 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
606 if (service == NULL) {
607 return NT_STATUS_NO_MEMORY;
610 /* if this is a user ccache, we need to act as the user to let the krb5
611 * library handle the chown, etc. */
613 /************************ ENTERING NON-ROOT **********************/
615 if (!internal_ccache) {
616 set_effective_uid(uid);
617 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
620 result = kerberos_return_info3_from_pac(state->mem_ctx,
621 principal_s,
622 state->request->data.auth.pass,
623 time_offset,
624 &ticket_lifetime,
625 &renewal_until,
627 true,
628 true,
629 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
630 NULL,
631 info3);
632 if (!internal_ccache) {
633 gain_root_privilege();
636 /************************ RETURNED TO ROOT **********************/
638 if (!NT_STATUS_IS_OK(result)) {
639 goto failed;
642 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
643 principal_s));
645 /* if we had a user's ccache then return that string for the pam
646 * environment */
648 if (!internal_ccache) {
650 setup_return_cc_name(state, cc);
652 result = add_ccache_to_list(principal_s,
654 service,
655 state->request->data.auth.user,
656 realm,
657 uid,
658 time(NULL),
659 ticket_lifetime,
660 renewal_until,
661 false);
663 if (!NT_STATUS_IS_OK(result)) {
664 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
665 nt_errstr(result)));
667 } else {
669 /* need to delete the memory cred cache, it is not used anymore */
671 krb5_ret = ads_kdestroy(cc);
672 if (krb5_ret) {
673 DEBUG(3,("winbindd_raw_kerberos_login: "
674 "could not destroy krb5 credential cache: "
675 "%s\n", error_message(krb5_ret)));
680 return NT_STATUS_OK;
682 failed:
684 /* we could have created a new credential cache with a valid tgt in it
685 * but we werent able to get or verify the service ticket for this
686 * local host and therefor didn't get the PAC, we need to remove that
687 * cache entirely now */
689 krb5_ret = ads_kdestroy(cc);
690 if (krb5_ret) {
691 DEBUG(3,("winbindd_raw_kerberos_login: "
692 "could not destroy krb5 credential cache: "
693 "%s\n", error_message(krb5_ret)));
696 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
697 DEBUG(3,("winbindd_raw_kerberos_login: "
698 "could not remove ccache for user %s\n",
699 state->request->data.auth.user));
702 return result;
703 #else
704 return NT_STATUS_NOT_SUPPORTED;
705 #endif /* HAVE_KRB5 */
708 /****************************************************************
709 ****************************************************************/
711 bool check_request_flags(uint32_t flags)
713 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
714 WBFLAG_PAM_INFO3_TEXT |
715 WBFLAG_PAM_INFO3_NDR;
717 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
718 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
719 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
720 !(flags & flags_edata) ) {
721 return true;
724 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
725 flags));
727 return false;
730 /****************************************************************
731 ****************************************************************/
733 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
734 struct netr_SamInfo3 *info3,
735 const char *name_domain,
736 const char *name_user)
738 NTSTATUS result;
739 uint32_t flags = state->request->flags;
741 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
742 memcpy(state->response->data.auth.user_session_key,
743 info3->base.key.key,
744 sizeof(state->response->data.auth.user_session_key)
745 /* 16 */);
748 if (flags & WBFLAG_PAM_LMKEY) {
749 memcpy(state->response->data.auth.first_8_lm_hash,
750 info3->base.LMSessKey.key,
751 sizeof(state->response->data.auth.first_8_lm_hash)
752 /* 8 */);
755 if (flags & WBFLAG_PAM_INFO3_TEXT) {
756 result = append_info3_as_txt(state->mem_ctx, state, info3);
757 if (!NT_STATUS_IS_OK(result)) {
758 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
759 nt_errstr(result)));
760 return result;
764 /* currently, anything from here on potentially overwrites extra_data. */
766 if (flags & WBFLAG_PAM_INFO3_NDR) {
767 result = append_info3_as_ndr(state->mem_ctx, state, info3);
768 if (!NT_STATUS_IS_OK(result)) {
769 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
770 nt_errstr(result)));
771 return result;
775 if (flags & WBFLAG_PAM_UNIX_NAME) {
776 result = append_unix_username(state->mem_ctx, state, info3,
777 name_domain, name_user);
778 if (!NT_STATUS_IS_OK(result)) {
779 DEBUG(10,("Failed to append Unix Username: %s\n",
780 nt_errstr(result)));
781 return result;
785 if (flags & WBFLAG_PAM_AFS_TOKEN) {
786 result = append_afs_token(state->mem_ctx, state, info3,
787 name_domain, name_user);
788 if (!NT_STATUS_IS_OK(result)) {
789 DEBUG(10,("Failed to append AFS token: %s\n",
790 nt_errstr(result)));
791 return result;
795 return NT_STATUS_OK;
798 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
799 struct winbindd_cli_state *state,
800 struct netr_SamInfo3 **info3)
802 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
803 uint16 max_allowed_bad_attempts;
804 fstring name_domain, name_user;
805 DOM_SID sid;
806 enum lsa_SidType type;
807 uchar new_nt_pass[NT_HASH_LEN];
808 const uint8 *cached_nt_pass;
809 const uint8 *cached_salt;
810 struct netr_SamInfo3 *my_info3;
811 time_t kickoff_time, must_change_time;
812 bool password_good = false;
813 #ifdef HAVE_KRB5
814 struct winbindd_tdc_domain *tdc_domain = NULL;
815 #endif
817 *info3 = NULL;
819 ZERO_STRUCTP(info3);
821 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
823 /* Parse domain and username */
825 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
828 if (!lookup_cached_name(state->mem_ctx,
829 name_domain,
830 name_user,
831 &sid,
832 &type)) {
833 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
834 return NT_STATUS_NO_SUCH_USER;
837 if (type != SID_NAME_USER) {
838 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
839 return NT_STATUS_LOGON_FAILURE;
842 result = winbindd_get_creds(domain,
843 state->mem_ctx,
844 &sid,
845 &my_info3,
846 &cached_nt_pass,
847 &cached_salt);
848 if (!NT_STATUS_IS_OK(result)) {
849 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
850 return result;
853 *info3 = my_info3;
855 E_md4hash(state->request->data.auth.pass, new_nt_pass);
857 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
858 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
859 if (cached_salt) {
860 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
863 if (cached_salt) {
864 /* In this case we didn't store the nt_hash itself,
865 but the MD5 combination of salt + nt_hash. */
866 uchar salted_hash[NT_HASH_LEN];
867 E_md5hash(cached_salt, new_nt_pass, salted_hash);
869 password_good = (memcmp(cached_nt_pass, salted_hash,
870 NT_HASH_LEN) == 0);
871 } else {
872 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
873 password_good = (memcmp(cached_nt_pass, new_nt_pass,
874 NT_HASH_LEN) == 0);
877 if (password_good) {
879 /* User *DOES* know the password, update logon_time and reset
880 * bad_pw_count */
882 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
884 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
885 return NT_STATUS_ACCOUNT_LOCKED_OUT;
888 if (my_info3->base.acct_flags & ACB_DISABLED) {
889 return NT_STATUS_ACCOUNT_DISABLED;
892 if (my_info3->base.acct_flags & ACB_WSTRUST) {
893 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
896 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
897 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
900 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
901 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
904 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
905 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
906 my_info3->base.acct_flags));
907 return NT_STATUS_LOGON_FAILURE;
910 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
911 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
912 return NT_STATUS_ACCOUNT_EXPIRED;
915 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
916 if (must_change_time != 0 && must_change_time < time(NULL)) {
917 /* we allow grace logons when the password has expired */
918 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
919 /* return NT_STATUS_PASSWORD_EXPIRED; */
920 goto success;
923 #ifdef HAVE_KRB5
924 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
925 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
926 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
927 /* used to cope with the case winbindd starting without network. */
928 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
930 uid_t uid = -1;
931 const char *cc = NULL;
932 char *realm = NULL;
933 const char *principal_s = NULL;
934 const char *service = NULL;
935 bool internal_ccache = false;
937 uid = get_uid_from_state(state);
938 if (uid == -1) {
939 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
940 return NT_STATUS_INVALID_PARAMETER;
943 cc = generate_krb5_ccache(state->mem_ctx,
944 state->request->data.auth.krb5_cc_type,
945 state->request->data.auth.uid,
946 &internal_ccache);
947 if (cc == NULL) {
948 return NT_STATUS_NO_MEMORY;
951 realm = domain->alt_name;
952 strupper_m(realm);
954 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
955 if (principal_s == NULL) {
956 return NT_STATUS_NO_MEMORY;
959 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
960 if (service == NULL) {
961 return NT_STATUS_NO_MEMORY;
964 if (!internal_ccache) {
966 setup_return_cc_name(state, cc);
968 result = add_ccache_to_list(principal_s,
970 service,
971 state->request->data.auth.user,
972 domain->alt_name,
973 uid,
974 time(NULL),
975 time(NULL) + lp_winbind_cache_time(),
976 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
977 true);
979 if (!NT_STATUS_IS_OK(result)) {
980 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
981 "to add ccache to list: %s\n",
982 nt_errstr(result)));
986 #endif /* HAVE_KRB5 */
987 success:
988 /* FIXME: we possibly should handle logon hours as well (does xp when
989 * offline?) see auth/auth_sam.c:sam_account_ok for details */
991 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
992 my_info3->base.bad_password_count = 0;
994 result = winbindd_update_creds_by_info3(domain,
995 state->mem_ctx,
996 state->request->data.auth.user,
997 state->request->data.auth.pass,
998 my_info3);
999 if (!NT_STATUS_IS_OK(result)) {
1000 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1001 nt_errstr(result)));
1002 return result;
1005 return NT_STATUS_OK;
1009 /* User does *NOT* know the correct password, modify info3 accordingly */
1011 /* failure of this is not critical */
1012 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1013 if (!NT_STATUS_IS_OK(result)) {
1014 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1015 "Won't be able to honour account lockout policies\n"));
1018 /* increase counter */
1019 my_info3->base.bad_password_count++;
1021 if (max_allowed_bad_attempts == 0) {
1022 goto failed;
1025 /* lockout user */
1026 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1028 uint32 password_properties;
1030 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1031 if (!NT_STATUS_IS_OK(result)) {
1032 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1035 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1036 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1037 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1041 failed:
1042 result = winbindd_update_creds_by_info3(domain,
1043 state->mem_ctx,
1044 state->request->data.auth.user,
1045 NULL,
1046 my_info3);
1048 if (!NT_STATUS_IS_OK(result)) {
1049 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1050 nt_errstr(result)));
1053 return NT_STATUS_LOGON_FAILURE;
1056 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1057 struct winbindd_cli_state *state,
1058 struct netr_SamInfo3 **info3)
1060 struct winbindd_domain *contact_domain;
1061 fstring name_domain, name_user;
1062 NTSTATUS result;
1064 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1066 /* Parse domain and username */
1068 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1070 /* what domain should we contact? */
1072 if ( IS_DC ) {
1073 if (!(contact_domain = find_domain_from_name(name_domain))) {
1074 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1075 state->request->data.auth.user, name_domain, name_user, name_domain));
1076 result = NT_STATUS_NO_SUCH_USER;
1077 goto done;
1080 } else {
1081 if (is_myname(name_domain)) {
1082 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1083 result = NT_STATUS_NO_SUCH_USER;
1084 goto done;
1087 contact_domain = find_domain_from_name(name_domain);
1088 if (contact_domain == NULL) {
1089 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1090 state->request->data.auth.user, name_domain, name_user, name_domain));
1092 contact_domain = find_our_domain();
1096 if (contact_domain->initialized &&
1097 contact_domain->active_directory) {
1098 goto try_login;
1101 if (!contact_domain->initialized) {
1102 init_dc_connection(contact_domain);
1105 if (!contact_domain->active_directory) {
1106 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1107 return NT_STATUS_INVALID_LOGON_TYPE;
1109 try_login:
1110 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1111 done:
1112 return result;
1115 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1116 const char *domain, const char *user,
1117 const DATA_BLOB *challenge,
1118 const DATA_BLOB *lm_resp,
1119 const DATA_BLOB *nt_resp,
1120 struct netr_SamInfo3 **pinfo3)
1122 struct auth_usersupplied_info *user_info = NULL;
1123 struct auth_serversupplied_info *server_info = NULL;
1124 struct netr_SamInfo3 *info3;
1125 NTSTATUS status;
1127 status = make_user_info(&user_info, user, user, domain, domain,
1128 global_myname(), lm_resp, nt_resp, NULL, NULL,
1129 NULL, True);
1130 if (!NT_STATUS_IS_OK(status)) {
1131 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1132 return status;
1135 status = check_sam_security(challenge, talloc_tos(), user_info,
1136 &server_info);
1137 free_user_info(&user_info);
1139 if (!NT_STATUS_IS_OK(status)) {
1140 DEBUG(10, ("check_ntlm_password failed: %s\n",
1141 nt_errstr(status)));
1142 return status;
1145 info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1146 if (info3 == NULL) {
1147 return NT_STATUS_NO_MEMORY;
1150 status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1151 if (!NT_STATUS_IS_OK(status)) {
1152 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1153 nt_errstr(status)));
1154 return status;
1157 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1158 *pinfo3 = info3;
1159 return NT_STATUS_OK;
1162 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1163 TALLOC_CTX *mem_ctx,
1164 uint32 logon_parameters,
1165 const char *server,
1166 const char *username,
1167 const char *domain,
1168 const char *workstation,
1169 const uint8 chal[8],
1170 DATA_BLOB lm_response,
1171 DATA_BLOB nt_response,
1172 struct netr_SamInfo3 **info3);
1174 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1175 struct winbindd_cli_state *state,
1176 struct netr_SamInfo3 **info3)
1179 struct rpc_pipe_client *netlogon_pipe;
1180 uchar chal[8];
1181 DATA_BLOB lm_resp;
1182 DATA_BLOB nt_resp;
1183 int attempts = 0;
1184 unsigned char local_lm_response[24];
1185 unsigned char local_nt_response[24];
1186 fstring name_domain, name_user;
1187 bool retry;
1188 NTSTATUS result;
1189 struct netr_SamInfo3 *my_info3 = NULL;
1191 *info3 = NULL;
1193 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1195 /* Parse domain and username */
1197 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1199 /* do password magic */
1201 generate_random_buffer(chal, sizeof(chal));
1203 if (lp_client_ntlmv2_auth()) {
1204 DATA_BLOB server_chal;
1205 DATA_BLOB names_blob;
1206 DATA_BLOB nt_response;
1207 DATA_BLOB lm_response;
1208 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1210 /* note that the 'workgroup' here is a best guess - we don't know
1211 the server's domain at this point. The 'server name' is also
1212 dodgy...
1214 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1216 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1217 state->request->data.auth.pass,
1218 &server_chal,
1219 &names_blob,
1220 &lm_response, &nt_response, NULL, NULL)) {
1221 data_blob_free(&names_blob);
1222 data_blob_free(&server_chal);
1223 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1224 result = NT_STATUS_NO_MEMORY;
1225 goto done;
1227 data_blob_free(&names_blob);
1228 data_blob_free(&server_chal);
1229 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1230 lm_response.length);
1231 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1232 nt_response.length);
1233 data_blob_free(&lm_response);
1234 data_blob_free(&nt_response);
1236 } else {
1237 if (lp_client_lanman_auth()
1238 && SMBencrypt(state->request->data.auth.pass,
1239 chal,
1240 local_lm_response)) {
1241 lm_resp = data_blob_talloc(state->mem_ctx,
1242 local_lm_response,
1243 sizeof(local_lm_response));
1244 } else {
1245 lm_resp = data_blob_null;
1247 SMBNTencrypt(state->request->data.auth.pass,
1248 chal,
1249 local_nt_response);
1251 nt_resp = data_blob_talloc(state->mem_ctx,
1252 local_nt_response,
1253 sizeof(local_nt_response));
1256 if (strequal(name_domain, get_global_sam_name())) {
1257 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1259 result = winbindd_dual_auth_passdb(
1260 state->mem_ctx, name_domain, name_user,
1261 &chal_blob, &lm_resp, &nt_resp, info3);
1262 goto done;
1265 /* check authentication loop */
1267 do {
1268 netlogon_fn_t logon_fn;
1270 ZERO_STRUCTP(my_info3);
1271 retry = false;
1273 result = cm_connect_netlogon(domain, &netlogon_pipe);
1275 if (!NT_STATUS_IS_OK(result)) {
1276 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1277 goto done;
1280 /* It is really important to try SamLogonEx here,
1281 * because in a clustered environment, we want to use
1282 * one machine account from multiple physical
1283 * computers.
1285 * With a normal SamLogon call, we must keep the
1286 * credentials chain updated and intact between all
1287 * users of the machine account (which would imply
1288 * cross-node communication for every NTLM logon).
1290 * (The credentials chain is not per NETLOGON pipe
1291 * connection, but globally on the server/client pair
1292 * by machine name).
1294 * When using SamLogonEx, the credentials are not
1295 * supplied, but the session key is implied by the
1296 * wrapping SamLogon context.
1298 * -- abartlet 21 April 2008
1301 logon_fn = domain->can_do_samlogon_ex
1302 ? rpccli_netlogon_sam_network_logon_ex
1303 : rpccli_netlogon_sam_network_logon;
1305 result = logon_fn(netlogon_pipe,
1306 state->mem_ctx,
1308 domain->dcname, /* server name */
1309 name_user, /* user name */
1310 name_domain, /* target domain */
1311 global_myname(), /* workstation */
1312 chal,
1313 lm_resp,
1314 nt_resp,
1315 &my_info3);
1316 attempts += 1;
1318 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1319 && domain->can_do_samlogon_ex) {
1320 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1321 "retrying with NetSamLogon\n"));
1322 domain->can_do_samlogon_ex = false;
1323 retry = true;
1324 continue;
1327 /* We have to try a second time as cm_connect_netlogon
1328 might not yet have noticed that the DC has killed
1329 our connection. */
1331 if (!rpccli_is_connected(netlogon_pipe)) {
1332 retry = true;
1333 continue;
1336 /* if we get access denied, a possible cause was that we had
1337 and open connection to the DC, but someone changed our
1338 machine account password out from underneath us using 'net
1339 rpc changetrustpw' */
1341 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1342 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1343 "ACCESS_DENIED. Maybe the trust account "
1344 "password was changed and we didn't know it. "
1345 "Killing connections to domain %s\n",
1346 name_domain));
1347 invalidate_cm_connection(&domain->conn);
1348 retry = true;
1351 } while ( (attempts < 2) && retry );
1353 /* handle the case where a NT4 DC does not fill in the acct_flags in
1354 * the samlogon reply info3. When accurate info3 is required by the
1355 * caller, we look up the account flags ourselve - gd */
1357 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1358 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1360 struct rpc_pipe_client *samr_pipe;
1361 struct policy_handle samr_domain_handle, user_pol;
1362 union samr_UserInfo *info = NULL;
1363 NTSTATUS status_tmp;
1364 uint32 acct_flags;
1366 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1367 &samr_pipe, &samr_domain_handle);
1369 if (!NT_STATUS_IS_OK(status_tmp)) {
1370 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1371 nt_errstr(status_tmp)));
1372 goto done;
1375 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1376 &samr_domain_handle,
1377 MAXIMUM_ALLOWED_ACCESS,
1378 my_info3->base.rid,
1379 &user_pol);
1381 if (!NT_STATUS_IS_OK(status_tmp)) {
1382 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1383 nt_errstr(status_tmp)));
1384 goto done;
1387 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1388 &user_pol,
1390 &info);
1392 if (!NT_STATUS_IS_OK(status_tmp)) {
1393 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1394 nt_errstr(status_tmp)));
1395 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1396 goto done;
1399 acct_flags = info->info16.acct_flags;
1401 if (acct_flags == 0) {
1402 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1403 goto done;
1406 my_info3->base.acct_flags = acct_flags;
1408 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1410 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1413 *info3 = my_info3;
1414 done:
1415 return result;
1418 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1419 struct winbindd_cli_state *state)
1421 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1422 NTSTATUS krb5_result = NT_STATUS_OK;
1423 fstring name_domain, name_user;
1424 char *mapped_user;
1425 fstring domain_user;
1426 struct netr_SamInfo3 *info3 = NULL;
1427 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1429 /* Ensure null termination */
1430 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1432 /* Ensure null termination */
1433 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1435 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1436 state->request->data.auth.user));
1438 if (!check_request_flags(state->request->flags)) {
1439 result = NT_STATUS_INVALID_PARAMETER_MIX;
1440 goto done;
1443 /* Parse domain and username */
1445 name_map_status = normalize_name_unmap(state->mem_ctx,
1446 state->request->data.auth.user,
1447 &mapped_user);
1449 /* If the name normalization didnt' actually do anything,
1450 just use the original name */
1452 if (!NT_STATUS_IS_OK(name_map_status) &&
1453 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1455 mapped_user = state->request->data.auth.user;
1458 parse_domain_user(mapped_user, name_domain, name_user);
1460 if ( mapped_user != state->request->data.auth.user ) {
1461 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1462 safe_strcpy( state->request->data.auth.user, domain_user,
1463 sizeof(state->request->data.auth.user)-1 );
1466 if (domain->online == false) {
1467 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1468 if (domain->startup) {
1469 /* Logons are very important to users. If we're offline and
1470 we get a request within the first 30 seconds of startup,
1471 try very hard to find a DC and go online. */
1473 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1474 "request in startup mode.\n", domain->name ));
1476 winbindd_flush_negative_conn_cache(domain);
1477 result = init_dc_connection(domain);
1481 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1483 /* Check for Kerberos authentication */
1484 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1486 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1487 /* save for later */
1488 krb5_result = result;
1491 if (NT_STATUS_IS_OK(result)) {
1492 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1493 goto process_result;
1494 } else {
1495 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1498 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1499 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1500 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1501 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1502 set_domain_offline( domain );
1503 goto cached_logon;
1506 /* there are quite some NT_STATUS errors where there is no
1507 * point in retrying with a samlogon, we explictly have to take
1508 * care not to increase the bad logon counter on the DC */
1510 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1511 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1512 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1513 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1514 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1515 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1516 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1517 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1518 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1519 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1520 goto done;
1523 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1524 DEBUG(3,("falling back to samlogon\n"));
1525 goto sam_logon;
1526 } else {
1527 goto cached_logon;
1531 sam_logon:
1532 /* Check for Samlogon authentication */
1533 if (domain->online) {
1534 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1536 if (NT_STATUS_IS_OK(result)) {
1537 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1538 /* add the Krb5 err if we have one */
1539 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1540 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1542 goto process_result;
1545 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1546 nt_errstr(result)));
1548 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1549 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1550 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1552 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1553 set_domain_offline( domain );
1554 goto cached_logon;
1557 if (domain->online) {
1558 /* We're still online - fail. */
1559 goto done;
1563 cached_logon:
1564 /* Check for Cached logons */
1565 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1566 lp_winbind_offline_logon()) {
1568 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1570 if (NT_STATUS_IS_OK(result)) {
1571 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1572 goto process_result;
1573 } else {
1574 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1575 goto done;
1579 process_result:
1581 if (NT_STATUS_IS_OK(result)) {
1583 DOM_SID user_sid;
1585 /* In all codepaths where result == NT_STATUS_OK info3 must have
1586 been initialized. */
1587 if (!info3) {
1588 result = NT_STATUS_INTERNAL_ERROR;
1589 goto done;
1592 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1593 netsamlogon_cache_store(name_user, info3);
1595 /* save name_to_sid info as early as possible (only if
1596 this is our primary domain so we don't invalidate
1597 the cache entry by storing the seq_num for the wrong
1598 domain). */
1599 if ( domain->primary ) {
1600 sid_compose(&user_sid, info3->base.domain_sid,
1601 info3->base.rid);
1602 cache_name2sid(domain, name_domain, name_user,
1603 SID_NAME_USER, &user_sid);
1606 /* Check if the user is in the right group */
1608 result = check_info3_in_group(
1609 info3,
1610 state->request->data.auth.require_membership_of_sid);
1611 if (!NT_STATUS_IS_OK(result)) {
1612 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1613 state->request->data.auth.user,
1614 state->request->data.auth.require_membership_of_sid));
1615 goto done;
1618 result = append_auth_data(state, info3, name_domain,
1619 name_user);
1620 if (!NT_STATUS_IS_OK(result)) {
1621 goto done;
1624 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1626 /* Store in-memory creds for single-signon using ntlm_auth. */
1627 result = winbindd_add_memory_creds(state->request->data.auth.user,
1628 get_uid_from_state(state),
1629 state->request->data.auth.pass);
1631 if (!NT_STATUS_IS_OK(result)) {
1632 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1633 goto done;
1636 if (lp_winbind_offline_logon()) {
1637 result = winbindd_store_creds(domain,
1638 state->mem_ctx,
1639 state->request->data.auth.user,
1640 state->request->data.auth.pass,
1641 info3, NULL);
1642 if (!NT_STATUS_IS_OK(result)) {
1644 /* Release refcount. */
1645 winbindd_delete_memory_creds(state->request->data.auth.user);
1647 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1648 goto done;
1654 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1655 struct winbindd_domain *our_domain = find_our_domain();
1657 /* This is not entirely correct I believe, but it is
1658 consistent. Only apply the password policy settings
1659 too warn users for our own domain. Cannot obtain these
1660 from trusted DCs all the time so don't do it at all.
1661 -- jerry */
1663 result = NT_STATUS_NOT_SUPPORTED;
1664 if (our_domain == domain ) {
1665 result = fillup_password_policy(our_domain, state);
1668 if (!NT_STATUS_IS_OK(result)
1669 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1671 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1672 domain->name, nt_errstr(result)));
1673 goto done;
1677 result = NT_STATUS_OK;
1680 done:
1681 /* give us a more useful (more correct?) error code */
1682 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1683 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1684 result = NT_STATUS_NO_LOGON_SERVERS;
1687 set_auth_errors(state->response, result);
1689 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1690 state->request->data.auth.user,
1691 state->response->data.auth.nt_status_string,
1692 state->response->data.auth.pam_error));
1694 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1698 /**********************************************************************
1699 Challenge Response Authentication Protocol
1700 **********************************************************************/
1702 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1704 struct winbindd_domain *domain = NULL;
1705 const char *domain_name = NULL;
1706 NTSTATUS result;
1708 if (!check_request_flags(state->request->flags)) {
1709 result = NT_STATUS_INVALID_PARAMETER_MIX;
1710 goto done;
1713 if (!state->privileged) {
1714 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1715 "denied. !\n"));
1716 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1717 "on %s are set correctly.\n",
1718 get_winbind_priv_pipe_dir()));
1719 /* send a better message than ACCESS_DENIED */
1720 fstr_sprintf(state->response->data.auth.error_string,
1721 "winbind client not authorized to use "
1722 "winbindd_pam_auth_crap. Ensure permissions on "
1723 "%s are set correctly.",
1724 get_winbind_priv_pipe_dir());
1725 result = NT_STATUS_ACCESS_DENIED;
1726 goto done;
1729 /* Ensure null termination */
1730 state->request->data.auth_crap.user
1731 [sizeof(state->request->data.auth_crap.user)-1]=0;
1732 state->request->data.auth_crap.domain
1733 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1735 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1736 (unsigned long)state->pid,
1737 state->request->data.auth_crap.domain,
1738 state->request->data.auth_crap.user));
1740 if (*state->request->data.auth_crap.domain != '\0') {
1741 domain_name = state->request->data.auth_crap.domain;
1742 } else if (lp_winbind_use_default_domain()) {
1743 domain_name = lp_workgroup();
1746 if (domain_name != NULL)
1747 domain = find_auth_domain(state->request->flags, domain_name);
1749 if (domain != NULL) {
1750 sendto_domain(state, domain);
1751 return;
1754 result = NT_STATUS_NO_SUCH_USER;
1756 done:
1757 set_auth_errors(state->response, result);
1758 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1759 state->request->data.auth_crap.domain,
1760 state->request->data.auth_crap.user,
1761 state->response->data.auth.nt_status_string,
1762 state->response->data.auth.pam_error));
1763 request_error(state);
1764 return;
1768 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1769 struct winbindd_cli_state *state)
1771 NTSTATUS result;
1772 struct netr_SamInfo3 *info3 = NULL;
1773 struct rpc_pipe_client *netlogon_pipe;
1774 const char *name_user = NULL;
1775 const char *name_domain = NULL;
1776 const char *workstation;
1777 int attempts = 0;
1778 bool retry;
1780 DATA_BLOB lm_resp, nt_resp;
1782 /* This is child-only, so no check for privileged access is needed
1783 anymore */
1785 /* Ensure null termination */
1786 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1787 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1789 if (!check_request_flags(state->request->flags)) {
1790 result = NT_STATUS_INVALID_PARAMETER_MIX;
1791 goto done;
1794 name_user = state->request->data.auth_crap.user;
1796 if (*state->request->data.auth_crap.domain) {
1797 name_domain = state->request->data.auth_crap.domain;
1798 } else if (lp_winbind_use_default_domain()) {
1799 name_domain = lp_workgroup();
1800 } else {
1801 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1802 name_user));
1803 result = NT_STATUS_NO_SUCH_USER;
1804 goto done;
1807 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1808 name_domain, name_user));
1810 if (*state->request->data.auth_crap.workstation) {
1811 workstation = state->request->data.auth_crap.workstation;
1812 } else {
1813 workstation = global_myname();
1816 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1817 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1818 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1819 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1820 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1821 state->request->data.auth_crap.lm_resp_len,
1822 state->request->data.auth_crap.nt_resp_len));
1823 result = NT_STATUS_INVALID_PARAMETER;
1824 goto done;
1828 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1829 state->request->data.auth_crap.lm_resp_len);
1831 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1832 nt_resp = data_blob_talloc(state->mem_ctx,
1833 state->request->extra_data.data,
1834 state->request->data.auth_crap.nt_resp_len);
1835 } else {
1836 nt_resp = data_blob_talloc(state->mem_ctx,
1837 state->request->data.auth_crap.nt_resp,
1838 state->request->data.auth_crap.nt_resp_len);
1841 if (strequal(name_domain, get_global_sam_name())) {
1842 DATA_BLOB chal_blob = data_blob_const(
1843 state->request->data.auth_crap.chal,
1844 sizeof(state->request->data.auth_crap.chal));
1846 result = winbindd_dual_auth_passdb(
1847 state->mem_ctx, name_domain, name_user,
1848 &chal_blob, &lm_resp, &nt_resp, &info3);
1849 goto process_result;
1852 do {
1853 netlogon_fn_t logon_fn;
1855 retry = false;
1857 netlogon_pipe = NULL;
1858 result = cm_connect_netlogon(domain, &netlogon_pipe);
1860 if (!NT_STATUS_IS_OK(result)) {
1861 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1862 nt_errstr(result)));
1863 goto done;
1866 logon_fn = domain->can_do_samlogon_ex
1867 ? rpccli_netlogon_sam_network_logon_ex
1868 : rpccli_netlogon_sam_network_logon;
1870 result = logon_fn(netlogon_pipe,
1871 state->mem_ctx,
1872 state->request->data.auth_crap.logon_parameters,
1873 domain->dcname,
1874 name_user,
1875 name_domain,
1876 /* Bug #3248 - found by Stefan Burkei. */
1877 workstation, /* We carefully set this above so use it... */
1878 state->request->data.auth_crap.chal,
1879 lm_resp,
1880 nt_resp,
1881 &info3);
1883 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1884 && domain->can_do_samlogon_ex) {
1885 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1886 "retrying with NetSamLogon\n"));
1887 domain->can_do_samlogon_ex = false;
1888 retry = true;
1889 continue;
1892 attempts += 1;
1894 /* We have to try a second time as cm_connect_netlogon
1895 might not yet have noticed that the DC has killed
1896 our connection. */
1898 if (!rpccli_is_connected(netlogon_pipe)) {
1899 retry = true;
1900 continue;
1903 /* if we get access denied, a possible cause was that we had and open
1904 connection to the DC, but someone changed our machine account password
1905 out from underneath us using 'net rpc changetrustpw' */
1907 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1908 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1909 "ACCESS_DENIED. Maybe the trust account "
1910 "password was changed and we didn't know it. "
1911 "Killing connections to domain %s\n",
1912 name_domain));
1913 invalidate_cm_connection(&domain->conn);
1914 retry = true;
1917 } while ( (attempts < 2) && retry );
1919 process_result:
1921 if (NT_STATUS_IS_OK(result)) {
1923 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1924 netsamlogon_cache_store(name_user, info3);
1926 /* Check if the user is in the right group */
1928 result = check_info3_in_group(
1929 info3,
1930 state->request->data.auth_crap.require_membership_of_sid);
1931 if (!NT_STATUS_IS_OK(result)) {
1932 DEBUG(3, ("User %s is not in the required group (%s), so "
1933 "crap authentication is rejected\n",
1934 state->request->data.auth_crap.user,
1935 state->request->data.auth_crap.require_membership_of_sid));
1936 goto done;
1939 result = append_auth_data(state, info3, name_domain,
1940 name_user);
1941 if (!NT_STATUS_IS_OK(result)) {
1942 goto done;
1946 done:
1948 /* give us a more useful (more correct?) error code */
1949 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1950 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1951 result = NT_STATUS_NO_LOGON_SERVERS;
1954 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1955 result = nt_status_squash(result);
1958 set_auth_errors(state->response, result);
1960 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1961 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1962 name_domain,
1963 name_user,
1964 state->response->data.auth.nt_status_string,
1965 state->response->data.auth.pam_error));
1967 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1970 /* Change a user password */
1972 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1974 fstring domain, user;
1975 char *mapped_user;
1976 struct winbindd_domain *contact_domain;
1977 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1979 /* Ensure null termination */
1980 state->request->data.chauthtok.user[
1981 sizeof(state->request->data.chauthtok.user)-1]='\0';
1983 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1984 state->request->data.chauthtok.user));
1986 /* Setup crap */
1988 nt_status = normalize_name_unmap(state->mem_ctx,
1989 state->request->data.chauthtok.user,
1990 &mapped_user);
1992 /* Update the chauthtok name if we did any mapping */
1994 if (NT_STATUS_IS_OK(nt_status) ||
1995 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
1997 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2000 /* Must pass in state->...chauthtok.user because
2001 canonicalize_username() assumes an fstring(). Since
2002 we have already copied it (if necessary), this is ok. */
2004 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2005 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2006 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2007 "(PAM: %d)\n",
2008 state->request->data.chauthtok.user,
2009 state->response->data.auth.nt_status_string,
2010 state->response->data.auth.pam_error));
2011 request_error(state);
2012 return;
2015 contact_domain = find_domain_from_name(domain);
2016 if (!contact_domain) {
2017 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2018 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2019 state->request->data.chauthtok.user, domain, user, domain));
2020 request_error(state);
2021 return;
2024 sendto_domain(state, contact_domain);
2027 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2028 struct winbindd_cli_state *state)
2030 char *oldpass;
2031 char *newpass = NULL;
2032 struct policy_handle dom_pol;
2033 struct rpc_pipe_client *cli;
2034 bool got_info = false;
2035 struct samr_DomInfo1 *info = NULL;
2036 struct userPwdChangeFailureInformation *reject = NULL;
2037 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2038 fstring domain, user;
2040 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2041 state->request->data.auth.user));
2043 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2044 goto done;
2047 /* Change password */
2049 oldpass = state->request->data.chauthtok.oldpass;
2050 newpass = state->request->data.chauthtok.newpass;
2052 /* Initialize reject reason */
2053 state->response->data.auth.reject_reason = Undefined;
2055 /* Get sam handle */
2057 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2058 &dom_pol);
2059 if (!NT_STATUS_IS_OK(result)) {
2060 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2061 goto done;
2064 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2065 user,
2066 newpass,
2067 oldpass,
2068 &info,
2069 &reject);
2071 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2073 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2075 fill_in_password_policy(state->response, info);
2077 state->response->data.auth.reject_reason =
2078 reject->extendedFailureReason;
2080 got_info = true;
2083 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2084 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2085 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2086 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2088 /* only fallback when the chgpasswd_user3 call is not supported */
2089 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2090 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2091 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2092 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2094 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2095 nt_errstr(result)));
2097 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2099 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2100 Map to the same status code as Windows 2003. */
2102 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2103 result = NT_STATUS_PASSWORD_RESTRICTION;
2107 done:
2109 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2111 /* Update the single sign-on memory creds. */
2112 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2113 newpass);
2115 /* When we login from gdm or xdm and password expires,
2116 * we change password, but there are no memory crendentials
2117 * So, winbindd_replace_memory_creds() returns
2118 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2119 * --- BoYang
2120 * */
2121 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2122 result = NT_STATUS_OK;
2125 if (!NT_STATUS_IS_OK(result)) {
2126 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2127 goto process_result;
2130 if (lp_winbind_offline_logon()) {
2131 result = winbindd_update_creds_by_name(contact_domain,
2132 state->mem_ctx, user,
2133 newpass);
2134 /* Again, this happens when we login from gdm or xdm
2135 * and the password expires, *BUT* cached crendentials
2136 * doesn't exist. winbindd_update_creds_by_name()
2137 * returns NT_STATUS_NO_SUCH_USER.
2138 * This is not a failure.
2139 * --- BoYang
2140 * */
2141 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2142 result = NT_STATUS_OK;
2145 if (!NT_STATUS_IS_OK(result)) {
2146 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2147 goto process_result;
2152 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2154 NTSTATUS policy_ret;
2156 policy_ret = fillup_password_policy(contact_domain, state);
2158 /* failure of this is non critical, it will just provide no
2159 * additional information to the client why the change has
2160 * failed - Guenther */
2162 if (!NT_STATUS_IS_OK(policy_ret)) {
2163 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2164 goto process_result;
2168 process_result:
2170 set_auth_errors(state->response, result);
2172 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2173 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2174 domain,
2175 user,
2176 state->response->data.auth.nt_status_string,
2177 state->response->data.auth.pam_error));
2179 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2182 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2184 struct winbindd_domain *domain;
2185 fstring name_domain, user;
2186 uid_t caller_uid = (uid_t)-1;
2187 uid_t request_uid = state->request->data.logoff.uid;
2189 /* Ensure null termination */
2190 state->request->data.logoff.user
2191 [sizeof(state->request->data.logoff.user)-1]='\0';
2193 state->request->data.logoff.krb5ccname
2194 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2196 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2197 state->request->data.logoff.user));
2199 if (request_uid == (uid_t)-1) {
2200 goto failed;
2203 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2204 goto failed;
2207 if ((domain = find_auth_domain(state->request->flags,
2208 name_domain)) == NULL) {
2209 goto failed;
2212 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2213 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2214 strerror(errno)));
2215 goto failed;
2218 switch (caller_uid) {
2219 case -1:
2220 goto failed;
2221 case 0:
2222 /* root must be able to logoff any user - gd */
2223 state->request->data.logoff.uid = request_uid;
2224 break;
2225 default:
2226 if (caller_uid != request_uid) {
2227 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2228 goto failed;
2230 state->request->data.logoff.uid = caller_uid;
2231 break;
2234 sendto_domain(state, domain);
2235 return;
2237 failed:
2238 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2239 DEBUG(5, ("Pam Logoff for %s returned %s "
2240 "(PAM: %d)\n",
2241 state->request->data.logoff.user,
2242 state->response->data.auth.nt_status_string,
2243 state->response->data.auth.pam_error));
2244 request_error(state);
2245 return;
2248 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2249 struct winbindd_cli_state *state)
2251 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2253 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2254 state->request->data.logoff.user));
2256 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2257 result = NT_STATUS_OK;
2258 goto process_result;
2261 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2262 result = NT_STATUS_OK;
2263 goto process_result;
2266 #ifdef HAVE_KRB5
2268 if (state->request->data.logoff.uid < 0) {
2269 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2270 goto process_result;
2273 /* what we need here is to find the corresponding krb5 ccache name *we*
2274 * created for a given username and destroy it */
2276 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2277 result = NT_STATUS_OK;
2278 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2279 goto process_result;
2282 if (!ccache_entry_identical(state->request->data.logoff.user,
2283 state->request->data.logoff.uid,
2284 state->request->data.logoff.krb5ccname)) {
2285 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2286 goto process_result;
2289 result = remove_ccache(state->request->data.logoff.user);
2290 if (!NT_STATUS_IS_OK(result)) {
2291 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2292 nt_errstr(result)));
2293 goto process_result;
2296 #else
2297 result = NT_STATUS_NOT_SUPPORTED;
2298 #endif
2300 process_result:
2302 winbindd_delete_memory_creds(state->request->data.logoff.user);
2304 set_auth_errors(state->response, result);
2306 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2309 /* Change user password with auth crap*/
2311 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2313 struct winbindd_domain *domain = NULL;
2314 const char *domain_name = NULL;
2316 /* Ensure null termination */
2317 state->request->data.chng_pswd_auth_crap.user[
2318 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2319 state->request->data.chng_pswd_auth_crap.domain[
2320 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2322 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2323 (unsigned long)state->pid,
2324 state->request->data.chng_pswd_auth_crap.domain,
2325 state->request->data.chng_pswd_auth_crap.user));
2327 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2328 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2329 } else if (lp_winbind_use_default_domain()) {
2330 domain_name = lp_workgroup();
2333 if (domain_name != NULL)
2334 domain = find_domain_from_name(domain_name);
2336 if (domain != NULL) {
2337 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2338 "%s\n", (unsigned long)state->pid,domain->name));
2339 sendto_domain(state, domain);
2340 return;
2343 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2344 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2345 state->request->data.chng_pswd_auth_crap.domain,
2346 state->request->data.chng_pswd_auth_crap.user,
2347 state->response->data.auth.nt_status_string,
2348 state->response->data.auth.pam_error));
2349 request_error(state);
2350 return;
2353 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2355 NTSTATUS result;
2356 DATA_BLOB new_nt_password;
2357 DATA_BLOB old_nt_hash_enc;
2358 DATA_BLOB new_lm_password;
2359 DATA_BLOB old_lm_hash_enc;
2360 fstring domain,user;
2361 struct policy_handle dom_pol;
2362 struct winbindd_domain *contact_domain = domainSt;
2363 struct rpc_pipe_client *cli;
2365 /* Ensure null termination */
2366 state->request->data.chng_pswd_auth_crap.user[
2367 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2368 state->request->data.chng_pswd_auth_crap.domain[
2369 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2370 *domain = 0;
2371 *user = 0;
2373 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2374 (unsigned long)state->pid,
2375 state->request->data.chng_pswd_auth_crap.domain,
2376 state->request->data.chng_pswd_auth_crap.user));
2378 if (lp_winbind_offline_logon()) {
2379 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2380 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2381 result = NT_STATUS_ACCESS_DENIED;
2382 goto done;
2385 if (*state->request->data.chng_pswd_auth_crap.domain) {
2386 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2387 } else {
2388 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2389 domain, user);
2391 if(!*domain) {
2392 DEBUG(3,("no domain specified with username (%s) - "
2393 "failing auth\n",
2394 state->request->data.chng_pswd_auth_crap.user));
2395 result = NT_STATUS_NO_SUCH_USER;
2396 goto done;
2400 if (!*domain && lp_winbind_use_default_domain()) {
2401 fstrcpy(domain,(char *)lp_workgroup());
2404 if(!*user) {
2405 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2408 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2409 (unsigned long)state->pid, domain, user));
2411 /* Change password */
2412 new_nt_password = data_blob_talloc(
2413 state->mem_ctx,
2414 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2415 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2417 old_nt_hash_enc = data_blob_talloc(
2418 state->mem_ctx,
2419 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2420 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2422 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2423 new_lm_password = data_blob_talloc(
2424 state->mem_ctx,
2425 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2426 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2428 old_lm_hash_enc = data_blob_talloc(
2429 state->mem_ctx,
2430 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2431 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2432 } else {
2433 new_lm_password.length = 0;
2434 old_lm_hash_enc.length = 0;
2437 /* Get sam handle */
2439 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2440 if (!NT_STATUS_IS_OK(result)) {
2441 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2442 goto done;
2445 result = rpccli_samr_chng_pswd_auth_crap(
2446 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2447 new_lm_password, old_lm_hash_enc);
2449 done:
2451 set_auth_errors(state->response, result);
2453 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2454 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2455 domain, user,
2456 state->response->data.auth.nt_status_string,
2457 state->response->data.auth.pam_error));
2459 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;