s3: Move the in-memory ccache to the parent
[Samba/ekacnet.git] / source3 / winbindd / winbindd_pam.c
blob3f350e3fb2c09906e1c38d2e53c4e4e05da24f9a
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 uid_t get_uid_from_request(struct winbindd_request *request)
527 uid_t uid;
529 uid = 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 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
540 return get_uid_from_request(state->request);
543 /**********************************************************************
544 Authenticate a user with a clear text password using Kerberos and fill up
545 ccache if required
546 **********************************************************************/
548 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
549 struct winbindd_cli_state *state,
550 struct netr_SamInfo3 **info3)
552 #ifdef HAVE_KRB5
553 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
554 krb5_error_code krb5_ret;
555 const char *cc = NULL;
556 const char *principal_s = NULL;
557 const char *service = NULL;
558 char *realm = NULL;
559 fstring name_domain, name_user;
560 time_t ticket_lifetime = 0;
561 time_t renewal_until = 0;
562 uid_t uid = -1;
563 ADS_STRUCT *ads;
564 time_t time_offset = 0;
565 bool internal_ccache = true;
567 ZERO_STRUCTP(info3);
569 *info3 = NULL;
571 /* 1st step:
572 * prepare a krb5_cc_cache string for the user */
574 uid = get_uid_from_state(state);
575 if (uid == -1) {
576 DEBUG(0,("no valid uid\n"));
579 cc = generate_krb5_ccache(state->mem_ctx,
580 state->request->data.auth.krb5_cc_type,
581 state->request->data.auth.uid,
582 &internal_ccache);
583 if (cc == NULL) {
584 return NT_STATUS_NO_MEMORY;
588 /* 2nd step:
589 * get kerberos properties */
591 if (domain->private_data) {
592 ads = (ADS_STRUCT *)domain->private_data;
593 time_offset = ads->auth.time_offset;
597 /* 3rd step:
598 * do kerberos auth and setup ccache as the user */
600 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
602 realm = domain->alt_name;
603 strupper_m(realm);
605 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
606 if (principal_s == NULL) {
607 return NT_STATUS_NO_MEMORY;
610 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
611 if (service == NULL) {
612 return NT_STATUS_NO_MEMORY;
615 /* if this is a user ccache, we need to act as the user to let the krb5
616 * library handle the chown, etc. */
618 /************************ ENTERING NON-ROOT **********************/
620 if (!internal_ccache) {
621 set_effective_uid(uid);
622 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
625 result = kerberos_return_info3_from_pac(state->mem_ctx,
626 principal_s,
627 state->request->data.auth.pass,
628 time_offset,
629 &ticket_lifetime,
630 &renewal_until,
632 true,
633 true,
634 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
635 NULL,
636 info3);
637 if (!internal_ccache) {
638 gain_root_privilege();
641 /************************ RETURNED TO ROOT **********************/
643 if (!NT_STATUS_IS_OK(result)) {
644 goto failed;
647 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
648 principal_s));
650 /* if we had a user's ccache then return that string for the pam
651 * environment */
653 if (!internal_ccache) {
655 setup_return_cc_name(state, cc);
657 result = add_ccache_to_list(principal_s,
659 service,
660 state->request->data.auth.user,
661 realm,
662 uid,
663 time(NULL),
664 ticket_lifetime,
665 renewal_until,
666 false);
668 if (!NT_STATUS_IS_OK(result)) {
669 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
670 nt_errstr(result)));
672 } else {
674 /* need to delete the memory cred cache, it is not used anymore */
676 krb5_ret = ads_kdestroy(cc);
677 if (krb5_ret) {
678 DEBUG(3,("winbindd_raw_kerberos_login: "
679 "could not destroy krb5 credential cache: "
680 "%s\n", error_message(krb5_ret)));
685 return NT_STATUS_OK;
687 failed:
689 /* we could have created a new credential cache with a valid tgt in it
690 * but we werent able to get or verify the service ticket for this
691 * local host and therefor didn't get the PAC, we need to remove that
692 * cache entirely now */
694 krb5_ret = ads_kdestroy(cc);
695 if (krb5_ret) {
696 DEBUG(3,("winbindd_raw_kerberos_login: "
697 "could not destroy krb5 credential cache: "
698 "%s\n", error_message(krb5_ret)));
701 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
702 DEBUG(3,("winbindd_raw_kerberos_login: "
703 "could not remove ccache for user %s\n",
704 state->request->data.auth.user));
707 return result;
708 #else
709 return NT_STATUS_NOT_SUPPORTED;
710 #endif /* HAVE_KRB5 */
713 /****************************************************************
714 ****************************************************************/
716 bool check_request_flags(uint32_t flags)
718 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
719 WBFLAG_PAM_INFO3_TEXT |
720 WBFLAG_PAM_INFO3_NDR;
722 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
723 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
724 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
725 !(flags & flags_edata) ) {
726 return true;
729 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
730 flags));
732 return false;
735 /****************************************************************
736 ****************************************************************/
738 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
739 struct netr_SamInfo3 *info3,
740 const char *name_domain,
741 const char *name_user)
743 NTSTATUS result;
744 uint32_t flags = state->request->flags;
746 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
747 memcpy(state->response->data.auth.user_session_key,
748 info3->base.key.key,
749 sizeof(state->response->data.auth.user_session_key)
750 /* 16 */);
753 if (flags & WBFLAG_PAM_LMKEY) {
754 memcpy(state->response->data.auth.first_8_lm_hash,
755 info3->base.LMSessKey.key,
756 sizeof(state->response->data.auth.first_8_lm_hash)
757 /* 8 */);
760 if (flags & WBFLAG_PAM_INFO3_TEXT) {
761 result = append_info3_as_txt(state->mem_ctx, state, info3);
762 if (!NT_STATUS_IS_OK(result)) {
763 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
764 nt_errstr(result)));
765 return result;
769 /* currently, anything from here on potentially overwrites extra_data. */
771 if (flags & WBFLAG_PAM_INFO3_NDR) {
772 result = append_info3_as_ndr(state->mem_ctx, state, info3);
773 if (!NT_STATUS_IS_OK(result)) {
774 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
775 nt_errstr(result)));
776 return result;
780 if (flags & WBFLAG_PAM_UNIX_NAME) {
781 result = append_unix_username(state->mem_ctx, state, info3,
782 name_domain, name_user);
783 if (!NT_STATUS_IS_OK(result)) {
784 DEBUG(10,("Failed to append Unix Username: %s\n",
785 nt_errstr(result)));
786 return result;
790 if (flags & WBFLAG_PAM_AFS_TOKEN) {
791 result = append_afs_token(state->mem_ctx, state, info3,
792 name_domain, name_user);
793 if (!NT_STATUS_IS_OK(result)) {
794 DEBUG(10,("Failed to append AFS token: %s\n",
795 nt_errstr(result)));
796 return result;
800 return NT_STATUS_OK;
803 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
804 struct winbindd_cli_state *state,
805 struct netr_SamInfo3 **info3)
807 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
808 uint16 max_allowed_bad_attempts;
809 fstring name_domain, name_user;
810 DOM_SID sid;
811 enum lsa_SidType type;
812 uchar new_nt_pass[NT_HASH_LEN];
813 const uint8 *cached_nt_pass;
814 const uint8 *cached_salt;
815 struct netr_SamInfo3 *my_info3;
816 time_t kickoff_time, must_change_time;
817 bool password_good = false;
818 #ifdef HAVE_KRB5
819 struct winbindd_tdc_domain *tdc_domain = NULL;
820 #endif
822 *info3 = NULL;
824 ZERO_STRUCTP(info3);
826 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
828 /* Parse domain and username */
830 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
833 if (!lookup_cached_name(state->mem_ctx,
834 name_domain,
835 name_user,
836 &sid,
837 &type)) {
838 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
839 return NT_STATUS_NO_SUCH_USER;
842 if (type != SID_NAME_USER) {
843 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
844 return NT_STATUS_LOGON_FAILURE;
847 result = winbindd_get_creds(domain,
848 state->mem_ctx,
849 &sid,
850 &my_info3,
851 &cached_nt_pass,
852 &cached_salt);
853 if (!NT_STATUS_IS_OK(result)) {
854 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
855 return result;
858 *info3 = my_info3;
860 E_md4hash(state->request->data.auth.pass, new_nt_pass);
862 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
863 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
864 if (cached_salt) {
865 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
868 if (cached_salt) {
869 /* In this case we didn't store the nt_hash itself,
870 but the MD5 combination of salt + nt_hash. */
871 uchar salted_hash[NT_HASH_LEN];
872 E_md5hash(cached_salt, new_nt_pass, salted_hash);
874 password_good = (memcmp(cached_nt_pass, salted_hash,
875 NT_HASH_LEN) == 0);
876 } else {
877 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
878 password_good = (memcmp(cached_nt_pass, new_nt_pass,
879 NT_HASH_LEN) == 0);
882 if (password_good) {
884 /* User *DOES* know the password, update logon_time and reset
885 * bad_pw_count */
887 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
889 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
890 return NT_STATUS_ACCOUNT_LOCKED_OUT;
893 if (my_info3->base.acct_flags & ACB_DISABLED) {
894 return NT_STATUS_ACCOUNT_DISABLED;
897 if (my_info3->base.acct_flags & ACB_WSTRUST) {
898 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
901 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
902 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
905 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
906 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
909 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
910 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
911 my_info3->base.acct_flags));
912 return NT_STATUS_LOGON_FAILURE;
915 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
916 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
917 return NT_STATUS_ACCOUNT_EXPIRED;
920 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
921 if (must_change_time != 0 && must_change_time < time(NULL)) {
922 /* we allow grace logons when the password has expired */
923 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
924 /* return NT_STATUS_PASSWORD_EXPIRED; */
925 goto success;
928 #ifdef HAVE_KRB5
929 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
930 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
931 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
932 /* used to cope with the case winbindd starting without network. */
933 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
935 uid_t uid = -1;
936 const char *cc = NULL;
937 char *realm = NULL;
938 const char *principal_s = NULL;
939 const char *service = NULL;
940 bool internal_ccache = false;
942 uid = get_uid_from_state(state);
943 if (uid == -1) {
944 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
945 return NT_STATUS_INVALID_PARAMETER;
948 cc = generate_krb5_ccache(state->mem_ctx,
949 state->request->data.auth.krb5_cc_type,
950 state->request->data.auth.uid,
951 &internal_ccache);
952 if (cc == NULL) {
953 return NT_STATUS_NO_MEMORY;
956 realm = domain->alt_name;
957 strupper_m(realm);
959 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
960 if (principal_s == NULL) {
961 return NT_STATUS_NO_MEMORY;
964 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
965 if (service == NULL) {
966 return NT_STATUS_NO_MEMORY;
969 if (!internal_ccache) {
971 setup_return_cc_name(state, cc);
973 result = add_ccache_to_list(principal_s,
975 service,
976 state->request->data.auth.user,
977 domain->alt_name,
978 uid,
979 time(NULL),
980 time(NULL) + lp_winbind_cache_time(),
981 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
982 true);
984 if (!NT_STATUS_IS_OK(result)) {
985 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
986 "to add ccache to list: %s\n",
987 nt_errstr(result)));
991 #endif /* HAVE_KRB5 */
992 success:
993 /* FIXME: we possibly should handle logon hours as well (does xp when
994 * offline?) see auth/auth_sam.c:sam_account_ok for details */
996 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
997 my_info3->base.bad_password_count = 0;
999 result = winbindd_update_creds_by_info3(domain,
1000 state->mem_ctx,
1001 state->request->data.auth.user,
1002 state->request->data.auth.pass,
1003 my_info3);
1004 if (!NT_STATUS_IS_OK(result)) {
1005 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1006 nt_errstr(result)));
1007 return result;
1010 return NT_STATUS_OK;
1014 /* User does *NOT* know the correct password, modify info3 accordingly */
1016 /* failure of this is not critical */
1017 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1018 if (!NT_STATUS_IS_OK(result)) {
1019 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1020 "Won't be able to honour account lockout policies\n"));
1023 /* increase counter */
1024 my_info3->base.bad_password_count++;
1026 if (max_allowed_bad_attempts == 0) {
1027 goto failed;
1030 /* lockout user */
1031 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1033 uint32 password_properties;
1035 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1036 if (!NT_STATUS_IS_OK(result)) {
1037 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1040 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1041 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1042 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1046 failed:
1047 result = winbindd_update_creds_by_info3(domain,
1048 state->mem_ctx,
1049 state->request->data.auth.user,
1050 NULL,
1051 my_info3);
1053 if (!NT_STATUS_IS_OK(result)) {
1054 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1055 nt_errstr(result)));
1058 return NT_STATUS_LOGON_FAILURE;
1061 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1062 struct winbindd_cli_state *state,
1063 struct netr_SamInfo3 **info3)
1065 struct winbindd_domain *contact_domain;
1066 fstring name_domain, name_user;
1067 NTSTATUS result;
1069 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1071 /* Parse domain and username */
1073 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1075 /* what domain should we contact? */
1077 if ( IS_DC ) {
1078 if (!(contact_domain = find_domain_from_name(name_domain))) {
1079 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1080 state->request->data.auth.user, name_domain, name_user, name_domain));
1081 result = NT_STATUS_NO_SUCH_USER;
1082 goto done;
1085 } else {
1086 if (is_myname(name_domain)) {
1087 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1088 result = NT_STATUS_NO_SUCH_USER;
1089 goto done;
1092 contact_domain = find_domain_from_name(name_domain);
1093 if (contact_domain == NULL) {
1094 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1095 state->request->data.auth.user, name_domain, name_user, name_domain));
1097 contact_domain = find_our_domain();
1101 if (contact_domain->initialized &&
1102 contact_domain->active_directory) {
1103 goto try_login;
1106 if (!contact_domain->initialized) {
1107 init_dc_connection(contact_domain);
1110 if (!contact_domain->active_directory) {
1111 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1112 return NT_STATUS_INVALID_LOGON_TYPE;
1114 try_login:
1115 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1116 done:
1117 return result;
1120 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1121 const char *domain, const char *user,
1122 const DATA_BLOB *challenge,
1123 const DATA_BLOB *lm_resp,
1124 const DATA_BLOB *nt_resp,
1125 struct netr_SamInfo3 **pinfo3)
1127 struct auth_usersupplied_info *user_info = NULL;
1128 struct auth_serversupplied_info *server_info = NULL;
1129 struct netr_SamInfo3 *info3;
1130 NTSTATUS status;
1132 status = make_user_info(&user_info, user, user, domain, domain,
1133 global_myname(), lm_resp, nt_resp, NULL, NULL,
1134 NULL, True);
1135 if (!NT_STATUS_IS_OK(status)) {
1136 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1137 return status;
1140 status = check_sam_security(challenge, talloc_tos(), user_info,
1141 &server_info);
1142 free_user_info(&user_info);
1144 if (!NT_STATUS_IS_OK(status)) {
1145 DEBUG(10, ("check_ntlm_password failed: %s\n",
1146 nt_errstr(status)));
1147 return status;
1150 info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1151 if (info3 == NULL) {
1152 return NT_STATUS_NO_MEMORY;
1155 status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1156 if (!NT_STATUS_IS_OK(status)) {
1157 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1158 nt_errstr(status)));
1159 return status;
1162 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1163 *pinfo3 = info3;
1164 return NT_STATUS_OK;
1167 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1168 TALLOC_CTX *mem_ctx,
1169 uint32 logon_parameters,
1170 const char *server,
1171 const char *username,
1172 const char *domain,
1173 const char *workstation,
1174 const uint8 chal[8],
1175 DATA_BLOB lm_response,
1176 DATA_BLOB nt_response,
1177 struct netr_SamInfo3 **info3);
1179 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1180 struct winbindd_cli_state *state,
1181 struct netr_SamInfo3 **info3)
1184 struct rpc_pipe_client *netlogon_pipe;
1185 uchar chal[8];
1186 DATA_BLOB lm_resp;
1187 DATA_BLOB nt_resp;
1188 int attempts = 0;
1189 unsigned char local_lm_response[24];
1190 unsigned char local_nt_response[24];
1191 fstring name_domain, name_user;
1192 bool retry;
1193 NTSTATUS result;
1194 struct netr_SamInfo3 *my_info3 = NULL;
1196 *info3 = NULL;
1198 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1200 /* Parse domain and username */
1202 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1204 /* do password magic */
1206 generate_random_buffer(chal, sizeof(chal));
1208 if (lp_client_ntlmv2_auth()) {
1209 DATA_BLOB server_chal;
1210 DATA_BLOB names_blob;
1211 DATA_BLOB nt_response;
1212 DATA_BLOB lm_response;
1213 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1215 /* note that the 'workgroup' here is a best guess - we don't know
1216 the server's domain at this point. The 'server name' is also
1217 dodgy...
1219 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1221 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1222 state->request->data.auth.pass,
1223 &server_chal,
1224 &names_blob,
1225 &lm_response, &nt_response, NULL, NULL)) {
1226 data_blob_free(&names_blob);
1227 data_blob_free(&server_chal);
1228 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1229 result = NT_STATUS_NO_MEMORY;
1230 goto done;
1232 data_blob_free(&names_blob);
1233 data_blob_free(&server_chal);
1234 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1235 lm_response.length);
1236 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1237 nt_response.length);
1238 data_blob_free(&lm_response);
1239 data_blob_free(&nt_response);
1241 } else {
1242 if (lp_client_lanman_auth()
1243 && SMBencrypt(state->request->data.auth.pass,
1244 chal,
1245 local_lm_response)) {
1246 lm_resp = data_blob_talloc(state->mem_ctx,
1247 local_lm_response,
1248 sizeof(local_lm_response));
1249 } else {
1250 lm_resp = data_blob_null;
1252 SMBNTencrypt(state->request->data.auth.pass,
1253 chal,
1254 local_nt_response);
1256 nt_resp = data_blob_talloc(state->mem_ctx,
1257 local_nt_response,
1258 sizeof(local_nt_response));
1261 if (strequal(name_domain, get_global_sam_name())) {
1262 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1264 result = winbindd_dual_auth_passdb(
1265 state->mem_ctx, name_domain, name_user,
1266 &chal_blob, &lm_resp, &nt_resp, info3);
1267 goto done;
1270 /* check authentication loop */
1272 do {
1273 netlogon_fn_t logon_fn;
1275 ZERO_STRUCTP(my_info3);
1276 retry = false;
1278 result = cm_connect_netlogon(domain, &netlogon_pipe);
1280 if (!NT_STATUS_IS_OK(result)) {
1281 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1282 goto done;
1285 /* It is really important to try SamLogonEx here,
1286 * because in a clustered environment, we want to use
1287 * one machine account from multiple physical
1288 * computers.
1290 * With a normal SamLogon call, we must keep the
1291 * credentials chain updated and intact between all
1292 * users of the machine account (which would imply
1293 * cross-node communication for every NTLM logon).
1295 * (The credentials chain is not per NETLOGON pipe
1296 * connection, but globally on the server/client pair
1297 * by machine name).
1299 * When using SamLogonEx, the credentials are not
1300 * supplied, but the session key is implied by the
1301 * wrapping SamLogon context.
1303 * -- abartlet 21 April 2008
1306 logon_fn = domain->can_do_samlogon_ex
1307 ? rpccli_netlogon_sam_network_logon_ex
1308 : rpccli_netlogon_sam_network_logon;
1310 result = logon_fn(netlogon_pipe,
1311 state->mem_ctx,
1313 domain->dcname, /* server name */
1314 name_user, /* user name */
1315 name_domain, /* target domain */
1316 global_myname(), /* workstation */
1317 chal,
1318 lm_resp,
1319 nt_resp,
1320 &my_info3);
1321 attempts += 1;
1323 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1324 && domain->can_do_samlogon_ex) {
1325 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1326 "retrying with NetSamLogon\n"));
1327 domain->can_do_samlogon_ex = false;
1328 retry = true;
1329 continue;
1332 /* We have to try a second time as cm_connect_netlogon
1333 might not yet have noticed that the DC has killed
1334 our connection. */
1336 if (!rpccli_is_connected(netlogon_pipe)) {
1337 retry = true;
1338 continue;
1341 /* if we get access denied, a possible cause was that we had
1342 and open connection to the DC, but someone changed our
1343 machine account password out from underneath us using 'net
1344 rpc changetrustpw' */
1346 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1347 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1348 "ACCESS_DENIED. Maybe the trust account "
1349 "password was changed and we didn't know it. "
1350 "Killing connections to domain %s\n",
1351 name_domain));
1352 invalidate_cm_connection(&domain->conn);
1353 retry = true;
1356 } while ( (attempts < 2) && retry );
1358 /* handle the case where a NT4 DC does not fill in the acct_flags in
1359 * the samlogon reply info3. When accurate info3 is required by the
1360 * caller, we look up the account flags ourselve - gd */
1362 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1363 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1365 struct rpc_pipe_client *samr_pipe;
1366 struct policy_handle samr_domain_handle, user_pol;
1367 union samr_UserInfo *info = NULL;
1368 NTSTATUS status_tmp;
1369 uint32 acct_flags;
1371 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1372 &samr_pipe, &samr_domain_handle);
1374 if (!NT_STATUS_IS_OK(status_tmp)) {
1375 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1376 nt_errstr(status_tmp)));
1377 goto done;
1380 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1381 &samr_domain_handle,
1382 MAXIMUM_ALLOWED_ACCESS,
1383 my_info3->base.rid,
1384 &user_pol);
1386 if (!NT_STATUS_IS_OK(status_tmp)) {
1387 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1388 nt_errstr(status_tmp)));
1389 goto done;
1392 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1393 &user_pol,
1395 &info);
1397 if (!NT_STATUS_IS_OK(status_tmp)) {
1398 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1399 nt_errstr(status_tmp)));
1400 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1401 goto done;
1404 acct_flags = info->info16.acct_flags;
1406 if (acct_flags == 0) {
1407 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1408 goto done;
1411 my_info3->base.acct_flags = acct_flags;
1413 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1415 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1418 *info3 = my_info3;
1419 done:
1420 return result;
1423 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1424 struct winbindd_cli_state *state)
1426 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1427 NTSTATUS krb5_result = NT_STATUS_OK;
1428 fstring name_domain, name_user;
1429 char *mapped_user;
1430 fstring domain_user;
1431 struct netr_SamInfo3 *info3 = NULL;
1432 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1434 /* Ensure null termination */
1435 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1437 /* Ensure null termination */
1438 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1440 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1441 state->request->data.auth.user));
1443 if (!check_request_flags(state->request->flags)) {
1444 result = NT_STATUS_INVALID_PARAMETER_MIX;
1445 goto done;
1448 /* Parse domain and username */
1450 name_map_status = normalize_name_unmap(state->mem_ctx,
1451 state->request->data.auth.user,
1452 &mapped_user);
1454 /* If the name normalization didnt' actually do anything,
1455 just use the original name */
1457 if (!NT_STATUS_IS_OK(name_map_status) &&
1458 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1460 mapped_user = state->request->data.auth.user;
1463 parse_domain_user(mapped_user, name_domain, name_user);
1465 if ( mapped_user != state->request->data.auth.user ) {
1466 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1467 safe_strcpy( state->request->data.auth.user, domain_user,
1468 sizeof(state->request->data.auth.user)-1 );
1471 if (domain->online == false) {
1472 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1473 if (domain->startup) {
1474 /* Logons are very important to users. If we're offline and
1475 we get a request within the first 30 seconds of startup,
1476 try very hard to find a DC and go online. */
1478 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1479 "request in startup mode.\n", domain->name ));
1481 winbindd_flush_negative_conn_cache(domain);
1482 result = init_dc_connection(domain);
1486 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1488 /* Check for Kerberos authentication */
1489 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1491 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1492 /* save for later */
1493 krb5_result = result;
1496 if (NT_STATUS_IS_OK(result)) {
1497 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1498 goto process_result;
1499 } else {
1500 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1503 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1504 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1505 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1506 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1507 set_domain_offline( domain );
1508 goto cached_logon;
1511 /* there are quite some NT_STATUS errors where there is no
1512 * point in retrying with a samlogon, we explictly have to take
1513 * care not to increase the bad logon counter on the DC */
1515 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1516 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1517 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1518 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1519 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1520 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1521 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1525 goto done;
1528 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1529 DEBUG(3,("falling back to samlogon\n"));
1530 goto sam_logon;
1531 } else {
1532 goto cached_logon;
1536 sam_logon:
1537 /* Check for Samlogon authentication */
1538 if (domain->online) {
1539 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1541 if (NT_STATUS_IS_OK(result)) {
1542 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1543 /* add the Krb5 err if we have one */
1544 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1545 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1547 goto process_result;
1550 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1551 nt_errstr(result)));
1553 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1554 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1555 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1557 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1558 set_domain_offline( domain );
1559 goto cached_logon;
1562 if (domain->online) {
1563 /* We're still online - fail. */
1564 goto done;
1568 cached_logon:
1569 /* Check for Cached logons */
1570 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1571 lp_winbind_offline_logon()) {
1573 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1575 if (NT_STATUS_IS_OK(result)) {
1576 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1577 goto process_result;
1578 } else {
1579 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1580 goto done;
1584 process_result:
1586 if (NT_STATUS_IS_OK(result)) {
1588 DOM_SID user_sid;
1590 /* In all codepaths where result == NT_STATUS_OK info3 must have
1591 been initialized. */
1592 if (!info3) {
1593 result = NT_STATUS_INTERNAL_ERROR;
1594 goto done;
1597 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1598 netsamlogon_cache_store(name_user, info3);
1600 /* save name_to_sid info as early as possible (only if
1601 this is our primary domain so we don't invalidate
1602 the cache entry by storing the seq_num for the wrong
1603 domain). */
1604 if ( domain->primary ) {
1605 sid_compose(&user_sid, info3->base.domain_sid,
1606 info3->base.rid);
1607 cache_name2sid(domain, name_domain, name_user,
1608 SID_NAME_USER, &user_sid);
1611 /* Check if the user is in the right group */
1613 result = check_info3_in_group(
1614 info3,
1615 state->request->data.auth.require_membership_of_sid);
1616 if (!NT_STATUS_IS_OK(result)) {
1617 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1618 state->request->data.auth.user,
1619 state->request->data.auth.require_membership_of_sid));
1620 goto done;
1623 result = append_auth_data(state, info3, name_domain,
1624 name_user);
1625 if (!NT_STATUS_IS_OK(result)) {
1626 goto done;
1629 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1631 if (lp_winbind_offline_logon()) {
1632 result = winbindd_store_creds(domain,
1633 state->mem_ctx,
1634 state->request->data.auth.user,
1635 state->request->data.auth.pass,
1636 info3, NULL);
1641 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1642 struct winbindd_domain *our_domain = find_our_domain();
1644 /* This is not entirely correct I believe, but it is
1645 consistent. Only apply the password policy settings
1646 too warn users for our own domain. Cannot obtain these
1647 from trusted DCs all the time so don't do it at all.
1648 -- jerry */
1650 result = NT_STATUS_NOT_SUPPORTED;
1651 if (our_domain == domain ) {
1652 result = fillup_password_policy(our_domain, state);
1655 if (!NT_STATUS_IS_OK(result)
1656 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1658 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1659 domain->name, nt_errstr(result)));
1660 goto done;
1664 result = NT_STATUS_OK;
1667 done:
1668 /* give us a more useful (more correct?) error code */
1669 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1670 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1671 result = NT_STATUS_NO_LOGON_SERVERS;
1674 set_auth_errors(state->response, result);
1676 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1677 state->request->data.auth.user,
1678 state->response->data.auth.nt_status_string,
1679 state->response->data.auth.pam_error));
1681 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1684 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1685 struct winbindd_cli_state *state)
1687 NTSTATUS result;
1688 struct netr_SamInfo3 *info3 = NULL;
1689 struct rpc_pipe_client *netlogon_pipe;
1690 const char *name_user = NULL;
1691 const char *name_domain = NULL;
1692 const char *workstation;
1693 int attempts = 0;
1694 bool retry;
1696 DATA_BLOB lm_resp, nt_resp;
1698 /* This is child-only, so no check for privileged access is needed
1699 anymore */
1701 /* Ensure null termination */
1702 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1703 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1705 if (!check_request_flags(state->request->flags)) {
1706 result = NT_STATUS_INVALID_PARAMETER_MIX;
1707 goto done;
1710 name_user = state->request->data.auth_crap.user;
1712 if (*state->request->data.auth_crap.domain) {
1713 name_domain = state->request->data.auth_crap.domain;
1714 } else if (lp_winbind_use_default_domain()) {
1715 name_domain = lp_workgroup();
1716 } else {
1717 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1718 name_user));
1719 result = NT_STATUS_NO_SUCH_USER;
1720 goto done;
1723 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1724 name_domain, name_user));
1726 if (*state->request->data.auth_crap.workstation) {
1727 workstation = state->request->data.auth_crap.workstation;
1728 } else {
1729 workstation = global_myname();
1732 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1733 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1734 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1735 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1736 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1737 state->request->data.auth_crap.lm_resp_len,
1738 state->request->data.auth_crap.nt_resp_len));
1739 result = NT_STATUS_INVALID_PARAMETER;
1740 goto done;
1744 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1745 state->request->data.auth_crap.lm_resp_len);
1747 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1748 nt_resp = data_blob_talloc(state->mem_ctx,
1749 state->request->extra_data.data,
1750 state->request->data.auth_crap.nt_resp_len);
1751 } else {
1752 nt_resp = data_blob_talloc(state->mem_ctx,
1753 state->request->data.auth_crap.nt_resp,
1754 state->request->data.auth_crap.nt_resp_len);
1757 if (strequal(name_domain, get_global_sam_name())) {
1758 DATA_BLOB chal_blob = data_blob_const(
1759 state->request->data.auth_crap.chal,
1760 sizeof(state->request->data.auth_crap.chal));
1762 result = winbindd_dual_auth_passdb(
1763 state->mem_ctx, name_domain, name_user,
1764 &chal_blob, &lm_resp, &nt_resp, &info3);
1765 goto process_result;
1768 do {
1769 netlogon_fn_t logon_fn;
1771 retry = false;
1773 netlogon_pipe = NULL;
1774 result = cm_connect_netlogon(domain, &netlogon_pipe);
1776 if (!NT_STATUS_IS_OK(result)) {
1777 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1778 nt_errstr(result)));
1779 goto done;
1782 logon_fn = domain->can_do_samlogon_ex
1783 ? rpccli_netlogon_sam_network_logon_ex
1784 : rpccli_netlogon_sam_network_logon;
1786 result = logon_fn(netlogon_pipe,
1787 state->mem_ctx,
1788 state->request->data.auth_crap.logon_parameters,
1789 domain->dcname,
1790 name_user,
1791 name_domain,
1792 /* Bug #3248 - found by Stefan Burkei. */
1793 workstation, /* We carefully set this above so use it... */
1794 state->request->data.auth_crap.chal,
1795 lm_resp,
1796 nt_resp,
1797 &info3);
1799 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1800 && domain->can_do_samlogon_ex) {
1801 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1802 "retrying with NetSamLogon\n"));
1803 domain->can_do_samlogon_ex = false;
1804 retry = true;
1805 continue;
1808 attempts += 1;
1810 /* We have to try a second time as cm_connect_netlogon
1811 might not yet have noticed that the DC has killed
1812 our connection. */
1814 if (!rpccli_is_connected(netlogon_pipe)) {
1815 retry = true;
1816 continue;
1819 /* if we get access denied, a possible cause was that we had and open
1820 connection to the DC, but someone changed our machine account password
1821 out from underneath us using 'net rpc changetrustpw' */
1823 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1824 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1825 "ACCESS_DENIED. Maybe the trust account "
1826 "password was changed and we didn't know it. "
1827 "Killing connections to domain %s\n",
1828 name_domain));
1829 invalidate_cm_connection(&domain->conn);
1830 retry = true;
1833 } while ( (attempts < 2) && retry );
1835 process_result:
1837 if (NT_STATUS_IS_OK(result)) {
1839 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1840 netsamlogon_cache_store(name_user, info3);
1842 /* Check if the user is in the right group */
1844 result = check_info3_in_group(
1845 info3,
1846 state->request->data.auth_crap.require_membership_of_sid);
1847 if (!NT_STATUS_IS_OK(result)) {
1848 DEBUG(3, ("User %s is not in the required group (%s), so "
1849 "crap authentication is rejected\n",
1850 state->request->data.auth_crap.user,
1851 state->request->data.auth_crap.require_membership_of_sid));
1852 goto done;
1855 result = append_auth_data(state, info3, name_domain,
1856 name_user);
1857 if (!NT_STATUS_IS_OK(result)) {
1858 goto done;
1862 done:
1864 /* give us a more useful (more correct?) error code */
1865 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1866 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1867 result = NT_STATUS_NO_LOGON_SERVERS;
1870 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1871 result = nt_status_squash(result);
1874 set_auth_errors(state->response, result);
1876 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1877 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1878 name_domain,
1879 name_user,
1880 state->response->data.auth.nt_status_string,
1881 state->response->data.auth.pam_error));
1883 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1886 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1887 struct winbindd_cli_state *state)
1889 char *oldpass;
1890 char *newpass = NULL;
1891 struct policy_handle dom_pol;
1892 struct rpc_pipe_client *cli;
1893 bool got_info = false;
1894 struct samr_DomInfo1 *info = NULL;
1895 struct userPwdChangeFailureInformation *reject = NULL;
1896 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1897 fstring domain, user;
1899 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1900 state->request->data.auth.user));
1902 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1903 goto done;
1906 /* Change password */
1908 oldpass = state->request->data.chauthtok.oldpass;
1909 newpass = state->request->data.chauthtok.newpass;
1911 /* Initialize reject reason */
1912 state->response->data.auth.reject_reason = Undefined;
1914 if (strequal(domain, get_global_sam_name())) {
1915 struct samr_CryptPassword new_nt_password;
1916 struct samr_CryptPassword new_lm_password;
1917 struct samr_Password old_nt_hash_enc;
1918 struct samr_Password old_lanman_hash_enc;
1919 enum samPwdChangeReason rejectReason;
1921 uchar old_nt_hash[16];
1922 uchar old_lanman_hash[16];
1923 uchar new_nt_hash[16];
1924 uchar new_lanman_hash[16];
1926 contact_domain = NULL;
1928 E_md4hash(oldpass, old_nt_hash);
1929 E_md4hash(newpass, new_nt_hash);
1931 if (lp_client_lanman_auth() &&
1932 E_deshash(newpass, new_lanman_hash) &&
1933 E_deshash(oldpass, old_lanman_hash)) {
1935 /* E_deshash returns false for 'long' passwords (> 14
1936 DOS chars). This allows us to match Win2k, which
1937 does not store a LM hash for these passwords (which
1938 would reduce the effective password length to 14) */
1940 encode_pw_buffer(new_lm_password.data, newpass, STR_UNICODE);
1941 arcfour_crypt(new_lm_password.data, old_nt_hash, 516);
1942 E_old_pw_hash(new_nt_hash, old_lanman_hash, old_lanman_hash_enc.hash);
1943 } else {
1944 ZERO_STRUCT(new_lm_password);
1945 ZERO_STRUCT(old_lanman_hash_enc);
1948 encode_pw_buffer(new_nt_password.data, newpass, STR_UNICODE);
1950 arcfour_crypt(new_nt_password.data, old_nt_hash, 516);
1951 E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
1953 result = pass_oem_change(
1954 user,
1955 new_lm_password.data, old_lanman_hash_enc.hash,
1956 new_nt_password.data, old_nt_hash_enc.hash,
1957 &rejectReason);
1958 goto done;
1961 /* Get sam handle */
1963 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1964 &dom_pol);
1965 if (!NT_STATUS_IS_OK(result)) {
1966 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1967 goto done;
1970 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1971 user,
1972 newpass,
1973 oldpass,
1974 &info,
1975 &reject);
1977 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1979 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1981 fill_in_password_policy(state->response, info);
1983 state->response->data.auth.reject_reason =
1984 reject->extendedFailureReason;
1986 got_info = true;
1989 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1990 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1991 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1992 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1994 /* only fallback when the chgpasswd_user3 call is not supported */
1995 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1996 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1997 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1998 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2000 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2001 nt_errstr(result)));
2003 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2005 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2006 Map to the same status code as Windows 2003. */
2008 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2009 result = NT_STATUS_PASSWORD_RESTRICTION;
2013 done:
2015 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2016 if (lp_winbind_offline_logon()) {
2017 result = winbindd_update_creds_by_name(contact_domain,
2018 state->mem_ctx, user,
2019 newpass);
2020 /* Again, this happens when we login from gdm or xdm
2021 * and the password expires, *BUT* cached crendentials
2022 * doesn't exist. winbindd_update_creds_by_name()
2023 * returns NT_STATUS_NO_SUCH_USER.
2024 * This is not a failure.
2025 * --- BoYang
2026 * */
2027 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2028 result = NT_STATUS_OK;
2031 if (!NT_STATUS_IS_OK(result)) {
2032 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2033 goto process_result;
2038 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2040 NTSTATUS policy_ret;
2042 policy_ret = fillup_password_policy(contact_domain, state);
2044 /* failure of this is non critical, it will just provide no
2045 * additional information to the client why the change has
2046 * failed - Guenther */
2048 if (!NT_STATUS_IS_OK(policy_ret)) {
2049 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2050 goto process_result;
2054 process_result:
2056 set_auth_errors(state->response, result);
2058 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2059 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2060 domain,
2061 user,
2062 state->response->data.auth.nt_status_string,
2063 state->response->data.auth.pam_error));
2065 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2068 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2069 struct winbindd_cli_state *state)
2071 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2073 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2074 state->request->data.logoff.user));
2076 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2077 result = NT_STATUS_OK;
2078 goto process_result;
2081 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2082 result = NT_STATUS_OK;
2083 goto process_result;
2086 #ifdef HAVE_KRB5
2088 if (state->request->data.logoff.uid < 0) {
2089 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2090 goto process_result;
2093 /* what we need here is to find the corresponding krb5 ccache name *we*
2094 * created for a given username and destroy it */
2096 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2097 result = NT_STATUS_OK;
2098 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2099 goto process_result;
2102 if (!ccache_entry_identical(state->request->data.logoff.user,
2103 state->request->data.logoff.uid,
2104 state->request->data.logoff.krb5ccname)) {
2105 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2106 goto process_result;
2109 result = remove_ccache(state->request->data.logoff.user);
2110 if (!NT_STATUS_IS_OK(result)) {
2111 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2112 nt_errstr(result)));
2113 goto process_result;
2116 #else
2117 result = NT_STATUS_NOT_SUPPORTED;
2118 #endif
2120 process_result:
2123 set_auth_errors(state->response, result);
2125 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2128 /* Change user password with auth crap*/
2130 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2132 struct winbindd_domain *domain = NULL;
2133 const char *domain_name = NULL;
2135 /* Ensure null termination */
2136 state->request->data.chng_pswd_auth_crap.user[
2137 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2138 state->request->data.chng_pswd_auth_crap.domain[
2139 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2141 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2142 (unsigned long)state->pid,
2143 state->request->data.chng_pswd_auth_crap.domain,
2144 state->request->data.chng_pswd_auth_crap.user));
2146 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2147 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2148 } else if (lp_winbind_use_default_domain()) {
2149 domain_name = lp_workgroup();
2152 if (domain_name != NULL)
2153 domain = find_domain_from_name(domain_name);
2155 if (domain != NULL) {
2156 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2157 "%s\n", (unsigned long)state->pid,domain->name));
2158 sendto_domain(state, domain);
2159 return;
2162 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2163 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2164 state->request->data.chng_pswd_auth_crap.domain,
2165 state->request->data.chng_pswd_auth_crap.user,
2166 state->response->data.auth.nt_status_string,
2167 state->response->data.auth.pam_error));
2168 request_error(state);
2169 return;
2172 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2174 NTSTATUS result;
2175 DATA_BLOB new_nt_password;
2176 DATA_BLOB old_nt_hash_enc;
2177 DATA_BLOB new_lm_password;
2178 DATA_BLOB old_lm_hash_enc;
2179 fstring domain,user;
2180 struct policy_handle dom_pol;
2181 struct winbindd_domain *contact_domain = domainSt;
2182 struct rpc_pipe_client *cli;
2184 /* Ensure null termination */
2185 state->request->data.chng_pswd_auth_crap.user[
2186 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2187 state->request->data.chng_pswd_auth_crap.domain[
2188 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2189 *domain = 0;
2190 *user = 0;
2192 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2193 (unsigned long)state->pid,
2194 state->request->data.chng_pswd_auth_crap.domain,
2195 state->request->data.chng_pswd_auth_crap.user));
2197 if (lp_winbind_offline_logon()) {
2198 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2199 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2200 result = NT_STATUS_ACCESS_DENIED;
2201 goto done;
2204 if (*state->request->data.chng_pswd_auth_crap.domain) {
2205 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2206 } else {
2207 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2208 domain, user);
2210 if(!*domain) {
2211 DEBUG(3,("no domain specified with username (%s) - "
2212 "failing auth\n",
2213 state->request->data.chng_pswd_auth_crap.user));
2214 result = NT_STATUS_NO_SUCH_USER;
2215 goto done;
2219 if (!*domain && lp_winbind_use_default_domain()) {
2220 fstrcpy(domain,(char *)lp_workgroup());
2223 if(!*user) {
2224 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2227 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2228 (unsigned long)state->pid, domain, user));
2230 /* Change password */
2231 new_nt_password = data_blob_talloc(
2232 state->mem_ctx,
2233 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2234 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2236 old_nt_hash_enc = data_blob_talloc(
2237 state->mem_ctx,
2238 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2239 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2241 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2242 new_lm_password = data_blob_talloc(
2243 state->mem_ctx,
2244 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2245 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2247 old_lm_hash_enc = data_blob_talloc(
2248 state->mem_ctx,
2249 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2250 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2251 } else {
2252 new_lm_password.length = 0;
2253 old_lm_hash_enc.length = 0;
2256 /* Get sam handle */
2258 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2259 if (!NT_STATUS_IS_OK(result)) {
2260 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2261 goto done;
2264 result = rpccli_samr_chng_pswd_auth_crap(
2265 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2266 new_lm_password, old_lm_hash_enc);
2268 done:
2270 set_auth_errors(state->response, result);
2272 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2273 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2274 domain, user,
2275 state->response->data.auth.nt_status_string,
2276 state->response->data.auth.pam_error));
2278 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;