Fix the build, missing ->.
[Samba/fernandojvsilva.git] / source3 / winbindd / winbindd_pam.c
blobfe6485522e083f8ac1b32cb381b8c1f8a47105fe
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
31 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
33 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
34 struct winbindd_cli_state *state,
35 struct netr_SamInfo3 *info3)
37 char *ex;
38 uint32_t i;
40 state->response->data.auth.info3.logon_time =
41 nt_time_to_unix(info3->base.last_logon);
42 state->response->data.auth.info3.logoff_time =
43 nt_time_to_unix(info3->base.last_logoff);
44 state->response->data.auth.info3.kickoff_time =
45 nt_time_to_unix(info3->base.acct_expiry);
46 state->response->data.auth.info3.pass_last_set_time =
47 nt_time_to_unix(info3->base.last_password_change);
48 state->response->data.auth.info3.pass_can_change_time =
49 nt_time_to_unix(info3->base.allow_password_change);
50 state->response->data.auth.info3.pass_must_change_time =
51 nt_time_to_unix(info3->base.force_password_change);
53 state->response->data.auth.info3.logon_count = info3->base.logon_count;
54 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
56 state->response->data.auth.info3.user_rid = info3->base.rid;
57 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
58 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
60 state->response->data.auth.info3.num_groups = info3->base.groups.count;
61 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
63 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
64 state->response->data.auth.info3.num_other_sids = info3->sidcount;
66 fstrcpy(state->response->data.auth.info3.user_name,
67 info3->base.account_name.string);
68 fstrcpy(state->response->data.auth.info3.full_name,
69 info3->base.full_name.string);
70 fstrcpy(state->response->data.auth.info3.logon_script,
71 info3->base.logon_script.string);
72 fstrcpy(state->response->data.auth.info3.profile_path,
73 info3->base.profile_path.string);
74 fstrcpy(state->response->data.auth.info3.home_dir,
75 info3->base.home_directory.string);
76 fstrcpy(state->response->data.auth.info3.dir_drive,
77 info3->base.home_drive.string);
79 fstrcpy(state->response->data.auth.info3.logon_srv,
80 info3->base.logon_server.string);
81 fstrcpy(state->response->data.auth.info3.logon_dom,
82 info3->base.domain.string);
84 ex = talloc_strdup(state->mem_ctx, "");
85 NT_STATUS_HAVE_NO_MEMORY(ex);
87 for (i=0; i < info3->base.groups.count; i++) {
88 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
89 info3->base.groups.rids[i].rid,
90 info3->base.groups.rids[i].attributes);
91 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->sidcount; i++) {
95 char *sid;
97 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
98 NT_STATUS_HAVE_NO_MEMORY(sid);
100 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
101 sid,
102 info3->sids[i].attributes);
103 NT_STATUS_HAVE_NO_MEMORY(ex);
105 talloc_free(sid);
108 state->response->extra_data.data = ex;
109 state->response->length += talloc_get_size(ex);
111 return NT_STATUS_OK;
114 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
115 struct winbindd_cli_state *state,
116 struct netr_SamInfo3 *info3)
118 DATA_BLOB blob;
119 enum ndr_err_code ndr_err;
121 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
122 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
124 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
125 return ndr_map_error2ntstatus(ndr_err);
128 state->response->extra_data.data = blob.data;
129 state->response->length += blob.length;
131 return NT_STATUS_OK;
134 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
135 struct winbindd_cli_state *state,
136 const struct netr_SamInfo3 *info3,
137 const char *name_domain,
138 const char *name_user)
140 /* We've been asked to return the unix username, per
141 'winbind use default domain' settings and the like */
143 const char *nt_username, *nt_domain;
145 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
146 if (!nt_domain) {
147 /* If the server didn't give us one, just use the one
148 * we sent them */
149 nt_domain = name_domain;
152 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
153 if (!nt_username) {
154 /* If the server didn't give us one, just use the one
155 * we sent them */
156 nt_username = name_user;
159 fill_domain_username(state->response->data.auth.unix_username,
160 nt_domain, nt_username, true);
162 DEBUG(5,("Setting unix username to [%s]\n",
163 state->response->data.auth.unix_username));
165 return NT_STATUS_OK;
168 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
169 struct winbindd_cli_state *state,
170 const struct netr_SamInfo3 *info3,
171 const char *name_domain,
172 const char *name_user)
174 char *afsname = NULL;
175 char *cell;
176 char *token;
178 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
179 if (afsname == NULL) {
180 return NT_STATUS_NO_MEMORY;
183 afsname = talloc_string_sub(mem_ctx,
184 lp_afs_username_map(),
185 "%D", name_domain);
186 afsname = talloc_string_sub(mem_ctx, afsname,
187 "%u", name_user);
188 afsname = talloc_string_sub(mem_ctx, afsname,
189 "%U", name_user);
192 DOM_SID user_sid;
193 fstring sidstr;
195 sid_copy(&user_sid, info3->base.domain_sid);
196 sid_append_rid(&user_sid, info3->base.rid);
197 sid_to_fstring(sidstr, &user_sid);
198 afsname = talloc_string_sub(mem_ctx, afsname,
199 "%s", sidstr);
202 if (afsname == NULL) {
203 return NT_STATUS_NO_MEMORY;
206 strlower_m(afsname);
208 DEBUG(10, ("Generating token for user %s\n", afsname));
210 cell = strchr(afsname, '@');
212 if (cell == NULL) {
213 return NT_STATUS_NO_MEMORY;
216 *cell = '\0';
217 cell += 1;
219 token = afs_createtoken_str(afsname, cell);
220 if (token == NULL) {
221 return NT_STATUS_OK;
223 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
224 token);
225 if (state->response->extra_data.data == NULL) {
226 return NT_STATUS_NO_MEMORY;
228 state->response->length +=
229 strlen((const char *)state->response->extra_data.data)+1;
231 return NT_STATUS_OK;
234 NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
235 const char *group_sid)
237 * Check whether a user belongs to a group or list of groups.
239 * @param mem_ctx talloc memory context.
240 * @param info3 user information, including group membership info.
241 * @param group_sid One or more groups , separated by commas.
243 * @return NT_STATUS_OK on success,
244 * NT_STATUS_LOGON_FAILURE if the user does not belong,
245 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
248 DOM_SID *require_membership_of_sid;
249 size_t num_require_membership_of_sid;
250 char *req_sid;
251 const char *p;
252 DOM_SID sid;
253 size_t i;
254 struct nt_user_token *token;
255 TALLOC_CTX *frame = talloc_stackframe();
256 NTSTATUS status;
258 /* Parse the 'required group' SID */
260 if (!group_sid || !group_sid[0]) {
261 /* NO sid supplied, all users may access */
262 return NT_STATUS_OK;
265 token = talloc_zero(talloc_tos(), struct nt_user_token);
266 if (token == NULL) {
267 DEBUG(0, ("talloc failed\n"));
268 TALLOC_FREE(frame);
269 return NT_STATUS_NO_MEMORY;
272 num_require_membership_of_sid = 0;
273 require_membership_of_sid = NULL;
275 p = group_sid;
277 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
278 if (!string_to_sid(&sid, req_sid)) {
279 DEBUG(0, ("check_info3_in_group: could not parse %s "
280 "as a SID!", req_sid));
281 TALLOC_FREE(frame);
282 return NT_STATUS_INVALID_PARAMETER;
285 status = add_sid_to_array(talloc_tos(), &sid,
286 &require_membership_of_sid,
287 &num_require_membership_of_sid);
288 if (!NT_STATUS_IS_OK(status)) {
289 DEBUG(0, ("add_sid_to_array failed\n"));
290 TALLOC_FREE(frame);
291 return status;
295 status = sid_array_from_info3(talloc_tos(), info3,
296 &token->user_sids,
297 &token->num_sids,
298 true, false);
299 if (!NT_STATUS_IS_OK(status)) {
300 TALLOC_FREE(frame);
301 return status;
304 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
305 token))
306 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
307 token))) {
308 DEBUG(3, ("could not add aliases: %s\n",
309 nt_errstr(status)));
310 TALLOC_FREE(frame);
311 return status;
314 debug_nt_user_token(DBGC_CLASS, 10, token);
316 for (i=0; i<num_require_membership_of_sid; i++) {
317 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
318 &require_membership_of_sid[i])));
319 if (nt_token_check_sid(&require_membership_of_sid[i],
320 token)) {
321 DEBUG(10, ("Access ok\n"));
322 TALLOC_FREE(frame);
323 return NT_STATUS_OK;
327 /* Do not distinguish this error from a wrong username/pw */
329 TALLOC_FREE(frame);
330 return NT_STATUS_LOGON_FAILURE;
333 struct winbindd_domain *find_auth_domain(uint8_t flags,
334 const char *domain_name)
336 struct winbindd_domain *domain;
338 if (IS_DC) {
339 domain = find_domain_from_name_noinit(domain_name);
340 if (domain == NULL) {
341 DEBUG(3, ("Authentication for domain [%s] refused "
342 "as it is not a trusted domain\n",
343 domain_name));
345 return domain;
348 if (is_myname(domain_name)) {
349 DEBUG(3, ("Authentication for domain %s (local domain "
350 "to this server) not supported at this "
351 "stage\n", domain_name));
352 return NULL;
355 /* we can auth against trusted domains */
356 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
357 domain = find_domain_from_name_noinit(domain_name);
358 if (domain == NULL) {
359 DEBUG(3, ("Authentication for domain [%s] skipped "
360 "as it is not a trusted domain\n",
361 domain_name));
362 } else {
363 return domain;
367 return find_our_domain();
370 static void fill_in_password_policy(struct winbindd_response *r,
371 const struct samr_DomInfo1 *p)
373 r->data.auth.policy.min_length_password =
374 p->min_password_length;
375 r->data.auth.policy.password_history =
376 p->password_history_length;
377 r->data.auth.policy.password_properties =
378 p->password_properties;
379 r->data.auth.policy.expire =
380 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
381 r->data.auth.policy.min_passwordage =
382 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
385 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
386 struct winbindd_cli_state *state)
388 struct winbindd_methods *methods;
389 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
390 struct samr_DomInfo1 password_policy;
392 if ( !winbindd_can_contact_domain( domain ) ) {
393 DEBUG(5,("fillup_password_policy: No inbound trust to "
394 "contact domain %s\n", domain->name));
395 return NT_STATUS_NOT_SUPPORTED;
398 methods = domain->methods;
400 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
401 if (NT_STATUS_IS_ERR(status)) {
402 return status;
405 fill_in_password_policy(state->response, &password_policy);
407 return NT_STATUS_OK;
410 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
411 TALLOC_CTX *mem_ctx,
412 uint16 *lockout_threshold)
414 struct winbindd_methods *methods;
415 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
416 struct samr_DomInfo12 lockout_policy;
418 *lockout_threshold = 0;
420 methods = domain->methods;
422 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
423 if (NT_STATUS_IS_ERR(status)) {
424 return status;
427 *lockout_threshold = lockout_policy.lockout_threshold;
429 return NT_STATUS_OK;
432 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
433 TALLOC_CTX *mem_ctx,
434 uint32 *password_properties)
436 struct winbindd_methods *methods;
437 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
438 struct samr_DomInfo1 password_policy;
440 *password_properties = 0;
442 methods = domain->methods;
444 status = methods->password_policy(domain, mem_ctx, &password_policy);
445 if (NT_STATUS_IS_ERR(status)) {
446 return status;
449 *password_properties = password_policy.password_properties;
451 return NT_STATUS_OK;
454 #ifdef HAVE_KRB5
456 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
457 const char *type,
458 uid_t uid,
459 bool *internal_ccache)
461 /* accept FILE and WRFILE as krb5_cc_type from the client and then
462 * build the full ccname string based on the user's uid here -
463 * Guenther*/
465 const char *gen_cc = NULL;
467 *internal_ccache = true;
469 if (uid == -1) {
470 goto memory_ccache;
473 if (!type || type[0] == '\0') {
474 goto memory_ccache;
477 if (strequal(type, "FILE")) {
478 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
479 } else if (strequal(type, "WRFILE")) {
480 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
481 } else {
482 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
483 goto memory_ccache;
486 *internal_ccache = false;
487 goto done;
489 memory_ccache:
490 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
492 done:
493 if (gen_cc == NULL) {
494 DEBUG(0,("out of memory\n"));
495 return NULL;
498 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
500 return gen_cc;
503 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
505 const char *type = state->request->data.auth.krb5_cc_type;
507 state->response->data.auth.krb5ccname[0] = '\0';
509 if (type[0] == '\0') {
510 return;
513 if (!strequal(type, "FILE") &&
514 !strequal(type, "WRFILE")) {
515 DEBUG(10,("won't return krbccname for a %s type ccache\n",
516 type));
517 return;
520 fstrcpy(state->response->data.auth.krb5ccname, cc);
523 #endif
525 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
527 uid_t uid;
529 uid = state->request->data.auth.uid;
531 if (uid < 0) {
532 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
533 return -1;
535 return uid;
538 /**********************************************************************
539 Authenticate a user with a clear text password using Kerberos and fill up
540 ccache if required
541 **********************************************************************/
543 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
544 struct winbindd_cli_state *state,
545 struct netr_SamInfo3 **info3)
547 #ifdef HAVE_KRB5
548 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
549 krb5_error_code krb5_ret;
550 const char *cc = NULL;
551 const char *principal_s = NULL;
552 const char *service = NULL;
553 char *realm = NULL;
554 fstring name_domain, name_user;
555 time_t ticket_lifetime = 0;
556 time_t renewal_until = 0;
557 uid_t uid = -1;
558 ADS_STRUCT *ads;
559 time_t time_offset = 0;
560 bool internal_ccache = true;
562 ZERO_STRUCTP(info3);
564 *info3 = NULL;
566 /* 1st step:
567 * prepare a krb5_cc_cache string for the user */
569 uid = get_uid_from_state(state);
570 if (uid == -1) {
571 DEBUG(0,("no valid uid\n"));
574 cc = generate_krb5_ccache(state->mem_ctx,
575 state->request->data.auth.krb5_cc_type,
576 state->request->data.auth.uid,
577 &internal_ccache);
578 if (cc == NULL) {
579 return NT_STATUS_NO_MEMORY;
583 /* 2nd step:
584 * get kerberos properties */
586 if (domain->private_data) {
587 ads = (ADS_STRUCT *)domain->private_data;
588 time_offset = ads->auth.time_offset;
592 /* 3rd step:
593 * do kerberos auth and setup ccache as the user */
595 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
597 realm = domain->alt_name;
598 strupper_m(realm);
600 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
601 if (principal_s == NULL) {
602 return NT_STATUS_NO_MEMORY;
605 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
606 if (service == NULL) {
607 return NT_STATUS_NO_MEMORY;
610 /* if this is a user ccache, we need to act as the user to let the krb5
611 * library handle the chown, etc. */
613 /************************ ENTERING NON-ROOT **********************/
615 if (!internal_ccache) {
616 set_effective_uid(uid);
617 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
620 result = kerberos_return_info3_from_pac(state->mem_ctx,
621 principal_s,
622 state->request->data.auth.pass,
623 time_offset,
624 &ticket_lifetime,
625 &renewal_until,
627 true,
628 true,
629 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
630 info3);
631 if (!internal_ccache) {
632 gain_root_privilege();
635 /************************ RETURNED TO ROOT **********************/
637 if (!NT_STATUS_IS_OK(result)) {
638 goto failed;
641 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
642 principal_s));
644 /* if we had a user's ccache then return that string for the pam
645 * environment */
647 if (!internal_ccache) {
649 setup_return_cc_name(state, cc);
651 result = add_ccache_to_list(principal_s,
653 service,
654 state->request->data.auth.user,
655 realm,
656 uid,
657 time(NULL),
658 ticket_lifetime,
659 renewal_until,
660 false);
662 if (!NT_STATUS_IS_OK(result)) {
663 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
664 nt_errstr(result)));
666 } else {
668 /* need to delete the memory cred cache, it is not used anymore */
670 krb5_ret = ads_kdestroy(cc);
671 if (krb5_ret) {
672 DEBUG(3,("winbindd_raw_kerberos_login: "
673 "could not destroy krb5 credential cache: "
674 "%s\n", error_message(krb5_ret)));
679 return NT_STATUS_OK;
681 failed:
683 /* we could have created a new credential cache with a valid tgt in it
684 * but we werent able to get or verify the service ticket for this
685 * local host and therefor didn't get the PAC, we need to remove that
686 * cache entirely now */
688 krb5_ret = ads_kdestroy(cc);
689 if (krb5_ret) {
690 DEBUG(3,("winbindd_raw_kerberos_login: "
691 "could not destroy krb5 credential cache: "
692 "%s\n", error_message(krb5_ret)));
695 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
696 DEBUG(3,("winbindd_raw_kerberos_login: "
697 "could not remove ccache for user %s\n",
698 state->request->data.auth.user));
701 return result;
702 #else
703 return NT_STATUS_NOT_SUPPORTED;
704 #endif /* HAVE_KRB5 */
707 /****************************************************************
708 ****************************************************************/
710 bool check_request_flags(uint32_t flags)
712 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
713 WBFLAG_PAM_INFO3_TEXT |
714 WBFLAG_PAM_INFO3_NDR;
716 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
717 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
718 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
719 !(flags & flags_edata) ) {
720 return true;
723 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
724 flags));
726 return false;
729 /****************************************************************
730 ****************************************************************/
732 NTSTATUS append_auth_data(struct winbindd_cli_state *state,
733 struct netr_SamInfo3 *info3,
734 const char *name_domain,
735 const char *name_user)
737 NTSTATUS result;
738 uint32_t flags = state->request->flags;
740 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
741 memcpy(state->response->data.auth.user_session_key,
742 info3->base.key.key,
743 sizeof(state->response->data.auth.user_session_key)
744 /* 16 */);
747 if (flags & WBFLAG_PAM_LMKEY) {
748 memcpy(state->response->data.auth.first_8_lm_hash,
749 info3->base.LMSessKey.key,
750 sizeof(state->response->data.auth.first_8_lm_hash)
751 /* 8 */);
754 if (flags & WBFLAG_PAM_INFO3_TEXT) {
755 result = append_info3_as_txt(state->mem_ctx, state, info3);
756 if (!NT_STATUS_IS_OK(result)) {
757 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
758 nt_errstr(result)));
759 return result;
763 /* currently, anything from here on potentially overwrites extra_data. */
765 if (flags & WBFLAG_PAM_INFO3_NDR) {
766 result = append_info3_as_ndr(state->mem_ctx, state, info3);
767 if (!NT_STATUS_IS_OK(result)) {
768 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
769 nt_errstr(result)));
770 return result;
774 if (flags & WBFLAG_PAM_UNIX_NAME) {
775 result = append_unix_username(state->mem_ctx, state, info3,
776 name_domain, name_user);
777 if (!NT_STATUS_IS_OK(result)) {
778 DEBUG(10,("Failed to append Unix Username: %s\n",
779 nt_errstr(result)));
780 return result;
784 if (flags & WBFLAG_PAM_AFS_TOKEN) {
785 result = append_afs_token(state->mem_ctx, state, info3,
786 name_domain, name_user);
787 if (!NT_STATUS_IS_OK(result)) {
788 DEBUG(10,("Failed to append AFS token: %s\n",
789 nt_errstr(result)));
790 return result;
794 return NT_STATUS_OK;
797 void winbindd_pam_auth(struct winbindd_cli_state *state)
799 struct winbindd_domain *domain;
800 fstring name_domain, name_user, mapped_user;
801 char *mapped = NULL;
802 NTSTATUS result;
803 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
805 /* Ensure null termination */
806 state->request->data.auth.user
807 [sizeof(state->request->data.auth.user)-1]='\0';
809 /* Ensure null termination */
810 state->request->data.auth.pass
811 [sizeof(state->request->data.auth.pass)-1]='\0';
813 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
814 state->request->data.auth.user));
816 if (!check_request_flags(state->request->flags)) {
817 result = NT_STATUS_INVALID_PARAMETER_MIX;
818 goto done;
821 /* Parse domain and username */
823 name_map_status = normalize_name_unmap(state->mem_ctx,
824 state->request->data.auth.user,
825 &mapped);
827 /* If the name normalization didnt' actually do anything,
828 just use the original name */
830 if (NT_STATUS_IS_OK(name_map_status)
831 ||NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
832 fstrcpy(mapped_user, mapped);
833 } else {
834 fstrcpy(mapped_user, state->request->data.auth.user);
837 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
838 result = NT_STATUS_NO_SUCH_USER;
839 goto done;
842 domain = find_auth_domain(state->request->flags, name_domain);
844 if (domain == NULL) {
845 result = NT_STATUS_NO_SUCH_USER;
846 goto done;
849 sendto_domain(state, domain);
850 return;
851 done:
852 set_auth_errors(state->response, result);
853 DEBUG(5, ("Plain text authentication for %s returned %s "
854 "(PAM: %d)\n",
855 state->request->data.auth.user,
856 state->response->data.auth.nt_status_string,
857 state->response->data.auth.pam_error));
858 request_error(state);
861 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
862 struct winbindd_cli_state *state,
863 struct netr_SamInfo3 **info3)
865 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
866 uint16 max_allowed_bad_attempts;
867 fstring name_domain, name_user;
868 DOM_SID sid;
869 enum lsa_SidType type;
870 uchar new_nt_pass[NT_HASH_LEN];
871 const uint8 *cached_nt_pass;
872 const uint8 *cached_salt;
873 struct netr_SamInfo3 *my_info3;
874 time_t kickoff_time, must_change_time;
875 bool password_good = false;
876 #ifdef HAVE_KRB5
877 struct winbindd_tdc_domain *tdc_domain = NULL;
878 #endif
880 *info3 = NULL;
882 ZERO_STRUCTP(info3);
884 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
886 /* Parse domain and username */
888 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
891 if (!lookup_cached_name(state->mem_ctx,
892 name_domain,
893 name_user,
894 &sid,
895 &type)) {
896 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
897 return NT_STATUS_NO_SUCH_USER;
900 if (type != SID_NAME_USER) {
901 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
902 return NT_STATUS_LOGON_FAILURE;
905 result = winbindd_get_creds(domain,
906 state->mem_ctx,
907 &sid,
908 &my_info3,
909 &cached_nt_pass,
910 &cached_salt);
911 if (!NT_STATUS_IS_OK(result)) {
912 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
913 return result;
916 *info3 = my_info3;
918 E_md4hash(state->request->data.auth.pass, new_nt_pass);
920 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
921 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
922 if (cached_salt) {
923 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
926 if (cached_salt) {
927 /* In this case we didn't store the nt_hash itself,
928 but the MD5 combination of salt + nt_hash. */
929 uchar salted_hash[NT_HASH_LEN];
930 E_md5hash(cached_salt, new_nt_pass, salted_hash);
932 password_good = (memcmp(cached_nt_pass, salted_hash,
933 NT_HASH_LEN) == 0);
934 } else {
935 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
936 password_good = (memcmp(cached_nt_pass, new_nt_pass,
937 NT_HASH_LEN) == 0);
940 if (password_good) {
942 /* User *DOES* know the password, update logon_time and reset
943 * bad_pw_count */
945 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
947 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
948 return NT_STATUS_ACCOUNT_LOCKED_OUT;
951 if (my_info3->base.acct_flags & ACB_DISABLED) {
952 return NT_STATUS_ACCOUNT_DISABLED;
955 if (my_info3->base.acct_flags & ACB_WSTRUST) {
956 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
959 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
960 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
963 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
964 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
967 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
968 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
969 my_info3->base.acct_flags));
970 return NT_STATUS_LOGON_FAILURE;
973 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
974 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
975 return NT_STATUS_ACCOUNT_EXPIRED;
978 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
979 if (must_change_time != 0 && must_change_time < time(NULL)) {
980 /* we allow grace logons when the password has expired */
981 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
982 /* return NT_STATUS_PASSWORD_EXPIRED; */
983 goto success;
986 #ifdef HAVE_KRB5
987 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
988 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
989 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
991 uid_t uid = -1;
992 const char *cc = NULL;
993 char *realm = NULL;
994 const char *principal_s = NULL;
995 const char *service = NULL;
996 bool internal_ccache = false;
998 uid = get_uid_from_state(state);
999 if (uid == -1) {
1000 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1001 return NT_STATUS_INVALID_PARAMETER;
1004 cc = generate_krb5_ccache(state->mem_ctx,
1005 state->request->data.auth.krb5_cc_type,
1006 state->request->data.auth.uid,
1007 &internal_ccache);
1008 if (cc == NULL) {
1009 return NT_STATUS_NO_MEMORY;
1012 realm = domain->alt_name;
1013 strupper_m(realm);
1015 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1016 if (principal_s == NULL) {
1017 return NT_STATUS_NO_MEMORY;
1020 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1021 if (service == NULL) {
1022 return NT_STATUS_NO_MEMORY;
1025 if (!internal_ccache) {
1027 setup_return_cc_name(state, cc);
1029 result = add_ccache_to_list(principal_s,
1031 service,
1032 state->request->data.auth.user,
1033 domain->alt_name,
1034 uid,
1035 time(NULL),
1036 time(NULL) + lp_winbind_cache_time(),
1037 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1038 true);
1040 if (!NT_STATUS_IS_OK(result)) {
1041 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1042 "to add ccache to list: %s\n",
1043 nt_errstr(result)));
1047 #endif /* HAVE_KRB5 */
1048 success:
1049 /* FIXME: we possibly should handle logon hours as well (does xp when
1050 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1052 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1053 my_info3->base.bad_password_count = 0;
1055 result = winbindd_update_creds_by_info3(domain,
1056 state->mem_ctx,
1057 state->request->data.auth.user,
1058 state->request->data.auth.pass,
1059 my_info3);
1060 if (!NT_STATUS_IS_OK(result)) {
1061 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1062 nt_errstr(result)));
1063 return result;
1066 return NT_STATUS_OK;
1070 /* User does *NOT* know the correct password, modify info3 accordingly */
1072 /* failure of this is not critical */
1073 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1074 if (!NT_STATUS_IS_OK(result)) {
1075 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1076 "Won't be able to honour account lockout policies\n"));
1079 /* increase counter */
1080 my_info3->base.bad_password_count++;
1082 if (max_allowed_bad_attempts == 0) {
1083 goto failed;
1086 /* lockout user */
1087 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1089 uint32 password_properties;
1091 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1092 if (!NT_STATUS_IS_OK(result)) {
1093 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1096 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1097 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1098 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1102 failed:
1103 result = winbindd_update_creds_by_info3(domain,
1104 state->mem_ctx,
1105 state->request->data.auth.user,
1106 NULL,
1107 my_info3);
1109 if (!NT_STATUS_IS_OK(result)) {
1110 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1111 nt_errstr(result)));
1114 return NT_STATUS_LOGON_FAILURE;
1117 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1118 struct winbindd_cli_state *state,
1119 struct netr_SamInfo3 **info3)
1121 struct winbindd_domain *contact_domain;
1122 fstring name_domain, name_user;
1123 NTSTATUS result;
1125 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1127 /* Parse domain and username */
1129 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1131 /* what domain should we contact? */
1133 if ( IS_DC ) {
1134 if (!(contact_domain = find_domain_from_name(name_domain))) {
1135 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1136 state->request->data.auth.user, name_domain, name_user, name_domain));
1137 result = NT_STATUS_NO_SUCH_USER;
1138 goto done;
1141 } else {
1142 if (is_myname(name_domain)) {
1143 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1144 result = NT_STATUS_NO_SUCH_USER;
1145 goto done;
1148 contact_domain = find_domain_from_name(name_domain);
1149 if (contact_domain == NULL) {
1150 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1151 state->request->data.auth.user, name_domain, name_user, name_domain));
1153 contact_domain = find_our_domain();
1157 if (contact_domain->initialized &&
1158 contact_domain->active_directory) {
1159 goto try_login;
1162 if (!contact_domain->initialized) {
1163 init_dc_connection(contact_domain);
1166 if (!contact_domain->active_directory) {
1167 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1168 return NT_STATUS_INVALID_LOGON_TYPE;
1170 try_login:
1171 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1172 done:
1173 return result;
1176 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1177 TALLOC_CTX *mem_ctx,
1178 uint32 logon_parameters,
1179 const char *server,
1180 const char *username,
1181 const char *domain,
1182 const char *workstation,
1183 const uint8 chal[8],
1184 DATA_BLOB lm_response,
1185 DATA_BLOB nt_response,
1186 struct netr_SamInfo3 **info3);
1188 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1189 struct winbindd_cli_state *state,
1190 struct netr_SamInfo3 **info3)
1193 struct rpc_pipe_client *netlogon_pipe;
1194 uchar chal[8];
1195 DATA_BLOB lm_resp;
1196 DATA_BLOB nt_resp;
1197 int attempts = 0;
1198 unsigned char local_lm_response[24];
1199 unsigned char local_nt_response[24];
1200 struct winbindd_domain *contact_domain;
1201 fstring name_domain, name_user;
1202 bool retry;
1203 NTSTATUS result;
1204 struct netr_SamInfo3 *my_info3 = NULL;
1206 *info3 = NULL;
1208 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1210 /* Parse domain and username */
1212 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1214 /* do password magic */
1217 generate_random_buffer(chal, 8);
1218 if (lp_client_ntlmv2_auth()) {
1219 DATA_BLOB server_chal;
1220 DATA_BLOB names_blob;
1221 DATA_BLOB nt_response;
1222 DATA_BLOB lm_response;
1223 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1225 /* note that the 'workgroup' here is a best guess - we don't know
1226 the server's domain at this point. The 'server name' is also
1227 dodgy...
1229 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1231 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1232 state->request->data.auth.pass,
1233 &server_chal,
1234 &names_blob,
1235 &lm_response, &nt_response, NULL, NULL)) {
1236 data_blob_free(&names_blob);
1237 data_blob_free(&server_chal);
1238 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1239 result = NT_STATUS_NO_MEMORY;
1240 goto done;
1242 data_blob_free(&names_blob);
1243 data_blob_free(&server_chal);
1244 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1245 lm_response.length);
1246 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1247 nt_response.length);
1248 data_blob_free(&lm_response);
1249 data_blob_free(&nt_response);
1251 } else {
1252 if (lp_client_lanman_auth()
1253 && SMBencrypt(state->request->data.auth.pass,
1254 chal,
1255 local_lm_response)) {
1256 lm_resp = data_blob_talloc(state->mem_ctx,
1257 local_lm_response,
1258 sizeof(local_lm_response));
1259 } else {
1260 lm_resp = data_blob_null;
1262 SMBNTencrypt(state->request->data.auth.pass,
1263 chal,
1264 local_nt_response);
1266 nt_resp = data_blob_talloc(state->mem_ctx,
1267 local_nt_response,
1268 sizeof(local_nt_response));
1271 /* what domain should we contact? */
1273 if ( IS_DC ) {
1274 if (!(contact_domain = find_domain_from_name(name_domain))) {
1275 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1276 state->request->data.auth.user, name_domain, name_user, name_domain));
1277 result = NT_STATUS_NO_SUCH_USER;
1278 goto done;
1281 } else {
1282 if (is_myname(name_domain)) {
1283 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1284 result = NT_STATUS_NO_SUCH_USER;
1285 goto done;
1288 contact_domain = find_our_domain();
1291 /* check authentication loop */
1293 do {
1294 netlogon_fn_t logon_fn;
1296 ZERO_STRUCTP(my_info3);
1297 retry = false;
1299 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1301 if (!NT_STATUS_IS_OK(result)) {
1302 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1303 goto done;
1306 /* It is really important to try SamLogonEx here,
1307 * because in a clustered environment, we want to use
1308 * one machine account from multiple physical
1309 * computers.
1311 * With a normal SamLogon call, we must keep the
1312 * credentials chain updated and intact between all
1313 * users of the machine account (which would imply
1314 * cross-node communication for every NTLM logon).
1316 * (The credentials chain is not per NETLOGON pipe
1317 * connection, but globally on the server/client pair
1318 * by machine name).
1320 * When using SamLogonEx, the credentials are not
1321 * supplied, but the session key is implied by the
1322 * wrapping SamLogon context.
1324 * -- abartlet 21 April 2008
1327 logon_fn = contact_domain->can_do_samlogon_ex
1328 ? rpccli_netlogon_sam_network_logon_ex
1329 : rpccli_netlogon_sam_network_logon;
1331 result = logon_fn(netlogon_pipe,
1332 state->mem_ctx,
1334 contact_domain->dcname, /* server name */
1335 name_user, /* user name */
1336 name_domain, /* target domain */
1337 global_myname(), /* workstation */
1338 chal,
1339 lm_resp,
1340 nt_resp,
1341 &my_info3);
1342 attempts += 1;
1344 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1345 && contact_domain->can_do_samlogon_ex) {
1346 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1347 "retrying with NetSamLogon\n"));
1348 contact_domain->can_do_samlogon_ex = false;
1349 retry = true;
1350 continue;
1353 /* We have to try a second time as cm_connect_netlogon
1354 might not yet have noticed that the DC has killed
1355 our connection. */
1357 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1358 retry = true;
1359 continue;
1362 /* if we get access denied, a possible cause was that we had
1363 and open connection to the DC, but someone changed our
1364 machine account password out from underneath us using 'net
1365 rpc changetrustpw' */
1367 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1368 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1369 "ACCESS_DENIED. Maybe the trust account "
1370 "password was changed and we didn't know it. "
1371 "Killing connections to domain %s\n",
1372 name_domain));
1373 invalidate_cm_connection(&contact_domain->conn);
1374 retry = true;
1377 } while ( (attempts < 2) && retry );
1379 /* handle the case where a NT4 DC does not fill in the acct_flags in
1380 * the samlogon reply info3. When accurate info3 is required by the
1381 * caller, we look up the account flags ourselve - gd */
1383 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1384 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1386 struct rpc_pipe_client *samr_pipe;
1387 struct policy_handle samr_domain_handle, user_pol;
1388 union samr_UserInfo *info = NULL;
1389 NTSTATUS status_tmp;
1390 uint32 acct_flags;
1392 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1393 &samr_pipe, &samr_domain_handle);
1395 if (!NT_STATUS_IS_OK(status_tmp)) {
1396 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1397 nt_errstr(status_tmp)));
1398 goto done;
1401 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1402 &samr_domain_handle,
1403 MAXIMUM_ALLOWED_ACCESS,
1404 my_info3->base.rid,
1405 &user_pol);
1407 if (!NT_STATUS_IS_OK(status_tmp)) {
1408 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1409 nt_errstr(status_tmp)));
1410 goto done;
1413 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1414 &user_pol,
1416 &info);
1418 if (!NT_STATUS_IS_OK(status_tmp)) {
1419 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1420 nt_errstr(status_tmp)));
1421 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1422 goto done;
1425 acct_flags = info->info16.acct_flags;
1427 if (acct_flags == 0) {
1428 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1429 goto done;
1432 my_info3->base.acct_flags = acct_flags;
1434 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1436 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1439 *info3 = my_info3;
1440 done:
1441 return result;
1444 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1445 struct winbindd_cli_state *state)
1447 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1448 NTSTATUS krb5_result = NT_STATUS_OK;
1449 fstring name_domain, name_user;
1450 char *mapped_user;
1451 fstring domain_user;
1452 struct netr_SamInfo3 *info3 = NULL;
1453 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1455 /* Ensure null termination */
1456 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1458 /* Ensure null termination */
1459 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1461 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1462 state->request->data.auth.user));
1464 if (!check_request_flags(state->request->flags)) {
1465 result = NT_STATUS_INVALID_PARAMETER_MIX;
1466 goto done;
1469 /* Parse domain and username */
1471 name_map_status = normalize_name_unmap(state->mem_ctx,
1472 state->request->data.auth.user,
1473 &mapped_user);
1475 /* If the name normalization didnt' actually do anything,
1476 just use the original name */
1478 if (!NT_STATUS_IS_OK(name_map_status) &&
1479 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1481 mapped_user = state->request->data.auth.user;
1484 parse_domain_user(mapped_user, name_domain, name_user);
1486 if ( mapped_user != state->request->data.auth.user ) {
1487 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1488 safe_strcpy( state->request->data.auth.user, domain_user,
1489 sizeof(state->request->data.auth.user)-1 );
1492 if (domain->online == false) {
1493 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1494 if (domain->startup) {
1495 /* Logons are very important to users. If we're offline and
1496 we get a request within the first 30 seconds of startup,
1497 try very hard to find a DC and go online. */
1499 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1500 "request in startup mode.\n", domain->name ));
1502 winbindd_flush_negative_conn_cache(domain);
1503 result = init_dc_connection(domain);
1507 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1509 /* Check for Kerberos authentication */
1510 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1512 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1513 /* save for later */
1514 krb5_result = result;
1517 if (NT_STATUS_IS_OK(result)) {
1518 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1519 goto process_result;
1520 } else {
1521 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1524 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1526 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1527 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1528 set_domain_offline( domain );
1529 goto cached_logon;
1532 /* there are quite some NT_STATUS errors where there is no
1533 * point in retrying with a samlogon, we explictly have to take
1534 * care not to increase the bad logon counter on the DC */
1536 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1537 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1538 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1541 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1542 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1543 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1544 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1545 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1546 goto process_result;
1549 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1550 DEBUG(3,("falling back to samlogon\n"));
1551 goto sam_logon;
1552 } else {
1553 goto cached_logon;
1557 sam_logon:
1558 /* Check for Samlogon authentication */
1559 if (domain->online) {
1560 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1562 if (NT_STATUS_IS_OK(result)) {
1563 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1564 /* add the Krb5 err if we have one */
1565 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1566 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1568 goto process_result;
1571 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1572 nt_errstr(result)));
1574 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1575 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1576 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1578 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1579 set_domain_offline( domain );
1580 goto cached_logon;
1583 if (domain->online) {
1584 /* We're still online - fail. */
1585 goto done;
1589 cached_logon:
1590 /* Check for Cached logons */
1591 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1592 lp_winbind_offline_logon()) {
1594 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1596 if (NT_STATUS_IS_OK(result)) {
1597 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1598 goto process_result;
1599 } else {
1600 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1601 goto done;
1605 process_result:
1607 if (NT_STATUS_IS_OK(result)) {
1609 DOM_SID user_sid;
1611 /* In all codepaths where result == NT_STATUS_OK info3 must have
1612 been initialized. */
1613 if (!info3) {
1614 result = NT_STATUS_INTERNAL_ERROR;
1615 goto done;
1618 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1619 netsamlogon_cache_store(name_user, info3);
1621 /* save name_to_sid info as early as possible (only if
1622 this is our primary domain so we don't invalidate
1623 the cache entry by storing the seq_num for the wrong
1624 domain). */
1625 if ( domain->primary ) {
1626 sid_compose(&user_sid, info3->base.domain_sid,
1627 info3->base.rid);
1628 cache_name2sid(domain, name_domain, name_user,
1629 SID_NAME_USER, &user_sid);
1632 /* Check if the user is in the right group */
1634 result = check_info3_in_group(
1635 info3,
1636 state->request->data.auth.require_membership_of_sid);
1637 if (!NT_STATUS_IS_OK(result)) {
1638 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1639 state->request->data.auth.user,
1640 state->request->data.auth.require_membership_of_sid));
1641 goto done;
1644 result = append_auth_data(state, info3, name_domain,
1645 name_user);
1646 if (!NT_STATUS_IS_OK(result)) {
1647 goto done;
1650 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1652 /* Store in-memory creds for single-signon using ntlm_auth. */
1653 result = winbindd_add_memory_creds(state->request->data.auth.user,
1654 get_uid_from_state(state),
1655 state->request->data.auth.pass);
1657 if (!NT_STATUS_IS_OK(result)) {
1658 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1659 goto done;
1662 if (lp_winbind_offline_logon()) {
1663 result = winbindd_store_creds(domain,
1664 state->mem_ctx,
1665 state->request->data.auth.user,
1666 state->request->data.auth.pass,
1667 info3, NULL);
1668 if (!NT_STATUS_IS_OK(result)) {
1670 /* Release refcount. */
1671 winbindd_delete_memory_creds(state->request->data.auth.user);
1673 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1674 goto done;
1680 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1681 struct winbindd_domain *our_domain = find_our_domain();
1683 /* This is not entirely correct I believe, but it is
1684 consistent. Only apply the password policy settings
1685 too warn users for our own domain. Cannot obtain these
1686 from trusted DCs all the time so don't do it at all.
1687 -- jerry */
1689 result = NT_STATUS_NOT_SUPPORTED;
1690 if (our_domain == domain ) {
1691 result = fillup_password_policy(our_domain, state);
1694 if (!NT_STATUS_IS_OK(result)
1695 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1697 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1698 domain->name, nt_errstr(result)));
1699 goto done;
1703 result = NT_STATUS_OK;
1706 done:
1707 /* give us a more useful (more correct?) error code */
1708 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1709 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1710 result = NT_STATUS_NO_LOGON_SERVERS;
1713 set_auth_errors(state->response, result);
1715 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1716 state->request->data.auth.user,
1717 state->response->data.auth.nt_status_string,
1718 state->response->data.auth.pam_error));
1720 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1724 /**********************************************************************
1725 Challenge Response Authentication Protocol
1726 **********************************************************************/
1728 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1730 struct winbindd_domain *domain = NULL;
1731 const char *domain_name = NULL;
1732 NTSTATUS result;
1734 if (!check_request_flags(state->request->flags)) {
1735 result = NT_STATUS_INVALID_PARAMETER_MIX;
1736 goto done;
1739 if (!state->privileged) {
1740 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1741 "denied. !\n"));
1742 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1743 "on %s are set correctly.\n",
1744 get_winbind_priv_pipe_dir()));
1745 /* send a better message than ACCESS_DENIED */
1746 fstr_sprintf(state->response->data.auth.error_string,
1747 "winbind client not authorized to use "
1748 "winbindd_pam_auth_crap. Ensure permissions on "
1749 "%s are set correctly.",
1750 get_winbind_priv_pipe_dir());
1751 result = NT_STATUS_ACCESS_DENIED;
1752 goto done;
1755 /* Ensure null termination */
1756 state->request->data.auth_crap.user
1757 [sizeof(state->request->data.auth_crap.user)-1]=0;
1758 state->request->data.auth_crap.domain
1759 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1761 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1762 (unsigned long)state->pid,
1763 state->request->data.auth_crap.domain,
1764 state->request->data.auth_crap.user));
1766 if (*state->request->data.auth_crap.domain != '\0') {
1767 domain_name = state->request->data.auth_crap.domain;
1768 } else if (lp_winbind_use_default_domain()) {
1769 domain_name = lp_workgroup();
1772 if (domain_name != NULL)
1773 domain = find_auth_domain(state->request->flags, domain_name);
1775 if (domain != NULL) {
1776 sendto_domain(state, domain);
1777 return;
1780 result = NT_STATUS_NO_SUCH_USER;
1782 done:
1783 set_auth_errors(state->response, result);
1784 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1785 state->request->data.auth_crap.domain,
1786 state->request->data.auth_crap.user,
1787 state->response->data.auth.nt_status_string,
1788 state->response->data.auth.pam_error));
1789 request_error(state);
1790 return;
1794 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1795 struct winbindd_cli_state *state)
1797 NTSTATUS result;
1798 struct netr_SamInfo3 *info3 = NULL;
1799 struct rpc_pipe_client *netlogon_pipe;
1800 const char *name_user = NULL;
1801 const char *name_domain = NULL;
1802 const char *workstation;
1803 struct winbindd_domain *contact_domain;
1804 int attempts = 0;
1805 bool retry;
1807 DATA_BLOB lm_resp, nt_resp;
1809 /* This is child-only, so no check for privileged access is needed
1810 anymore */
1812 /* Ensure null termination */
1813 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1814 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1816 if (!check_request_flags(state->request->flags)) {
1817 result = NT_STATUS_INVALID_PARAMETER_MIX;
1818 goto done;
1821 name_user = state->request->data.auth_crap.user;
1823 if (*state->request->data.auth_crap.domain) {
1824 name_domain = state->request->data.auth_crap.domain;
1825 } else if (lp_winbind_use_default_domain()) {
1826 name_domain = lp_workgroup();
1827 } else {
1828 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1829 name_user));
1830 result = NT_STATUS_NO_SUCH_USER;
1831 goto done;
1834 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1835 name_domain, name_user));
1837 if (*state->request->data.auth_crap.workstation) {
1838 workstation = state->request->data.auth_crap.workstation;
1839 } else {
1840 workstation = global_myname();
1843 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1844 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1845 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1846 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1847 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1848 state->request->data.auth_crap.lm_resp_len,
1849 state->request->data.auth_crap.nt_resp_len));
1850 result = NT_STATUS_INVALID_PARAMETER;
1851 goto done;
1855 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1856 state->request->data.auth_crap.lm_resp_len);
1858 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1859 nt_resp = data_blob_talloc(state->mem_ctx,
1860 state->request->extra_data.data,
1861 state->request->data.auth_crap.nt_resp_len);
1862 } else {
1863 nt_resp = data_blob_talloc(state->mem_ctx,
1864 state->request->data.auth_crap.nt_resp,
1865 state->request->data.auth_crap.nt_resp_len);
1868 /* what domain should we contact? */
1870 if ( IS_DC ) {
1871 if (!(contact_domain = find_domain_from_name(name_domain))) {
1872 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1873 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1874 result = NT_STATUS_NO_SUCH_USER;
1875 goto done;
1877 } else {
1878 if (is_myname(name_domain)) {
1879 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1880 result = NT_STATUS_NO_SUCH_USER;
1881 goto done;
1883 contact_domain = find_our_domain();
1886 do {
1887 netlogon_fn_t logon_fn;
1889 retry = false;
1891 netlogon_pipe = NULL;
1892 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1894 if (!NT_STATUS_IS_OK(result)) {
1895 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1896 nt_errstr(result)));
1897 goto done;
1900 logon_fn = contact_domain->can_do_samlogon_ex
1901 ? rpccli_netlogon_sam_network_logon_ex
1902 : rpccli_netlogon_sam_network_logon;
1904 result = logon_fn(netlogon_pipe,
1905 state->mem_ctx,
1906 state->request->data.auth_crap.logon_parameters,
1907 contact_domain->dcname,
1908 name_user,
1909 name_domain,
1910 /* Bug #3248 - found by Stefan Burkei. */
1911 workstation, /* We carefully set this above so use it... */
1912 state->request->data.auth_crap.chal,
1913 lm_resp,
1914 nt_resp,
1915 &info3);
1917 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1918 && contact_domain->can_do_samlogon_ex) {
1919 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1920 "retrying with NetSamLogon\n"));
1921 contact_domain->can_do_samlogon_ex = false;
1922 retry = true;
1923 continue;
1926 attempts += 1;
1928 /* We have to try a second time as cm_connect_netlogon
1929 might not yet have noticed that the DC has killed
1930 our connection. */
1932 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1933 retry = true;
1934 continue;
1937 /* if we get access denied, a possible cause was that we had and open
1938 connection to the DC, but someone changed our machine account password
1939 out from underneath us using 'net rpc changetrustpw' */
1941 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1942 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1943 "ACCESS_DENIED. Maybe the trust account "
1944 "password was changed and we didn't know it. "
1945 "Killing connections to domain %s\n",
1946 name_domain));
1947 invalidate_cm_connection(&contact_domain->conn);
1948 retry = true;
1951 } while ( (attempts < 2) && retry );
1953 if (NT_STATUS_IS_OK(result)) {
1955 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1956 netsamlogon_cache_store(name_user, info3);
1958 /* Check if the user is in the right group */
1960 result = check_info3_in_group(
1961 info3,
1962 state->request->data.auth_crap.require_membership_of_sid);
1963 if (!NT_STATUS_IS_OK(result)) {
1964 DEBUG(3, ("User %s is not in the required group (%s), so "
1965 "crap authentication is rejected\n",
1966 state->request->data.auth_crap.user,
1967 state->request->data.auth_crap.require_membership_of_sid));
1968 goto done;
1971 result = append_auth_data(state, info3, name_domain,
1972 name_user);
1973 if (!NT_STATUS_IS_OK(result)) {
1974 goto done;
1978 done:
1980 /* give us a more useful (more correct?) error code */
1981 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1982 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1983 result = NT_STATUS_NO_LOGON_SERVERS;
1986 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1987 result = nt_status_squash(result);
1990 set_auth_errors(state->response, result);
1992 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1993 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1994 name_domain,
1995 name_user,
1996 state->response->data.auth.nt_status_string,
1997 state->response->data.auth.pam_error));
1999 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2002 /* Change a user password */
2004 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2006 fstring domain, user;
2007 char *mapped_user;
2008 struct winbindd_domain *contact_domain;
2009 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2011 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2012 state->request->data.chauthtok.user));
2014 /* Setup crap */
2016 nt_status = normalize_name_unmap(state->mem_ctx,
2017 state->request->data.chauthtok.user,
2018 &mapped_user);
2020 /* Update the chauthtok name if we did any mapping */
2022 if (NT_STATUS_IS_OK(nt_status) ||
2023 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2025 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2028 /* Must pass in state->...chauthtok.user because
2029 canonicalize_username() assumes an fstring(). Since
2030 we have already copied it (if necessary), this is ok. */
2032 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2033 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2034 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2035 "(PAM: %d)\n",
2036 state->request->data.auth.user,
2037 state->response->data.auth.nt_status_string,
2038 state->response->data.auth.pam_error));
2039 request_error(state);
2040 return;
2043 contact_domain = find_domain_from_name(domain);
2044 if (!contact_domain) {
2045 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2046 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2047 state->request->data.chauthtok.user, domain, user, domain));
2048 request_error(state);
2049 return;
2052 sendto_domain(state, contact_domain);
2055 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2056 struct winbindd_cli_state *state)
2058 char *oldpass;
2059 char *newpass = NULL;
2060 struct policy_handle dom_pol;
2061 struct rpc_pipe_client *cli;
2062 bool got_info = false;
2063 struct samr_DomInfo1 *info = NULL;
2064 struct userPwdChangeFailureInformation *reject = NULL;
2065 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2066 fstring domain, user;
2068 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2069 state->request->data.auth.user));
2071 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2072 goto done;
2075 /* Change password */
2077 oldpass = state->request->data.chauthtok.oldpass;
2078 newpass = state->request->data.chauthtok.newpass;
2080 /* Initialize reject reason */
2081 state->response->data.auth.reject_reason = Undefined;
2083 /* Get sam handle */
2085 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2086 &dom_pol);
2087 if (!NT_STATUS_IS_OK(result)) {
2088 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2089 goto done;
2092 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2093 user,
2094 newpass,
2095 oldpass,
2096 &info,
2097 &reject);
2099 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2101 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2103 fill_in_password_policy(state->response, info);
2105 state->response->data.auth.reject_reason =
2106 reject->extendedFailureReason;
2108 got_info = true;
2111 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2112 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2113 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2114 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2116 /* only fallback when the chgpasswd_user3 call is not supported */
2117 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2118 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2119 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2120 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2122 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2123 nt_errstr(result)));
2125 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2127 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2128 Map to the same status code as Windows 2003. */
2130 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2131 result = NT_STATUS_PASSWORD_RESTRICTION;
2135 done:
2137 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2139 /* Update the single sign-on memory creds. */
2140 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2141 newpass);
2143 /* When we login from gdm or xdm and password expires,
2144 * we change password, but there are no memory crendentials
2145 * So, winbindd_replace_memory_creds() returns
2146 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2147 * --- BoYang
2148 * */
2149 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2150 result = NT_STATUS_OK;
2153 if (!NT_STATUS_IS_OK(result)) {
2154 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2155 goto process_result;
2158 if (lp_winbind_offline_logon()) {
2159 result = winbindd_update_creds_by_name(contact_domain,
2160 state->mem_ctx, user,
2161 newpass);
2162 /* Again, this happens when we login from gdm or xdm
2163 * and the password expires, *BUT* cached crendentials
2164 * doesn't exist. winbindd_update_creds_by_name()
2165 * returns NT_STATUS_NO_SUCH_USER.
2166 * This is not a failure.
2167 * --- BoYang
2168 * */
2169 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2170 result = NT_STATUS_OK;
2173 if (!NT_STATUS_IS_OK(result)) {
2174 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2175 goto process_result;
2180 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2182 NTSTATUS policy_ret;
2184 policy_ret = fillup_password_policy(contact_domain, state);
2186 /* failure of this is non critical, it will just provide no
2187 * additional information to the client why the change has
2188 * failed - Guenther */
2190 if (!NT_STATUS_IS_OK(policy_ret)) {
2191 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2192 goto process_result;
2196 process_result:
2198 set_auth_errors(state->response, result);
2200 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2201 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2202 domain,
2203 user,
2204 state->response->data.auth.nt_status_string,
2205 state->response->data.auth.pam_error));
2207 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2210 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2212 struct winbindd_domain *domain;
2213 fstring name_domain, user;
2214 uid_t caller_uid = (uid_t)-1;
2215 uid_t request_uid = state->request->data.logoff.uid;
2217 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2218 state->request->data.logoff.user));
2220 /* Ensure null termination */
2221 state->request->data.logoff.user
2222 [sizeof(state->request->data.logoff.user)-1]='\0';
2224 state->request->data.logoff.krb5ccname
2225 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2227 if (request_uid == (gid_t)-1) {
2228 goto failed;
2231 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2232 goto failed;
2235 if ((domain = find_auth_domain(state->request->flags,
2236 name_domain)) == NULL) {
2237 goto failed;
2240 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2241 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2242 strerror(errno)));
2243 goto failed;
2246 switch (caller_uid) {
2247 case -1:
2248 goto failed;
2249 case 0:
2250 /* root must be able to logoff any user - gd */
2251 state->request->data.logoff.uid = request_uid;
2252 break;
2253 default:
2254 if (caller_uid != request_uid) {
2255 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2256 goto failed;
2258 state->request->data.logoff.uid = caller_uid;
2259 break;
2262 sendto_domain(state, domain);
2263 return;
2265 failed:
2266 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2267 DEBUG(5, ("Pam Logoff for %s returned %s "
2268 "(PAM: %d)\n",
2269 state->request->data.logoff.user,
2270 state->response->data.auth.nt_status_string,
2271 state->response->data.auth.pam_error));
2272 request_error(state);
2273 return;
2276 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2277 struct winbindd_cli_state *state)
2279 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2281 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2282 state->request->data.logoff.user));
2284 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2285 result = NT_STATUS_OK;
2286 goto process_result;
2289 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2290 result = NT_STATUS_OK;
2291 goto process_result;
2294 #ifdef HAVE_KRB5
2296 if (state->request->data.logoff.uid < 0) {
2297 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2298 goto process_result;
2301 /* what we need here is to find the corresponding krb5 ccache name *we*
2302 * created for a given username and destroy it */
2304 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2305 result = NT_STATUS_OK;
2306 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2307 goto process_result;
2310 if (!ccache_entry_identical(state->request->data.logoff.user,
2311 state->request->data.logoff.uid,
2312 state->request->data.logoff.krb5ccname)) {
2313 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2314 goto process_result;
2317 result = remove_ccache(state->request->data.logoff.user);
2318 if (!NT_STATUS_IS_OK(result)) {
2319 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2320 nt_errstr(result)));
2321 goto process_result;
2324 #else
2325 result = NT_STATUS_NOT_SUPPORTED;
2326 #endif
2328 process_result:
2330 winbindd_delete_memory_creds(state->request->data.logoff.user);
2332 set_auth_errors(state->response, result);
2334 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2337 /* Change user password with auth crap*/
2339 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2341 struct winbindd_domain *domain = NULL;
2342 const char *domain_name = NULL;
2344 /* Ensure null termination */
2345 state->request->data.chng_pswd_auth_crap.user[
2346 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2347 state->request->data.chng_pswd_auth_crap.domain[
2348 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2350 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2351 (unsigned long)state->pid,
2352 state->request->data.chng_pswd_auth_crap.domain,
2353 state->request->data.chng_pswd_auth_crap.user));
2355 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2356 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2357 } else if (lp_winbind_use_default_domain()) {
2358 domain_name = lp_workgroup();
2361 if (domain_name != NULL)
2362 domain = find_domain_from_name(domain_name);
2364 if (domain != NULL) {
2365 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2366 "%s\n", (unsigned long)state->pid,domain->name));
2367 sendto_domain(state, domain);
2368 return;
2371 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2372 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2373 state->request->data.chng_pswd_auth_crap.domain,
2374 state->request->data.chng_pswd_auth_crap.user,
2375 state->response->data.auth.nt_status_string,
2376 state->response->data.auth.pam_error));
2377 request_error(state);
2378 return;
2381 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2383 NTSTATUS result;
2384 DATA_BLOB new_nt_password;
2385 DATA_BLOB old_nt_hash_enc;
2386 DATA_BLOB new_lm_password;
2387 DATA_BLOB old_lm_hash_enc;
2388 fstring domain,user;
2389 struct policy_handle dom_pol;
2390 struct winbindd_domain *contact_domain = domainSt;
2391 struct rpc_pipe_client *cli;
2393 /* Ensure null termination */
2394 state->request->data.chng_pswd_auth_crap.user[
2395 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2396 state->request->data.chng_pswd_auth_crap.domain[
2397 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2398 *domain = 0;
2399 *user = 0;
2401 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2402 (unsigned long)state->pid,
2403 state->request->data.chng_pswd_auth_crap.domain,
2404 state->request->data.chng_pswd_auth_crap.user));
2406 if (lp_winbind_offline_logon()) {
2407 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2408 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2409 result = NT_STATUS_ACCESS_DENIED;
2410 goto done;
2413 if (*state->request->data.chng_pswd_auth_crap.domain) {
2414 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2415 } else {
2416 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2417 domain, user);
2419 if(!*domain) {
2420 DEBUG(3,("no domain specified with username (%s) - "
2421 "failing auth\n",
2422 state->request->data.chng_pswd_auth_crap.user));
2423 result = NT_STATUS_NO_SUCH_USER;
2424 goto done;
2428 if (!*domain && lp_winbind_use_default_domain()) {
2429 fstrcpy(domain,(char *)lp_workgroup());
2432 if(!*user) {
2433 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2436 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2437 (unsigned long)state->pid, domain, user));
2439 /* Change password */
2440 new_nt_password = data_blob_talloc(
2441 state->mem_ctx,
2442 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2443 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2445 old_nt_hash_enc = data_blob_talloc(
2446 state->mem_ctx,
2447 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2448 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2450 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2451 new_lm_password = data_blob_talloc(
2452 state->mem_ctx,
2453 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2454 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2456 old_lm_hash_enc = data_blob_talloc(
2457 state->mem_ctx,
2458 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2459 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2460 } else {
2461 new_lm_password.length = 0;
2462 old_lm_hash_enc.length = 0;
2465 /* Get sam handle */
2467 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2468 if (!NT_STATUS_IS_OK(result)) {
2469 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2470 goto done;
2473 result = rpccli_samr_chng_pswd_auth_crap(
2474 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2475 new_lm_password, old_lm_hash_enc);
2477 done:
2479 set_auth_errors(state->response, result);
2481 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2482 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2483 domain, user,
2484 state->response->data.auth.nt_status_string,
2485 state->response->data.auth.pam_error));
2487 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;