s3:winbind: remove a pointless initialization
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob178b3ea74be68c11f95a08580ad0359926f272ae
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 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
31 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
33 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
34 struct winbindd_cli_state *state,
35 struct netr_SamInfo3 *info3)
37 char *ex;
38 uint32_t i;
40 state->response->data.auth.info3.logon_time =
41 nt_time_to_unix(info3->base.last_logon);
42 state->response->data.auth.info3.logoff_time =
43 nt_time_to_unix(info3->base.last_logoff);
44 state->response->data.auth.info3.kickoff_time =
45 nt_time_to_unix(info3->base.acct_expiry);
46 state->response->data.auth.info3.pass_last_set_time =
47 nt_time_to_unix(info3->base.last_password_change);
48 state->response->data.auth.info3.pass_can_change_time =
49 nt_time_to_unix(info3->base.allow_password_change);
50 state->response->data.auth.info3.pass_must_change_time =
51 nt_time_to_unix(info3->base.force_password_change);
53 state->response->data.auth.info3.logon_count = info3->base.logon_count;
54 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
56 state->response->data.auth.info3.user_rid = info3->base.rid;
57 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
58 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
60 state->response->data.auth.info3.num_groups = info3->base.groups.count;
61 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
63 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
64 state->response->data.auth.info3.num_other_sids = info3->sidcount;
66 fstrcpy(state->response->data.auth.info3.user_name,
67 info3->base.account_name.string);
68 fstrcpy(state->response->data.auth.info3.full_name,
69 info3->base.full_name.string);
70 fstrcpy(state->response->data.auth.info3.logon_script,
71 info3->base.logon_script.string);
72 fstrcpy(state->response->data.auth.info3.profile_path,
73 info3->base.profile_path.string);
74 fstrcpy(state->response->data.auth.info3.home_dir,
75 info3->base.home_directory.string);
76 fstrcpy(state->response->data.auth.info3.dir_drive,
77 info3->base.home_drive.string);
79 fstrcpy(state->response->data.auth.info3.logon_srv,
80 info3->base.logon_server.string);
81 fstrcpy(state->response->data.auth.info3.logon_dom,
82 info3->base.domain.string);
84 ex = talloc_strdup(state->mem_ctx, "");
85 NT_STATUS_HAVE_NO_MEMORY(ex);
87 for (i=0; i < info3->base.groups.count; i++) {
88 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
89 info3->base.groups.rids[i].rid,
90 info3->base.groups.rids[i].attributes);
91 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->sidcount; i++) {
95 char *sid;
97 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
98 NT_STATUS_HAVE_NO_MEMORY(sid);
100 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
101 sid,
102 info3->sids[i].attributes);
103 NT_STATUS_HAVE_NO_MEMORY(ex);
105 talloc_free(sid);
108 state->response->extra_data.data = ex;
109 state->response->length += talloc_get_size(ex);
111 return NT_STATUS_OK;
114 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
115 struct winbindd_cli_state *state,
116 struct netr_SamInfo3 *info3)
118 DATA_BLOB blob;
119 enum ndr_err_code ndr_err;
121 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
122 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
124 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
125 return ndr_map_error2ntstatus(ndr_err);
128 state->response->extra_data.data = blob.data;
129 state->response->length += blob.length;
131 return NT_STATUS_OK;
134 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
135 struct winbindd_cli_state *state,
136 const struct netr_SamInfo3 *info3,
137 const char *name_domain,
138 const char *name_user)
140 /* We've been asked to return the unix username, per
141 'winbind use default domain' settings and the like */
143 const char *nt_username, *nt_domain;
145 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
146 if (!nt_domain) {
147 /* If the server didn't give us one, just use the one
148 * we sent them */
149 nt_domain = name_domain;
152 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
153 if (!nt_username) {
154 /* If the server didn't give us one, just use the one
155 * we sent them */
156 nt_username = name_user;
159 fill_domain_username(state->response->data.auth.unix_username,
160 nt_domain, nt_username, true);
162 DEBUG(5,("Setting unix username to [%s]\n",
163 state->response->data.auth.unix_username));
165 return NT_STATUS_OK;
168 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
169 struct winbindd_cli_state *state,
170 const struct netr_SamInfo3 *info3,
171 const char *name_domain,
172 const char *name_user)
174 char *afsname = NULL;
175 char *cell;
176 char *token;
178 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
179 if (afsname == NULL) {
180 return NT_STATUS_NO_MEMORY;
183 afsname = talloc_string_sub(mem_ctx,
184 lp_afs_username_map(),
185 "%D", name_domain);
186 afsname = talloc_string_sub(mem_ctx, afsname,
187 "%u", name_user);
188 afsname = talloc_string_sub(mem_ctx, afsname,
189 "%U", name_user);
192 DOM_SID user_sid;
193 fstring sidstr;
195 sid_copy(&user_sid, info3->base.domain_sid);
196 sid_append_rid(&user_sid, info3->base.rid);
197 sid_to_fstring(sidstr, &user_sid);
198 afsname = talloc_string_sub(mem_ctx, afsname,
199 "%s", sidstr);
202 if (afsname == NULL) {
203 return NT_STATUS_NO_MEMORY;
206 strlower_m(afsname);
208 DEBUG(10, ("Generating token for user %s\n", afsname));
210 cell = strchr(afsname, '@');
212 if (cell == NULL) {
213 return NT_STATUS_NO_MEMORY;
216 *cell = '\0';
217 cell += 1;
219 token = afs_createtoken_str(afsname, cell);
220 if (token == NULL) {
221 return NT_STATUS_OK;
223 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
224 token);
225 if (state->response->extra_data.data == NULL) {
226 return NT_STATUS_NO_MEMORY;
228 state->response->length +=
229 strlen((const char *)state->response->extra_data.data)+1;
231 return NT_STATUS_OK;
234 NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
235 const char *group_sid)
237 * Check whether a user belongs to a group or list of groups.
239 * @param mem_ctx talloc memory context.
240 * @param info3 user information, including group membership info.
241 * @param group_sid One or more groups , separated by commas.
243 * @return NT_STATUS_OK on success,
244 * NT_STATUS_LOGON_FAILURE if the user does not belong,
245 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
248 DOM_SID *require_membership_of_sid;
249 size_t num_require_membership_of_sid;
250 char *req_sid;
251 const char *p;
252 DOM_SID sid;
253 size_t i;
254 struct nt_user_token *token;
255 TALLOC_CTX *frame = talloc_stackframe();
256 NTSTATUS status;
258 /* Parse the 'required group' SID */
260 if (!group_sid || !group_sid[0]) {
261 /* NO sid supplied, all users may access */
262 return NT_STATUS_OK;
265 token = talloc_zero(talloc_tos(), struct nt_user_token);
266 if (token == NULL) {
267 DEBUG(0, ("talloc failed\n"));
268 TALLOC_FREE(frame);
269 return NT_STATUS_NO_MEMORY;
272 num_require_membership_of_sid = 0;
273 require_membership_of_sid = NULL;
275 p = group_sid;
277 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
278 if (!string_to_sid(&sid, req_sid)) {
279 DEBUG(0, ("check_info3_in_group: could not parse %s "
280 "as a SID!", req_sid));
281 TALLOC_FREE(frame);
282 return NT_STATUS_INVALID_PARAMETER;
285 status = add_sid_to_array(talloc_tos(), &sid,
286 &require_membership_of_sid,
287 &num_require_membership_of_sid);
288 if (!NT_STATUS_IS_OK(status)) {
289 DEBUG(0, ("add_sid_to_array failed\n"));
290 TALLOC_FREE(frame);
291 return status;
295 status = sid_array_from_info3(talloc_tos(), info3,
296 &token->user_sids,
297 &token->num_sids,
298 true, false);
299 if (!NT_STATUS_IS_OK(status)) {
300 TALLOC_FREE(frame);
301 return status;
304 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
305 token))
306 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
307 token))) {
308 DEBUG(3, ("could not add aliases: %s\n",
309 nt_errstr(status)));
310 TALLOC_FREE(frame);
311 return status;
314 debug_nt_user_token(DBGC_CLASS, 10, token);
316 for (i=0; i<num_require_membership_of_sid; i++) {
317 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
318 &require_membership_of_sid[i])));
319 if (nt_token_check_sid(&require_membership_of_sid[i],
320 token)) {
321 DEBUG(10, ("Access ok\n"));
322 TALLOC_FREE(frame);
323 return NT_STATUS_OK;
327 /* Do not distinguish this error from a wrong username/pw */
329 TALLOC_FREE(frame);
330 return NT_STATUS_LOGON_FAILURE;
333 struct winbindd_domain *find_auth_domain(uint8_t flags,
334 const char *domain_name)
336 struct winbindd_domain *domain;
338 if (IS_DC) {
339 domain = find_domain_from_name_noinit(domain_name);
340 if (domain == NULL) {
341 DEBUG(3, ("Authentication for domain [%s] refused "
342 "as it is not a trusted domain\n",
343 domain_name));
345 return domain;
348 if (is_myname(domain_name)) {
349 DEBUG(3, ("Authentication for domain %s (local domain "
350 "to this server) not supported at this "
351 "stage\n", domain_name));
352 return NULL;
355 /* we can auth against trusted domains */
356 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
357 domain = find_domain_from_name_noinit(domain_name);
358 if (domain == NULL) {
359 DEBUG(3, ("Authentication for domain [%s] skipped "
360 "as it is not a trusted domain\n",
361 domain_name));
362 } else {
363 return domain;
367 return find_our_domain();
370 static void fill_in_password_policy(struct winbindd_response *r,
371 const struct samr_DomInfo1 *p)
373 r->data.auth.policy.min_length_password =
374 p->min_password_length;
375 r->data.auth.policy.password_history =
376 p->password_history_length;
377 r->data.auth.policy.password_properties =
378 p->password_properties;
379 r->data.auth.policy.expire =
380 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
381 r->data.auth.policy.min_passwordage =
382 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
385 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
386 struct winbindd_cli_state *state)
388 struct winbindd_methods *methods;
389 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
390 struct samr_DomInfo1 password_policy;
392 if ( !winbindd_can_contact_domain( domain ) ) {
393 DEBUG(5,("fillup_password_policy: No inbound trust to "
394 "contact domain %s\n", domain->name));
395 return NT_STATUS_NOT_SUPPORTED;
398 methods = domain->methods;
400 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
401 if (NT_STATUS_IS_ERR(status)) {
402 return status;
405 fill_in_password_policy(state->response, &password_policy);
407 return NT_STATUS_OK;
410 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
411 TALLOC_CTX *mem_ctx,
412 uint16 *lockout_threshold)
414 struct winbindd_methods *methods;
415 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
416 struct samr_DomInfo12 lockout_policy;
418 *lockout_threshold = 0;
420 methods = domain->methods;
422 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
423 if (NT_STATUS_IS_ERR(status)) {
424 return status;
427 *lockout_threshold = lockout_policy.lockout_threshold;
429 return NT_STATUS_OK;
432 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
433 TALLOC_CTX *mem_ctx,
434 uint32 *password_properties)
436 struct winbindd_methods *methods;
437 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
438 struct samr_DomInfo1 password_policy;
440 *password_properties = 0;
442 methods = domain->methods;
444 status = methods->password_policy(domain, mem_ctx, &password_policy);
445 if (NT_STATUS_IS_ERR(status)) {
446 return status;
449 *password_properties = password_policy.password_properties;
451 return NT_STATUS_OK;
454 #ifdef HAVE_KRB5
456 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
457 const char *type,
458 uid_t uid,
459 bool *internal_ccache)
461 /* accept FILE and WRFILE as krb5_cc_type from the client and then
462 * build the full ccname string based on the user's uid here -
463 * Guenther*/
465 const char *gen_cc = NULL;
467 *internal_ccache = true;
469 if (uid == -1) {
470 goto memory_ccache;
473 if (!type || type[0] == '\0') {
474 goto memory_ccache;
477 if (strequal(type, "FILE")) {
478 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
479 } else if (strequal(type, "WRFILE")) {
480 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
481 } else {
482 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
483 goto memory_ccache;
486 *internal_ccache = false;
487 goto done;
489 memory_ccache:
490 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
492 done:
493 if (gen_cc == NULL) {
494 DEBUG(0,("out of memory\n"));
495 return NULL;
498 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
500 return gen_cc;
503 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
505 const char *type = state->request->data.auth.krb5_cc_type;
507 state->response->data.auth.krb5ccname[0] = '\0';
509 if (type[0] == '\0') {
510 return;
513 if (!strequal(type, "FILE") &&
514 !strequal(type, "WRFILE")) {
515 DEBUG(10,("won't return krbccname for a %s type ccache\n",
516 type));
517 return;
520 fstrcpy(state->response->data.auth.krb5ccname, cc);
523 #endif
525 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
527 uid_t uid;
529 uid = state->request->data.auth.uid;
531 if (uid < 0) {
532 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
533 return -1;
535 return uid;
538 /**********************************************************************
539 Authenticate a user with a clear text password using Kerberos and fill up
540 ccache if required
541 **********************************************************************/
543 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
544 struct winbindd_cli_state *state,
545 struct netr_SamInfo3 **info3)
547 #ifdef HAVE_KRB5
548 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
549 krb5_error_code krb5_ret;
550 const char *cc = NULL;
551 const char *principal_s = NULL;
552 const char *service = NULL;
553 char *realm = NULL;
554 fstring name_domain, name_user;
555 time_t ticket_lifetime = 0;
556 time_t renewal_until = 0;
557 uid_t uid = -1;
558 ADS_STRUCT *ads;
559 time_t time_offset = 0;
560 bool internal_ccache = true;
562 ZERO_STRUCTP(info3);
564 *info3 = NULL;
566 /* 1st step:
567 * prepare a krb5_cc_cache string for the user */
569 uid = get_uid_from_state(state);
570 if (uid == -1) {
571 DEBUG(0,("no valid uid\n"));
574 cc = generate_krb5_ccache(state->mem_ctx,
575 state->request->data.auth.krb5_cc_type,
576 state->request->data.auth.uid,
577 &internal_ccache);
578 if (cc == NULL) {
579 return NT_STATUS_NO_MEMORY;
583 /* 2nd step:
584 * get kerberos properties */
586 if (domain->private_data) {
587 ads = (ADS_STRUCT *)domain->private_data;
588 time_offset = ads->auth.time_offset;
592 /* 3rd step:
593 * do kerberos auth and setup ccache as the user */
595 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
597 realm = domain->alt_name;
598 strupper_m(realm);
600 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
601 if (principal_s == NULL) {
602 return NT_STATUS_NO_MEMORY;
605 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
606 if (service == NULL) {
607 return NT_STATUS_NO_MEMORY;
610 /* if this is a user ccache, we need to act as the user to let the krb5
611 * library handle the chown, etc. */
613 /************************ ENTERING NON-ROOT **********************/
615 if (!internal_ccache) {
616 set_effective_uid(uid);
617 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
620 result = kerberos_return_info3_from_pac(state->mem_ctx,
621 principal_s,
622 state->request->data.auth.pass,
623 time_offset,
624 &ticket_lifetime,
625 &renewal_until,
627 true,
628 true,
629 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
630 info3);
631 if (!internal_ccache) {
632 gain_root_privilege();
635 /************************ RETURNED TO ROOT **********************/
637 if (!NT_STATUS_IS_OK(result)) {
638 goto failed;
641 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
642 principal_s));
644 /* if we had a user's ccache then return that string for the pam
645 * environment */
647 if (!internal_ccache) {
649 setup_return_cc_name(state, cc);
651 result = add_ccache_to_list(principal_s,
653 service,
654 state->request->data.auth.user,
655 realm,
656 uid,
657 time(NULL),
658 ticket_lifetime,
659 renewal_until,
660 false);
662 if (!NT_STATUS_IS_OK(result)) {
663 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
664 nt_errstr(result)));
666 } else {
668 /* need to delete the memory cred cache, it is not used anymore */
670 krb5_ret = ads_kdestroy(cc);
671 if (krb5_ret) {
672 DEBUG(3,("winbindd_raw_kerberos_login: "
673 "could not destroy krb5 credential cache: "
674 "%s\n", error_message(krb5_ret)));
679 return NT_STATUS_OK;
681 failed:
683 /* we could have created a new credential cache with a valid tgt in it
684 * but we werent able to get or verify the service ticket for this
685 * local host and therefor didn't get the PAC, we need to remove that
686 * cache entirely now */
688 krb5_ret = ads_kdestroy(cc);
689 if (krb5_ret) {
690 DEBUG(3,("winbindd_raw_kerberos_login: "
691 "could not destroy krb5 credential cache: "
692 "%s\n", error_message(krb5_ret)));
695 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
696 DEBUG(3,("winbindd_raw_kerberos_login: "
697 "could not remove ccache for user %s\n",
698 state->request->data.auth.user));
701 return result;
702 #else
703 return NT_STATUS_NOT_SUPPORTED;
704 #endif /* HAVE_KRB5 */
707 /****************************************************************
708 ****************************************************************/
710 bool check_request_flags(uint32_t flags)
712 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
713 WBFLAG_PAM_INFO3_TEXT |
714 WBFLAG_PAM_INFO3_NDR;
716 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
717 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
718 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
719 !(flags & flags_edata) ) {
720 return true;
723 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
724 flags));
726 return false;
729 /****************************************************************
730 ****************************************************************/
732 NTSTATUS append_auth_data(struct winbindd_cli_state *state,
733 struct netr_SamInfo3 *info3,
734 const char *name_domain,
735 const char *name_user)
737 NTSTATUS result;
738 uint32_t flags = state->request->flags;
740 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
741 memcpy(state->response->data.auth.user_session_key,
742 info3->base.key.key,
743 sizeof(state->response->data.auth.user_session_key)
744 /* 16 */);
747 if (flags & WBFLAG_PAM_LMKEY) {
748 memcpy(state->response->data.auth.first_8_lm_hash,
749 info3->base.LMSessKey.key,
750 sizeof(state->response->data.auth.first_8_lm_hash)
751 /* 8 */);
754 if (flags & WBFLAG_PAM_INFO3_TEXT) {
755 result = append_info3_as_txt(state->mem_ctx, state, info3);
756 if (!NT_STATUS_IS_OK(result)) {
757 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
758 nt_errstr(result)));
759 return result;
763 /* currently, anything from here on potentially overwrites extra_data. */
765 if (flags & WBFLAG_PAM_INFO3_NDR) {
766 result = append_info3_as_ndr(state->mem_ctx, state, info3);
767 if (!NT_STATUS_IS_OK(result)) {
768 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
769 nt_errstr(result)));
770 return result;
774 if (flags & WBFLAG_PAM_UNIX_NAME) {
775 result = append_unix_username(state->mem_ctx, state, info3,
776 name_domain, name_user);
777 if (!NT_STATUS_IS_OK(result)) {
778 DEBUG(10,("Failed to append Unix Username: %s\n",
779 nt_errstr(result)));
780 return result;
784 if (flags & WBFLAG_PAM_AFS_TOKEN) {
785 result = append_afs_token(state->mem_ctx, state, info3,
786 name_domain, name_user);
787 if (!NT_STATUS_IS_OK(result)) {
788 DEBUG(10,("Failed to append AFS token: %s\n",
789 nt_errstr(result)));
790 return result;
794 return NT_STATUS_OK;
797 void winbindd_pam_auth(struct winbindd_cli_state *state)
799 struct winbindd_domain *domain;
800 fstring name_domain, name_user;
801 char *mapped_user = NULL;
802 NTSTATUS result;
803 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
805 /* Ensure null termination */
806 state->request->data.auth.user
807 [sizeof(state->request->data.auth.user)-1]='\0';
809 /* Ensure null termination */
810 state->request->data.auth.pass
811 [sizeof(state->request->data.auth.pass)-1]='\0';
813 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
814 state->request->data.auth.user));
816 if (!check_request_flags(state->request->flags)) {
817 result = NT_STATUS_INVALID_PARAMETER_MIX;
818 goto done;
821 /* Parse domain and username */
823 name_map_status = normalize_name_unmap(state->mem_ctx,
824 state->request->data.auth.user,
825 &mapped_user);
827 /* If the name normalization didnt' actually do anything,
828 just use the original name */
830 if (!NT_STATUS_IS_OK(name_map_status) &&
831 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
833 mapped_user = state->request->data.auth.user;
836 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
837 result = NT_STATUS_NO_SUCH_USER;
838 goto done;
841 domain = find_auth_domain(state->request->flags, name_domain);
843 if (domain == NULL) {
844 result = NT_STATUS_NO_SUCH_USER;
845 goto done;
848 sendto_domain(state, domain);
849 return;
850 done:
851 set_auth_errors(state->response, result);
852 DEBUG(5, ("Plain text authentication for %s returned %s "
853 "(PAM: %d)\n",
854 state->request->data.auth.user,
855 state->response->data.auth.nt_status_string,
856 state->response->data.auth.pam_error));
857 request_error(state);
860 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
861 struct winbindd_cli_state *state,
862 struct netr_SamInfo3 **info3)
864 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
865 uint16 max_allowed_bad_attempts;
866 fstring name_domain, name_user;
867 DOM_SID sid;
868 enum lsa_SidType type;
869 uchar new_nt_pass[NT_HASH_LEN];
870 const uint8 *cached_nt_pass;
871 const uint8 *cached_salt;
872 struct netr_SamInfo3 *my_info3;
873 time_t kickoff_time, must_change_time;
874 bool password_good = false;
875 #ifdef HAVE_KRB5
876 struct winbindd_tdc_domain *tdc_domain = NULL;
877 #endif
879 *info3 = NULL;
881 ZERO_STRUCTP(info3);
883 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
885 /* Parse domain and username */
887 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
890 if (!lookup_cached_name(state->mem_ctx,
891 name_domain,
892 name_user,
893 &sid,
894 &type)) {
895 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
896 return NT_STATUS_NO_SUCH_USER;
899 if (type != SID_NAME_USER) {
900 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
901 return NT_STATUS_LOGON_FAILURE;
904 result = winbindd_get_creds(domain,
905 state->mem_ctx,
906 &sid,
907 &my_info3,
908 &cached_nt_pass,
909 &cached_salt);
910 if (!NT_STATUS_IS_OK(result)) {
911 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
912 return result;
915 *info3 = my_info3;
917 E_md4hash(state->request->data.auth.pass, new_nt_pass);
919 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
920 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
921 if (cached_salt) {
922 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
925 if (cached_salt) {
926 /* In this case we didn't store the nt_hash itself,
927 but the MD5 combination of salt + nt_hash. */
928 uchar salted_hash[NT_HASH_LEN];
929 E_md5hash(cached_salt, new_nt_pass, salted_hash);
931 password_good = (memcmp(cached_nt_pass, salted_hash,
932 NT_HASH_LEN) == 0);
933 } else {
934 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
935 password_good = (memcmp(cached_nt_pass, new_nt_pass,
936 NT_HASH_LEN) == 0);
939 if (password_good) {
941 /* User *DOES* know the password, update logon_time and reset
942 * bad_pw_count */
944 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
946 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
947 return NT_STATUS_ACCOUNT_LOCKED_OUT;
950 if (my_info3->base.acct_flags & ACB_DISABLED) {
951 return NT_STATUS_ACCOUNT_DISABLED;
954 if (my_info3->base.acct_flags & ACB_WSTRUST) {
955 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
958 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
959 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
962 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
963 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
966 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
967 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
968 my_info3->base.acct_flags));
969 return NT_STATUS_LOGON_FAILURE;
972 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
973 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
974 return NT_STATUS_ACCOUNT_EXPIRED;
977 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
978 if (must_change_time != 0 && must_change_time < time(NULL)) {
979 /* we allow grace logons when the password has expired */
980 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
981 /* return NT_STATUS_PASSWORD_EXPIRED; */
982 goto success;
985 #ifdef HAVE_KRB5
986 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
987 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
988 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
990 uid_t uid = -1;
991 const char *cc = NULL;
992 char *realm = NULL;
993 const char *principal_s = NULL;
994 const char *service = NULL;
995 bool internal_ccache = false;
997 uid = get_uid_from_state(state);
998 if (uid == -1) {
999 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1000 return NT_STATUS_INVALID_PARAMETER;
1003 cc = generate_krb5_ccache(state->mem_ctx,
1004 state->request->data.auth.krb5_cc_type,
1005 state->request->data.auth.uid,
1006 &internal_ccache);
1007 if (cc == NULL) {
1008 return NT_STATUS_NO_MEMORY;
1011 realm = domain->alt_name;
1012 strupper_m(realm);
1014 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1015 if (principal_s == NULL) {
1016 return NT_STATUS_NO_MEMORY;
1019 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1020 if (service == NULL) {
1021 return NT_STATUS_NO_MEMORY;
1024 if (!internal_ccache) {
1026 setup_return_cc_name(state, cc);
1028 result = add_ccache_to_list(principal_s,
1030 service,
1031 state->request->data.auth.user,
1032 domain->alt_name,
1033 uid,
1034 time(NULL),
1035 time(NULL) + lp_winbind_cache_time(),
1036 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1037 true);
1039 if (!NT_STATUS_IS_OK(result)) {
1040 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1041 "to add ccache to list: %s\n",
1042 nt_errstr(result)));
1046 #endif /* HAVE_KRB5 */
1047 success:
1048 /* FIXME: we possibly should handle logon hours as well (does xp when
1049 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1051 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1052 my_info3->base.bad_password_count = 0;
1054 result = winbindd_update_creds_by_info3(domain,
1055 state->mem_ctx,
1056 state->request->data.auth.user,
1057 state->request->data.auth.pass,
1058 my_info3);
1059 if (!NT_STATUS_IS_OK(result)) {
1060 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1061 nt_errstr(result)));
1062 return result;
1065 return NT_STATUS_OK;
1069 /* User does *NOT* know the correct password, modify info3 accordingly */
1071 /* failure of this is not critical */
1072 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1073 if (!NT_STATUS_IS_OK(result)) {
1074 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1075 "Won't be able to honour account lockout policies\n"));
1078 /* increase counter */
1079 my_info3->base.bad_password_count++;
1081 if (max_allowed_bad_attempts == 0) {
1082 goto failed;
1085 /* lockout user */
1086 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1088 uint32 password_properties;
1090 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1091 if (!NT_STATUS_IS_OK(result)) {
1092 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1095 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1096 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1097 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1101 failed:
1102 result = winbindd_update_creds_by_info3(domain,
1103 state->mem_ctx,
1104 state->request->data.auth.user,
1105 NULL,
1106 my_info3);
1108 if (!NT_STATUS_IS_OK(result)) {
1109 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1110 nt_errstr(result)));
1113 return NT_STATUS_LOGON_FAILURE;
1116 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1117 struct winbindd_cli_state *state,
1118 struct netr_SamInfo3 **info3)
1120 struct winbindd_domain *contact_domain;
1121 fstring name_domain, name_user;
1122 NTSTATUS result;
1124 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1126 /* Parse domain and username */
1128 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1130 /* what domain should we contact? */
1132 if ( IS_DC ) {
1133 if (!(contact_domain = find_domain_from_name(name_domain))) {
1134 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1135 state->request->data.auth.user, name_domain, name_user, name_domain));
1136 result = NT_STATUS_NO_SUCH_USER;
1137 goto done;
1140 } else {
1141 if (is_myname(name_domain)) {
1142 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1143 result = NT_STATUS_NO_SUCH_USER;
1144 goto done;
1147 contact_domain = find_domain_from_name(name_domain);
1148 if (contact_domain == NULL) {
1149 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1150 state->request->data.auth.user, name_domain, name_user, name_domain));
1152 contact_domain = find_our_domain();
1156 if (contact_domain->initialized &&
1157 contact_domain->active_directory) {
1158 goto try_login;
1161 if (!contact_domain->initialized) {
1162 init_dc_connection(contact_domain);
1165 if (!contact_domain->active_directory) {
1166 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1167 return NT_STATUS_INVALID_LOGON_TYPE;
1169 try_login:
1170 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1171 done:
1172 return result;
1175 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1176 TALLOC_CTX *mem_ctx,
1177 uint32 logon_parameters,
1178 const char *server,
1179 const char *username,
1180 const char *domain,
1181 const char *workstation,
1182 const uint8 chal[8],
1183 DATA_BLOB lm_response,
1184 DATA_BLOB nt_response,
1185 struct netr_SamInfo3 **info3);
1187 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1188 struct winbindd_cli_state *state,
1189 struct netr_SamInfo3 **info3)
1192 struct rpc_pipe_client *netlogon_pipe;
1193 uchar chal[8];
1194 DATA_BLOB lm_resp;
1195 DATA_BLOB nt_resp;
1196 int attempts = 0;
1197 unsigned char local_lm_response[24];
1198 unsigned char local_nt_response[24];
1199 struct winbindd_domain *contact_domain;
1200 fstring name_domain, name_user;
1201 bool retry;
1202 NTSTATUS result;
1203 struct netr_SamInfo3 *my_info3 = NULL;
1205 *info3 = NULL;
1207 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1209 /* Parse domain and username */
1211 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1213 /* do password magic */
1216 generate_random_buffer(chal, 8);
1217 if (lp_client_ntlmv2_auth()) {
1218 DATA_BLOB server_chal;
1219 DATA_BLOB names_blob;
1220 DATA_BLOB nt_response;
1221 DATA_BLOB lm_response;
1222 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1224 /* note that the 'workgroup' here is a best guess - we don't know
1225 the server's domain at this point. The 'server name' is also
1226 dodgy...
1228 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1230 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1231 state->request->data.auth.pass,
1232 &server_chal,
1233 &names_blob,
1234 &lm_response, &nt_response, NULL, NULL)) {
1235 data_blob_free(&names_blob);
1236 data_blob_free(&server_chal);
1237 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1238 result = NT_STATUS_NO_MEMORY;
1239 goto done;
1241 data_blob_free(&names_blob);
1242 data_blob_free(&server_chal);
1243 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1244 lm_response.length);
1245 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1246 nt_response.length);
1247 data_blob_free(&lm_response);
1248 data_blob_free(&nt_response);
1250 } else {
1251 if (lp_client_lanman_auth()
1252 && SMBencrypt(state->request->data.auth.pass,
1253 chal,
1254 local_lm_response)) {
1255 lm_resp = data_blob_talloc(state->mem_ctx,
1256 local_lm_response,
1257 sizeof(local_lm_response));
1258 } else {
1259 lm_resp = data_blob_null;
1261 SMBNTencrypt(state->request->data.auth.pass,
1262 chal,
1263 local_nt_response);
1265 nt_resp = data_blob_talloc(state->mem_ctx,
1266 local_nt_response,
1267 sizeof(local_nt_response));
1270 /* what domain should we contact? */
1272 if ( IS_DC ) {
1273 if (!(contact_domain = find_domain_from_name(name_domain))) {
1274 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1275 state->request->data.auth.user, name_domain, name_user, name_domain));
1276 result = NT_STATUS_NO_SUCH_USER;
1277 goto done;
1280 } else {
1281 if (is_myname(name_domain)) {
1282 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1283 result = NT_STATUS_NO_SUCH_USER;
1284 goto done;
1287 contact_domain = find_our_domain();
1290 /* check authentication loop */
1292 do {
1293 netlogon_fn_t logon_fn;
1295 ZERO_STRUCTP(my_info3);
1296 retry = false;
1298 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1300 if (!NT_STATUS_IS_OK(result)) {
1301 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1302 goto done;
1305 /* It is really important to try SamLogonEx here,
1306 * because in a clustered environment, we want to use
1307 * one machine account from multiple physical
1308 * computers.
1310 * With a normal SamLogon call, we must keep the
1311 * credentials chain updated and intact between all
1312 * users of the machine account (which would imply
1313 * cross-node communication for every NTLM logon).
1315 * (The credentials chain is not per NETLOGON pipe
1316 * connection, but globally on the server/client pair
1317 * by machine name).
1319 * When using SamLogonEx, the credentials are not
1320 * supplied, but the session key is implied by the
1321 * wrapping SamLogon context.
1323 * -- abartlet 21 April 2008
1326 logon_fn = contact_domain->can_do_samlogon_ex
1327 ? rpccli_netlogon_sam_network_logon_ex
1328 : rpccli_netlogon_sam_network_logon;
1330 result = logon_fn(netlogon_pipe,
1331 state->mem_ctx,
1333 contact_domain->dcname, /* server name */
1334 name_user, /* user name */
1335 name_domain, /* target domain */
1336 global_myname(), /* workstation */
1337 chal,
1338 lm_resp,
1339 nt_resp,
1340 &my_info3);
1341 attempts += 1;
1343 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1344 && contact_domain->can_do_samlogon_ex) {
1345 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1346 "retrying with NetSamLogon\n"));
1347 contact_domain->can_do_samlogon_ex = false;
1348 retry = true;
1349 continue;
1352 /* We have to try a second time as cm_connect_netlogon
1353 might not yet have noticed that the DC has killed
1354 our connection. */
1356 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1357 retry = true;
1358 continue;
1361 /* if we get access denied, a possible cause was that we had
1362 and open connection to the DC, but someone changed our
1363 machine account password out from underneath us using 'net
1364 rpc changetrustpw' */
1366 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1367 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1368 "ACCESS_DENIED. Maybe the trust account "
1369 "password was changed and we didn't know it. "
1370 "Killing connections to domain %s\n",
1371 name_domain));
1372 invalidate_cm_connection(&contact_domain->conn);
1373 retry = true;
1376 } while ( (attempts < 2) && retry );
1378 /* handle the case where a NT4 DC does not fill in the acct_flags in
1379 * the samlogon reply info3. When accurate info3 is required by the
1380 * caller, we look up the account flags ourselve - gd */
1382 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1383 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1385 struct rpc_pipe_client *samr_pipe;
1386 struct policy_handle samr_domain_handle, user_pol;
1387 union samr_UserInfo *info = NULL;
1388 NTSTATUS status_tmp;
1389 uint32 acct_flags;
1391 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1392 &samr_pipe, &samr_domain_handle);
1394 if (!NT_STATUS_IS_OK(status_tmp)) {
1395 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1396 nt_errstr(status_tmp)));
1397 goto done;
1400 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1401 &samr_domain_handle,
1402 MAXIMUM_ALLOWED_ACCESS,
1403 my_info3->base.rid,
1404 &user_pol);
1406 if (!NT_STATUS_IS_OK(status_tmp)) {
1407 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1408 nt_errstr(status_tmp)));
1409 goto done;
1412 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1413 &user_pol,
1415 &info);
1417 if (!NT_STATUS_IS_OK(status_tmp)) {
1418 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1419 nt_errstr(status_tmp)));
1420 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1421 goto done;
1424 acct_flags = info->info16.acct_flags;
1426 if (acct_flags == 0) {
1427 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1428 goto done;
1431 my_info3->base.acct_flags = acct_flags;
1433 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1435 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1438 *info3 = my_info3;
1439 done:
1440 return result;
1443 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1444 struct winbindd_cli_state *state)
1446 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1447 NTSTATUS krb5_result = NT_STATUS_OK;
1448 fstring name_domain, name_user;
1449 char *mapped_user;
1450 fstring domain_user;
1451 struct netr_SamInfo3 *info3 = NULL;
1452 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1454 /* Ensure null termination */
1455 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1457 /* Ensure null termination */
1458 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1460 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1461 state->request->data.auth.user));
1463 if (!check_request_flags(state->request->flags)) {
1464 result = NT_STATUS_INVALID_PARAMETER_MIX;
1465 goto done;
1468 /* Parse domain and username */
1470 name_map_status = normalize_name_unmap(state->mem_ctx,
1471 state->request->data.auth.user,
1472 &mapped_user);
1474 /* If the name normalization didnt' actually do anything,
1475 just use the original name */
1477 if (!NT_STATUS_IS_OK(name_map_status) &&
1478 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1480 mapped_user = state->request->data.auth.user;
1483 parse_domain_user(mapped_user, name_domain, name_user);
1485 if ( mapped_user != state->request->data.auth.user ) {
1486 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1487 safe_strcpy( state->request->data.auth.user, domain_user,
1488 sizeof(state->request->data.auth.user)-1 );
1491 if (domain->online == false) {
1492 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1493 if (domain->startup) {
1494 /* Logons are very important to users. If we're offline and
1495 we get a request within the first 30 seconds of startup,
1496 try very hard to find a DC and go online. */
1498 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1499 "request in startup mode.\n", domain->name ));
1501 winbindd_flush_negative_conn_cache(domain);
1502 result = init_dc_connection(domain);
1506 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1508 /* Check for Kerberos authentication */
1509 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1511 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1512 /* save for later */
1513 krb5_result = result;
1516 if (NT_STATUS_IS_OK(result)) {
1517 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1518 goto process_result;
1519 } else {
1520 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1523 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1526 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1527 set_domain_offline( domain );
1528 goto cached_logon;
1531 /* there are quite some NT_STATUS errors where there is no
1532 * point in retrying with a samlogon, we explictly have to take
1533 * care not to increase the bad logon counter on the DC */
1535 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1536 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1537 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1538 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1541 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1542 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1543 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1544 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1545 goto process_result;
1548 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1549 DEBUG(3,("falling back to samlogon\n"));
1550 goto sam_logon;
1551 } else {
1552 goto cached_logon;
1556 sam_logon:
1557 /* Check for Samlogon authentication */
1558 if (domain->online) {
1559 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1561 if (NT_STATUS_IS_OK(result)) {
1562 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1563 /* add the Krb5 err if we have one */
1564 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1565 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1567 goto process_result;
1570 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1571 nt_errstr(result)));
1573 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1574 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1575 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1577 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1578 set_domain_offline( domain );
1579 goto cached_logon;
1582 if (domain->online) {
1583 /* We're still online - fail. */
1584 goto done;
1588 cached_logon:
1589 /* Check for Cached logons */
1590 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1591 lp_winbind_offline_logon()) {
1593 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1595 if (NT_STATUS_IS_OK(result)) {
1596 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1597 goto process_result;
1598 } else {
1599 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1600 goto done;
1604 process_result:
1606 if (NT_STATUS_IS_OK(result)) {
1608 DOM_SID user_sid;
1610 /* In all codepaths where result == NT_STATUS_OK info3 must have
1611 been initialized. */
1612 if (!info3) {
1613 result = NT_STATUS_INTERNAL_ERROR;
1614 goto done;
1617 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1618 netsamlogon_cache_store(name_user, info3);
1620 /* save name_to_sid info as early as possible (only if
1621 this is our primary domain so we don't invalidate
1622 the cache entry by storing the seq_num for the wrong
1623 domain). */
1624 if ( domain->primary ) {
1625 sid_compose(&user_sid, info3->base.domain_sid,
1626 info3->base.rid);
1627 cache_name2sid(domain, name_domain, name_user,
1628 SID_NAME_USER, &user_sid);
1631 /* Check if the user is in the right group */
1633 result = check_info3_in_group(
1634 info3,
1635 state->request->data.auth.require_membership_of_sid);
1636 if (!NT_STATUS_IS_OK(result)) {
1637 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1638 state->request->data.auth.user,
1639 state->request->data.auth.require_membership_of_sid));
1640 goto done;
1643 result = append_auth_data(state, info3, name_domain,
1644 name_user);
1645 if (!NT_STATUS_IS_OK(result)) {
1646 goto done;
1649 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1651 /* Store in-memory creds for single-signon using ntlm_auth. */
1652 result = winbindd_add_memory_creds(state->request->data.auth.user,
1653 get_uid_from_state(state),
1654 state->request->data.auth.pass);
1656 if (!NT_STATUS_IS_OK(result)) {
1657 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1658 goto done;
1661 if (lp_winbind_offline_logon()) {
1662 result = winbindd_store_creds(domain,
1663 state->mem_ctx,
1664 state->request->data.auth.user,
1665 state->request->data.auth.pass,
1666 info3, NULL);
1667 if (!NT_STATUS_IS_OK(result)) {
1669 /* Release refcount. */
1670 winbindd_delete_memory_creds(state->request->data.auth.user);
1672 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1673 goto done;
1679 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1680 struct winbindd_domain *our_domain = find_our_domain();
1682 /* This is not entirely correct I believe, but it is
1683 consistent. Only apply the password policy settings
1684 too warn users for our own domain. Cannot obtain these
1685 from trusted DCs all the time so don't do it at all.
1686 -- jerry */
1688 result = NT_STATUS_NOT_SUPPORTED;
1689 if (our_domain == domain ) {
1690 result = fillup_password_policy(our_domain, state);
1693 if (!NT_STATUS_IS_OK(result)
1694 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1696 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1697 domain->name, nt_errstr(result)));
1698 goto done;
1702 result = NT_STATUS_OK;
1705 done:
1706 /* give us a more useful (more correct?) error code */
1707 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1708 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1709 result = NT_STATUS_NO_LOGON_SERVERS;
1712 set_auth_errors(state->response, result);
1714 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1715 state->request->data.auth.user,
1716 state->response->data.auth.nt_status_string,
1717 state->response->data.auth.pam_error));
1719 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1723 /**********************************************************************
1724 Challenge Response Authentication Protocol
1725 **********************************************************************/
1727 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1729 struct winbindd_domain *domain = NULL;
1730 const char *domain_name = NULL;
1731 NTSTATUS result;
1733 if (!check_request_flags(state->request->flags)) {
1734 result = NT_STATUS_INVALID_PARAMETER_MIX;
1735 goto done;
1738 if (!state->privileged) {
1739 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1740 "denied. !\n"));
1741 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1742 "on %s are set correctly.\n",
1743 get_winbind_priv_pipe_dir()));
1744 /* send a better message than ACCESS_DENIED */
1745 fstr_sprintf(state->response->data.auth.error_string,
1746 "winbind client not authorized to use "
1747 "winbindd_pam_auth_crap. Ensure permissions on "
1748 "%s are set correctly.",
1749 get_winbind_priv_pipe_dir());
1750 result = NT_STATUS_ACCESS_DENIED;
1751 goto done;
1754 /* Ensure null termination */
1755 state->request->data.auth_crap.user
1756 [sizeof(state->request->data.auth_crap.user)-1]=0;
1757 state->request->data.auth_crap.domain
1758 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1760 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1761 (unsigned long)state->pid,
1762 state->request->data.auth_crap.domain,
1763 state->request->data.auth_crap.user));
1765 if (*state->request->data.auth_crap.domain != '\0') {
1766 domain_name = state->request->data.auth_crap.domain;
1767 } else if (lp_winbind_use_default_domain()) {
1768 domain_name = lp_workgroup();
1771 if (domain_name != NULL)
1772 domain = find_auth_domain(state->request->flags, domain_name);
1774 if (domain != NULL) {
1775 sendto_domain(state, domain);
1776 return;
1779 result = NT_STATUS_NO_SUCH_USER;
1781 done:
1782 set_auth_errors(state->response, result);
1783 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1784 state->request->data.auth_crap.domain,
1785 state->request->data.auth_crap.user,
1786 state->response->data.auth.nt_status_string,
1787 state->response->data.auth.pam_error));
1788 request_error(state);
1789 return;
1793 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1794 struct winbindd_cli_state *state)
1796 NTSTATUS result;
1797 struct netr_SamInfo3 *info3 = NULL;
1798 struct rpc_pipe_client *netlogon_pipe;
1799 const char *name_user = NULL;
1800 const char *name_domain = NULL;
1801 const char *workstation;
1802 struct winbindd_domain *contact_domain;
1803 int attempts = 0;
1804 bool retry;
1806 DATA_BLOB lm_resp, nt_resp;
1808 /* This is child-only, so no check for privileged access is needed
1809 anymore */
1811 /* Ensure null termination */
1812 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1813 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1815 if (!check_request_flags(state->request->flags)) {
1816 result = NT_STATUS_INVALID_PARAMETER_MIX;
1817 goto done;
1820 name_user = state->request->data.auth_crap.user;
1822 if (*state->request->data.auth_crap.domain) {
1823 name_domain = state->request->data.auth_crap.domain;
1824 } else if (lp_winbind_use_default_domain()) {
1825 name_domain = lp_workgroup();
1826 } else {
1827 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1828 name_user));
1829 result = NT_STATUS_NO_SUCH_USER;
1830 goto done;
1833 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1834 name_domain, name_user));
1836 if (*state->request->data.auth_crap.workstation) {
1837 workstation = state->request->data.auth_crap.workstation;
1838 } else {
1839 workstation = global_myname();
1842 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1843 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1844 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1845 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1846 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1847 state->request->data.auth_crap.lm_resp_len,
1848 state->request->data.auth_crap.nt_resp_len));
1849 result = NT_STATUS_INVALID_PARAMETER;
1850 goto done;
1854 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1855 state->request->data.auth_crap.lm_resp_len);
1857 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1858 nt_resp = data_blob_talloc(state->mem_ctx,
1859 state->request->extra_data.data,
1860 state->request->data.auth_crap.nt_resp_len);
1861 } else {
1862 nt_resp = data_blob_talloc(state->mem_ctx,
1863 state->request->data.auth_crap.nt_resp,
1864 state->request->data.auth_crap.nt_resp_len);
1867 /* what domain should we contact? */
1869 if ( IS_DC ) {
1870 if (!(contact_domain = find_domain_from_name(name_domain))) {
1871 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1872 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1873 result = NT_STATUS_NO_SUCH_USER;
1874 goto done;
1876 } else {
1877 if (is_myname(name_domain)) {
1878 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1879 result = NT_STATUS_NO_SUCH_USER;
1880 goto done;
1882 contact_domain = find_our_domain();
1885 do {
1886 netlogon_fn_t logon_fn;
1888 retry = false;
1890 netlogon_pipe = NULL;
1891 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1893 if (!NT_STATUS_IS_OK(result)) {
1894 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1895 nt_errstr(result)));
1896 goto done;
1899 logon_fn = contact_domain->can_do_samlogon_ex
1900 ? rpccli_netlogon_sam_network_logon_ex
1901 : rpccli_netlogon_sam_network_logon;
1903 result = logon_fn(netlogon_pipe,
1904 state->mem_ctx,
1905 state->request->data.auth_crap.logon_parameters,
1906 contact_domain->dcname,
1907 name_user,
1908 name_domain,
1909 /* Bug #3248 - found by Stefan Burkei. */
1910 workstation, /* We carefully set this above so use it... */
1911 state->request->data.auth_crap.chal,
1912 lm_resp,
1913 nt_resp,
1914 &info3);
1916 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1917 && contact_domain->can_do_samlogon_ex) {
1918 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1919 "retrying with NetSamLogon\n"));
1920 contact_domain->can_do_samlogon_ex = false;
1921 retry = true;
1922 continue;
1925 attempts += 1;
1927 /* We have to try a second time as cm_connect_netlogon
1928 might not yet have noticed that the DC has killed
1929 our connection. */
1931 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1932 retry = true;
1933 continue;
1936 /* if we get access denied, a possible cause was that we had and open
1937 connection to the DC, but someone changed our machine account password
1938 out from underneath us using 'net rpc changetrustpw' */
1940 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1941 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1942 "ACCESS_DENIED. Maybe the trust account "
1943 "password was changed and we didn't know it. "
1944 "Killing connections to domain %s\n",
1945 name_domain));
1946 invalidate_cm_connection(&contact_domain->conn);
1947 retry = true;
1950 } while ( (attempts < 2) && retry );
1952 if (NT_STATUS_IS_OK(result)) {
1954 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1955 netsamlogon_cache_store(name_user, info3);
1957 /* Check if the user is in the right group */
1959 result = check_info3_in_group(
1960 info3,
1961 state->request->data.auth_crap.require_membership_of_sid);
1962 if (!NT_STATUS_IS_OK(result)) {
1963 DEBUG(3, ("User %s is not in the required group (%s), so "
1964 "crap authentication is rejected\n",
1965 state->request->data.auth_crap.user,
1966 state->request->data.auth_crap.require_membership_of_sid));
1967 goto done;
1970 result = append_auth_data(state, info3, name_domain,
1971 name_user);
1972 if (!NT_STATUS_IS_OK(result)) {
1973 goto done;
1977 done:
1979 /* give us a more useful (more correct?) error code */
1980 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1981 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1982 result = NT_STATUS_NO_LOGON_SERVERS;
1985 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1986 result = nt_status_squash(result);
1989 set_auth_errors(state->response, result);
1991 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1992 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1993 name_domain,
1994 name_user,
1995 state->response->data.auth.nt_status_string,
1996 state->response->data.auth.pam_error));
1998 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2001 /* Change a user password */
2003 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2005 fstring domain, user;
2006 char *mapped_user;
2007 struct winbindd_domain *contact_domain;
2008 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2010 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2011 state->request->data.chauthtok.user));
2013 /* Setup crap */
2015 nt_status = normalize_name_unmap(state->mem_ctx,
2016 state->request->data.chauthtok.user,
2017 &mapped_user);
2019 /* Update the chauthtok name if we did any mapping */
2021 if (NT_STATUS_IS_OK(nt_status) ||
2022 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2024 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2027 /* Must pass in state->...chauthtok.user because
2028 canonicalize_username() assumes an fstring(). Since
2029 we have already copied it (if necessary), this is ok. */
2031 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2032 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2033 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2034 "(PAM: %d)\n",
2035 state->request->data.auth.user,
2036 state->response->data.auth.nt_status_string,
2037 state->response->data.auth.pam_error));
2038 request_error(state);
2039 return;
2042 contact_domain = find_domain_from_name(domain);
2043 if (!contact_domain) {
2044 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2045 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2046 state->request->data.chauthtok.user, domain, user, domain));
2047 request_error(state);
2048 return;
2051 sendto_domain(state, contact_domain);
2054 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2055 struct winbindd_cli_state *state)
2057 char *oldpass;
2058 char *newpass = NULL;
2059 struct policy_handle dom_pol;
2060 struct rpc_pipe_client *cli;
2061 bool got_info = false;
2062 struct samr_DomInfo1 *info = NULL;
2063 struct samr_ChangeReject *reject = NULL;
2064 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2065 fstring domain, user;
2067 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2068 state->request->data.auth.user));
2070 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2071 goto done;
2074 /* Change password */
2076 oldpass = state->request->data.chauthtok.oldpass;
2077 newpass = state->request->data.chauthtok.newpass;
2079 /* Initialize reject reason */
2080 state->response->data.auth.reject_reason = Undefined;
2082 /* Get sam handle */
2084 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2085 &dom_pol);
2086 if (!NT_STATUS_IS_OK(result)) {
2087 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2088 goto done;
2091 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2092 user,
2093 newpass,
2094 oldpass,
2095 &info,
2096 &reject);
2098 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2100 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2102 fill_in_password_policy(state->response, info);
2104 state->response->data.auth.reject_reason =
2105 reject->reason;
2107 got_info = true;
2110 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2111 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2112 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2113 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2115 /* only fallback when the chgpasswd_user3 call is not supported */
2116 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2117 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2118 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2119 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2121 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2122 nt_errstr(result)));
2124 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2126 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2127 Map to the same status code as Windows 2003. */
2129 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2130 result = NT_STATUS_PASSWORD_RESTRICTION;
2134 done:
2136 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2138 /* Update the single sign-on memory creds. */
2139 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2140 newpass);
2142 /* When we login from gdm or xdm and password expires,
2143 * we change password, but there are no memory crendentials
2144 * So, winbindd_replace_memory_creds() returns
2145 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2146 * --- BoYang
2147 * */
2148 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2149 result = NT_STATUS_OK;
2152 if (!NT_STATUS_IS_OK(result)) {
2153 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2154 goto process_result;
2157 if (lp_winbind_offline_logon()) {
2158 result = winbindd_update_creds_by_name(contact_domain,
2159 state->mem_ctx, user,
2160 newpass);
2161 /* Again, this happens when we login from gdm or xdm
2162 * and the password expires, *BUT* cached crendentials
2163 * doesn't exist. winbindd_update_creds_by_name()
2164 * returns NT_STATUS_NO_SUCH_USER.
2165 * This is not a failure.
2166 * --- BoYang
2167 * */
2168 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2169 result = NT_STATUS_OK;
2172 if (!NT_STATUS_IS_OK(result)) {
2173 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2174 goto process_result;
2179 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2181 NTSTATUS policy_ret;
2183 policy_ret = fillup_password_policy(contact_domain, state);
2185 /* failure of this is non critical, it will just provide no
2186 * additional information to the client why the change has
2187 * failed - Guenther */
2189 if (!NT_STATUS_IS_OK(policy_ret)) {
2190 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2191 goto process_result;
2195 process_result:
2197 set_auth_errors(state->response, result);
2199 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2200 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2201 domain,
2202 user,
2203 state->response->data.auth.nt_status_string,
2204 state->response->data.auth.pam_error));
2206 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2209 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2211 struct winbindd_domain *domain;
2212 fstring name_domain, user;
2213 uid_t caller_uid = (uid_t)-1;
2214 uid_t request_uid = state->request->data.logoff.uid;
2216 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2217 state->request->data.logoff.user));
2219 /* Ensure null termination */
2220 state->request->data.logoff.user
2221 [sizeof(state->request->data.logoff.user)-1]='\0';
2223 state->request->data.logoff.krb5ccname
2224 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2226 if (request_uid == (gid_t)-1) {
2227 goto failed;
2230 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2231 goto failed;
2234 if ((domain = find_auth_domain(state->request->flags,
2235 name_domain)) == NULL) {
2236 goto failed;
2239 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2240 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2241 strerror(errno)));
2242 goto failed;
2245 switch (caller_uid) {
2246 case -1:
2247 goto failed;
2248 case 0:
2249 /* root must be able to logoff any user - gd */
2250 state->request->data.logoff.uid = request_uid;
2251 break;
2252 default:
2253 if (caller_uid != request_uid) {
2254 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2255 goto failed;
2257 state->request->data.logoff.uid = caller_uid;
2258 break;
2261 sendto_domain(state, domain);
2262 return;
2264 failed:
2265 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2266 DEBUG(5, ("Pam Logoff for %s returned %s "
2267 "(PAM: %d)\n",
2268 state->request->data.logoff.user,
2269 state->response->data.auth.nt_status_string,
2270 state->response->data.auth.pam_error));
2271 request_error(state);
2272 return;
2275 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2276 struct winbindd_cli_state *state)
2278 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2280 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2281 state->request->data.logoff.user));
2283 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2284 result = NT_STATUS_OK;
2285 goto process_result;
2288 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2289 result = NT_STATUS_OK;
2290 goto process_result;
2293 #ifdef HAVE_KRB5
2295 if (state->request->data.logoff.uid < 0) {
2296 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2297 goto process_result;
2300 /* what we need here is to find the corresponding krb5 ccache name *we*
2301 * created for a given username and destroy it */
2303 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2304 result = NT_STATUS_OK;
2305 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2306 goto process_result;
2309 if (!ccache_entry_identical(state->request->data.logoff.user,
2310 state->request->data.logoff.uid,
2311 state->request->data.logoff.krb5ccname)) {
2312 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2313 goto process_result;
2316 result = remove_ccache(state->request->data.logoff.user);
2317 if (!NT_STATUS_IS_OK(result)) {
2318 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2319 nt_errstr(result)));
2320 goto process_result;
2323 #else
2324 result = NT_STATUS_NOT_SUPPORTED;
2325 #endif
2327 process_result:
2329 winbindd_delete_memory_creds(state->request->data.logoff.user);
2331 set_auth_errors(state->response, result);
2333 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2336 /* Change user password with auth crap*/
2338 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2340 struct winbindd_domain *domain = NULL;
2341 const char *domain_name = NULL;
2343 /* Ensure null termination */
2344 state->request->data.chng_pswd_auth_crap.user[
2345 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2346 state->request->data.chng_pswd_auth_crap.domain[
2347 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2349 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2350 (unsigned long)state->pid,
2351 state->request->data.chng_pswd_auth_crap.domain,
2352 state->request->data.chng_pswd_auth_crap.user));
2354 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2355 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2356 } else if (lp_winbind_use_default_domain()) {
2357 domain_name = lp_workgroup();
2360 if (domain_name != NULL)
2361 domain = find_domain_from_name(domain_name);
2363 if (domain != NULL) {
2364 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2365 "%s\n", (unsigned long)state->pid,domain->name));
2366 sendto_domain(state, domain);
2367 return;
2370 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2371 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2372 state->request->data.chng_pswd_auth_crap.domain,
2373 state->request->data.chng_pswd_auth_crap.user,
2374 state->response->data.auth.nt_status_string,
2375 state->response->data.auth.pam_error));
2376 request_error(state);
2377 return;
2380 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2382 NTSTATUS result;
2383 DATA_BLOB new_nt_password;
2384 DATA_BLOB old_nt_hash_enc;
2385 DATA_BLOB new_lm_password;
2386 DATA_BLOB old_lm_hash_enc;
2387 fstring domain,user;
2388 struct policy_handle dom_pol;
2389 struct winbindd_domain *contact_domain = domainSt;
2390 struct rpc_pipe_client *cli;
2392 /* Ensure null termination */
2393 state->request->data.chng_pswd_auth_crap.user[
2394 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2395 state->request->data.chng_pswd_auth_crap.domain[
2396 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2397 *domain = 0;
2398 *user = 0;
2400 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2401 (unsigned long)state->pid,
2402 state->request->data.chng_pswd_auth_crap.domain,
2403 state->request->data.chng_pswd_auth_crap.user));
2405 if (lp_winbind_offline_logon()) {
2406 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2407 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2408 result = NT_STATUS_ACCESS_DENIED;
2409 goto done;
2412 if (*state->request->data.chng_pswd_auth_crap.domain) {
2413 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2414 } else {
2415 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2416 domain, user);
2418 if(!*domain) {
2419 DEBUG(3,("no domain specified with username (%s) - "
2420 "failing auth\n",
2421 state->request->data.chng_pswd_auth_crap.user));
2422 result = NT_STATUS_NO_SUCH_USER;
2423 goto done;
2427 if (!*domain && lp_winbind_use_default_domain()) {
2428 fstrcpy(domain,(char *)lp_workgroup());
2431 if(!*user) {
2432 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2435 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2436 (unsigned long)state->pid, domain, user));
2438 /* Change password */
2439 new_nt_password = data_blob_talloc(
2440 state->mem_ctx,
2441 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2442 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2444 old_nt_hash_enc = data_blob_talloc(
2445 state->mem_ctx,
2446 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2447 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2449 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2450 new_lm_password = data_blob_talloc(
2451 state->mem_ctx,
2452 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2453 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2455 old_lm_hash_enc = data_blob_talloc(
2456 state->mem_ctx,
2457 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2458 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2459 } else {
2460 new_lm_password.length = 0;
2461 old_lm_hash_enc.length = 0;
2464 /* Get sam handle */
2466 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2467 if (!NT_STATUS_IS_OK(result)) {
2468 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2469 goto done;
2472 result = rpccli_samr_chng_pswd_auth_crap(
2473 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2474 new_lm_password, old_lm_hash_enc);
2476 done:
2478 set_auth_errors(state->response, result);
2480 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2481 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2482 domain, user,
2483 state->response->data.auth.nt_status_string,
2484 state->response->data.auth.pam_error));
2486 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;