s3: Cosmetics -- I could not spot where "chal" was initialized
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
blobd5650d9cc5f970d352bb395b7c00bc8efb88cbc4
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 (is_myname(domain_name)) {
352 DEBUG(3, ("Authentication for domain %s (local domain "
353 "to this server) not supported at this "
354 "stage\n", domain_name));
355 return NULL;
358 /* we can auth against trusted domains */
359 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
360 domain = find_domain_from_name_noinit(domain_name);
361 if (domain == NULL) {
362 DEBUG(3, ("Authentication for domain [%s] skipped "
363 "as it is not a trusted domain\n",
364 domain_name));
365 } else {
366 return domain;
370 return find_our_domain();
373 static void fill_in_password_policy(struct winbindd_response *r,
374 const struct samr_DomInfo1 *p)
376 r->data.auth.policy.min_length_password =
377 p->min_password_length;
378 r->data.auth.policy.password_history =
379 p->password_history_length;
380 r->data.auth.policy.password_properties =
381 p->password_properties;
382 r->data.auth.policy.expire =
383 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
384 r->data.auth.policy.min_passwordage =
385 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
388 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
389 struct winbindd_cli_state *state)
391 struct winbindd_methods *methods;
392 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
393 struct samr_DomInfo1 password_policy;
395 if ( !winbindd_can_contact_domain( domain ) ) {
396 DEBUG(5,("fillup_password_policy: No inbound trust to "
397 "contact domain %s\n", domain->name));
398 return NT_STATUS_NOT_SUPPORTED;
401 methods = domain->methods;
403 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
404 if (NT_STATUS_IS_ERR(status)) {
405 return status;
408 fill_in_password_policy(state->response, &password_policy);
410 return NT_STATUS_OK;
413 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
414 TALLOC_CTX *mem_ctx,
415 uint16 *lockout_threshold)
417 struct winbindd_methods *methods;
418 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
419 struct samr_DomInfo12 lockout_policy;
421 *lockout_threshold = 0;
423 methods = domain->methods;
425 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
426 if (NT_STATUS_IS_ERR(status)) {
427 return status;
430 *lockout_threshold = lockout_policy.lockout_threshold;
432 return NT_STATUS_OK;
435 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
436 TALLOC_CTX *mem_ctx,
437 uint32 *password_properties)
439 struct winbindd_methods *methods;
440 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
441 struct samr_DomInfo1 password_policy;
443 *password_properties = 0;
445 methods = domain->methods;
447 status = methods->password_policy(domain, mem_ctx, &password_policy);
448 if (NT_STATUS_IS_ERR(status)) {
449 return status;
452 *password_properties = password_policy.password_properties;
454 return NT_STATUS_OK;
457 #ifdef HAVE_KRB5
459 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
460 const char *type,
461 uid_t uid,
462 bool *internal_ccache)
464 /* accept FILE and WRFILE as krb5_cc_type from the client and then
465 * build the full ccname string based on the user's uid here -
466 * Guenther*/
468 const char *gen_cc = NULL;
470 *internal_ccache = true;
472 if (uid == -1) {
473 goto memory_ccache;
476 if (!type || type[0] == '\0') {
477 goto memory_ccache;
480 if (strequal(type, "FILE")) {
481 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
482 } else if (strequal(type, "WRFILE")) {
483 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
484 } else {
485 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
486 goto memory_ccache;
489 *internal_ccache = false;
490 goto done;
492 memory_ccache:
493 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
495 done:
496 if (gen_cc == NULL) {
497 DEBUG(0,("out of memory\n"));
498 return NULL;
501 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
503 return gen_cc;
506 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
508 const char *type = state->request->data.auth.krb5_cc_type;
510 state->response->data.auth.krb5ccname[0] = '\0';
512 if (type[0] == '\0') {
513 return;
516 if (!strequal(type, "FILE") &&
517 !strequal(type, "WRFILE")) {
518 DEBUG(10,("won't return krbccname for a %s type ccache\n",
519 type));
520 return;
523 fstrcpy(state->response->data.auth.krb5ccname, cc);
526 #endif
528 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
530 uid_t uid;
532 uid = state->request->data.auth.uid;
534 if (uid < 0) {
535 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
536 return -1;
538 return uid;
541 /**********************************************************************
542 Authenticate a user with a clear text password using Kerberos and fill up
543 ccache if required
544 **********************************************************************/
546 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
547 struct winbindd_cli_state *state,
548 struct netr_SamInfo3 **info3)
550 #ifdef HAVE_KRB5
551 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
552 krb5_error_code krb5_ret;
553 const char *cc = NULL;
554 const char *principal_s = NULL;
555 const char *service = NULL;
556 char *realm = NULL;
557 fstring name_domain, name_user;
558 time_t ticket_lifetime = 0;
559 time_t renewal_until = 0;
560 uid_t uid = -1;
561 ADS_STRUCT *ads;
562 time_t time_offset = 0;
563 bool internal_ccache = true;
565 ZERO_STRUCTP(info3);
567 *info3 = NULL;
569 /* 1st step:
570 * prepare a krb5_cc_cache string for the user */
572 uid = get_uid_from_state(state);
573 if (uid == -1) {
574 DEBUG(0,("no valid uid\n"));
577 cc = generate_krb5_ccache(state->mem_ctx,
578 state->request->data.auth.krb5_cc_type,
579 state->request->data.auth.uid,
580 &internal_ccache);
581 if (cc == NULL) {
582 return NT_STATUS_NO_MEMORY;
586 /* 2nd step:
587 * get kerberos properties */
589 if (domain->private_data) {
590 ads = (ADS_STRUCT *)domain->private_data;
591 time_offset = ads->auth.time_offset;
595 /* 3rd step:
596 * do kerberos auth and setup ccache as the user */
598 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
600 realm = domain->alt_name;
601 strupper_m(realm);
603 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
604 if (principal_s == NULL) {
605 return NT_STATUS_NO_MEMORY;
608 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
609 if (service == NULL) {
610 return NT_STATUS_NO_MEMORY;
613 /* if this is a user ccache, we need to act as the user to let the krb5
614 * library handle the chown, etc. */
616 /************************ ENTERING NON-ROOT **********************/
618 if (!internal_ccache) {
619 set_effective_uid(uid);
620 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
623 result = kerberos_return_info3_from_pac(state->mem_ctx,
624 principal_s,
625 state->request->data.auth.pass,
626 time_offset,
627 &ticket_lifetime,
628 &renewal_until,
630 true,
631 true,
632 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
633 NULL,
634 info3);
635 if (!internal_ccache) {
636 gain_root_privilege();
639 /************************ RETURNED TO ROOT **********************/
641 if (!NT_STATUS_IS_OK(result)) {
642 goto failed;
645 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
646 principal_s));
648 /* if we had a user's ccache then return that string for the pam
649 * environment */
651 if (!internal_ccache) {
653 setup_return_cc_name(state, cc);
655 result = add_ccache_to_list(principal_s,
657 service,
658 state->request->data.auth.user,
659 realm,
660 uid,
661 time(NULL),
662 ticket_lifetime,
663 renewal_until,
664 false);
666 if (!NT_STATUS_IS_OK(result)) {
667 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
668 nt_errstr(result)));
670 } else {
672 /* need to delete the memory cred cache, it is not used anymore */
674 krb5_ret = ads_kdestroy(cc);
675 if (krb5_ret) {
676 DEBUG(3,("winbindd_raw_kerberos_login: "
677 "could not destroy krb5 credential cache: "
678 "%s\n", error_message(krb5_ret)));
683 return NT_STATUS_OK;
685 failed:
687 /* we could have created a new credential cache with a valid tgt in it
688 * but we werent able to get or verify the service ticket for this
689 * local host and therefor didn't get the PAC, we need to remove that
690 * cache entirely now */
692 krb5_ret = ads_kdestroy(cc);
693 if (krb5_ret) {
694 DEBUG(3,("winbindd_raw_kerberos_login: "
695 "could not destroy krb5 credential cache: "
696 "%s\n", error_message(krb5_ret)));
699 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
700 DEBUG(3,("winbindd_raw_kerberos_login: "
701 "could not remove ccache for user %s\n",
702 state->request->data.auth.user));
705 return result;
706 #else
707 return NT_STATUS_NOT_SUPPORTED;
708 #endif /* HAVE_KRB5 */
711 /****************************************************************
712 ****************************************************************/
714 bool check_request_flags(uint32_t flags)
716 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
717 WBFLAG_PAM_INFO3_TEXT |
718 WBFLAG_PAM_INFO3_NDR;
720 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
721 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
722 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
723 !(flags & flags_edata) ) {
724 return true;
727 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
728 flags));
730 return false;
733 /****************************************************************
734 ****************************************************************/
736 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
737 struct netr_SamInfo3 *info3,
738 const char *name_domain,
739 const char *name_user)
741 NTSTATUS result;
742 uint32_t flags = state->request->flags;
744 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
745 memcpy(state->response->data.auth.user_session_key,
746 info3->base.key.key,
747 sizeof(state->response->data.auth.user_session_key)
748 /* 16 */);
751 if (flags & WBFLAG_PAM_LMKEY) {
752 memcpy(state->response->data.auth.first_8_lm_hash,
753 info3->base.LMSessKey.key,
754 sizeof(state->response->data.auth.first_8_lm_hash)
755 /* 8 */);
758 if (flags & WBFLAG_PAM_INFO3_TEXT) {
759 result = append_info3_as_txt(state->mem_ctx, state, info3);
760 if (!NT_STATUS_IS_OK(result)) {
761 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
762 nt_errstr(result)));
763 return result;
767 /* currently, anything from here on potentially overwrites extra_data. */
769 if (flags & WBFLAG_PAM_INFO3_NDR) {
770 result = append_info3_as_ndr(state->mem_ctx, state, info3);
771 if (!NT_STATUS_IS_OK(result)) {
772 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
773 nt_errstr(result)));
774 return result;
778 if (flags & WBFLAG_PAM_UNIX_NAME) {
779 result = append_unix_username(state->mem_ctx, state, info3,
780 name_domain, name_user);
781 if (!NT_STATUS_IS_OK(result)) {
782 DEBUG(10,("Failed to append Unix Username: %s\n",
783 nt_errstr(result)));
784 return result;
788 if (flags & WBFLAG_PAM_AFS_TOKEN) {
789 result = append_afs_token(state->mem_ctx, state, info3,
790 name_domain, name_user);
791 if (!NT_STATUS_IS_OK(result)) {
792 DEBUG(10,("Failed to append AFS token: %s\n",
793 nt_errstr(result)));
794 return result;
798 return NT_STATUS_OK;
801 void winbindd_pam_auth(struct winbindd_cli_state *state)
803 struct winbindd_domain *domain;
804 fstring name_domain, name_user, mapped_user;
805 char *mapped = NULL;
806 NTSTATUS result;
807 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
809 /* Ensure null termination */
810 state->request->data.auth.user
811 [sizeof(state->request->data.auth.user)-1]='\0';
813 /* Ensure null termination */
814 state->request->data.auth.pass
815 [sizeof(state->request->data.auth.pass)-1]='\0';
817 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
818 state->request->data.auth.user));
820 if (!check_request_flags(state->request->flags)) {
821 result = NT_STATUS_INVALID_PARAMETER_MIX;
822 goto done;
825 /* Parse domain and username */
827 name_map_status = normalize_name_unmap(state->mem_ctx,
828 state->request->data.auth.user,
829 &mapped);
831 /* If the name normalization didnt' actually do anything,
832 just use the original name */
834 if (NT_STATUS_IS_OK(name_map_status)
835 ||NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
836 fstrcpy(mapped_user, mapped);
837 } else {
838 fstrcpy(mapped_user, state->request->data.auth.user);
841 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
842 result = NT_STATUS_NO_SUCH_USER;
843 goto done;
846 domain = find_auth_domain(state->request->flags, name_domain);
848 if (domain == NULL) {
849 result = NT_STATUS_NO_SUCH_USER;
850 goto done;
853 sendto_domain(state, domain);
854 return;
855 done:
856 set_auth_errors(state->response, result);
857 DEBUG(5, ("Plain text authentication for %s returned %s "
858 "(PAM: %d)\n",
859 state->request->data.auth.user,
860 state->response->data.auth.nt_status_string,
861 state->response->data.auth.pam_error));
862 request_error(state);
865 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
866 struct winbindd_cli_state *state,
867 struct netr_SamInfo3 **info3)
869 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
870 uint16 max_allowed_bad_attempts;
871 fstring name_domain, name_user;
872 DOM_SID sid;
873 enum lsa_SidType type;
874 uchar new_nt_pass[NT_HASH_LEN];
875 const uint8 *cached_nt_pass;
876 const uint8 *cached_salt;
877 struct netr_SamInfo3 *my_info3;
878 time_t kickoff_time, must_change_time;
879 bool password_good = false;
880 #ifdef HAVE_KRB5
881 struct winbindd_tdc_domain *tdc_domain = NULL;
882 #endif
884 *info3 = NULL;
886 ZERO_STRUCTP(info3);
888 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
890 /* Parse domain and username */
892 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
895 if (!lookup_cached_name(state->mem_ctx,
896 name_domain,
897 name_user,
898 &sid,
899 &type)) {
900 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
901 return NT_STATUS_NO_SUCH_USER;
904 if (type != SID_NAME_USER) {
905 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
906 return NT_STATUS_LOGON_FAILURE;
909 result = winbindd_get_creds(domain,
910 state->mem_ctx,
911 &sid,
912 &my_info3,
913 &cached_nt_pass,
914 &cached_salt);
915 if (!NT_STATUS_IS_OK(result)) {
916 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
917 return result;
920 *info3 = my_info3;
922 E_md4hash(state->request->data.auth.pass, new_nt_pass);
924 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
925 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
926 if (cached_salt) {
927 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
930 if (cached_salt) {
931 /* In this case we didn't store the nt_hash itself,
932 but the MD5 combination of salt + nt_hash. */
933 uchar salted_hash[NT_HASH_LEN];
934 E_md5hash(cached_salt, new_nt_pass, salted_hash);
936 password_good = (memcmp(cached_nt_pass, salted_hash,
937 NT_HASH_LEN) == 0);
938 } else {
939 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
940 password_good = (memcmp(cached_nt_pass, new_nt_pass,
941 NT_HASH_LEN) == 0);
944 if (password_good) {
946 /* User *DOES* know the password, update logon_time and reset
947 * bad_pw_count */
949 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
951 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
952 return NT_STATUS_ACCOUNT_LOCKED_OUT;
955 if (my_info3->base.acct_flags & ACB_DISABLED) {
956 return NT_STATUS_ACCOUNT_DISABLED;
959 if (my_info3->base.acct_flags & ACB_WSTRUST) {
960 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
963 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
964 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
967 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
968 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
971 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
972 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
973 my_info3->base.acct_flags));
974 return NT_STATUS_LOGON_FAILURE;
977 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
978 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
979 return NT_STATUS_ACCOUNT_EXPIRED;
982 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
983 if (must_change_time != 0 && must_change_time < time(NULL)) {
984 /* we allow grace logons when the password has expired */
985 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
986 /* return NT_STATUS_PASSWORD_EXPIRED; */
987 goto success;
990 #ifdef HAVE_KRB5
991 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
992 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
993 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
994 /* used to cope with the case winbindd starting without network. */
995 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
997 uid_t uid = -1;
998 const char *cc = NULL;
999 char *realm = NULL;
1000 const char *principal_s = NULL;
1001 const char *service = NULL;
1002 bool internal_ccache = false;
1004 uid = get_uid_from_state(state);
1005 if (uid == -1) {
1006 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1007 return NT_STATUS_INVALID_PARAMETER;
1010 cc = generate_krb5_ccache(state->mem_ctx,
1011 state->request->data.auth.krb5_cc_type,
1012 state->request->data.auth.uid,
1013 &internal_ccache);
1014 if (cc == NULL) {
1015 return NT_STATUS_NO_MEMORY;
1018 realm = domain->alt_name;
1019 strupper_m(realm);
1021 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1022 if (principal_s == NULL) {
1023 return NT_STATUS_NO_MEMORY;
1026 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1027 if (service == NULL) {
1028 return NT_STATUS_NO_MEMORY;
1031 if (!internal_ccache) {
1033 setup_return_cc_name(state, cc);
1035 result = add_ccache_to_list(principal_s,
1037 service,
1038 state->request->data.auth.user,
1039 domain->alt_name,
1040 uid,
1041 time(NULL),
1042 time(NULL) + lp_winbind_cache_time(),
1043 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1044 true);
1046 if (!NT_STATUS_IS_OK(result)) {
1047 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1048 "to add ccache to list: %s\n",
1049 nt_errstr(result)));
1053 #endif /* HAVE_KRB5 */
1054 success:
1055 /* FIXME: we possibly should handle logon hours as well (does xp when
1056 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1058 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1059 my_info3->base.bad_password_count = 0;
1061 result = winbindd_update_creds_by_info3(domain,
1062 state->mem_ctx,
1063 state->request->data.auth.user,
1064 state->request->data.auth.pass,
1065 my_info3);
1066 if (!NT_STATUS_IS_OK(result)) {
1067 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1068 nt_errstr(result)));
1069 return result;
1072 return NT_STATUS_OK;
1076 /* User does *NOT* know the correct password, modify info3 accordingly */
1078 /* failure of this is not critical */
1079 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1080 if (!NT_STATUS_IS_OK(result)) {
1081 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1082 "Won't be able to honour account lockout policies\n"));
1085 /* increase counter */
1086 my_info3->base.bad_password_count++;
1088 if (max_allowed_bad_attempts == 0) {
1089 goto failed;
1092 /* lockout user */
1093 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1095 uint32 password_properties;
1097 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1098 if (!NT_STATUS_IS_OK(result)) {
1099 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1102 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1103 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1104 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1108 failed:
1109 result = winbindd_update_creds_by_info3(domain,
1110 state->mem_ctx,
1111 state->request->data.auth.user,
1112 NULL,
1113 my_info3);
1115 if (!NT_STATUS_IS_OK(result)) {
1116 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1117 nt_errstr(result)));
1120 return NT_STATUS_LOGON_FAILURE;
1123 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1124 struct winbindd_cli_state *state,
1125 struct netr_SamInfo3 **info3)
1127 struct winbindd_domain *contact_domain;
1128 fstring name_domain, name_user;
1129 NTSTATUS result;
1131 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1133 /* Parse domain and username */
1135 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1137 /* what domain should we contact? */
1139 if ( IS_DC ) {
1140 if (!(contact_domain = find_domain_from_name(name_domain))) {
1141 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1142 state->request->data.auth.user, name_domain, name_user, name_domain));
1143 result = NT_STATUS_NO_SUCH_USER;
1144 goto done;
1147 } else {
1148 if (is_myname(name_domain)) {
1149 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1150 result = NT_STATUS_NO_SUCH_USER;
1151 goto done;
1154 contact_domain = find_domain_from_name(name_domain);
1155 if (contact_domain == NULL) {
1156 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1157 state->request->data.auth.user, name_domain, name_user, name_domain));
1159 contact_domain = find_our_domain();
1163 if (contact_domain->initialized &&
1164 contact_domain->active_directory) {
1165 goto try_login;
1168 if (!contact_domain->initialized) {
1169 init_dc_connection(contact_domain);
1172 if (!contact_domain->active_directory) {
1173 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1174 return NT_STATUS_INVALID_LOGON_TYPE;
1176 try_login:
1177 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1178 done:
1179 return result;
1182 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1183 TALLOC_CTX *mem_ctx,
1184 uint32 logon_parameters,
1185 const char *server,
1186 const char *username,
1187 const char *domain,
1188 const char *workstation,
1189 const uint8 chal[8],
1190 DATA_BLOB lm_response,
1191 DATA_BLOB nt_response,
1192 struct netr_SamInfo3 **info3);
1194 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1195 struct winbindd_cli_state *state,
1196 struct netr_SamInfo3 **info3)
1199 struct rpc_pipe_client *netlogon_pipe;
1200 uchar chal[8];
1201 DATA_BLOB lm_resp;
1202 DATA_BLOB nt_resp;
1203 int attempts = 0;
1204 unsigned char local_lm_response[24];
1205 unsigned char local_nt_response[24];
1206 fstring name_domain, name_user;
1207 bool retry;
1208 NTSTATUS result;
1209 struct netr_SamInfo3 *my_info3 = NULL;
1211 *info3 = NULL;
1213 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1215 /* Parse domain and username */
1217 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1219 /* do password magic */
1221 generate_random_buffer(chal, 8);
1223 if (lp_client_ntlmv2_auth()) {
1224 DATA_BLOB server_chal;
1225 DATA_BLOB names_blob;
1226 DATA_BLOB nt_response;
1227 DATA_BLOB lm_response;
1228 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1230 /* note that the 'workgroup' here is a best guess - we don't know
1231 the server's domain at this point. The 'server name' is also
1232 dodgy...
1234 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1236 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1237 state->request->data.auth.pass,
1238 &server_chal,
1239 &names_blob,
1240 &lm_response, &nt_response, NULL, NULL)) {
1241 data_blob_free(&names_blob);
1242 data_blob_free(&server_chal);
1243 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1244 result = NT_STATUS_NO_MEMORY;
1245 goto done;
1247 data_blob_free(&names_blob);
1248 data_blob_free(&server_chal);
1249 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1250 lm_response.length);
1251 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1252 nt_response.length);
1253 data_blob_free(&lm_response);
1254 data_blob_free(&nt_response);
1256 } else {
1257 if (lp_client_lanman_auth()
1258 && SMBencrypt(state->request->data.auth.pass,
1259 chal,
1260 local_lm_response)) {
1261 lm_resp = data_blob_talloc(state->mem_ctx,
1262 local_lm_response,
1263 sizeof(local_lm_response));
1264 } else {
1265 lm_resp = data_blob_null;
1267 SMBNTencrypt(state->request->data.auth.pass,
1268 chal,
1269 local_nt_response);
1271 nt_resp = data_blob_talloc(state->mem_ctx,
1272 local_nt_response,
1273 sizeof(local_nt_response));
1276 /* check authentication loop */
1278 do {
1279 netlogon_fn_t logon_fn;
1281 ZERO_STRUCTP(my_info3);
1282 retry = false;
1284 result = cm_connect_netlogon(domain, &netlogon_pipe);
1286 if (!NT_STATUS_IS_OK(result)) {
1287 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1288 goto done;
1291 /* It is really important to try SamLogonEx here,
1292 * because in a clustered environment, we want to use
1293 * one machine account from multiple physical
1294 * computers.
1296 * With a normal SamLogon call, we must keep the
1297 * credentials chain updated and intact between all
1298 * users of the machine account (which would imply
1299 * cross-node communication for every NTLM logon).
1301 * (The credentials chain is not per NETLOGON pipe
1302 * connection, but globally on the server/client pair
1303 * by machine name).
1305 * When using SamLogonEx, the credentials are not
1306 * supplied, but the session key is implied by the
1307 * wrapping SamLogon context.
1309 * -- abartlet 21 April 2008
1312 logon_fn = domain->can_do_samlogon_ex
1313 ? rpccli_netlogon_sam_network_logon_ex
1314 : rpccli_netlogon_sam_network_logon;
1316 result = logon_fn(netlogon_pipe,
1317 state->mem_ctx,
1319 domain->dcname, /* server name */
1320 name_user, /* user name */
1321 name_domain, /* target domain */
1322 global_myname(), /* workstation */
1323 chal,
1324 lm_resp,
1325 nt_resp,
1326 &my_info3);
1327 attempts += 1;
1329 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1330 && domain->can_do_samlogon_ex) {
1331 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1332 "retrying with NetSamLogon\n"));
1333 domain->can_do_samlogon_ex = false;
1334 retry = true;
1335 continue;
1338 /* We have to try a second time as cm_connect_netlogon
1339 might not yet have noticed that the DC has killed
1340 our connection. */
1342 if (!rpccli_is_connected(netlogon_pipe)) {
1343 retry = true;
1344 continue;
1347 /* if we get access denied, a possible cause was that we had
1348 and open connection to the DC, but someone changed our
1349 machine account password out from underneath us using 'net
1350 rpc changetrustpw' */
1352 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1353 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1354 "ACCESS_DENIED. Maybe the trust account "
1355 "password was changed and we didn't know it. "
1356 "Killing connections to domain %s\n",
1357 name_domain));
1358 invalidate_cm_connection(&domain->conn);
1359 retry = true;
1362 } while ( (attempts < 2) && retry );
1364 /* handle the case where a NT4 DC does not fill in the acct_flags in
1365 * the samlogon reply info3. When accurate info3 is required by the
1366 * caller, we look up the account flags ourselve - gd */
1368 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1369 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1371 struct rpc_pipe_client *samr_pipe;
1372 struct policy_handle samr_domain_handle, user_pol;
1373 union samr_UserInfo *info = NULL;
1374 NTSTATUS status_tmp;
1375 uint32 acct_flags;
1377 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1378 &samr_pipe, &samr_domain_handle);
1380 if (!NT_STATUS_IS_OK(status_tmp)) {
1381 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1382 nt_errstr(status_tmp)));
1383 goto done;
1386 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1387 &samr_domain_handle,
1388 MAXIMUM_ALLOWED_ACCESS,
1389 my_info3->base.rid,
1390 &user_pol);
1392 if (!NT_STATUS_IS_OK(status_tmp)) {
1393 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1394 nt_errstr(status_tmp)));
1395 goto done;
1398 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1399 &user_pol,
1401 &info);
1403 if (!NT_STATUS_IS_OK(status_tmp)) {
1404 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1405 nt_errstr(status_tmp)));
1406 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1407 goto done;
1410 acct_flags = info->info16.acct_flags;
1412 if (acct_flags == 0) {
1413 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1414 goto done;
1417 my_info3->base.acct_flags = acct_flags;
1419 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1421 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1424 *info3 = my_info3;
1425 done:
1426 return result;
1429 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1430 struct winbindd_cli_state *state)
1432 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1433 NTSTATUS krb5_result = NT_STATUS_OK;
1434 fstring name_domain, name_user;
1435 char *mapped_user;
1436 fstring domain_user;
1437 struct netr_SamInfo3 *info3 = NULL;
1438 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1440 /* Ensure null termination */
1441 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1443 /* Ensure null termination */
1444 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1446 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1447 state->request->data.auth.user));
1449 if (!check_request_flags(state->request->flags)) {
1450 result = NT_STATUS_INVALID_PARAMETER_MIX;
1451 goto done;
1454 /* Parse domain and username */
1456 name_map_status = normalize_name_unmap(state->mem_ctx,
1457 state->request->data.auth.user,
1458 &mapped_user);
1460 /* If the name normalization didnt' actually do anything,
1461 just use the original name */
1463 if (!NT_STATUS_IS_OK(name_map_status) &&
1464 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1466 mapped_user = state->request->data.auth.user;
1469 parse_domain_user(mapped_user, name_domain, name_user);
1471 if ( mapped_user != state->request->data.auth.user ) {
1472 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1473 safe_strcpy( state->request->data.auth.user, domain_user,
1474 sizeof(state->request->data.auth.user)-1 );
1477 if (domain->online == false) {
1478 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1479 if (domain->startup) {
1480 /* Logons are very important to users. If we're offline and
1481 we get a request within the first 30 seconds of startup,
1482 try very hard to find a DC and go online. */
1484 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1485 "request in startup mode.\n", domain->name ));
1487 winbindd_flush_negative_conn_cache(domain);
1488 result = init_dc_connection(domain);
1492 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1494 /* Check for Kerberos authentication */
1495 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1497 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1498 /* save for later */
1499 krb5_result = result;
1502 if (NT_STATUS_IS_OK(result)) {
1503 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1504 goto process_result;
1505 } else {
1506 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1509 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1510 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1511 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1512 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1513 set_domain_offline( domain );
1514 goto cached_logon;
1517 /* there are quite some NT_STATUS errors where there is no
1518 * point in retrying with a samlogon, we explictly have to take
1519 * care not to increase the bad logon counter on the DC */
1521 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1526 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1527 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1528 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1529 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1530 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1531 goto done;
1534 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1535 DEBUG(3,("falling back to samlogon\n"));
1536 goto sam_logon;
1537 } else {
1538 goto cached_logon;
1542 sam_logon:
1543 /* Check for Samlogon authentication */
1544 if (domain->online) {
1545 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1547 if (NT_STATUS_IS_OK(result)) {
1548 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1549 /* add the Krb5 err if we have one */
1550 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1551 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1553 goto process_result;
1556 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1557 nt_errstr(result)));
1559 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1560 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1561 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1563 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1564 set_domain_offline( domain );
1565 goto cached_logon;
1568 if (domain->online) {
1569 /* We're still online - fail. */
1570 goto done;
1574 cached_logon:
1575 /* Check for Cached logons */
1576 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1577 lp_winbind_offline_logon()) {
1579 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1581 if (NT_STATUS_IS_OK(result)) {
1582 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1583 goto process_result;
1584 } else {
1585 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1586 goto done;
1590 process_result:
1592 if (NT_STATUS_IS_OK(result)) {
1594 DOM_SID user_sid;
1596 /* In all codepaths where result == NT_STATUS_OK info3 must have
1597 been initialized. */
1598 if (!info3) {
1599 result = NT_STATUS_INTERNAL_ERROR;
1600 goto done;
1603 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1604 netsamlogon_cache_store(name_user, info3);
1606 /* save name_to_sid info as early as possible (only if
1607 this is our primary domain so we don't invalidate
1608 the cache entry by storing the seq_num for the wrong
1609 domain). */
1610 if ( domain->primary ) {
1611 sid_compose(&user_sid, info3->base.domain_sid,
1612 info3->base.rid);
1613 cache_name2sid(domain, name_domain, name_user,
1614 SID_NAME_USER, &user_sid);
1617 /* Check if the user is in the right group */
1619 result = check_info3_in_group(
1620 info3,
1621 state->request->data.auth.require_membership_of_sid);
1622 if (!NT_STATUS_IS_OK(result)) {
1623 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1624 state->request->data.auth.user,
1625 state->request->data.auth.require_membership_of_sid));
1626 goto done;
1629 result = append_auth_data(state, info3, name_domain,
1630 name_user);
1631 if (!NT_STATUS_IS_OK(result)) {
1632 goto done;
1635 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1637 /* Store in-memory creds for single-signon using ntlm_auth. */
1638 result = winbindd_add_memory_creds(state->request->data.auth.user,
1639 get_uid_from_state(state),
1640 state->request->data.auth.pass);
1642 if (!NT_STATUS_IS_OK(result)) {
1643 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1644 goto done;
1647 if (lp_winbind_offline_logon()) {
1648 result = winbindd_store_creds(domain,
1649 state->mem_ctx,
1650 state->request->data.auth.user,
1651 state->request->data.auth.pass,
1652 info3, NULL);
1653 if (!NT_STATUS_IS_OK(result)) {
1655 /* Release refcount. */
1656 winbindd_delete_memory_creds(state->request->data.auth.user);
1658 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1659 goto done;
1665 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1666 struct winbindd_domain *our_domain = find_our_domain();
1668 /* This is not entirely correct I believe, but it is
1669 consistent. Only apply the password policy settings
1670 too warn users for our own domain. Cannot obtain these
1671 from trusted DCs all the time so don't do it at all.
1672 -- jerry */
1674 result = NT_STATUS_NOT_SUPPORTED;
1675 if (our_domain == domain ) {
1676 result = fillup_password_policy(our_domain, state);
1679 if (!NT_STATUS_IS_OK(result)
1680 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1682 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1683 domain->name, nt_errstr(result)));
1684 goto done;
1688 result = NT_STATUS_OK;
1691 done:
1692 /* give us a more useful (more correct?) error code */
1693 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1694 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1695 result = NT_STATUS_NO_LOGON_SERVERS;
1698 set_auth_errors(state->response, result);
1700 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1701 state->request->data.auth.user,
1702 state->response->data.auth.nt_status_string,
1703 state->response->data.auth.pam_error));
1705 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1709 /**********************************************************************
1710 Challenge Response Authentication Protocol
1711 **********************************************************************/
1713 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1715 struct winbindd_domain *domain = NULL;
1716 const char *domain_name = NULL;
1717 NTSTATUS result;
1719 if (!check_request_flags(state->request->flags)) {
1720 result = NT_STATUS_INVALID_PARAMETER_MIX;
1721 goto done;
1724 if (!state->privileged) {
1725 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1726 "denied. !\n"));
1727 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1728 "on %s are set correctly.\n",
1729 get_winbind_priv_pipe_dir()));
1730 /* send a better message than ACCESS_DENIED */
1731 fstr_sprintf(state->response->data.auth.error_string,
1732 "winbind client not authorized to use "
1733 "winbindd_pam_auth_crap. Ensure permissions on "
1734 "%s are set correctly.",
1735 get_winbind_priv_pipe_dir());
1736 result = NT_STATUS_ACCESS_DENIED;
1737 goto done;
1740 /* Ensure null termination */
1741 state->request->data.auth_crap.user
1742 [sizeof(state->request->data.auth_crap.user)-1]=0;
1743 state->request->data.auth_crap.domain
1744 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1746 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1747 (unsigned long)state->pid,
1748 state->request->data.auth_crap.domain,
1749 state->request->data.auth_crap.user));
1751 if (*state->request->data.auth_crap.domain != '\0') {
1752 domain_name = state->request->data.auth_crap.domain;
1753 } else if (lp_winbind_use_default_domain()) {
1754 domain_name = lp_workgroup();
1757 if (domain_name != NULL)
1758 domain = find_auth_domain(state->request->flags, domain_name);
1760 if (domain != NULL) {
1761 sendto_domain(state, domain);
1762 return;
1765 result = NT_STATUS_NO_SUCH_USER;
1767 done:
1768 set_auth_errors(state->response, result);
1769 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1770 state->request->data.auth_crap.domain,
1771 state->request->data.auth_crap.user,
1772 state->response->data.auth.nt_status_string,
1773 state->response->data.auth.pam_error));
1774 request_error(state);
1775 return;
1779 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1780 struct winbindd_cli_state *state)
1782 NTSTATUS result;
1783 struct netr_SamInfo3 *info3 = NULL;
1784 struct rpc_pipe_client *netlogon_pipe;
1785 const char *name_user = NULL;
1786 const char *name_domain = NULL;
1787 const char *workstation;
1788 int attempts = 0;
1789 bool retry;
1791 DATA_BLOB lm_resp, nt_resp;
1793 /* This is child-only, so no check for privileged access is needed
1794 anymore */
1796 /* Ensure null termination */
1797 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1798 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1800 if (!check_request_flags(state->request->flags)) {
1801 result = NT_STATUS_INVALID_PARAMETER_MIX;
1802 goto done;
1805 name_user = state->request->data.auth_crap.user;
1807 if (*state->request->data.auth_crap.domain) {
1808 name_domain = state->request->data.auth_crap.domain;
1809 } else if (lp_winbind_use_default_domain()) {
1810 name_domain = lp_workgroup();
1811 } else {
1812 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1813 name_user));
1814 result = NT_STATUS_NO_SUCH_USER;
1815 goto done;
1818 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1819 name_domain, name_user));
1821 if (*state->request->data.auth_crap.workstation) {
1822 workstation = state->request->data.auth_crap.workstation;
1823 } else {
1824 workstation = global_myname();
1827 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1828 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1829 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1830 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1831 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1832 state->request->data.auth_crap.lm_resp_len,
1833 state->request->data.auth_crap.nt_resp_len));
1834 result = NT_STATUS_INVALID_PARAMETER;
1835 goto done;
1839 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1840 state->request->data.auth_crap.lm_resp_len);
1842 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1843 nt_resp = data_blob_talloc(state->mem_ctx,
1844 state->request->extra_data.data,
1845 state->request->data.auth_crap.nt_resp_len);
1846 } else {
1847 nt_resp = data_blob_talloc(state->mem_ctx,
1848 state->request->data.auth_crap.nt_resp,
1849 state->request->data.auth_crap.nt_resp_len);
1852 do {
1853 netlogon_fn_t logon_fn;
1855 retry = false;
1857 netlogon_pipe = NULL;
1858 result = cm_connect_netlogon(domain, &netlogon_pipe);
1860 if (!NT_STATUS_IS_OK(result)) {
1861 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1862 nt_errstr(result)));
1863 goto done;
1866 logon_fn = domain->can_do_samlogon_ex
1867 ? rpccli_netlogon_sam_network_logon_ex
1868 : rpccli_netlogon_sam_network_logon;
1870 result = logon_fn(netlogon_pipe,
1871 state->mem_ctx,
1872 state->request->data.auth_crap.logon_parameters,
1873 domain->dcname,
1874 name_user,
1875 name_domain,
1876 /* Bug #3248 - found by Stefan Burkei. */
1877 workstation, /* We carefully set this above so use it... */
1878 state->request->data.auth_crap.chal,
1879 lm_resp,
1880 nt_resp,
1881 &info3);
1883 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1884 && domain->can_do_samlogon_ex) {
1885 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1886 "retrying with NetSamLogon\n"));
1887 domain->can_do_samlogon_ex = false;
1888 retry = true;
1889 continue;
1892 attempts += 1;
1894 /* We have to try a second time as cm_connect_netlogon
1895 might not yet have noticed that the DC has killed
1896 our connection. */
1898 if (!rpccli_is_connected(netlogon_pipe)) {
1899 retry = true;
1900 continue;
1903 /* if we get access denied, a possible cause was that we had and open
1904 connection to the DC, but someone changed our machine account password
1905 out from underneath us using 'net rpc changetrustpw' */
1907 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1908 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1909 "ACCESS_DENIED. Maybe the trust account "
1910 "password was changed and we didn't know it. "
1911 "Killing connections to domain %s\n",
1912 name_domain));
1913 invalidate_cm_connection(&domain->conn);
1914 retry = true;
1917 } while ( (attempts < 2) && retry );
1919 if (NT_STATUS_IS_OK(result)) {
1921 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1922 netsamlogon_cache_store(name_user, info3);
1924 /* Check if the user is in the right group */
1926 result = check_info3_in_group(
1927 info3,
1928 state->request->data.auth_crap.require_membership_of_sid);
1929 if (!NT_STATUS_IS_OK(result)) {
1930 DEBUG(3, ("User %s is not in the required group (%s), so "
1931 "crap authentication is rejected\n",
1932 state->request->data.auth_crap.user,
1933 state->request->data.auth_crap.require_membership_of_sid));
1934 goto done;
1937 result = append_auth_data(state, info3, name_domain,
1938 name_user);
1939 if (!NT_STATUS_IS_OK(result)) {
1940 goto done;
1944 done:
1946 /* give us a more useful (more correct?) error code */
1947 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1948 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1949 result = NT_STATUS_NO_LOGON_SERVERS;
1952 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1953 result = nt_status_squash(result);
1956 set_auth_errors(state->response, result);
1958 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1959 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1960 name_domain,
1961 name_user,
1962 state->response->data.auth.nt_status_string,
1963 state->response->data.auth.pam_error));
1965 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1968 /* Change a user password */
1970 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1972 fstring domain, user;
1973 char *mapped_user;
1974 struct winbindd_domain *contact_domain;
1975 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1977 /* Ensure null termination */
1978 state->request->data.chauthtok.user[
1979 sizeof(state->request->data.chauthtok.user)-1]='\0';
1981 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1982 state->request->data.chauthtok.user));
1984 /* Setup crap */
1986 nt_status = normalize_name_unmap(state->mem_ctx,
1987 state->request->data.chauthtok.user,
1988 &mapped_user);
1990 /* Update the chauthtok name if we did any mapping */
1992 if (NT_STATUS_IS_OK(nt_status) ||
1993 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
1995 fstrcpy(state->request->data.chauthtok.user, mapped_user);
1998 /* Must pass in state->...chauthtok.user because
1999 canonicalize_username() assumes an fstring(). Since
2000 we have already copied it (if necessary), this is ok. */
2002 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2003 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2004 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2005 "(PAM: %d)\n",
2006 state->request->data.chauthtok.user,
2007 state->response->data.auth.nt_status_string,
2008 state->response->data.auth.pam_error));
2009 request_error(state);
2010 return;
2013 contact_domain = find_domain_from_name(domain);
2014 if (!contact_domain) {
2015 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2016 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2017 state->request->data.chauthtok.user, domain, user, domain));
2018 request_error(state);
2019 return;
2022 sendto_domain(state, contact_domain);
2025 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2026 struct winbindd_cli_state *state)
2028 char *oldpass;
2029 char *newpass = NULL;
2030 struct policy_handle dom_pol;
2031 struct rpc_pipe_client *cli;
2032 bool got_info = false;
2033 struct samr_DomInfo1 *info = NULL;
2034 struct userPwdChangeFailureInformation *reject = NULL;
2035 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2036 fstring domain, user;
2038 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2039 state->request->data.auth.user));
2041 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2042 goto done;
2045 /* Change password */
2047 oldpass = state->request->data.chauthtok.oldpass;
2048 newpass = state->request->data.chauthtok.newpass;
2050 /* Initialize reject reason */
2051 state->response->data.auth.reject_reason = Undefined;
2053 /* Get sam handle */
2055 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2056 &dom_pol);
2057 if (!NT_STATUS_IS_OK(result)) {
2058 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2059 goto done;
2062 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2063 user,
2064 newpass,
2065 oldpass,
2066 &info,
2067 &reject);
2069 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2071 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2073 fill_in_password_policy(state->response, info);
2075 state->response->data.auth.reject_reason =
2076 reject->extendedFailureReason;
2078 got_info = true;
2081 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2082 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2083 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2084 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2086 /* only fallback when the chgpasswd_user3 call is not supported */
2087 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2088 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2089 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2090 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2092 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2093 nt_errstr(result)));
2095 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2097 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2098 Map to the same status code as Windows 2003. */
2100 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2101 result = NT_STATUS_PASSWORD_RESTRICTION;
2105 done:
2107 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2109 /* Update the single sign-on memory creds. */
2110 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2111 newpass);
2113 /* When we login from gdm or xdm and password expires,
2114 * we change password, but there are no memory crendentials
2115 * So, winbindd_replace_memory_creds() returns
2116 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2117 * --- BoYang
2118 * */
2119 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2120 result = NT_STATUS_OK;
2123 if (!NT_STATUS_IS_OK(result)) {
2124 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2125 goto process_result;
2128 if (lp_winbind_offline_logon()) {
2129 result = winbindd_update_creds_by_name(contact_domain,
2130 state->mem_ctx, user,
2131 newpass);
2132 /* Again, this happens when we login from gdm or xdm
2133 * and the password expires, *BUT* cached crendentials
2134 * doesn't exist. winbindd_update_creds_by_name()
2135 * returns NT_STATUS_NO_SUCH_USER.
2136 * This is not a failure.
2137 * --- BoYang
2138 * */
2139 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2140 result = NT_STATUS_OK;
2143 if (!NT_STATUS_IS_OK(result)) {
2144 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2145 goto process_result;
2150 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2152 NTSTATUS policy_ret;
2154 policy_ret = fillup_password_policy(contact_domain, state);
2156 /* failure of this is non critical, it will just provide no
2157 * additional information to the client why the change has
2158 * failed - Guenther */
2160 if (!NT_STATUS_IS_OK(policy_ret)) {
2161 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2162 goto process_result;
2166 process_result:
2168 set_auth_errors(state->response, result);
2170 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2171 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2172 domain,
2173 user,
2174 state->response->data.auth.nt_status_string,
2175 state->response->data.auth.pam_error));
2177 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2180 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2182 struct winbindd_domain *domain;
2183 fstring name_domain, user;
2184 uid_t caller_uid = (uid_t)-1;
2185 uid_t request_uid = state->request->data.logoff.uid;
2187 /* Ensure null termination */
2188 state->request->data.logoff.user
2189 [sizeof(state->request->data.logoff.user)-1]='\0';
2191 state->request->data.logoff.krb5ccname
2192 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2194 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2195 state->request->data.logoff.user));
2197 if (request_uid == (uid_t)-1) {
2198 goto failed;
2201 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2202 goto failed;
2205 if ((domain = find_auth_domain(state->request->flags,
2206 name_domain)) == NULL) {
2207 goto failed;
2210 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2211 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2212 strerror(errno)));
2213 goto failed;
2216 switch (caller_uid) {
2217 case -1:
2218 goto failed;
2219 case 0:
2220 /* root must be able to logoff any user - gd */
2221 state->request->data.logoff.uid = request_uid;
2222 break;
2223 default:
2224 if (caller_uid != request_uid) {
2225 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2226 goto failed;
2228 state->request->data.logoff.uid = caller_uid;
2229 break;
2232 sendto_domain(state, domain);
2233 return;
2235 failed:
2236 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2237 DEBUG(5, ("Pam Logoff for %s returned %s "
2238 "(PAM: %d)\n",
2239 state->request->data.logoff.user,
2240 state->response->data.auth.nt_status_string,
2241 state->response->data.auth.pam_error));
2242 request_error(state);
2243 return;
2246 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2247 struct winbindd_cli_state *state)
2249 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2251 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2252 state->request->data.logoff.user));
2254 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2255 result = NT_STATUS_OK;
2256 goto process_result;
2259 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2260 result = NT_STATUS_OK;
2261 goto process_result;
2264 #ifdef HAVE_KRB5
2266 if (state->request->data.logoff.uid < 0) {
2267 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2268 goto process_result;
2271 /* what we need here is to find the corresponding krb5 ccache name *we*
2272 * created for a given username and destroy it */
2274 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2275 result = NT_STATUS_OK;
2276 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2277 goto process_result;
2280 if (!ccache_entry_identical(state->request->data.logoff.user,
2281 state->request->data.logoff.uid,
2282 state->request->data.logoff.krb5ccname)) {
2283 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2284 goto process_result;
2287 result = remove_ccache(state->request->data.logoff.user);
2288 if (!NT_STATUS_IS_OK(result)) {
2289 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2290 nt_errstr(result)));
2291 goto process_result;
2294 #else
2295 result = NT_STATUS_NOT_SUPPORTED;
2296 #endif
2298 process_result:
2300 winbindd_delete_memory_creds(state->request->data.logoff.user);
2302 set_auth_errors(state->response, result);
2304 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2307 /* Change user password with auth crap*/
2309 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2311 struct winbindd_domain *domain = NULL;
2312 const char *domain_name = NULL;
2314 /* Ensure null termination */
2315 state->request->data.chng_pswd_auth_crap.user[
2316 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2317 state->request->data.chng_pswd_auth_crap.domain[
2318 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2320 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2321 (unsigned long)state->pid,
2322 state->request->data.chng_pswd_auth_crap.domain,
2323 state->request->data.chng_pswd_auth_crap.user));
2325 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2326 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2327 } else if (lp_winbind_use_default_domain()) {
2328 domain_name = lp_workgroup();
2331 if (domain_name != NULL)
2332 domain = find_domain_from_name(domain_name);
2334 if (domain != NULL) {
2335 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2336 "%s\n", (unsigned long)state->pid,domain->name));
2337 sendto_domain(state, domain);
2338 return;
2341 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2342 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2343 state->request->data.chng_pswd_auth_crap.domain,
2344 state->request->data.chng_pswd_auth_crap.user,
2345 state->response->data.auth.nt_status_string,
2346 state->response->data.auth.pam_error));
2347 request_error(state);
2348 return;
2351 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2353 NTSTATUS result;
2354 DATA_BLOB new_nt_password;
2355 DATA_BLOB old_nt_hash_enc;
2356 DATA_BLOB new_lm_password;
2357 DATA_BLOB old_lm_hash_enc;
2358 fstring domain,user;
2359 struct policy_handle dom_pol;
2360 struct winbindd_domain *contact_domain = domainSt;
2361 struct rpc_pipe_client *cli;
2363 /* Ensure null termination */
2364 state->request->data.chng_pswd_auth_crap.user[
2365 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2366 state->request->data.chng_pswd_auth_crap.domain[
2367 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2368 *domain = 0;
2369 *user = 0;
2371 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2372 (unsigned long)state->pid,
2373 state->request->data.chng_pswd_auth_crap.domain,
2374 state->request->data.chng_pswd_auth_crap.user));
2376 if (lp_winbind_offline_logon()) {
2377 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2378 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2379 result = NT_STATUS_ACCESS_DENIED;
2380 goto done;
2383 if (*state->request->data.chng_pswd_auth_crap.domain) {
2384 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2385 } else {
2386 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2387 domain, user);
2389 if(!*domain) {
2390 DEBUG(3,("no domain specified with username (%s) - "
2391 "failing auth\n",
2392 state->request->data.chng_pswd_auth_crap.user));
2393 result = NT_STATUS_NO_SUCH_USER;
2394 goto done;
2398 if (!*domain && lp_winbind_use_default_domain()) {
2399 fstrcpy(domain,(char *)lp_workgroup());
2402 if(!*user) {
2403 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2406 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2407 (unsigned long)state->pid, domain, user));
2409 /* Change password */
2410 new_nt_password = data_blob_talloc(
2411 state->mem_ctx,
2412 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2413 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2415 old_nt_hash_enc = data_blob_talloc(
2416 state->mem_ctx,
2417 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2418 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2420 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2421 new_lm_password = data_blob_talloc(
2422 state->mem_ctx,
2423 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2424 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2426 old_lm_hash_enc = data_blob_talloc(
2427 state->mem_ctx,
2428 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2429 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2430 } else {
2431 new_lm_password.length = 0;
2432 old_lm_hash_enc.length = 0;
2435 /* Get sam handle */
2437 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2438 if (!NT_STATUS_IS_OK(result)) {
2439 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2440 goto done;
2443 result = rpccli_samr_chng_pswd_auth_crap(
2444 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2445 new_lm_password, old_lm_hash_enc);
2447 done:
2449 set_auth_errors(state->response, result);
2451 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2452 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2453 domain, user,
2454 state->response->data.auth.nt_status_string,
2455 state->response->data.auth.pam_error));
2457 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;