s4:dsdb Add 'dsdb_flags' to dsdb_module_search() to enable often-used features
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob755f703d634d1ab2d84637b68aa82176653b07fa
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 NULL,
631 info3);
632 if (!internal_ccache) {
633 gain_root_privilege();
636 /************************ RETURNED TO ROOT **********************/
638 if (!NT_STATUS_IS_OK(result)) {
639 goto failed;
642 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
643 principal_s));
645 /* if we had a user's ccache then return that string for the pam
646 * environment */
648 if (!internal_ccache) {
650 setup_return_cc_name(state, cc);
652 result = add_ccache_to_list(principal_s,
654 service,
655 state->request->data.auth.user,
656 realm,
657 uid,
658 time(NULL),
659 ticket_lifetime,
660 renewal_until,
661 false);
663 if (!NT_STATUS_IS_OK(result)) {
664 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
665 nt_errstr(result)));
667 } else {
669 /* need to delete the memory cred cache, it is not used anymore */
671 krb5_ret = ads_kdestroy(cc);
672 if (krb5_ret) {
673 DEBUG(3,("winbindd_raw_kerberos_login: "
674 "could not destroy krb5 credential cache: "
675 "%s\n", error_message(krb5_ret)));
680 return NT_STATUS_OK;
682 failed:
684 /* we could have created a new credential cache with a valid tgt in it
685 * but we werent able to get or verify the service ticket for this
686 * local host and therefor didn't get the PAC, we need to remove that
687 * cache entirely now */
689 krb5_ret = ads_kdestroy(cc);
690 if (krb5_ret) {
691 DEBUG(3,("winbindd_raw_kerberos_login: "
692 "could not destroy krb5 credential cache: "
693 "%s\n", error_message(krb5_ret)));
696 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
697 DEBUG(3,("winbindd_raw_kerberos_login: "
698 "could not remove ccache for user %s\n",
699 state->request->data.auth.user));
702 return result;
703 #else
704 return NT_STATUS_NOT_SUPPORTED;
705 #endif /* HAVE_KRB5 */
708 /****************************************************************
709 ****************************************************************/
711 bool check_request_flags(uint32_t flags)
713 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
714 WBFLAG_PAM_INFO3_TEXT |
715 WBFLAG_PAM_INFO3_NDR;
717 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
718 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
719 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
720 !(flags & flags_edata) ) {
721 return true;
724 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
725 flags));
727 return false;
730 /****************************************************************
731 ****************************************************************/
733 NTSTATUS append_auth_data(struct winbindd_cli_state *state,
734 struct netr_SamInfo3 *info3,
735 const char *name_domain,
736 const char *name_user)
738 NTSTATUS result;
739 uint32_t flags = state->request->flags;
741 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
742 memcpy(state->response->data.auth.user_session_key,
743 info3->base.key.key,
744 sizeof(state->response->data.auth.user_session_key)
745 /* 16 */);
748 if (flags & WBFLAG_PAM_LMKEY) {
749 memcpy(state->response->data.auth.first_8_lm_hash,
750 info3->base.LMSessKey.key,
751 sizeof(state->response->data.auth.first_8_lm_hash)
752 /* 8 */);
755 if (flags & WBFLAG_PAM_INFO3_TEXT) {
756 result = append_info3_as_txt(state->mem_ctx, state, info3);
757 if (!NT_STATUS_IS_OK(result)) {
758 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
759 nt_errstr(result)));
760 return result;
764 /* currently, anything from here on potentially overwrites extra_data. */
766 if (flags & WBFLAG_PAM_INFO3_NDR) {
767 result = append_info3_as_ndr(state->mem_ctx, state, info3);
768 if (!NT_STATUS_IS_OK(result)) {
769 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
770 nt_errstr(result)));
771 return result;
775 if (flags & WBFLAG_PAM_UNIX_NAME) {
776 result = append_unix_username(state->mem_ctx, state, info3,
777 name_domain, name_user);
778 if (!NT_STATUS_IS_OK(result)) {
779 DEBUG(10,("Failed to append Unix Username: %s\n",
780 nt_errstr(result)));
781 return result;
785 if (flags & WBFLAG_PAM_AFS_TOKEN) {
786 result = append_afs_token(state->mem_ctx, state, info3,
787 name_domain, name_user);
788 if (!NT_STATUS_IS_OK(result)) {
789 DEBUG(10,("Failed to append AFS token: %s\n",
790 nt_errstr(result)));
791 return result;
795 return NT_STATUS_OK;
798 void winbindd_pam_auth(struct winbindd_cli_state *state)
800 struct winbindd_domain *domain;
801 fstring name_domain, name_user, mapped_user;
802 char *mapped = NULL;
803 NTSTATUS result;
804 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
806 /* Ensure null termination */
807 state->request->data.auth.user
808 [sizeof(state->request->data.auth.user)-1]='\0';
810 /* Ensure null termination */
811 state->request->data.auth.pass
812 [sizeof(state->request->data.auth.pass)-1]='\0';
814 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
815 state->request->data.auth.user));
817 if (!check_request_flags(state->request->flags)) {
818 result = NT_STATUS_INVALID_PARAMETER_MIX;
819 goto done;
822 /* Parse domain and username */
824 name_map_status = normalize_name_unmap(state->mem_ctx,
825 state->request->data.auth.user,
826 &mapped);
828 /* If the name normalization didnt' actually do anything,
829 just use the original name */
831 if (NT_STATUS_IS_OK(name_map_status)
832 ||NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
833 fstrcpy(mapped_user, mapped);
834 } else {
835 fstrcpy(mapped_user, state->request->data.auth.user);
838 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
839 result = NT_STATUS_NO_SUCH_USER;
840 goto done;
843 domain = find_auth_domain(state->request->flags, name_domain);
845 if (domain == NULL) {
846 result = NT_STATUS_NO_SUCH_USER;
847 goto done;
850 sendto_domain(state, domain);
851 return;
852 done:
853 set_auth_errors(state->response, result);
854 DEBUG(5, ("Plain text authentication for %s returned %s "
855 "(PAM: %d)\n",
856 state->request->data.auth.user,
857 state->response->data.auth.nt_status_string,
858 state->response->data.auth.pam_error));
859 request_error(state);
862 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
863 struct winbindd_cli_state *state,
864 struct netr_SamInfo3 **info3)
866 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
867 uint16 max_allowed_bad_attempts;
868 fstring name_domain, name_user;
869 DOM_SID sid;
870 enum lsa_SidType type;
871 uchar new_nt_pass[NT_HASH_LEN];
872 const uint8 *cached_nt_pass;
873 const uint8 *cached_salt;
874 struct netr_SamInfo3 *my_info3;
875 time_t kickoff_time, must_change_time;
876 bool password_good = false;
877 #ifdef HAVE_KRB5
878 struct winbindd_tdc_domain *tdc_domain = NULL;
879 #endif
881 *info3 = NULL;
883 ZERO_STRUCTP(info3);
885 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
887 /* Parse domain and username */
889 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
892 if (!lookup_cached_name(state->mem_ctx,
893 name_domain,
894 name_user,
895 &sid,
896 &type)) {
897 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
898 return NT_STATUS_NO_SUCH_USER;
901 if (type != SID_NAME_USER) {
902 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
903 return NT_STATUS_LOGON_FAILURE;
906 result = winbindd_get_creds(domain,
907 state->mem_ctx,
908 &sid,
909 &my_info3,
910 &cached_nt_pass,
911 &cached_salt);
912 if (!NT_STATUS_IS_OK(result)) {
913 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
914 return result;
917 *info3 = my_info3;
919 E_md4hash(state->request->data.auth.pass, new_nt_pass);
921 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
922 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
923 if (cached_salt) {
924 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
927 if (cached_salt) {
928 /* In this case we didn't store the nt_hash itself,
929 but the MD5 combination of salt + nt_hash. */
930 uchar salted_hash[NT_HASH_LEN];
931 E_md5hash(cached_salt, new_nt_pass, salted_hash);
933 password_good = (memcmp(cached_nt_pass, salted_hash,
934 NT_HASH_LEN) == 0);
935 } else {
936 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
937 password_good = (memcmp(cached_nt_pass, new_nt_pass,
938 NT_HASH_LEN) == 0);
941 if (password_good) {
943 /* User *DOES* know the password, update logon_time and reset
944 * bad_pw_count */
946 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
948 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
949 return NT_STATUS_ACCOUNT_LOCKED_OUT;
952 if (my_info3->base.acct_flags & ACB_DISABLED) {
953 return NT_STATUS_ACCOUNT_DISABLED;
956 if (my_info3->base.acct_flags & ACB_WSTRUST) {
957 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
960 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
961 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
964 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
965 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
968 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
969 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
970 my_info3->base.acct_flags));
971 return NT_STATUS_LOGON_FAILURE;
974 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
975 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
976 return NT_STATUS_ACCOUNT_EXPIRED;
979 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
980 if (must_change_time != 0 && must_change_time < time(NULL)) {
981 /* we allow grace logons when the password has expired */
982 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
983 /* return NT_STATUS_PASSWORD_EXPIRED; */
984 goto success;
987 #ifdef HAVE_KRB5
988 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
989 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
990 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
991 /* used to cope with the case winbindd starting without network. */
992 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
994 uid_t uid = -1;
995 const char *cc = NULL;
996 char *realm = NULL;
997 const char *principal_s = NULL;
998 const char *service = NULL;
999 bool internal_ccache = false;
1001 uid = get_uid_from_state(state);
1002 if (uid == -1) {
1003 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1004 return NT_STATUS_INVALID_PARAMETER;
1007 cc = generate_krb5_ccache(state->mem_ctx,
1008 state->request->data.auth.krb5_cc_type,
1009 state->request->data.auth.uid,
1010 &internal_ccache);
1011 if (cc == NULL) {
1012 return NT_STATUS_NO_MEMORY;
1015 realm = domain->alt_name;
1016 strupper_m(realm);
1018 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1019 if (principal_s == NULL) {
1020 return NT_STATUS_NO_MEMORY;
1023 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1024 if (service == NULL) {
1025 return NT_STATUS_NO_MEMORY;
1028 if (!internal_ccache) {
1030 setup_return_cc_name(state, cc);
1032 result = add_ccache_to_list(principal_s,
1034 service,
1035 state->request->data.auth.user,
1036 domain->alt_name,
1037 uid,
1038 time(NULL),
1039 time(NULL) + lp_winbind_cache_time(),
1040 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1041 true);
1043 if (!NT_STATUS_IS_OK(result)) {
1044 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1045 "to add ccache to list: %s\n",
1046 nt_errstr(result)));
1050 #endif /* HAVE_KRB5 */
1051 success:
1052 /* FIXME: we possibly should handle logon hours as well (does xp when
1053 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1055 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1056 my_info3->base.bad_password_count = 0;
1058 result = winbindd_update_creds_by_info3(domain,
1059 state->mem_ctx,
1060 state->request->data.auth.user,
1061 state->request->data.auth.pass,
1062 my_info3);
1063 if (!NT_STATUS_IS_OK(result)) {
1064 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1065 nt_errstr(result)));
1066 return result;
1069 return NT_STATUS_OK;
1073 /* User does *NOT* know the correct password, modify info3 accordingly */
1075 /* failure of this is not critical */
1076 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1077 if (!NT_STATUS_IS_OK(result)) {
1078 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1079 "Won't be able to honour account lockout policies\n"));
1082 /* increase counter */
1083 my_info3->base.bad_password_count++;
1085 if (max_allowed_bad_attempts == 0) {
1086 goto failed;
1089 /* lockout user */
1090 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1092 uint32 password_properties;
1094 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1095 if (!NT_STATUS_IS_OK(result)) {
1096 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1099 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1100 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1101 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1105 failed:
1106 result = winbindd_update_creds_by_info3(domain,
1107 state->mem_ctx,
1108 state->request->data.auth.user,
1109 NULL,
1110 my_info3);
1112 if (!NT_STATUS_IS_OK(result)) {
1113 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1114 nt_errstr(result)));
1117 return NT_STATUS_LOGON_FAILURE;
1120 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1121 struct winbindd_cli_state *state,
1122 struct netr_SamInfo3 **info3)
1124 struct winbindd_domain *contact_domain;
1125 fstring name_domain, name_user;
1126 NTSTATUS result;
1128 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1130 /* Parse domain and username */
1132 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1134 /* what domain should we contact? */
1136 if ( IS_DC ) {
1137 if (!(contact_domain = find_domain_from_name(name_domain))) {
1138 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1139 state->request->data.auth.user, name_domain, name_user, name_domain));
1140 result = NT_STATUS_NO_SUCH_USER;
1141 goto done;
1144 } else {
1145 if (is_myname(name_domain)) {
1146 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1147 result = NT_STATUS_NO_SUCH_USER;
1148 goto done;
1151 contact_domain = find_domain_from_name(name_domain);
1152 if (contact_domain == NULL) {
1153 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1154 state->request->data.auth.user, name_domain, name_user, name_domain));
1156 contact_domain = find_our_domain();
1160 if (contact_domain->initialized &&
1161 contact_domain->active_directory) {
1162 goto try_login;
1165 if (!contact_domain->initialized) {
1166 init_dc_connection(contact_domain);
1169 if (!contact_domain->active_directory) {
1170 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1171 return NT_STATUS_INVALID_LOGON_TYPE;
1173 try_login:
1174 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1175 done:
1176 return result;
1179 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1180 TALLOC_CTX *mem_ctx,
1181 uint32 logon_parameters,
1182 const char *server,
1183 const char *username,
1184 const char *domain,
1185 const char *workstation,
1186 const uint8 chal[8],
1187 DATA_BLOB lm_response,
1188 DATA_BLOB nt_response,
1189 struct netr_SamInfo3 **info3);
1191 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1192 struct winbindd_cli_state *state,
1193 struct netr_SamInfo3 **info3)
1196 struct rpc_pipe_client *netlogon_pipe;
1197 uchar chal[8];
1198 DATA_BLOB lm_resp;
1199 DATA_BLOB nt_resp;
1200 int attempts = 0;
1201 unsigned char local_lm_response[24];
1202 unsigned char local_nt_response[24];
1203 struct winbindd_domain *contact_domain;
1204 fstring name_domain, name_user;
1205 bool retry;
1206 NTSTATUS result;
1207 struct netr_SamInfo3 *my_info3 = NULL;
1209 *info3 = NULL;
1211 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1213 /* Parse domain and username */
1215 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1217 /* do password magic */
1220 generate_random_buffer(chal, 8);
1221 if (lp_client_ntlmv2_auth()) {
1222 DATA_BLOB server_chal;
1223 DATA_BLOB names_blob;
1224 DATA_BLOB nt_response;
1225 DATA_BLOB lm_response;
1226 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1228 /* note that the 'workgroup' here is a best guess - we don't know
1229 the server's domain at this point. The 'server name' is also
1230 dodgy...
1232 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1234 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1235 state->request->data.auth.pass,
1236 &server_chal,
1237 &names_blob,
1238 &lm_response, &nt_response, NULL, NULL)) {
1239 data_blob_free(&names_blob);
1240 data_blob_free(&server_chal);
1241 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1242 result = NT_STATUS_NO_MEMORY;
1243 goto done;
1245 data_blob_free(&names_blob);
1246 data_blob_free(&server_chal);
1247 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1248 lm_response.length);
1249 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1250 nt_response.length);
1251 data_blob_free(&lm_response);
1252 data_blob_free(&nt_response);
1254 } else {
1255 if (lp_client_lanman_auth()
1256 && SMBencrypt(state->request->data.auth.pass,
1257 chal,
1258 local_lm_response)) {
1259 lm_resp = data_blob_talloc(state->mem_ctx,
1260 local_lm_response,
1261 sizeof(local_lm_response));
1262 } else {
1263 lm_resp = data_blob_null;
1265 SMBNTencrypt(state->request->data.auth.pass,
1266 chal,
1267 local_nt_response);
1269 nt_resp = data_blob_talloc(state->mem_ctx,
1270 local_nt_response,
1271 sizeof(local_nt_response));
1274 /* what domain should we contact? */
1276 if ( IS_DC ) {
1277 if (!(contact_domain = find_domain_from_name(name_domain))) {
1278 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1279 state->request->data.auth.user, name_domain, name_user, name_domain));
1280 result = NT_STATUS_NO_SUCH_USER;
1281 goto done;
1284 } else {
1285 if (is_myname(name_domain)) {
1286 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1287 result = NT_STATUS_NO_SUCH_USER;
1288 goto done;
1291 contact_domain = find_our_domain();
1294 /* check authentication loop */
1296 do {
1297 netlogon_fn_t logon_fn;
1299 ZERO_STRUCTP(my_info3);
1300 retry = false;
1302 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1304 if (!NT_STATUS_IS_OK(result)) {
1305 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1306 goto done;
1309 /* It is really important to try SamLogonEx here,
1310 * because in a clustered environment, we want to use
1311 * one machine account from multiple physical
1312 * computers.
1314 * With a normal SamLogon call, we must keep the
1315 * credentials chain updated and intact between all
1316 * users of the machine account (which would imply
1317 * cross-node communication for every NTLM logon).
1319 * (The credentials chain is not per NETLOGON pipe
1320 * connection, but globally on the server/client pair
1321 * by machine name).
1323 * When using SamLogonEx, the credentials are not
1324 * supplied, but the session key is implied by the
1325 * wrapping SamLogon context.
1327 * -- abartlet 21 April 2008
1330 logon_fn = contact_domain->can_do_samlogon_ex
1331 ? rpccli_netlogon_sam_network_logon_ex
1332 : rpccli_netlogon_sam_network_logon;
1334 result = logon_fn(netlogon_pipe,
1335 state->mem_ctx,
1337 contact_domain->dcname, /* server name */
1338 name_user, /* user name */
1339 name_domain, /* target domain */
1340 global_myname(), /* workstation */
1341 chal,
1342 lm_resp,
1343 nt_resp,
1344 &my_info3);
1345 attempts += 1;
1347 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1348 && contact_domain->can_do_samlogon_ex) {
1349 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1350 "retrying with NetSamLogon\n"));
1351 contact_domain->can_do_samlogon_ex = false;
1352 retry = true;
1353 continue;
1356 /* We have to try a second time as cm_connect_netlogon
1357 might not yet have noticed that the DC has killed
1358 our connection. */
1360 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1361 retry = true;
1362 continue;
1365 /* if we get access denied, a possible cause was that we had
1366 and open connection to the DC, but someone changed our
1367 machine account password out from underneath us using 'net
1368 rpc changetrustpw' */
1370 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1371 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1372 "ACCESS_DENIED. Maybe the trust account "
1373 "password was changed and we didn't know it. "
1374 "Killing connections to domain %s\n",
1375 name_domain));
1376 invalidate_cm_connection(&contact_domain->conn);
1377 retry = true;
1380 } while ( (attempts < 2) && retry );
1382 /* handle the case where a NT4 DC does not fill in the acct_flags in
1383 * the samlogon reply info3. When accurate info3 is required by the
1384 * caller, we look up the account flags ourselve - gd */
1386 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1387 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1389 struct rpc_pipe_client *samr_pipe;
1390 struct policy_handle samr_domain_handle, user_pol;
1391 union samr_UserInfo *info = NULL;
1392 NTSTATUS status_tmp;
1393 uint32 acct_flags;
1395 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1396 &samr_pipe, &samr_domain_handle);
1398 if (!NT_STATUS_IS_OK(status_tmp)) {
1399 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1400 nt_errstr(status_tmp)));
1401 goto done;
1404 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1405 &samr_domain_handle,
1406 MAXIMUM_ALLOWED_ACCESS,
1407 my_info3->base.rid,
1408 &user_pol);
1410 if (!NT_STATUS_IS_OK(status_tmp)) {
1411 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1412 nt_errstr(status_tmp)));
1413 goto done;
1416 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1417 &user_pol,
1419 &info);
1421 if (!NT_STATUS_IS_OK(status_tmp)) {
1422 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1423 nt_errstr(status_tmp)));
1424 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1425 goto done;
1428 acct_flags = info->info16.acct_flags;
1430 if (acct_flags == 0) {
1431 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1432 goto done;
1435 my_info3->base.acct_flags = acct_flags;
1437 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1439 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1442 *info3 = my_info3;
1443 done:
1444 return result;
1447 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1448 struct winbindd_cli_state *state)
1450 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1451 NTSTATUS krb5_result = NT_STATUS_OK;
1452 fstring name_domain, name_user;
1453 char *mapped_user;
1454 fstring domain_user;
1455 struct netr_SamInfo3 *info3 = NULL;
1456 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1458 /* Ensure null termination */
1459 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1461 /* Ensure null termination */
1462 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1464 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1465 state->request->data.auth.user));
1467 if (!check_request_flags(state->request->flags)) {
1468 result = NT_STATUS_INVALID_PARAMETER_MIX;
1469 goto done;
1472 /* Parse domain and username */
1474 name_map_status = normalize_name_unmap(state->mem_ctx,
1475 state->request->data.auth.user,
1476 &mapped_user);
1478 /* If the name normalization didnt' actually do anything,
1479 just use the original name */
1481 if (!NT_STATUS_IS_OK(name_map_status) &&
1482 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1484 mapped_user = state->request->data.auth.user;
1487 parse_domain_user(mapped_user, name_domain, name_user);
1489 if ( mapped_user != state->request->data.auth.user ) {
1490 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1491 safe_strcpy( state->request->data.auth.user, domain_user,
1492 sizeof(state->request->data.auth.user)-1 );
1495 if (domain->online == false) {
1496 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1497 if (domain->startup) {
1498 /* Logons are very important to users. If we're offline and
1499 we get a request within the first 30 seconds of startup,
1500 try very hard to find a DC and go online. */
1502 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1503 "request in startup mode.\n", domain->name ));
1505 winbindd_flush_negative_conn_cache(domain);
1506 result = init_dc_connection(domain);
1510 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1512 /* Check for Kerberos authentication */
1513 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1515 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1516 /* save for later */
1517 krb5_result = result;
1520 if (NT_STATUS_IS_OK(result)) {
1521 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1522 goto process_result;
1523 } else {
1524 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1527 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1528 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1529 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1530 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1531 set_domain_offline( domain );
1532 goto cached_logon;
1535 /* there are quite some NT_STATUS errors where there is no
1536 * point in retrying with a samlogon, we explictly have to take
1537 * care not to increase the bad logon counter on the DC */
1539 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1541 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1542 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1543 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1544 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1545 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1546 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1547 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1548 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1549 goto process_result;
1552 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1553 DEBUG(3,("falling back to samlogon\n"));
1554 goto sam_logon;
1555 } else {
1556 goto cached_logon;
1560 sam_logon:
1561 /* Check for Samlogon authentication */
1562 if (domain->online) {
1563 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1565 if (NT_STATUS_IS_OK(result)) {
1566 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1567 /* add the Krb5 err if we have one */
1568 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1569 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1571 goto process_result;
1574 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1575 nt_errstr(result)));
1577 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1578 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1579 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1581 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1582 set_domain_offline( domain );
1583 goto cached_logon;
1586 if (domain->online) {
1587 /* We're still online - fail. */
1588 goto done;
1592 cached_logon:
1593 /* Check for Cached logons */
1594 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1595 lp_winbind_offline_logon()) {
1597 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1599 if (NT_STATUS_IS_OK(result)) {
1600 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1601 goto process_result;
1602 } else {
1603 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1604 goto done;
1608 process_result:
1610 if (NT_STATUS_IS_OK(result)) {
1612 DOM_SID user_sid;
1614 /* In all codepaths where result == NT_STATUS_OK info3 must have
1615 been initialized. */
1616 if (!info3) {
1617 result = NT_STATUS_INTERNAL_ERROR;
1618 goto done;
1621 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1622 netsamlogon_cache_store(name_user, info3);
1624 /* save name_to_sid info as early as possible (only if
1625 this is our primary domain so we don't invalidate
1626 the cache entry by storing the seq_num for the wrong
1627 domain). */
1628 if ( domain->primary ) {
1629 sid_compose(&user_sid, info3->base.domain_sid,
1630 info3->base.rid);
1631 cache_name2sid(domain, name_domain, name_user,
1632 SID_NAME_USER, &user_sid);
1635 /* Check if the user is in the right group */
1637 result = check_info3_in_group(
1638 info3,
1639 state->request->data.auth.require_membership_of_sid);
1640 if (!NT_STATUS_IS_OK(result)) {
1641 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1642 state->request->data.auth.user,
1643 state->request->data.auth.require_membership_of_sid));
1644 goto done;
1647 result = append_auth_data(state, info3, name_domain,
1648 name_user);
1649 if (!NT_STATUS_IS_OK(result)) {
1650 goto done;
1653 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1655 /* Store in-memory creds for single-signon using ntlm_auth. */
1656 result = winbindd_add_memory_creds(state->request->data.auth.user,
1657 get_uid_from_state(state),
1658 state->request->data.auth.pass);
1660 if (!NT_STATUS_IS_OK(result)) {
1661 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1662 goto done;
1665 if (lp_winbind_offline_logon()) {
1666 result = winbindd_store_creds(domain,
1667 state->mem_ctx,
1668 state->request->data.auth.user,
1669 state->request->data.auth.pass,
1670 info3, NULL);
1671 if (!NT_STATUS_IS_OK(result)) {
1673 /* Release refcount. */
1674 winbindd_delete_memory_creds(state->request->data.auth.user);
1676 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1677 goto done;
1683 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1684 struct winbindd_domain *our_domain = find_our_domain();
1686 /* This is not entirely correct I believe, but it is
1687 consistent. Only apply the password policy settings
1688 too warn users for our own domain. Cannot obtain these
1689 from trusted DCs all the time so don't do it at all.
1690 -- jerry */
1692 result = NT_STATUS_NOT_SUPPORTED;
1693 if (our_domain == domain ) {
1694 result = fillup_password_policy(our_domain, state);
1697 if (!NT_STATUS_IS_OK(result)
1698 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1700 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1701 domain->name, nt_errstr(result)));
1702 goto done;
1706 result = NT_STATUS_OK;
1709 done:
1710 /* give us a more useful (more correct?) error code */
1711 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1712 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1713 result = NT_STATUS_NO_LOGON_SERVERS;
1716 set_auth_errors(state->response, result);
1718 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1719 state->request->data.auth.user,
1720 state->response->data.auth.nt_status_string,
1721 state->response->data.auth.pam_error));
1723 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1727 /**********************************************************************
1728 Challenge Response Authentication Protocol
1729 **********************************************************************/
1731 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1733 struct winbindd_domain *domain = NULL;
1734 const char *domain_name = NULL;
1735 NTSTATUS result;
1737 if (!check_request_flags(state->request->flags)) {
1738 result = NT_STATUS_INVALID_PARAMETER_MIX;
1739 goto done;
1742 if (!state->privileged) {
1743 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1744 "denied. !\n"));
1745 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1746 "on %s are set correctly.\n",
1747 get_winbind_priv_pipe_dir()));
1748 /* send a better message than ACCESS_DENIED */
1749 fstr_sprintf(state->response->data.auth.error_string,
1750 "winbind client not authorized to use "
1751 "winbindd_pam_auth_crap. Ensure permissions on "
1752 "%s are set correctly.",
1753 get_winbind_priv_pipe_dir());
1754 result = NT_STATUS_ACCESS_DENIED;
1755 goto done;
1758 /* Ensure null termination */
1759 state->request->data.auth_crap.user
1760 [sizeof(state->request->data.auth_crap.user)-1]=0;
1761 state->request->data.auth_crap.domain
1762 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1764 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1765 (unsigned long)state->pid,
1766 state->request->data.auth_crap.domain,
1767 state->request->data.auth_crap.user));
1769 if (*state->request->data.auth_crap.domain != '\0') {
1770 domain_name = state->request->data.auth_crap.domain;
1771 } else if (lp_winbind_use_default_domain()) {
1772 domain_name = lp_workgroup();
1775 if (domain_name != NULL)
1776 domain = find_auth_domain(state->request->flags, domain_name);
1778 if (domain != NULL) {
1779 sendto_domain(state, domain);
1780 return;
1783 result = NT_STATUS_NO_SUCH_USER;
1785 done:
1786 set_auth_errors(state->response, result);
1787 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1788 state->request->data.auth_crap.domain,
1789 state->request->data.auth_crap.user,
1790 state->response->data.auth.nt_status_string,
1791 state->response->data.auth.pam_error));
1792 request_error(state);
1793 return;
1797 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1798 struct winbindd_cli_state *state)
1800 NTSTATUS result;
1801 struct netr_SamInfo3 *info3 = NULL;
1802 struct rpc_pipe_client *netlogon_pipe;
1803 const char *name_user = NULL;
1804 const char *name_domain = NULL;
1805 const char *workstation;
1806 struct winbindd_domain *contact_domain;
1807 int attempts = 0;
1808 bool retry;
1810 DATA_BLOB lm_resp, nt_resp;
1812 /* This is child-only, so no check for privileged access is needed
1813 anymore */
1815 /* Ensure null termination */
1816 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1817 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1819 if (!check_request_flags(state->request->flags)) {
1820 result = NT_STATUS_INVALID_PARAMETER_MIX;
1821 goto done;
1824 name_user = state->request->data.auth_crap.user;
1826 if (*state->request->data.auth_crap.domain) {
1827 name_domain = state->request->data.auth_crap.domain;
1828 } else if (lp_winbind_use_default_domain()) {
1829 name_domain = lp_workgroup();
1830 } else {
1831 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1832 name_user));
1833 result = NT_STATUS_NO_SUCH_USER;
1834 goto done;
1837 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1838 name_domain, name_user));
1840 if (*state->request->data.auth_crap.workstation) {
1841 workstation = state->request->data.auth_crap.workstation;
1842 } else {
1843 workstation = global_myname();
1846 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1847 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1848 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1849 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1850 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1851 state->request->data.auth_crap.lm_resp_len,
1852 state->request->data.auth_crap.nt_resp_len));
1853 result = NT_STATUS_INVALID_PARAMETER;
1854 goto done;
1858 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1859 state->request->data.auth_crap.lm_resp_len);
1861 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1862 nt_resp = data_blob_talloc(state->mem_ctx,
1863 state->request->extra_data.data,
1864 state->request->data.auth_crap.nt_resp_len);
1865 } else {
1866 nt_resp = data_blob_talloc(state->mem_ctx,
1867 state->request->data.auth_crap.nt_resp,
1868 state->request->data.auth_crap.nt_resp_len);
1871 /* what domain should we contact? */
1873 if ( IS_DC ) {
1874 if (!(contact_domain = find_domain_from_name(name_domain))) {
1875 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1876 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1877 result = NT_STATUS_NO_SUCH_USER;
1878 goto done;
1880 } else {
1881 if (is_myname(name_domain)) {
1882 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1883 result = NT_STATUS_NO_SUCH_USER;
1884 goto done;
1886 contact_domain = find_our_domain();
1889 do {
1890 netlogon_fn_t logon_fn;
1892 retry = false;
1894 netlogon_pipe = NULL;
1895 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1897 if (!NT_STATUS_IS_OK(result)) {
1898 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1899 nt_errstr(result)));
1900 goto done;
1903 logon_fn = contact_domain->can_do_samlogon_ex
1904 ? rpccli_netlogon_sam_network_logon_ex
1905 : rpccli_netlogon_sam_network_logon;
1907 result = logon_fn(netlogon_pipe,
1908 state->mem_ctx,
1909 state->request->data.auth_crap.logon_parameters,
1910 contact_domain->dcname,
1911 name_user,
1912 name_domain,
1913 /* Bug #3248 - found by Stefan Burkei. */
1914 workstation, /* We carefully set this above so use it... */
1915 state->request->data.auth_crap.chal,
1916 lm_resp,
1917 nt_resp,
1918 &info3);
1920 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1921 && contact_domain->can_do_samlogon_ex) {
1922 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1923 "retrying with NetSamLogon\n"));
1924 contact_domain->can_do_samlogon_ex = false;
1925 retry = true;
1926 continue;
1929 attempts += 1;
1931 /* We have to try a second time as cm_connect_netlogon
1932 might not yet have noticed that the DC has killed
1933 our connection. */
1935 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1936 retry = true;
1937 continue;
1940 /* if we get access denied, a possible cause was that we had and open
1941 connection to the DC, but someone changed our machine account password
1942 out from underneath us using 'net rpc changetrustpw' */
1944 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1945 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1946 "ACCESS_DENIED. Maybe the trust account "
1947 "password was changed and we didn't know it. "
1948 "Killing connections to domain %s\n",
1949 name_domain));
1950 invalidate_cm_connection(&contact_domain->conn);
1951 retry = true;
1954 } while ( (attempts < 2) && retry );
1956 if (NT_STATUS_IS_OK(result)) {
1958 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1959 netsamlogon_cache_store(name_user, info3);
1961 /* Check if the user is in the right group */
1963 result = check_info3_in_group(
1964 info3,
1965 state->request->data.auth_crap.require_membership_of_sid);
1966 if (!NT_STATUS_IS_OK(result)) {
1967 DEBUG(3, ("User %s is not in the required group (%s), so "
1968 "crap authentication is rejected\n",
1969 state->request->data.auth_crap.user,
1970 state->request->data.auth_crap.require_membership_of_sid));
1971 goto done;
1974 result = append_auth_data(state, info3, name_domain,
1975 name_user);
1976 if (!NT_STATUS_IS_OK(result)) {
1977 goto done;
1981 done:
1983 /* give us a more useful (more correct?) error code */
1984 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1985 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1986 result = NT_STATUS_NO_LOGON_SERVERS;
1989 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1990 result = nt_status_squash(result);
1993 set_auth_errors(state->response, result);
1995 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1996 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1997 name_domain,
1998 name_user,
1999 state->response->data.auth.nt_status_string,
2000 state->response->data.auth.pam_error));
2002 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2005 /* Change a user password */
2007 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2009 fstring domain, user;
2010 char *mapped_user;
2011 struct winbindd_domain *contact_domain;
2012 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2014 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2015 state->request->data.chauthtok.user));
2017 /* Setup crap */
2019 nt_status = normalize_name_unmap(state->mem_ctx,
2020 state->request->data.chauthtok.user,
2021 &mapped_user);
2023 /* Update the chauthtok name if we did any mapping */
2025 if (NT_STATUS_IS_OK(nt_status) ||
2026 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2028 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2031 /* Must pass in state->...chauthtok.user because
2032 canonicalize_username() assumes an fstring(). Since
2033 we have already copied it (if necessary), this is ok. */
2035 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2036 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2037 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2038 "(PAM: %d)\n",
2039 state->request->data.auth.user,
2040 state->response->data.auth.nt_status_string,
2041 state->response->data.auth.pam_error));
2042 request_error(state);
2043 return;
2046 contact_domain = find_domain_from_name(domain);
2047 if (!contact_domain) {
2048 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2049 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2050 state->request->data.chauthtok.user, domain, user, domain));
2051 request_error(state);
2052 return;
2055 sendto_domain(state, contact_domain);
2058 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2059 struct winbindd_cli_state *state)
2061 char *oldpass;
2062 char *newpass = NULL;
2063 struct policy_handle dom_pol;
2064 struct rpc_pipe_client *cli;
2065 bool got_info = false;
2066 struct samr_DomInfo1 *info = NULL;
2067 struct userPwdChangeFailureInformation *reject = NULL;
2068 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2069 fstring domain, user;
2071 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2072 state->request->data.auth.user));
2074 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2075 goto done;
2078 /* Change password */
2080 oldpass = state->request->data.chauthtok.oldpass;
2081 newpass = state->request->data.chauthtok.newpass;
2083 /* Initialize reject reason */
2084 state->response->data.auth.reject_reason = Undefined;
2086 /* Get sam handle */
2088 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2089 &dom_pol);
2090 if (!NT_STATUS_IS_OK(result)) {
2091 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2092 goto done;
2095 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2096 user,
2097 newpass,
2098 oldpass,
2099 &info,
2100 &reject);
2102 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2104 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2106 fill_in_password_policy(state->response, info);
2108 state->response->data.auth.reject_reason =
2109 reject->extendedFailureReason;
2111 got_info = true;
2114 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2115 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2116 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2117 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2119 /* only fallback when the chgpasswd_user3 call is not supported */
2120 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2121 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2122 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2123 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2125 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2126 nt_errstr(result)));
2128 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2130 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2131 Map to the same status code as Windows 2003. */
2133 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2134 result = NT_STATUS_PASSWORD_RESTRICTION;
2138 done:
2140 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2142 /* Update the single sign-on memory creds. */
2143 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2144 newpass);
2146 /* When we login from gdm or xdm and password expires,
2147 * we change password, but there are no memory crendentials
2148 * So, winbindd_replace_memory_creds() returns
2149 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2150 * --- BoYang
2151 * */
2152 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2153 result = NT_STATUS_OK;
2156 if (!NT_STATUS_IS_OK(result)) {
2157 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2158 goto process_result;
2161 if (lp_winbind_offline_logon()) {
2162 result = winbindd_update_creds_by_name(contact_domain,
2163 state->mem_ctx, user,
2164 newpass);
2165 /* Again, this happens when we login from gdm or xdm
2166 * and the password expires, *BUT* cached crendentials
2167 * doesn't exist. winbindd_update_creds_by_name()
2168 * returns NT_STATUS_NO_SUCH_USER.
2169 * This is not a failure.
2170 * --- BoYang
2171 * */
2172 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2173 result = NT_STATUS_OK;
2176 if (!NT_STATUS_IS_OK(result)) {
2177 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2178 goto process_result;
2183 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2185 NTSTATUS policy_ret;
2187 policy_ret = fillup_password_policy(contact_domain, state);
2189 /* failure of this is non critical, it will just provide no
2190 * additional information to the client why the change has
2191 * failed - Guenther */
2193 if (!NT_STATUS_IS_OK(policy_ret)) {
2194 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2195 goto process_result;
2199 process_result:
2201 set_auth_errors(state->response, result);
2203 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2204 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2205 domain,
2206 user,
2207 state->response->data.auth.nt_status_string,
2208 state->response->data.auth.pam_error));
2210 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2213 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2215 struct winbindd_domain *domain;
2216 fstring name_domain, user;
2217 uid_t caller_uid = (uid_t)-1;
2218 uid_t request_uid = state->request->data.logoff.uid;
2220 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2221 state->request->data.logoff.user));
2223 /* Ensure null termination */
2224 state->request->data.logoff.user
2225 [sizeof(state->request->data.logoff.user)-1]='\0';
2227 state->request->data.logoff.krb5ccname
2228 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2230 if (request_uid == (gid_t)-1) {
2231 goto failed;
2234 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2235 goto failed;
2238 if ((domain = find_auth_domain(state->request->flags,
2239 name_domain)) == NULL) {
2240 goto failed;
2243 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2244 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2245 strerror(errno)));
2246 goto failed;
2249 switch (caller_uid) {
2250 case -1:
2251 goto failed;
2252 case 0:
2253 /* root must be able to logoff any user - gd */
2254 state->request->data.logoff.uid = request_uid;
2255 break;
2256 default:
2257 if (caller_uid != request_uid) {
2258 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2259 goto failed;
2261 state->request->data.logoff.uid = caller_uid;
2262 break;
2265 sendto_domain(state, domain);
2266 return;
2268 failed:
2269 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2270 DEBUG(5, ("Pam Logoff for %s returned %s "
2271 "(PAM: %d)\n",
2272 state->request->data.logoff.user,
2273 state->response->data.auth.nt_status_string,
2274 state->response->data.auth.pam_error));
2275 request_error(state);
2276 return;
2279 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2280 struct winbindd_cli_state *state)
2282 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2284 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2285 state->request->data.logoff.user));
2287 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2288 result = NT_STATUS_OK;
2289 goto process_result;
2292 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2293 result = NT_STATUS_OK;
2294 goto process_result;
2297 #ifdef HAVE_KRB5
2299 if (state->request->data.logoff.uid < 0) {
2300 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2301 goto process_result;
2304 /* what we need here is to find the corresponding krb5 ccache name *we*
2305 * created for a given username and destroy it */
2307 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2308 result = NT_STATUS_OK;
2309 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2310 goto process_result;
2313 if (!ccache_entry_identical(state->request->data.logoff.user,
2314 state->request->data.logoff.uid,
2315 state->request->data.logoff.krb5ccname)) {
2316 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2317 goto process_result;
2320 result = remove_ccache(state->request->data.logoff.user);
2321 if (!NT_STATUS_IS_OK(result)) {
2322 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2323 nt_errstr(result)));
2324 goto process_result;
2327 #else
2328 result = NT_STATUS_NOT_SUPPORTED;
2329 #endif
2331 process_result:
2333 winbindd_delete_memory_creds(state->request->data.logoff.user);
2335 set_auth_errors(state->response, result);
2337 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2340 /* Change user password with auth crap*/
2342 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2344 struct winbindd_domain *domain = NULL;
2345 const char *domain_name = NULL;
2347 /* Ensure null termination */
2348 state->request->data.chng_pswd_auth_crap.user[
2349 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2350 state->request->data.chng_pswd_auth_crap.domain[
2351 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2353 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2354 (unsigned long)state->pid,
2355 state->request->data.chng_pswd_auth_crap.domain,
2356 state->request->data.chng_pswd_auth_crap.user));
2358 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2359 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2360 } else if (lp_winbind_use_default_domain()) {
2361 domain_name = lp_workgroup();
2364 if (domain_name != NULL)
2365 domain = find_domain_from_name(domain_name);
2367 if (domain != NULL) {
2368 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2369 "%s\n", (unsigned long)state->pid,domain->name));
2370 sendto_domain(state, domain);
2371 return;
2374 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2375 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2376 state->request->data.chng_pswd_auth_crap.domain,
2377 state->request->data.chng_pswd_auth_crap.user,
2378 state->response->data.auth.nt_status_string,
2379 state->response->data.auth.pam_error));
2380 request_error(state);
2381 return;
2384 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2386 NTSTATUS result;
2387 DATA_BLOB new_nt_password;
2388 DATA_BLOB old_nt_hash_enc;
2389 DATA_BLOB new_lm_password;
2390 DATA_BLOB old_lm_hash_enc;
2391 fstring domain,user;
2392 struct policy_handle dom_pol;
2393 struct winbindd_domain *contact_domain = domainSt;
2394 struct rpc_pipe_client *cli;
2396 /* Ensure null termination */
2397 state->request->data.chng_pswd_auth_crap.user[
2398 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2399 state->request->data.chng_pswd_auth_crap.domain[
2400 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2401 *domain = 0;
2402 *user = 0;
2404 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2405 (unsigned long)state->pid,
2406 state->request->data.chng_pswd_auth_crap.domain,
2407 state->request->data.chng_pswd_auth_crap.user));
2409 if (lp_winbind_offline_logon()) {
2410 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2411 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2412 result = NT_STATUS_ACCESS_DENIED;
2413 goto done;
2416 if (*state->request->data.chng_pswd_auth_crap.domain) {
2417 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2418 } else {
2419 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2420 domain, user);
2422 if(!*domain) {
2423 DEBUG(3,("no domain specified with username (%s) - "
2424 "failing auth\n",
2425 state->request->data.chng_pswd_auth_crap.user));
2426 result = NT_STATUS_NO_SUCH_USER;
2427 goto done;
2431 if (!*domain && lp_winbind_use_default_domain()) {
2432 fstrcpy(domain,(char *)lp_workgroup());
2435 if(!*user) {
2436 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2439 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2440 (unsigned long)state->pid, domain, user));
2442 /* Change password */
2443 new_nt_password = data_blob_talloc(
2444 state->mem_ctx,
2445 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2446 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2448 old_nt_hash_enc = data_blob_talloc(
2449 state->mem_ctx,
2450 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2451 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2453 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2454 new_lm_password = data_blob_talloc(
2455 state->mem_ctx,
2456 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2457 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2459 old_lm_hash_enc = data_blob_talloc(
2460 state->mem_ctx,
2461 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2462 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2463 } else {
2464 new_lm_password.length = 0;
2465 old_lm_hash_enc.length = 0;
2468 /* Get sam handle */
2470 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2471 if (!NT_STATUS_IS_OK(result)) {
2472 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2473 goto done;
2476 result = rpccli_samr_chng_pswd_auth_crap(
2477 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2478 new_lm_password, old_lm_hash_enc);
2480 done:
2482 set_auth_errors(state->response, result);
2484 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2485 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2486 domain, user,
2487 state->response->data.auth.nt_status_string,
2488 state->response->data.auth.pam_error));
2490 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;