s3-docs: Improve "winbind nss info" section in man smb.conf.
[Samba/gbeck.git] / source3 / winbindd / winbindd_pam.c
blob796bc3eaed7930570e55e89a77baf4ef410165cb
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/cli_samr.h"
29 #include "smb_krb5.h"
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
34 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
36 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
37 struct winbindd_cli_state *state,
38 struct netr_SamInfo3 *info3)
40 char *ex;
41 uint32_t i;
43 state->response->data.auth.info3.logon_time =
44 nt_time_to_unix(info3->base.last_logon);
45 state->response->data.auth.info3.logoff_time =
46 nt_time_to_unix(info3->base.last_logoff);
47 state->response->data.auth.info3.kickoff_time =
48 nt_time_to_unix(info3->base.acct_expiry);
49 state->response->data.auth.info3.pass_last_set_time =
50 nt_time_to_unix(info3->base.last_password_change);
51 state->response->data.auth.info3.pass_can_change_time =
52 nt_time_to_unix(info3->base.allow_password_change);
53 state->response->data.auth.info3.pass_must_change_time =
54 nt_time_to_unix(info3->base.force_password_change);
56 state->response->data.auth.info3.logon_count = info3->base.logon_count;
57 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
59 state->response->data.auth.info3.user_rid = info3->base.rid;
60 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
61 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
63 state->response->data.auth.info3.num_groups = info3->base.groups.count;
64 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
66 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
67 state->response->data.auth.info3.num_other_sids = info3->sidcount;
69 fstrcpy(state->response->data.auth.info3.user_name,
70 info3->base.account_name.string);
71 fstrcpy(state->response->data.auth.info3.full_name,
72 info3->base.full_name.string);
73 fstrcpy(state->response->data.auth.info3.logon_script,
74 info3->base.logon_script.string);
75 fstrcpy(state->response->data.auth.info3.profile_path,
76 info3->base.profile_path.string);
77 fstrcpy(state->response->data.auth.info3.home_dir,
78 info3->base.home_directory.string);
79 fstrcpy(state->response->data.auth.info3.dir_drive,
80 info3->base.home_drive.string);
82 fstrcpy(state->response->data.auth.info3.logon_srv,
83 info3->base.logon_server.string);
84 fstrcpy(state->response->data.auth.info3.logon_dom,
85 info3->base.domain.string);
87 ex = talloc_strdup(state->mem_ctx, "");
88 NT_STATUS_HAVE_NO_MEMORY(ex);
90 for (i=0; i < info3->base.groups.count; i++) {
91 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
92 info3->base.groups.rids[i].rid,
93 info3->base.groups.rids[i].attributes);
94 NT_STATUS_HAVE_NO_MEMORY(ex);
97 for (i=0; i < info3->sidcount; i++) {
98 char *sid;
100 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
101 NT_STATUS_HAVE_NO_MEMORY(sid);
103 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
104 sid,
105 info3->sids[i].attributes);
106 NT_STATUS_HAVE_NO_MEMORY(ex);
108 talloc_free(sid);
111 state->response->extra_data.data = ex;
112 state->response->length += talloc_get_size(ex);
114 return NT_STATUS_OK;
117 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
118 struct winbindd_cli_state *state,
119 struct netr_SamInfo3 *info3)
121 DATA_BLOB blob;
122 enum ndr_err_code ndr_err;
124 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
125 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
126 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
127 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
128 return ndr_map_error2ntstatus(ndr_err);
131 state->response->extra_data.data = blob.data;
132 state->response->length += blob.length;
134 return NT_STATUS_OK;
137 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
138 struct winbindd_cli_state *state,
139 const struct netr_SamInfo3 *info3,
140 const char *name_domain,
141 const char *name_user)
143 /* We've been asked to return the unix username, per
144 'winbind use default domain' settings and the like */
146 const char *nt_username, *nt_domain;
148 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
149 if (!nt_domain) {
150 /* If the server didn't give us one, just use the one
151 * we sent them */
152 nt_domain = name_domain;
155 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
156 if (!nt_username) {
157 /* If the server didn't give us one, just use the one
158 * we sent them */
159 nt_username = name_user;
162 fill_domain_username(state->response->data.auth.unix_username,
163 nt_domain, nt_username, true);
165 DEBUG(5,("Setting unix username to [%s]\n",
166 state->response->data.auth.unix_username));
168 return NT_STATUS_OK;
171 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
172 struct winbindd_cli_state *state,
173 const struct netr_SamInfo3 *info3,
174 const char *name_domain,
175 const char *name_user)
177 char *afsname = NULL;
178 char *cell;
179 char *token;
181 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
182 if (afsname == NULL) {
183 return NT_STATUS_NO_MEMORY;
186 afsname = talloc_string_sub(mem_ctx,
187 lp_afs_username_map(),
188 "%D", name_domain);
189 afsname = talloc_string_sub(mem_ctx, afsname,
190 "%u", name_user);
191 afsname = talloc_string_sub(mem_ctx, afsname,
192 "%U", name_user);
195 DOM_SID user_sid;
196 fstring sidstr;
198 sid_compose(&user_sid, info3->base.domain_sid,
199 info3->base.rid);
200 sid_to_fstring(sidstr, &user_sid);
201 afsname = talloc_string_sub(mem_ctx, afsname,
202 "%s", sidstr);
205 if (afsname == NULL) {
206 return NT_STATUS_NO_MEMORY;
209 strlower_m(afsname);
211 DEBUG(10, ("Generating token for user %s\n", afsname));
213 cell = strchr(afsname, '@');
215 if (cell == NULL) {
216 return NT_STATUS_NO_MEMORY;
219 *cell = '\0';
220 cell += 1;
222 token = afs_createtoken_str(afsname, cell);
223 if (token == NULL) {
224 return NT_STATUS_OK;
226 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
227 token);
228 if (state->response->extra_data.data == NULL) {
229 return NT_STATUS_NO_MEMORY;
231 state->response->length +=
232 strlen((const char *)state->response->extra_data.data)+1;
234 return NT_STATUS_OK;
237 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
238 const char *group_sid)
240 * Check whether a user belongs to a group or list of groups.
242 * @param mem_ctx talloc memory context.
243 * @param info3 user information, including group membership info.
244 * @param group_sid One or more groups , separated by commas.
246 * @return NT_STATUS_OK on success,
247 * NT_STATUS_LOGON_FAILURE if the user does not belong,
248 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
251 DOM_SID *require_membership_of_sid;
252 size_t num_require_membership_of_sid;
253 char *req_sid;
254 const char *p;
255 DOM_SID sid;
256 size_t i;
257 struct nt_user_token *token;
258 TALLOC_CTX *frame = talloc_stackframe();
259 NTSTATUS status;
261 /* Parse the 'required group' SID */
263 if (!group_sid || !group_sid[0]) {
264 /* NO sid supplied, all users may access */
265 return NT_STATUS_OK;
268 token = talloc_zero(talloc_tos(), struct nt_user_token);
269 if (token == NULL) {
270 DEBUG(0, ("talloc failed\n"));
271 TALLOC_FREE(frame);
272 return NT_STATUS_NO_MEMORY;
275 num_require_membership_of_sid = 0;
276 require_membership_of_sid = NULL;
278 p = group_sid;
280 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
281 if (!string_to_sid(&sid, req_sid)) {
282 DEBUG(0, ("check_info3_in_group: could not parse %s "
283 "as a SID!", req_sid));
284 TALLOC_FREE(frame);
285 return NT_STATUS_INVALID_PARAMETER;
288 status = add_sid_to_array(talloc_tos(), &sid,
289 &require_membership_of_sid,
290 &num_require_membership_of_sid);
291 if (!NT_STATUS_IS_OK(status)) {
292 DEBUG(0, ("add_sid_to_array failed\n"));
293 TALLOC_FREE(frame);
294 return status;
298 status = sid_array_from_info3(talloc_tos(), info3,
299 &token->user_sids,
300 &token->num_sids,
301 true, false);
302 if (!NT_STATUS_IS_OK(status)) {
303 TALLOC_FREE(frame);
304 return status;
307 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
308 token))
309 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
310 token))) {
311 DEBUG(3, ("could not add aliases: %s\n",
312 nt_errstr(status)));
313 TALLOC_FREE(frame);
314 return status;
317 debug_nt_user_token(DBGC_CLASS, 10, token);
319 for (i=0; i<num_require_membership_of_sid; i++) {
320 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
321 &require_membership_of_sid[i])));
322 if (nt_token_check_sid(&require_membership_of_sid[i],
323 token)) {
324 DEBUG(10, ("Access ok\n"));
325 TALLOC_FREE(frame);
326 return NT_STATUS_OK;
330 /* Do not distinguish this error from a wrong username/pw */
332 TALLOC_FREE(frame);
333 return NT_STATUS_LOGON_FAILURE;
336 struct winbindd_domain *find_auth_domain(uint8_t flags,
337 const char *domain_name)
339 struct winbindd_domain *domain;
341 if (IS_DC) {
342 domain = find_domain_from_name_noinit(domain_name);
343 if (domain == NULL) {
344 DEBUG(3, ("Authentication for domain [%s] refused "
345 "as it is not a trusted domain\n",
346 domain_name));
348 return domain;
351 if (strequal(domain_name, get_global_sam_name())) {
352 return find_domain_from_name_noinit(domain_name);
355 /* we can auth against trusted domains */
356 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
357 domain = find_domain_from_name_noinit(domain_name);
358 if (domain == NULL) {
359 DEBUG(3, ("Authentication for domain [%s] skipped "
360 "as it is not a trusted domain\n",
361 domain_name));
362 } else {
363 return domain;
367 return find_our_domain();
370 static void fill_in_password_policy(struct winbindd_response *r,
371 const struct samr_DomInfo1 *p)
373 r->data.auth.policy.min_length_password =
374 p->min_password_length;
375 r->data.auth.policy.password_history =
376 p->password_history_length;
377 r->data.auth.policy.password_properties =
378 p->password_properties;
379 r->data.auth.policy.expire =
380 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
381 r->data.auth.policy.min_passwordage =
382 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
385 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
386 struct winbindd_cli_state *state)
388 struct winbindd_methods *methods;
389 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
390 struct samr_DomInfo1 password_policy;
392 if ( !winbindd_can_contact_domain( domain ) ) {
393 DEBUG(5,("fillup_password_policy: No inbound trust to "
394 "contact domain %s\n", domain->name));
395 return NT_STATUS_NOT_SUPPORTED;
398 methods = domain->methods;
400 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
401 if (NT_STATUS_IS_ERR(status)) {
402 return status;
405 fill_in_password_policy(state->response, &password_policy);
407 return NT_STATUS_OK;
410 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
411 TALLOC_CTX *mem_ctx,
412 uint16 *lockout_threshold)
414 struct winbindd_methods *methods;
415 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
416 struct samr_DomInfo12 lockout_policy;
418 *lockout_threshold = 0;
420 methods = domain->methods;
422 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
423 if (NT_STATUS_IS_ERR(status)) {
424 return status;
427 *lockout_threshold = lockout_policy.lockout_threshold;
429 return NT_STATUS_OK;
432 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
433 TALLOC_CTX *mem_ctx,
434 uint32 *password_properties)
436 struct winbindd_methods *methods;
437 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
438 struct samr_DomInfo1 password_policy;
440 *password_properties = 0;
442 methods = domain->methods;
444 status = methods->password_policy(domain, mem_ctx, &password_policy);
445 if (NT_STATUS_IS_ERR(status)) {
446 return status;
449 *password_properties = password_policy.password_properties;
451 return NT_STATUS_OK;
454 #ifdef HAVE_KRB5
456 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
457 const char *type,
458 uid_t uid,
459 bool *internal_ccache)
461 /* accept FILE and WRFILE as krb5_cc_type from the client and then
462 * build the full ccname string based on the user's uid here -
463 * Guenther*/
465 const char *gen_cc = NULL;
467 *internal_ccache = true;
469 if (uid == -1) {
470 goto memory_ccache;
473 if (!type || type[0] == '\0') {
474 goto memory_ccache;
477 if (strequal(type, "FILE")) {
478 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
479 } else if (strequal(type, "WRFILE")) {
480 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
481 } else {
482 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
483 goto memory_ccache;
486 *internal_ccache = false;
487 goto done;
489 memory_ccache:
490 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
492 done:
493 if (gen_cc == NULL) {
494 DEBUG(0,("out of memory\n"));
495 return NULL;
498 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
500 return gen_cc;
503 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
505 const char *type = state->request->data.auth.krb5_cc_type;
507 state->response->data.auth.krb5ccname[0] = '\0';
509 if (type[0] == '\0') {
510 return;
513 if (!strequal(type, "FILE") &&
514 !strequal(type, "WRFILE")) {
515 DEBUG(10,("won't return krbccname for a %s type ccache\n",
516 type));
517 return;
520 fstrcpy(state->response->data.auth.krb5ccname, cc);
523 #endif
525 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
527 uid_t uid;
529 uid = state->request->data.auth.uid;
531 if (uid < 0) {
532 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
533 return -1;
535 return uid;
538 /**********************************************************************
539 Authenticate a user with a clear text password using Kerberos and fill up
540 ccache if required
541 **********************************************************************/
543 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
544 struct winbindd_cli_state *state,
545 struct netr_SamInfo3 **info3)
547 #ifdef HAVE_KRB5
548 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
549 krb5_error_code krb5_ret;
550 const char *cc = NULL;
551 const char *principal_s = NULL;
552 const char *service = NULL;
553 char *realm = NULL;
554 fstring name_domain, name_user;
555 time_t ticket_lifetime = 0;
556 time_t renewal_until = 0;
557 uid_t uid = -1;
558 ADS_STRUCT *ads;
559 time_t time_offset = 0;
560 bool internal_ccache = true;
562 ZERO_STRUCTP(info3);
564 *info3 = NULL;
566 /* 1st step:
567 * prepare a krb5_cc_cache string for the user */
569 uid = get_uid_from_state(state);
570 if (uid == -1) {
571 DEBUG(0,("no valid uid\n"));
574 cc = generate_krb5_ccache(state->mem_ctx,
575 state->request->data.auth.krb5_cc_type,
576 state->request->data.auth.uid,
577 &internal_ccache);
578 if (cc == NULL) {
579 return NT_STATUS_NO_MEMORY;
583 /* 2nd step:
584 * get kerberos properties */
586 if (domain->private_data) {
587 ads = (ADS_STRUCT *)domain->private_data;
588 time_offset = ads->auth.time_offset;
592 /* 3rd step:
593 * do kerberos auth and setup ccache as the user */
595 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
597 realm = domain->alt_name;
598 strupper_m(realm);
600 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
601 if (principal_s == NULL) {
602 return NT_STATUS_NO_MEMORY;
605 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
606 if (service == NULL) {
607 return NT_STATUS_NO_MEMORY;
610 /* if this is a user ccache, we need to act as the user to let the krb5
611 * library handle the chown, etc. */
613 /************************ ENTERING NON-ROOT **********************/
615 if (!internal_ccache) {
616 set_effective_uid(uid);
617 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
620 result = kerberos_return_info3_from_pac(state->mem_ctx,
621 principal_s,
622 state->request->data.auth.pass,
623 time_offset,
624 &ticket_lifetime,
625 &renewal_until,
627 true,
628 true,
629 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
630 NULL,
631 info3);
632 if (!internal_ccache) {
633 gain_root_privilege();
636 /************************ RETURNED TO ROOT **********************/
638 if (!NT_STATUS_IS_OK(result)) {
639 goto failed;
642 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
643 principal_s));
645 /* if we had a user's ccache then return that string for the pam
646 * environment */
648 if (!internal_ccache) {
650 setup_return_cc_name(state, cc);
652 result = add_ccache_to_list(principal_s,
654 service,
655 state->request->data.auth.user,
656 realm,
657 uid,
658 time(NULL),
659 ticket_lifetime,
660 renewal_until,
661 false);
663 if (!NT_STATUS_IS_OK(result)) {
664 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
665 nt_errstr(result)));
667 } else {
669 /* need to delete the memory cred cache, it is not used anymore */
671 krb5_ret = ads_kdestroy(cc);
672 if (krb5_ret) {
673 DEBUG(3,("winbindd_raw_kerberos_login: "
674 "could not destroy krb5 credential cache: "
675 "%s\n", error_message(krb5_ret)));
680 return NT_STATUS_OK;
682 failed:
684 /* we could have created a new credential cache with a valid tgt in it
685 * but we werent able to get or verify the service ticket for this
686 * local host and therefor didn't get the PAC, we need to remove that
687 * cache entirely now */
689 krb5_ret = ads_kdestroy(cc);
690 if (krb5_ret) {
691 DEBUG(3,("winbindd_raw_kerberos_login: "
692 "could not destroy krb5 credential cache: "
693 "%s\n", error_message(krb5_ret)));
696 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
697 DEBUG(3,("winbindd_raw_kerberos_login: "
698 "could not remove ccache for user %s\n",
699 state->request->data.auth.user));
702 return result;
703 #else
704 return NT_STATUS_NOT_SUPPORTED;
705 #endif /* HAVE_KRB5 */
708 /****************************************************************
709 ****************************************************************/
711 bool check_request_flags(uint32_t flags)
713 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
714 WBFLAG_PAM_INFO3_TEXT |
715 WBFLAG_PAM_INFO3_NDR;
717 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
718 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
719 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
720 !(flags & flags_edata) ) {
721 return true;
724 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
725 flags));
727 return false;
730 /****************************************************************
731 ****************************************************************/
733 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
734 struct netr_SamInfo3 *info3,
735 const char *name_domain,
736 const char *name_user)
738 NTSTATUS result;
739 uint32_t flags = state->request->flags;
741 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
742 memcpy(state->response->data.auth.user_session_key,
743 info3->base.key.key,
744 sizeof(state->response->data.auth.user_session_key)
745 /* 16 */);
748 if (flags & WBFLAG_PAM_LMKEY) {
749 memcpy(state->response->data.auth.first_8_lm_hash,
750 info3->base.LMSessKey.key,
751 sizeof(state->response->data.auth.first_8_lm_hash)
752 /* 8 */);
755 if (flags & WBFLAG_PAM_INFO3_TEXT) {
756 result = append_info3_as_txt(state->mem_ctx, state, info3);
757 if (!NT_STATUS_IS_OK(result)) {
758 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
759 nt_errstr(result)));
760 return result;
764 /* currently, anything from here on potentially overwrites extra_data. */
766 if (flags & WBFLAG_PAM_INFO3_NDR) {
767 result = append_info3_as_ndr(state->mem_ctx, state, info3);
768 if (!NT_STATUS_IS_OK(result)) {
769 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
770 nt_errstr(result)));
771 return result;
775 if (flags & WBFLAG_PAM_UNIX_NAME) {
776 result = append_unix_username(state->mem_ctx, state, info3,
777 name_domain, name_user);
778 if (!NT_STATUS_IS_OK(result)) {
779 DEBUG(10,("Failed to append Unix Username: %s\n",
780 nt_errstr(result)));
781 return result;
785 if (flags & WBFLAG_PAM_AFS_TOKEN) {
786 result = append_afs_token(state->mem_ctx, state, info3,
787 name_domain, name_user);
788 if (!NT_STATUS_IS_OK(result)) {
789 DEBUG(10,("Failed to append AFS token: %s\n",
790 nt_errstr(result)));
791 return result;
795 return NT_STATUS_OK;
798 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 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1180 const char *domain, const char *user,
1181 const DATA_BLOB *challenge,
1182 const DATA_BLOB *lm_resp,
1183 const DATA_BLOB *nt_resp,
1184 struct netr_SamInfo3 **pinfo3)
1186 struct auth_usersupplied_info *user_info = NULL;
1187 struct auth_serversupplied_info *server_info = NULL;
1188 struct netr_SamInfo3 *info3;
1189 NTSTATUS status;
1191 status = make_user_info(&user_info, user, user, domain, domain,
1192 global_myname(), lm_resp, nt_resp, NULL, NULL,
1193 NULL, True);
1194 if (!NT_STATUS_IS_OK(status)) {
1195 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1196 return status;
1199 status = check_sam_security(challenge, talloc_tos(), user_info,
1200 &server_info);
1201 free_user_info(&user_info);
1203 if (!NT_STATUS_IS_OK(status)) {
1204 DEBUG(10, ("check_ntlm_password failed: %s\n",
1205 nt_errstr(status)));
1206 return status;
1209 info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1210 if (info3 == NULL) {
1211 return NT_STATUS_NO_MEMORY;
1214 status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1215 if (!NT_STATUS_IS_OK(status)) {
1216 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1217 nt_errstr(status)));
1218 return status;
1221 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1222 *pinfo3 = info3;
1223 return NT_STATUS_OK;
1226 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1227 TALLOC_CTX *mem_ctx,
1228 uint32 logon_parameters,
1229 const char *server,
1230 const char *username,
1231 const char *domain,
1232 const char *workstation,
1233 const uint8 chal[8],
1234 DATA_BLOB lm_response,
1235 DATA_BLOB nt_response,
1236 struct netr_SamInfo3 **info3);
1238 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1239 struct winbindd_cli_state *state,
1240 struct netr_SamInfo3 **info3)
1243 struct rpc_pipe_client *netlogon_pipe;
1244 uchar chal[8];
1245 DATA_BLOB lm_resp;
1246 DATA_BLOB nt_resp;
1247 int attempts = 0;
1248 unsigned char local_lm_response[24];
1249 unsigned char local_nt_response[24];
1250 fstring name_domain, name_user;
1251 bool retry;
1252 NTSTATUS result;
1253 struct netr_SamInfo3 *my_info3 = NULL;
1255 *info3 = NULL;
1257 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1259 /* Parse domain and username */
1261 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1263 /* do password magic */
1265 generate_random_buffer(chal, sizeof(chal));
1267 if (lp_client_ntlmv2_auth()) {
1268 DATA_BLOB server_chal;
1269 DATA_BLOB names_blob;
1270 DATA_BLOB nt_response;
1271 DATA_BLOB lm_response;
1272 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1274 /* note that the 'workgroup' here is a best guess - we don't know
1275 the server's domain at this point. The 'server name' is also
1276 dodgy...
1278 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1280 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1281 state->request->data.auth.pass,
1282 &server_chal,
1283 &names_blob,
1284 &lm_response, &nt_response, NULL, NULL)) {
1285 data_blob_free(&names_blob);
1286 data_blob_free(&server_chal);
1287 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1288 result = NT_STATUS_NO_MEMORY;
1289 goto done;
1291 data_blob_free(&names_blob);
1292 data_blob_free(&server_chal);
1293 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1294 lm_response.length);
1295 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1296 nt_response.length);
1297 data_blob_free(&lm_response);
1298 data_blob_free(&nt_response);
1300 } else {
1301 if (lp_client_lanman_auth()
1302 && SMBencrypt(state->request->data.auth.pass,
1303 chal,
1304 local_lm_response)) {
1305 lm_resp = data_blob_talloc(state->mem_ctx,
1306 local_lm_response,
1307 sizeof(local_lm_response));
1308 } else {
1309 lm_resp = data_blob_null;
1311 SMBNTencrypt(state->request->data.auth.pass,
1312 chal,
1313 local_nt_response);
1315 nt_resp = data_blob_talloc(state->mem_ctx,
1316 local_nt_response,
1317 sizeof(local_nt_response));
1320 if (strequal(name_domain, get_global_sam_name())) {
1321 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1323 result = winbindd_dual_auth_passdb(
1324 state->mem_ctx, name_domain, name_user,
1325 &chal_blob, &lm_resp, &nt_resp, info3);
1326 goto done;
1329 /* check authentication loop */
1331 do {
1332 netlogon_fn_t logon_fn;
1334 ZERO_STRUCTP(my_info3);
1335 retry = false;
1337 result = cm_connect_netlogon(domain, &netlogon_pipe);
1339 if (!NT_STATUS_IS_OK(result)) {
1340 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1341 goto done;
1344 /* It is really important to try SamLogonEx here,
1345 * because in a clustered environment, we want to use
1346 * one machine account from multiple physical
1347 * computers.
1349 * With a normal SamLogon call, we must keep the
1350 * credentials chain updated and intact between all
1351 * users of the machine account (which would imply
1352 * cross-node communication for every NTLM logon).
1354 * (The credentials chain is not per NETLOGON pipe
1355 * connection, but globally on the server/client pair
1356 * by machine name).
1358 * When using SamLogonEx, the credentials are not
1359 * supplied, but the session key is implied by the
1360 * wrapping SamLogon context.
1362 * -- abartlet 21 April 2008
1365 logon_fn = domain->can_do_samlogon_ex
1366 ? rpccli_netlogon_sam_network_logon_ex
1367 : rpccli_netlogon_sam_network_logon;
1369 result = logon_fn(netlogon_pipe,
1370 state->mem_ctx,
1372 domain->dcname, /* server name */
1373 name_user, /* user name */
1374 name_domain, /* target domain */
1375 global_myname(), /* workstation */
1376 chal,
1377 lm_resp,
1378 nt_resp,
1379 &my_info3);
1380 attempts += 1;
1382 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1383 && domain->can_do_samlogon_ex) {
1384 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1385 "retrying with NetSamLogon\n"));
1386 domain->can_do_samlogon_ex = false;
1387 retry = true;
1388 continue;
1391 /* We have to try a second time as cm_connect_netlogon
1392 might not yet have noticed that the DC has killed
1393 our connection. */
1395 if (!rpccli_is_connected(netlogon_pipe)) {
1396 retry = true;
1397 continue;
1400 /* if we get access denied, a possible cause was that we had
1401 and open connection to the DC, but someone changed our
1402 machine account password out from underneath us using 'net
1403 rpc changetrustpw' */
1405 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1406 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1407 "ACCESS_DENIED. Maybe the trust account "
1408 "password was changed and we didn't know it. "
1409 "Killing connections to domain %s\n",
1410 name_domain));
1411 invalidate_cm_connection(&domain->conn);
1412 retry = true;
1415 } while ( (attempts < 2) && retry );
1417 /* handle the case where a NT4 DC does not fill in the acct_flags in
1418 * the samlogon reply info3. When accurate info3 is required by the
1419 * caller, we look up the account flags ourselve - gd */
1421 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1422 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1424 struct rpc_pipe_client *samr_pipe;
1425 struct policy_handle samr_domain_handle, user_pol;
1426 union samr_UserInfo *info = NULL;
1427 NTSTATUS status_tmp;
1428 uint32 acct_flags;
1430 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1431 &samr_pipe, &samr_domain_handle);
1433 if (!NT_STATUS_IS_OK(status_tmp)) {
1434 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1435 nt_errstr(status_tmp)));
1436 goto done;
1439 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1440 &samr_domain_handle,
1441 MAXIMUM_ALLOWED_ACCESS,
1442 my_info3->base.rid,
1443 &user_pol);
1445 if (!NT_STATUS_IS_OK(status_tmp)) {
1446 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1447 nt_errstr(status_tmp)));
1448 goto done;
1451 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1452 &user_pol,
1454 &info);
1456 if (!NT_STATUS_IS_OK(status_tmp)) {
1457 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1458 nt_errstr(status_tmp)));
1459 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1460 goto done;
1463 acct_flags = info->info16.acct_flags;
1465 if (acct_flags == 0) {
1466 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1467 goto done;
1470 my_info3->base.acct_flags = acct_flags;
1472 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1474 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1477 *info3 = my_info3;
1478 done:
1479 return result;
1482 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1483 struct winbindd_cli_state *state)
1485 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1486 NTSTATUS krb5_result = NT_STATUS_OK;
1487 fstring name_domain, name_user;
1488 char *mapped_user;
1489 fstring domain_user;
1490 struct netr_SamInfo3 *info3 = NULL;
1491 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1493 /* Ensure null termination */
1494 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1496 /* Ensure null termination */
1497 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1499 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1500 state->request->data.auth.user));
1502 if (!check_request_flags(state->request->flags)) {
1503 result = NT_STATUS_INVALID_PARAMETER_MIX;
1504 goto done;
1507 /* Parse domain and username */
1509 name_map_status = normalize_name_unmap(state->mem_ctx,
1510 state->request->data.auth.user,
1511 &mapped_user);
1513 /* If the name normalization didnt' actually do anything,
1514 just use the original name */
1516 if (!NT_STATUS_IS_OK(name_map_status) &&
1517 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1519 mapped_user = state->request->data.auth.user;
1522 parse_domain_user(mapped_user, name_domain, name_user);
1524 if ( mapped_user != state->request->data.auth.user ) {
1525 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1526 safe_strcpy( state->request->data.auth.user, domain_user,
1527 sizeof(state->request->data.auth.user)-1 );
1530 if (domain->online == false) {
1531 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1532 if (domain->startup) {
1533 /* Logons are very important to users. If we're offline and
1534 we get a request within the first 30 seconds of startup,
1535 try very hard to find a DC and go online. */
1537 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1538 "request in startup mode.\n", domain->name ));
1540 winbindd_flush_negative_conn_cache(domain);
1541 result = init_dc_connection(domain);
1545 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1547 /* Check for Kerberos authentication */
1548 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1550 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1551 /* save for later */
1552 krb5_result = result;
1555 if (NT_STATUS_IS_OK(result)) {
1556 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1557 goto process_result;
1558 } else {
1559 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1562 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1563 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1564 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1565 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1566 set_domain_offline( domain );
1567 goto cached_logon;
1570 /* there are quite some NT_STATUS errors where there is no
1571 * point in retrying with a samlogon, we explictly have to take
1572 * care not to increase the bad logon counter on the DC */
1574 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1575 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1576 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1577 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1578 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1579 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1580 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1581 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1582 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1583 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1584 goto done;
1587 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1588 DEBUG(3,("falling back to samlogon\n"));
1589 goto sam_logon;
1590 } else {
1591 goto cached_logon;
1595 sam_logon:
1596 /* Check for Samlogon authentication */
1597 if (domain->online) {
1598 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1600 if (NT_STATUS_IS_OK(result)) {
1601 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1602 /* add the Krb5 err if we have one */
1603 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1604 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1606 goto process_result;
1609 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1610 nt_errstr(result)));
1612 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1613 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1614 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1616 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1617 set_domain_offline( domain );
1618 goto cached_logon;
1621 if (domain->online) {
1622 /* We're still online - fail. */
1623 goto done;
1627 cached_logon:
1628 /* Check for Cached logons */
1629 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1630 lp_winbind_offline_logon()) {
1632 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1634 if (NT_STATUS_IS_OK(result)) {
1635 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1636 goto process_result;
1637 } else {
1638 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1639 goto done;
1643 process_result:
1645 if (NT_STATUS_IS_OK(result)) {
1647 DOM_SID user_sid;
1649 /* In all codepaths where result == NT_STATUS_OK info3 must have
1650 been initialized. */
1651 if (!info3) {
1652 result = NT_STATUS_INTERNAL_ERROR;
1653 goto done;
1656 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1657 netsamlogon_cache_store(name_user, info3);
1659 /* save name_to_sid info as early as possible (only if
1660 this is our primary domain so we don't invalidate
1661 the cache entry by storing the seq_num for the wrong
1662 domain). */
1663 if ( domain->primary ) {
1664 sid_compose(&user_sid, info3->base.domain_sid,
1665 info3->base.rid);
1666 cache_name2sid(domain, name_domain, name_user,
1667 SID_NAME_USER, &user_sid);
1670 /* Check if the user is in the right group */
1672 result = check_info3_in_group(
1673 info3,
1674 state->request->data.auth.require_membership_of_sid);
1675 if (!NT_STATUS_IS_OK(result)) {
1676 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1677 state->request->data.auth.user,
1678 state->request->data.auth.require_membership_of_sid));
1679 goto done;
1682 result = append_auth_data(state, info3, name_domain,
1683 name_user);
1684 if (!NT_STATUS_IS_OK(result)) {
1685 goto done;
1688 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1690 /* Store in-memory creds for single-signon using ntlm_auth. */
1691 result = winbindd_add_memory_creds(state->request->data.auth.user,
1692 get_uid_from_state(state),
1693 state->request->data.auth.pass);
1695 if (!NT_STATUS_IS_OK(result)) {
1696 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1697 goto done;
1700 if (lp_winbind_offline_logon()) {
1701 result = winbindd_store_creds(domain,
1702 state->mem_ctx,
1703 state->request->data.auth.user,
1704 state->request->data.auth.pass,
1705 info3, NULL);
1706 if (!NT_STATUS_IS_OK(result)) {
1708 /* Release refcount. */
1709 winbindd_delete_memory_creds(state->request->data.auth.user);
1711 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1712 goto done;
1718 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1719 struct winbindd_domain *our_domain = find_our_domain();
1721 /* This is not entirely correct I believe, but it is
1722 consistent. Only apply the password policy settings
1723 too warn users for our own domain. Cannot obtain these
1724 from trusted DCs all the time so don't do it at all.
1725 -- jerry */
1727 result = NT_STATUS_NOT_SUPPORTED;
1728 if (our_domain == domain ) {
1729 result = fillup_password_policy(our_domain, state);
1732 if (!NT_STATUS_IS_OK(result)
1733 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1735 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1736 domain->name, nt_errstr(result)));
1737 goto done;
1741 result = NT_STATUS_OK;
1744 done:
1745 /* give us a more useful (more correct?) error code */
1746 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1747 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1748 result = NT_STATUS_NO_LOGON_SERVERS;
1751 set_auth_errors(state->response, result);
1753 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1754 state->request->data.auth.user,
1755 state->response->data.auth.nt_status_string,
1756 state->response->data.auth.pam_error));
1758 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1762 /**********************************************************************
1763 Challenge Response Authentication Protocol
1764 **********************************************************************/
1766 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1768 struct winbindd_domain *domain = NULL;
1769 const char *domain_name = NULL;
1770 NTSTATUS result;
1772 if (!check_request_flags(state->request->flags)) {
1773 result = NT_STATUS_INVALID_PARAMETER_MIX;
1774 goto done;
1777 if (!state->privileged) {
1778 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1779 "denied. !\n"));
1780 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1781 "on %s are set correctly.\n",
1782 get_winbind_priv_pipe_dir()));
1783 /* send a better message than ACCESS_DENIED */
1784 fstr_sprintf(state->response->data.auth.error_string,
1785 "winbind client not authorized to use "
1786 "winbindd_pam_auth_crap. Ensure permissions on "
1787 "%s are set correctly.",
1788 get_winbind_priv_pipe_dir());
1789 result = NT_STATUS_ACCESS_DENIED;
1790 goto done;
1793 /* Ensure null termination */
1794 state->request->data.auth_crap.user
1795 [sizeof(state->request->data.auth_crap.user)-1]=0;
1796 state->request->data.auth_crap.domain
1797 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1799 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1800 (unsigned long)state->pid,
1801 state->request->data.auth_crap.domain,
1802 state->request->data.auth_crap.user));
1804 if (*state->request->data.auth_crap.domain != '\0') {
1805 domain_name = state->request->data.auth_crap.domain;
1806 } else if (lp_winbind_use_default_domain()) {
1807 domain_name = lp_workgroup();
1810 if (domain_name != NULL)
1811 domain = find_auth_domain(state->request->flags, domain_name);
1813 if (domain != NULL) {
1814 sendto_domain(state, domain);
1815 return;
1818 result = NT_STATUS_NO_SUCH_USER;
1820 done:
1821 set_auth_errors(state->response, result);
1822 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1823 state->request->data.auth_crap.domain,
1824 state->request->data.auth_crap.user,
1825 state->response->data.auth.nt_status_string,
1826 state->response->data.auth.pam_error));
1827 request_error(state);
1828 return;
1832 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1833 struct winbindd_cli_state *state)
1835 NTSTATUS result;
1836 struct netr_SamInfo3 *info3 = NULL;
1837 struct rpc_pipe_client *netlogon_pipe;
1838 const char *name_user = NULL;
1839 const char *name_domain = NULL;
1840 const char *workstation;
1841 int attempts = 0;
1842 bool retry;
1844 DATA_BLOB lm_resp, nt_resp;
1846 /* This is child-only, so no check for privileged access is needed
1847 anymore */
1849 /* Ensure null termination */
1850 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1851 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1853 if (!check_request_flags(state->request->flags)) {
1854 result = NT_STATUS_INVALID_PARAMETER_MIX;
1855 goto done;
1858 name_user = state->request->data.auth_crap.user;
1860 if (*state->request->data.auth_crap.domain) {
1861 name_domain = state->request->data.auth_crap.domain;
1862 } else if (lp_winbind_use_default_domain()) {
1863 name_domain = lp_workgroup();
1864 } else {
1865 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1866 name_user));
1867 result = NT_STATUS_NO_SUCH_USER;
1868 goto done;
1871 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1872 name_domain, name_user));
1874 if (*state->request->data.auth_crap.workstation) {
1875 workstation = state->request->data.auth_crap.workstation;
1876 } else {
1877 workstation = global_myname();
1880 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1881 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1882 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1883 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1884 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1885 state->request->data.auth_crap.lm_resp_len,
1886 state->request->data.auth_crap.nt_resp_len));
1887 result = NT_STATUS_INVALID_PARAMETER;
1888 goto done;
1892 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1893 state->request->data.auth_crap.lm_resp_len);
1895 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1896 nt_resp = data_blob_talloc(state->mem_ctx,
1897 state->request->extra_data.data,
1898 state->request->data.auth_crap.nt_resp_len);
1899 } else {
1900 nt_resp = data_blob_talloc(state->mem_ctx,
1901 state->request->data.auth_crap.nt_resp,
1902 state->request->data.auth_crap.nt_resp_len);
1905 if (strequal(name_domain, get_global_sam_name())) {
1906 DATA_BLOB chal_blob = data_blob_const(
1907 state->request->data.auth_crap.chal,
1908 sizeof(state->request->data.auth_crap.chal));
1910 result = winbindd_dual_auth_passdb(
1911 state->mem_ctx, name_domain, name_user,
1912 &chal_blob, &lm_resp, &nt_resp, &info3);
1913 goto process_result;
1916 do {
1917 netlogon_fn_t logon_fn;
1919 retry = false;
1921 netlogon_pipe = NULL;
1922 result = cm_connect_netlogon(domain, &netlogon_pipe);
1924 if (!NT_STATUS_IS_OK(result)) {
1925 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1926 nt_errstr(result)));
1927 goto done;
1930 logon_fn = domain->can_do_samlogon_ex
1931 ? rpccli_netlogon_sam_network_logon_ex
1932 : rpccli_netlogon_sam_network_logon;
1934 result = logon_fn(netlogon_pipe,
1935 state->mem_ctx,
1936 state->request->data.auth_crap.logon_parameters,
1937 domain->dcname,
1938 name_user,
1939 name_domain,
1940 /* Bug #3248 - found by Stefan Burkei. */
1941 workstation, /* We carefully set this above so use it... */
1942 state->request->data.auth_crap.chal,
1943 lm_resp,
1944 nt_resp,
1945 &info3);
1947 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1948 && domain->can_do_samlogon_ex) {
1949 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1950 "retrying with NetSamLogon\n"));
1951 domain->can_do_samlogon_ex = false;
1952 retry = true;
1953 continue;
1956 attempts += 1;
1958 /* We have to try a second time as cm_connect_netlogon
1959 might not yet have noticed that the DC has killed
1960 our connection. */
1962 if (!rpccli_is_connected(netlogon_pipe)) {
1963 retry = true;
1964 continue;
1967 /* if we get access denied, a possible cause was that we had and open
1968 connection to the DC, but someone changed our machine account password
1969 out from underneath us using 'net rpc changetrustpw' */
1971 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1972 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1973 "ACCESS_DENIED. Maybe the trust account "
1974 "password was changed and we didn't know it. "
1975 "Killing connections to domain %s\n",
1976 name_domain));
1977 invalidate_cm_connection(&domain->conn);
1978 retry = true;
1981 } while ( (attempts < 2) && retry );
1983 process_result:
1985 if (NT_STATUS_IS_OK(result)) {
1987 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1988 netsamlogon_cache_store(name_user, info3);
1990 /* Check if the user is in the right group */
1992 result = check_info3_in_group(
1993 info3,
1994 state->request->data.auth_crap.require_membership_of_sid);
1995 if (!NT_STATUS_IS_OK(result)) {
1996 DEBUG(3, ("User %s is not in the required group (%s), so "
1997 "crap authentication is rejected\n",
1998 state->request->data.auth_crap.user,
1999 state->request->data.auth_crap.require_membership_of_sid));
2000 goto done;
2003 result = append_auth_data(state, info3, name_domain,
2004 name_user);
2005 if (!NT_STATUS_IS_OK(result)) {
2006 goto done;
2010 done:
2012 /* give us a more useful (more correct?) error code */
2013 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2014 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2015 result = NT_STATUS_NO_LOGON_SERVERS;
2018 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2019 result = nt_status_squash(result);
2022 set_auth_errors(state->response, result);
2024 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2025 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2026 name_domain,
2027 name_user,
2028 state->response->data.auth.nt_status_string,
2029 state->response->data.auth.pam_error));
2031 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2034 /* Change a user password */
2036 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2038 fstring domain, user;
2039 char *mapped_user;
2040 struct winbindd_domain *contact_domain;
2041 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2043 /* Ensure null termination */
2044 state->request->data.chauthtok.user[
2045 sizeof(state->request->data.chauthtok.user)-1]='\0';
2047 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2048 state->request->data.chauthtok.user));
2050 /* Setup crap */
2052 nt_status = normalize_name_unmap(state->mem_ctx,
2053 state->request->data.chauthtok.user,
2054 &mapped_user);
2056 /* Update the chauthtok name if we did any mapping */
2058 if (NT_STATUS_IS_OK(nt_status) ||
2059 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2061 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2064 /* Must pass in state->...chauthtok.user because
2065 canonicalize_username() assumes an fstring(). Since
2066 we have already copied it (if necessary), this is ok. */
2068 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2069 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2070 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2071 "(PAM: %d)\n",
2072 state->request->data.chauthtok.user,
2073 state->response->data.auth.nt_status_string,
2074 state->response->data.auth.pam_error));
2075 request_error(state);
2076 return;
2079 contact_domain = find_domain_from_name(domain);
2080 if (!contact_domain) {
2081 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2082 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2083 state->request->data.chauthtok.user, domain, user, domain));
2084 request_error(state);
2085 return;
2088 sendto_domain(state, contact_domain);
2091 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2092 struct winbindd_cli_state *state)
2094 char *oldpass;
2095 char *newpass = NULL;
2096 struct policy_handle dom_pol;
2097 struct rpc_pipe_client *cli;
2098 bool got_info = false;
2099 struct samr_DomInfo1 *info = NULL;
2100 struct userPwdChangeFailureInformation *reject = NULL;
2101 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2102 fstring domain, user;
2104 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2105 state->request->data.auth.user));
2107 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2108 goto done;
2111 /* Change password */
2113 oldpass = state->request->data.chauthtok.oldpass;
2114 newpass = state->request->data.chauthtok.newpass;
2116 /* Initialize reject reason */
2117 state->response->data.auth.reject_reason = Undefined;
2119 /* Get sam handle */
2121 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2122 &dom_pol);
2123 if (!NT_STATUS_IS_OK(result)) {
2124 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2125 goto done;
2128 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2129 user,
2130 newpass,
2131 oldpass,
2132 &info,
2133 &reject);
2135 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2137 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2139 fill_in_password_policy(state->response, info);
2141 state->response->data.auth.reject_reason =
2142 reject->extendedFailureReason;
2144 got_info = true;
2147 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2148 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2149 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2150 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2152 /* only fallback when the chgpasswd_user3 call is not supported */
2153 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2154 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2155 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2156 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2158 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2159 nt_errstr(result)));
2161 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2163 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2164 Map to the same status code as Windows 2003. */
2166 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2167 result = NT_STATUS_PASSWORD_RESTRICTION;
2171 done:
2173 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2175 /* Update the single sign-on memory creds. */
2176 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2177 newpass);
2179 /* When we login from gdm or xdm and password expires,
2180 * we change password, but there are no memory crendentials
2181 * So, winbindd_replace_memory_creds() returns
2182 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2183 * --- BoYang
2184 * */
2185 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2186 result = NT_STATUS_OK;
2189 if (!NT_STATUS_IS_OK(result)) {
2190 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2191 goto process_result;
2194 if (lp_winbind_offline_logon()) {
2195 result = winbindd_update_creds_by_name(contact_domain,
2196 state->mem_ctx, user,
2197 newpass);
2198 /* Again, this happens when we login from gdm or xdm
2199 * and the password expires, *BUT* cached crendentials
2200 * doesn't exist. winbindd_update_creds_by_name()
2201 * returns NT_STATUS_NO_SUCH_USER.
2202 * This is not a failure.
2203 * --- BoYang
2204 * */
2205 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2206 result = NT_STATUS_OK;
2209 if (!NT_STATUS_IS_OK(result)) {
2210 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2211 goto process_result;
2216 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2218 NTSTATUS policy_ret;
2220 policy_ret = fillup_password_policy(contact_domain, state);
2222 /* failure of this is non critical, it will just provide no
2223 * additional information to the client why the change has
2224 * failed - Guenther */
2226 if (!NT_STATUS_IS_OK(policy_ret)) {
2227 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2228 goto process_result;
2232 process_result:
2234 set_auth_errors(state->response, result);
2236 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2237 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2238 domain,
2239 user,
2240 state->response->data.auth.nt_status_string,
2241 state->response->data.auth.pam_error));
2243 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2246 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2248 struct winbindd_domain *domain;
2249 fstring name_domain, user;
2250 uid_t caller_uid = (uid_t)-1;
2251 uid_t request_uid = state->request->data.logoff.uid;
2253 /* Ensure null termination */
2254 state->request->data.logoff.user
2255 [sizeof(state->request->data.logoff.user)-1]='\0';
2257 state->request->data.logoff.krb5ccname
2258 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2260 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2261 state->request->data.logoff.user));
2263 if (request_uid == (uid_t)-1) {
2264 goto failed;
2267 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2268 goto failed;
2271 if ((domain = find_auth_domain(state->request->flags,
2272 name_domain)) == NULL) {
2273 goto failed;
2276 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2277 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2278 strerror(errno)));
2279 goto failed;
2282 switch (caller_uid) {
2283 case -1:
2284 goto failed;
2285 case 0:
2286 /* root must be able to logoff any user - gd */
2287 state->request->data.logoff.uid = request_uid;
2288 break;
2289 default:
2290 if (caller_uid != request_uid) {
2291 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2292 goto failed;
2294 state->request->data.logoff.uid = caller_uid;
2295 break;
2298 sendto_domain(state, domain);
2299 return;
2301 failed:
2302 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2303 DEBUG(5, ("Pam Logoff for %s returned %s "
2304 "(PAM: %d)\n",
2305 state->request->data.logoff.user,
2306 state->response->data.auth.nt_status_string,
2307 state->response->data.auth.pam_error));
2308 request_error(state);
2309 return;
2312 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2313 struct winbindd_cli_state *state)
2315 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2317 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2318 state->request->data.logoff.user));
2320 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2321 result = NT_STATUS_OK;
2322 goto process_result;
2325 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2326 result = NT_STATUS_OK;
2327 goto process_result;
2330 #ifdef HAVE_KRB5
2332 if (state->request->data.logoff.uid < 0) {
2333 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2334 goto process_result;
2337 /* what we need here is to find the corresponding krb5 ccache name *we*
2338 * created for a given username and destroy it */
2340 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2341 result = NT_STATUS_OK;
2342 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2343 goto process_result;
2346 if (!ccache_entry_identical(state->request->data.logoff.user,
2347 state->request->data.logoff.uid,
2348 state->request->data.logoff.krb5ccname)) {
2349 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2350 goto process_result;
2353 result = remove_ccache(state->request->data.logoff.user);
2354 if (!NT_STATUS_IS_OK(result)) {
2355 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2356 nt_errstr(result)));
2357 goto process_result;
2360 #else
2361 result = NT_STATUS_NOT_SUPPORTED;
2362 #endif
2364 process_result:
2366 winbindd_delete_memory_creds(state->request->data.logoff.user);
2368 set_auth_errors(state->response, result);
2370 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2373 /* Change user password with auth crap*/
2375 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2377 struct winbindd_domain *domain = NULL;
2378 const char *domain_name = NULL;
2380 /* Ensure null termination */
2381 state->request->data.chng_pswd_auth_crap.user[
2382 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2383 state->request->data.chng_pswd_auth_crap.domain[
2384 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2386 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2387 (unsigned long)state->pid,
2388 state->request->data.chng_pswd_auth_crap.domain,
2389 state->request->data.chng_pswd_auth_crap.user));
2391 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2392 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2393 } else if (lp_winbind_use_default_domain()) {
2394 domain_name = lp_workgroup();
2397 if (domain_name != NULL)
2398 domain = find_domain_from_name(domain_name);
2400 if (domain != NULL) {
2401 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2402 "%s\n", (unsigned long)state->pid,domain->name));
2403 sendto_domain(state, domain);
2404 return;
2407 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2408 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2409 state->request->data.chng_pswd_auth_crap.domain,
2410 state->request->data.chng_pswd_auth_crap.user,
2411 state->response->data.auth.nt_status_string,
2412 state->response->data.auth.pam_error));
2413 request_error(state);
2414 return;
2417 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2419 NTSTATUS result;
2420 DATA_BLOB new_nt_password;
2421 DATA_BLOB old_nt_hash_enc;
2422 DATA_BLOB new_lm_password;
2423 DATA_BLOB old_lm_hash_enc;
2424 fstring domain,user;
2425 struct policy_handle dom_pol;
2426 struct winbindd_domain *contact_domain = domainSt;
2427 struct rpc_pipe_client *cli;
2429 /* Ensure null termination */
2430 state->request->data.chng_pswd_auth_crap.user[
2431 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2432 state->request->data.chng_pswd_auth_crap.domain[
2433 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2434 *domain = 0;
2435 *user = 0;
2437 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2438 (unsigned long)state->pid,
2439 state->request->data.chng_pswd_auth_crap.domain,
2440 state->request->data.chng_pswd_auth_crap.user));
2442 if (lp_winbind_offline_logon()) {
2443 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2444 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2445 result = NT_STATUS_ACCESS_DENIED;
2446 goto done;
2449 if (*state->request->data.chng_pswd_auth_crap.domain) {
2450 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2451 } else {
2452 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2453 domain, user);
2455 if(!*domain) {
2456 DEBUG(3,("no domain specified with username (%s) - "
2457 "failing auth\n",
2458 state->request->data.chng_pswd_auth_crap.user));
2459 result = NT_STATUS_NO_SUCH_USER;
2460 goto done;
2464 if (!*domain && lp_winbind_use_default_domain()) {
2465 fstrcpy(domain,(char *)lp_workgroup());
2468 if(!*user) {
2469 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2472 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2473 (unsigned long)state->pid, domain, user));
2475 /* Change password */
2476 new_nt_password = data_blob_talloc(
2477 state->mem_ctx,
2478 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2479 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2481 old_nt_hash_enc = data_blob_talloc(
2482 state->mem_ctx,
2483 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2484 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2486 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2487 new_lm_password = data_blob_talloc(
2488 state->mem_ctx,
2489 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2490 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2492 old_lm_hash_enc = data_blob_talloc(
2493 state->mem_ctx,
2494 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2495 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2496 } else {
2497 new_lm_password.length = 0;
2498 old_lm_hash_enc.length = 0;
2501 /* Get sam handle */
2503 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2504 if (!NT_STATUS_IS_OK(result)) {
2505 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2506 goto done;
2509 result = rpccli_samr_chng_pswd_auth_crap(
2510 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2511 new_lm_password, old_lm_hash_enc);
2513 done:
2515 set_auth_errors(state->response, result);
2517 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2518 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2519 domain, user,
2520 state->response->data.auth.nt_status_string,
2521 state->response->data.auth.pam_error));
2523 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;