s3: Convert WINBINDD_PAM_CHAUTHTOK to the new async API
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
blob61c8c298f1b615d1823e45ce6266184abf0a81cb
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;
1697 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1698 struct winbindd_cli_state *state)
1700 NTSTATUS result;
1701 struct netr_SamInfo3 *info3 = NULL;
1702 struct rpc_pipe_client *netlogon_pipe;
1703 const char *name_user = NULL;
1704 const char *name_domain = NULL;
1705 const char *workstation;
1706 int attempts = 0;
1707 bool retry;
1709 DATA_BLOB lm_resp, nt_resp;
1711 /* This is child-only, so no check for privileged access is needed
1712 anymore */
1714 /* Ensure null termination */
1715 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1716 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1718 if (!check_request_flags(state->request->flags)) {
1719 result = NT_STATUS_INVALID_PARAMETER_MIX;
1720 goto done;
1723 name_user = state->request->data.auth_crap.user;
1725 if (*state->request->data.auth_crap.domain) {
1726 name_domain = state->request->data.auth_crap.domain;
1727 } else if (lp_winbind_use_default_domain()) {
1728 name_domain = lp_workgroup();
1729 } else {
1730 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1731 name_user));
1732 result = NT_STATUS_NO_SUCH_USER;
1733 goto done;
1736 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1737 name_domain, name_user));
1739 if (*state->request->data.auth_crap.workstation) {
1740 workstation = state->request->data.auth_crap.workstation;
1741 } else {
1742 workstation = global_myname();
1745 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1746 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1747 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1748 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1749 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1750 state->request->data.auth_crap.lm_resp_len,
1751 state->request->data.auth_crap.nt_resp_len));
1752 result = NT_STATUS_INVALID_PARAMETER;
1753 goto done;
1757 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1758 state->request->data.auth_crap.lm_resp_len);
1760 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1761 nt_resp = data_blob_talloc(state->mem_ctx,
1762 state->request->extra_data.data,
1763 state->request->data.auth_crap.nt_resp_len);
1764 } else {
1765 nt_resp = data_blob_talloc(state->mem_ctx,
1766 state->request->data.auth_crap.nt_resp,
1767 state->request->data.auth_crap.nt_resp_len);
1770 if (strequal(name_domain, get_global_sam_name())) {
1771 DATA_BLOB chal_blob = data_blob_const(
1772 state->request->data.auth_crap.chal,
1773 sizeof(state->request->data.auth_crap.chal));
1775 result = winbindd_dual_auth_passdb(
1776 state->mem_ctx, name_domain, name_user,
1777 &chal_blob, &lm_resp, &nt_resp, &info3);
1778 goto process_result;
1781 do {
1782 netlogon_fn_t logon_fn;
1784 retry = false;
1786 netlogon_pipe = NULL;
1787 result = cm_connect_netlogon(domain, &netlogon_pipe);
1789 if (!NT_STATUS_IS_OK(result)) {
1790 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1791 nt_errstr(result)));
1792 goto done;
1795 logon_fn = domain->can_do_samlogon_ex
1796 ? rpccli_netlogon_sam_network_logon_ex
1797 : rpccli_netlogon_sam_network_logon;
1799 result = logon_fn(netlogon_pipe,
1800 state->mem_ctx,
1801 state->request->data.auth_crap.logon_parameters,
1802 domain->dcname,
1803 name_user,
1804 name_domain,
1805 /* Bug #3248 - found by Stefan Burkei. */
1806 workstation, /* We carefully set this above so use it... */
1807 state->request->data.auth_crap.chal,
1808 lm_resp,
1809 nt_resp,
1810 &info3);
1812 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1813 && domain->can_do_samlogon_ex) {
1814 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1815 "retrying with NetSamLogon\n"));
1816 domain->can_do_samlogon_ex = false;
1817 retry = true;
1818 continue;
1821 attempts += 1;
1823 /* We have to try a second time as cm_connect_netlogon
1824 might not yet have noticed that the DC has killed
1825 our connection. */
1827 if (!rpccli_is_connected(netlogon_pipe)) {
1828 retry = true;
1829 continue;
1832 /* if we get access denied, a possible cause was that we had and open
1833 connection to the DC, but someone changed our machine account password
1834 out from underneath us using 'net rpc changetrustpw' */
1836 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1837 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1838 "ACCESS_DENIED. Maybe the trust account "
1839 "password was changed and we didn't know it. "
1840 "Killing connections to domain %s\n",
1841 name_domain));
1842 invalidate_cm_connection(&domain->conn);
1843 retry = true;
1846 } while ( (attempts < 2) && retry );
1848 process_result:
1850 if (NT_STATUS_IS_OK(result)) {
1852 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1853 netsamlogon_cache_store(name_user, info3);
1855 /* Check if the user is in the right group */
1857 result = check_info3_in_group(
1858 info3,
1859 state->request->data.auth_crap.require_membership_of_sid);
1860 if (!NT_STATUS_IS_OK(result)) {
1861 DEBUG(3, ("User %s is not in the required group (%s), so "
1862 "crap authentication is rejected\n",
1863 state->request->data.auth_crap.user,
1864 state->request->data.auth_crap.require_membership_of_sid));
1865 goto done;
1868 result = append_auth_data(state, info3, name_domain,
1869 name_user);
1870 if (!NT_STATUS_IS_OK(result)) {
1871 goto done;
1875 done:
1877 /* give us a more useful (more correct?) error code */
1878 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1879 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1880 result = NT_STATUS_NO_LOGON_SERVERS;
1883 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1884 result = nt_status_squash(result);
1887 set_auth_errors(state->response, result);
1889 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1890 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1891 name_domain,
1892 name_user,
1893 state->response->data.auth.nt_status_string,
1894 state->response->data.auth.pam_error));
1896 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1899 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1900 struct winbindd_cli_state *state)
1902 char *oldpass;
1903 char *newpass = NULL;
1904 struct policy_handle dom_pol;
1905 struct rpc_pipe_client *cli;
1906 bool got_info = false;
1907 struct samr_DomInfo1 *info = NULL;
1908 struct userPwdChangeFailureInformation *reject = NULL;
1909 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1910 fstring domain, user;
1912 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1913 state->request->data.auth.user));
1915 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1916 goto done;
1919 /* Change password */
1921 oldpass = state->request->data.chauthtok.oldpass;
1922 newpass = state->request->data.chauthtok.newpass;
1924 /* Initialize reject reason */
1925 state->response->data.auth.reject_reason = Undefined;
1927 /* Get sam handle */
1929 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1930 &dom_pol);
1931 if (!NT_STATUS_IS_OK(result)) {
1932 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1933 goto done;
1936 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1937 user,
1938 newpass,
1939 oldpass,
1940 &info,
1941 &reject);
1943 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1945 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1947 fill_in_password_policy(state->response, info);
1949 state->response->data.auth.reject_reason =
1950 reject->extendedFailureReason;
1952 got_info = true;
1955 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1956 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1957 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1958 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1960 /* only fallback when the chgpasswd_user3 call is not supported */
1961 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1962 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1963 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1964 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1966 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1967 nt_errstr(result)));
1969 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1971 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1972 Map to the same status code as Windows 2003. */
1974 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1975 result = NT_STATUS_PASSWORD_RESTRICTION;
1979 done:
1981 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1983 /* Update the single sign-on memory creds. */
1984 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
1985 newpass);
1987 /* When we login from gdm or xdm and password expires,
1988 * we change password, but there are no memory crendentials
1989 * So, winbindd_replace_memory_creds() returns
1990 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
1991 * --- BoYang
1992 * */
1993 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1994 result = NT_STATUS_OK;
1997 if (!NT_STATUS_IS_OK(result)) {
1998 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
1999 goto process_result;
2002 if (lp_winbind_offline_logon()) {
2003 result = winbindd_update_creds_by_name(contact_domain,
2004 state->mem_ctx, user,
2005 newpass);
2006 /* Again, this happens when we login from gdm or xdm
2007 * and the password expires, *BUT* cached crendentials
2008 * doesn't exist. winbindd_update_creds_by_name()
2009 * returns NT_STATUS_NO_SUCH_USER.
2010 * This is not a failure.
2011 * --- BoYang
2012 * */
2013 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2014 result = NT_STATUS_OK;
2017 if (!NT_STATUS_IS_OK(result)) {
2018 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2019 goto process_result;
2024 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2026 NTSTATUS policy_ret;
2028 policy_ret = fillup_password_policy(contact_domain, state);
2030 /* failure of this is non critical, it will just provide no
2031 * additional information to the client why the change has
2032 * failed - Guenther */
2034 if (!NT_STATUS_IS_OK(policy_ret)) {
2035 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2036 goto process_result;
2040 process_result:
2042 set_auth_errors(state->response, result);
2044 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2045 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2046 domain,
2047 user,
2048 state->response->data.auth.nt_status_string,
2049 state->response->data.auth.pam_error));
2051 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2054 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2056 struct winbindd_domain *domain;
2057 fstring name_domain, user;
2058 uid_t caller_uid = (uid_t)-1;
2059 uid_t request_uid = state->request->data.logoff.uid;
2061 /* Ensure null termination */
2062 state->request->data.logoff.user
2063 [sizeof(state->request->data.logoff.user)-1]='\0';
2065 state->request->data.logoff.krb5ccname
2066 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2068 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2069 state->request->data.logoff.user));
2071 if (request_uid == (uid_t)-1) {
2072 goto failed;
2075 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2076 goto failed;
2079 if ((domain = find_auth_domain(state->request->flags,
2080 name_domain)) == NULL) {
2081 goto failed;
2084 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2085 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2086 strerror(errno)));
2087 goto failed;
2090 switch (caller_uid) {
2091 case -1:
2092 goto failed;
2093 case 0:
2094 /* root must be able to logoff any user - gd */
2095 state->request->data.logoff.uid = request_uid;
2096 break;
2097 default:
2098 if (caller_uid != request_uid) {
2099 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2100 goto failed;
2102 state->request->data.logoff.uid = caller_uid;
2103 break;
2106 sendto_domain(state, domain);
2107 return;
2109 failed:
2110 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2111 DEBUG(5, ("Pam Logoff for %s returned %s "
2112 "(PAM: %d)\n",
2113 state->request->data.logoff.user,
2114 state->response->data.auth.nt_status_string,
2115 state->response->data.auth.pam_error));
2116 request_error(state);
2117 return;
2120 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2121 struct winbindd_cli_state *state)
2123 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2125 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2126 state->request->data.logoff.user));
2128 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2129 result = NT_STATUS_OK;
2130 goto process_result;
2133 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2134 result = NT_STATUS_OK;
2135 goto process_result;
2138 #ifdef HAVE_KRB5
2140 if (state->request->data.logoff.uid < 0) {
2141 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2142 goto process_result;
2145 /* what we need here is to find the corresponding krb5 ccache name *we*
2146 * created for a given username and destroy it */
2148 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2149 result = NT_STATUS_OK;
2150 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2151 goto process_result;
2154 if (!ccache_entry_identical(state->request->data.logoff.user,
2155 state->request->data.logoff.uid,
2156 state->request->data.logoff.krb5ccname)) {
2157 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2158 goto process_result;
2161 result = remove_ccache(state->request->data.logoff.user);
2162 if (!NT_STATUS_IS_OK(result)) {
2163 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2164 nt_errstr(result)));
2165 goto process_result;
2168 #else
2169 result = NT_STATUS_NOT_SUPPORTED;
2170 #endif
2172 process_result:
2174 winbindd_delete_memory_creds(state->request->data.logoff.user);
2176 set_auth_errors(state->response, result);
2178 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2181 /* Change user password with auth crap*/
2183 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2185 struct winbindd_domain *domain = NULL;
2186 const char *domain_name = NULL;
2188 /* Ensure null termination */
2189 state->request->data.chng_pswd_auth_crap.user[
2190 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2191 state->request->data.chng_pswd_auth_crap.domain[
2192 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2194 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2195 (unsigned long)state->pid,
2196 state->request->data.chng_pswd_auth_crap.domain,
2197 state->request->data.chng_pswd_auth_crap.user));
2199 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2200 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2201 } else if (lp_winbind_use_default_domain()) {
2202 domain_name = lp_workgroup();
2205 if (domain_name != NULL)
2206 domain = find_domain_from_name(domain_name);
2208 if (domain != NULL) {
2209 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2210 "%s\n", (unsigned long)state->pid,domain->name));
2211 sendto_domain(state, domain);
2212 return;
2215 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2216 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2217 state->request->data.chng_pswd_auth_crap.domain,
2218 state->request->data.chng_pswd_auth_crap.user,
2219 state->response->data.auth.nt_status_string,
2220 state->response->data.auth.pam_error));
2221 request_error(state);
2222 return;
2225 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2227 NTSTATUS result;
2228 DATA_BLOB new_nt_password;
2229 DATA_BLOB old_nt_hash_enc;
2230 DATA_BLOB new_lm_password;
2231 DATA_BLOB old_lm_hash_enc;
2232 fstring domain,user;
2233 struct policy_handle dom_pol;
2234 struct winbindd_domain *contact_domain = domainSt;
2235 struct rpc_pipe_client *cli;
2237 /* Ensure null termination */
2238 state->request->data.chng_pswd_auth_crap.user[
2239 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2240 state->request->data.chng_pswd_auth_crap.domain[
2241 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2242 *domain = 0;
2243 *user = 0;
2245 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2246 (unsigned long)state->pid,
2247 state->request->data.chng_pswd_auth_crap.domain,
2248 state->request->data.chng_pswd_auth_crap.user));
2250 if (lp_winbind_offline_logon()) {
2251 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2252 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2253 result = NT_STATUS_ACCESS_DENIED;
2254 goto done;
2257 if (*state->request->data.chng_pswd_auth_crap.domain) {
2258 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2259 } else {
2260 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2261 domain, user);
2263 if(!*domain) {
2264 DEBUG(3,("no domain specified with username (%s) - "
2265 "failing auth\n",
2266 state->request->data.chng_pswd_auth_crap.user));
2267 result = NT_STATUS_NO_SUCH_USER;
2268 goto done;
2272 if (!*domain && lp_winbind_use_default_domain()) {
2273 fstrcpy(domain,(char *)lp_workgroup());
2276 if(!*user) {
2277 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2280 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2281 (unsigned long)state->pid, domain, user));
2283 /* Change password */
2284 new_nt_password = data_blob_talloc(
2285 state->mem_ctx,
2286 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2287 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2289 old_nt_hash_enc = data_blob_talloc(
2290 state->mem_ctx,
2291 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2292 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2294 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2295 new_lm_password = data_blob_talloc(
2296 state->mem_ctx,
2297 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2298 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2300 old_lm_hash_enc = data_blob_talloc(
2301 state->mem_ctx,
2302 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2303 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2304 } else {
2305 new_lm_password.length = 0;
2306 old_lm_hash_enc.length = 0;
2309 /* Get sam handle */
2311 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2312 if (!NT_STATUS_IS_OK(result)) {
2313 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2314 goto done;
2317 result = rpccli_samr_chng_pswd_auth_crap(
2318 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2319 new_lm_password, old_lm_hash_enc);
2321 done:
2323 set_auth_errors(state->response, result);
2325 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2326 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2327 domain, user,
2328 state->response->data.auth.nt_status_string,
2329 state->response->data.auth.pam_error));
2331 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;