Fix a bunch of compiler warnings about wrong format types.
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
blobbbf3526fd04f847ed81ee2b3bf95b4843d4a164d
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 size_t size;
39 uint32_t i;
41 state->response.data.auth.info3.logon_time =
42 nt_time_to_unix(info3->base.last_logon);
43 state->response.data.auth.info3.logoff_time =
44 nt_time_to_unix(info3->base.last_logoff);
45 state->response.data.auth.info3.kickoff_time =
46 nt_time_to_unix(info3->base.acct_expiry);
47 state->response.data.auth.info3.pass_last_set_time =
48 nt_time_to_unix(info3->base.last_password_change);
49 state->response.data.auth.info3.pass_can_change_time =
50 nt_time_to_unix(info3->base.allow_password_change);
51 state->response.data.auth.info3.pass_must_change_time =
52 nt_time_to_unix(info3->base.force_password_change);
54 state->response.data.auth.info3.logon_count = info3->base.logon_count;
55 state->response.data.auth.info3.bad_pw_count = info3->base.bad_password_count;
57 state->response.data.auth.info3.user_rid = info3->base.rid;
58 state->response.data.auth.info3.group_rid = info3->base.primary_gid;
59 sid_to_fstring(state->response.data.auth.info3.dom_sid, info3->base.domain_sid);
61 state->response.data.auth.info3.num_groups = info3->base.groups.count;
62 state->response.data.auth.info3.user_flgs = info3->base.user_flags;
64 state->response.data.auth.info3.acct_flags = info3->base.acct_flags;
65 state->response.data.auth.info3.num_other_sids = info3->sidcount;
67 fstrcpy(state->response.data.auth.info3.user_name,
68 info3->base.account_name.string);
69 fstrcpy(state->response.data.auth.info3.full_name,
70 info3->base.full_name.string);
71 fstrcpy(state->response.data.auth.info3.logon_script,
72 info3->base.logon_script.string);
73 fstrcpy(state->response.data.auth.info3.profile_path,
74 info3->base.profile_path.string);
75 fstrcpy(state->response.data.auth.info3.home_dir,
76 info3->base.home_directory.string);
77 fstrcpy(state->response.data.auth.info3.dir_drive,
78 info3->base.home_drive.string);
80 fstrcpy(state->response.data.auth.info3.logon_srv,
81 info3->base.logon_server.string);
82 fstrcpy(state->response.data.auth.info3.logon_dom,
83 info3->base.domain.string);
85 ex = talloc_strdup(mem_ctx, "");
86 NT_STATUS_HAVE_NO_MEMORY(ex);
88 for (i=0; i < info3->base.groups.count; i++) {
89 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
90 info3->base.groups.rids[i].rid,
91 info3->base.groups.rids[i].attributes);
92 NT_STATUS_HAVE_NO_MEMORY(ex);
95 for (i=0; i < info3->sidcount; i++) {
96 char *sid;
98 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
99 NT_STATUS_HAVE_NO_MEMORY(sid);
101 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
102 sid,
103 info3->sids[i].attributes);
104 NT_STATUS_HAVE_NO_MEMORY(ex);
106 talloc_free(sid);
109 size = talloc_get_size(ex);
111 SAFE_FREE(state->response.extra_data.data);
112 state->response.extra_data.data = SMB_MALLOC(size);
113 if (!state->response.extra_data.data) {
114 return NT_STATUS_NO_MEMORY;
116 memcpy(state->response.extra_data.data, ex, size);
117 talloc_free(ex);
119 state->response.length += size;
121 return NT_STATUS_OK;
124 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
125 struct winbindd_cli_state *state,
126 struct netr_SamInfo3 *info3)
128 DATA_BLOB blob;
129 enum ndr_err_code ndr_err;
131 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
132 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
133 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
134 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
135 return ndr_map_error2ntstatus(ndr_err);
138 SAFE_FREE(state->response.extra_data.data);
139 state->response.extra_data.data = SMB_MALLOC(blob.length);
140 if (!state->response.extra_data.data) {
141 data_blob_free(&blob);
142 return NT_STATUS_NO_MEMORY;
145 memset(state->response.extra_data.data, '\0', blob.length);
146 memcpy(state->response.extra_data.data, blob.data, blob.length);
147 state->response.length += blob.length;
149 data_blob_free(&blob);
151 return NT_STATUS_OK;
154 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
155 struct winbindd_cli_state *state,
156 const struct netr_SamInfo3 *info3,
157 const char *name_domain,
158 const char *name_user)
160 /* We've been asked to return the unix username, per
161 'winbind use default domain' settings and the like */
163 const char *nt_username, *nt_domain;
165 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
166 if (!nt_domain) {
167 /* If the server didn't give us one, just use the one
168 * we sent them */
169 nt_domain = name_domain;
172 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
173 if (!nt_username) {
174 /* If the server didn't give us one, just use the one
175 * we sent them */
176 nt_username = name_user;
179 fill_domain_username(state->response.data.auth.unix_username,
180 nt_domain, nt_username, true);
182 DEBUG(5,("Setting unix username to [%s]\n",
183 state->response.data.auth.unix_username));
185 return NT_STATUS_OK;
188 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
189 struct winbindd_cli_state *state,
190 const struct netr_SamInfo3 *info3,
191 const char *name_domain,
192 const char *name_user)
194 char *afsname = NULL;
195 char *cell;
197 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
198 if (afsname == NULL) {
199 return NT_STATUS_NO_MEMORY;
202 afsname = talloc_string_sub(mem_ctx,
203 lp_afs_username_map(),
204 "%D", name_domain);
205 afsname = talloc_string_sub(mem_ctx, afsname,
206 "%u", name_user);
207 afsname = talloc_string_sub(mem_ctx, afsname,
208 "%U", name_user);
211 DOM_SID user_sid;
212 fstring sidstr;
214 sid_copy(&user_sid, info3->base.domain_sid);
215 sid_append_rid(&user_sid, info3->base.rid);
216 sid_to_fstring(sidstr, &user_sid);
217 afsname = talloc_string_sub(mem_ctx, afsname,
218 "%s", sidstr);
221 if (afsname == NULL) {
222 return NT_STATUS_NO_MEMORY;
225 strlower_m(afsname);
227 DEBUG(10, ("Generating token for user %s\n", afsname));
229 cell = strchr(afsname, '@');
231 if (cell == NULL) {
232 return NT_STATUS_NO_MEMORY;
235 *cell = '\0';
236 cell += 1;
238 /* Append an AFS token string */
239 SAFE_FREE(state->response.extra_data.data);
240 state->response.extra_data.data =
241 afs_createtoken_str(afsname, cell);
243 if (state->response.extra_data.data != NULL) {
244 state->response.length +=
245 strlen((const char *)state->response.extra_data.data)+1;
248 return NT_STATUS_OK;
251 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
252 struct netr_SamInfo3 *info3,
253 const char *group_sid)
255 * Check whether a user belongs to a group or list of groups.
257 * @param mem_ctx talloc memory context.
258 * @param info3 user information, including group membership info.
259 * @param group_sid One or more groups , separated by commas.
261 * @return NT_STATUS_OK on success,
262 * NT_STATUS_LOGON_FAILURE if the user does not belong,
263 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
266 DOM_SID *require_membership_of_sid;
267 size_t num_require_membership_of_sid;
268 char *req_sid;
269 const char *p;
270 DOM_SID sid;
271 size_t i;
272 struct nt_user_token *token;
273 TALLOC_CTX *frame = NULL;
274 NTSTATUS status;
276 /* Parse the 'required group' SID */
278 if (!group_sid || !group_sid[0]) {
279 /* NO sid supplied, all users may access */
280 return NT_STATUS_OK;
283 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
284 DEBUG(0, ("talloc failed\n"));
285 return NT_STATUS_NO_MEMORY;
288 num_require_membership_of_sid = 0;
289 require_membership_of_sid = NULL;
291 p = group_sid;
293 frame = talloc_stackframe();
294 while (next_token_talloc(frame, &p, &req_sid, ",")) {
295 if (!string_to_sid(&sid, req_sid)) {
296 DEBUG(0, ("check_info3_in_group: could not parse %s "
297 "as a SID!", req_sid));
298 TALLOC_FREE(frame);
299 return NT_STATUS_INVALID_PARAMETER;
302 status = add_sid_to_array(mem_ctx, &sid,
303 &require_membership_of_sid,
304 &num_require_membership_of_sid);
305 if (!NT_STATUS_IS_OK(status)) {
306 DEBUG(0, ("add_sid_to_array failed\n"));
307 TALLOC_FREE(frame);
308 return status;
312 TALLOC_FREE(frame);
314 status = sid_array_from_info3(mem_ctx, info3,
315 &token->user_sids,
316 &token->num_sids,
317 true, false);
318 if (!NT_STATUS_IS_OK(status)) {
319 return status;
322 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
323 token))
324 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
325 token))) {
326 DEBUG(3, ("could not add aliases: %s\n",
327 nt_errstr(status)));
328 return status;
331 debug_nt_user_token(DBGC_CLASS, 10, token);
333 for (i=0; i<num_require_membership_of_sid; i++) {
334 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
335 &require_membership_of_sid[i])));
336 if (nt_token_check_sid(&require_membership_of_sid[i],
337 token)) {
338 DEBUG(10, ("Access ok\n"));
339 return NT_STATUS_OK;
343 /* Do not distinguish this error from a wrong username/pw */
345 return NT_STATUS_LOGON_FAILURE;
348 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
349 const char *domain_name)
351 struct winbindd_domain *domain;
353 if (IS_DC) {
354 domain = find_domain_from_name_noinit(domain_name);
355 if (domain == NULL) {
356 DEBUG(3, ("Authentication for domain [%s] refused "
357 "as it is not a trusted domain\n",
358 domain_name));
360 return domain;
363 if (is_myname(domain_name)) {
364 DEBUG(3, ("Authentication for domain %s (local domain "
365 "to this server) not supported at this "
366 "stage\n", domain_name));
367 return NULL;
370 /* we can auth against trusted domains */
371 if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
372 domain = find_domain_from_name_noinit(domain_name);
373 if (domain == NULL) {
374 DEBUG(3, ("Authentication for domain [%s] skipped "
375 "as it is not a trusted domain\n",
376 domain_name));
377 } else {
378 return domain;
382 return find_our_domain();
385 static void fill_in_password_policy(struct winbindd_response *r,
386 const struct samr_DomInfo1 *p)
388 r->data.auth.policy.min_length_password =
389 p->min_password_length;
390 r->data.auth.policy.password_history =
391 p->password_history_length;
392 r->data.auth.policy.password_properties =
393 p->password_properties;
394 r->data.auth.policy.expire =
395 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
396 r->data.auth.policy.min_passwordage =
397 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
400 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
401 struct winbindd_cli_state *state)
403 struct winbindd_methods *methods;
404 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
405 struct samr_DomInfo1 password_policy;
407 if ( !winbindd_can_contact_domain( domain ) ) {
408 DEBUG(5,("fillup_password_policy: No inbound trust to "
409 "contact domain %s\n", domain->name));
410 return NT_STATUS_NOT_SUPPORTED;
413 methods = domain->methods;
415 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
416 if (NT_STATUS_IS_ERR(status)) {
417 return status;
420 fill_in_password_policy(&state->response, &password_policy);
422 return NT_STATUS_OK;
425 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
426 TALLOC_CTX *mem_ctx,
427 uint16 *lockout_threshold)
429 struct winbindd_methods *methods;
430 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
431 struct samr_DomInfo12 lockout_policy;
433 *lockout_threshold = 0;
435 methods = domain->methods;
437 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
438 if (NT_STATUS_IS_ERR(status)) {
439 return status;
442 *lockout_threshold = lockout_policy.lockout_threshold;
444 return NT_STATUS_OK;
447 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
448 TALLOC_CTX *mem_ctx,
449 uint32 *password_properties)
451 struct winbindd_methods *methods;
452 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
453 struct samr_DomInfo1 password_policy;
455 *password_properties = 0;
457 methods = domain->methods;
459 status = methods->password_policy(domain, mem_ctx, &password_policy);
460 if (NT_STATUS_IS_ERR(status)) {
461 return status;
464 *password_properties = password_policy.password_properties;
466 return NT_STATUS_OK;
469 #ifdef HAVE_KRB5
471 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
472 const char *type,
473 uid_t uid,
474 bool *internal_ccache)
476 /* accept FILE and WRFILE as krb5_cc_type from the client and then
477 * build the full ccname string based on the user's uid here -
478 * Guenther*/
480 const char *gen_cc = NULL;
482 *internal_ccache = true;
484 if (uid == -1) {
485 goto memory_ccache;
488 if (!type || type[0] == '\0') {
489 goto memory_ccache;
492 if (strequal(type, "FILE")) {
493 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
494 } else if (strequal(type, "WRFILE")) {
495 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
496 } else {
497 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
498 goto memory_ccache;
501 *internal_ccache = false;
502 goto done;
504 memory_ccache:
505 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
507 done:
508 if (gen_cc == NULL) {
509 DEBUG(0,("out of memory\n"));
510 return NULL;
513 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
515 return gen_cc;
518 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
520 const char *type = state->request.data.auth.krb5_cc_type;
522 state->response.data.auth.krb5ccname[0] = '\0';
524 if (type[0] == '\0') {
525 return;
528 if (!strequal(type, "FILE") &&
529 !strequal(type, "WRFILE")) {
530 DEBUG(10,("won't return krbccname for a %s type ccache\n",
531 type));
532 return;
535 fstrcpy(state->response.data.auth.krb5ccname, cc);
538 #endif
540 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
542 uid_t uid = -1;
544 uid = state->request.data.auth.uid;
546 if (uid < 0) {
547 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
548 return -1;
550 return uid;
553 /**********************************************************************
554 Authenticate a user with a clear text password using Kerberos and fill up
555 ccache if required
556 **********************************************************************/
558 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
559 struct winbindd_cli_state *state,
560 struct netr_SamInfo3 **info3)
562 #ifdef HAVE_KRB5
563 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
564 krb5_error_code krb5_ret;
565 const char *cc = NULL;
566 const char *principal_s = NULL;
567 const char *service = NULL;
568 char *realm = NULL;
569 fstring name_domain, name_user;
570 time_t ticket_lifetime = 0;
571 time_t renewal_until = 0;
572 uid_t uid = -1;
573 ADS_STRUCT *ads;
574 time_t time_offset = 0;
575 bool internal_ccache = true;
577 ZERO_STRUCTP(info3);
579 *info3 = NULL;
581 /* 1st step:
582 * prepare a krb5_cc_cache string for the user */
584 uid = get_uid_from_state(state);
585 if (uid == -1) {
586 DEBUG(0,("no valid uid\n"));
589 cc = generate_krb5_ccache(state->mem_ctx,
590 state->request.data.auth.krb5_cc_type,
591 state->request.data.auth.uid,
592 &internal_ccache);
593 if (cc == NULL) {
594 return NT_STATUS_NO_MEMORY;
598 /* 2nd step:
599 * get kerberos properties */
601 if (domain->private_data) {
602 ads = (ADS_STRUCT *)domain->private_data;
603 time_offset = ads->auth.time_offset;
607 /* 3rd step:
608 * do kerberos auth and setup ccache as the user */
610 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
612 realm = domain->alt_name;
613 strupper_m(realm);
615 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
616 if (principal_s == NULL) {
617 return NT_STATUS_NO_MEMORY;
620 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
621 if (service == NULL) {
622 return NT_STATUS_NO_MEMORY;
625 /* if this is a user ccache, we need to act as the user to let the krb5
626 * library handle the chown, etc. */
628 /************************ ENTERING NON-ROOT **********************/
630 if (!internal_ccache) {
631 set_effective_uid(uid);
632 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
635 result = kerberos_return_info3_from_pac(state->mem_ctx,
636 principal_s,
637 state->request.data.auth.pass,
638 time_offset,
639 &ticket_lifetime,
640 &renewal_until,
642 true,
643 true,
644 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
645 info3);
646 if (!internal_ccache) {
647 gain_root_privilege();
650 /************************ RETURNED TO ROOT **********************/
652 if (!NT_STATUS_IS_OK(result)) {
653 goto failed;
656 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
657 principal_s));
659 /* if we had a user's ccache then return that string for the pam
660 * environment */
662 if (!internal_ccache) {
664 setup_return_cc_name(state, cc);
666 result = add_ccache_to_list(principal_s,
668 service,
669 state->request.data.auth.user,
670 realm,
671 uid,
672 time(NULL),
673 ticket_lifetime,
674 renewal_until,
675 false);
677 if (!NT_STATUS_IS_OK(result)) {
678 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
679 nt_errstr(result)));
681 } else {
683 /* need to delete the memory cred cache, it is not used anymore */
685 krb5_ret = ads_kdestroy(cc);
686 if (krb5_ret) {
687 DEBUG(3,("winbindd_raw_kerberos_login: "
688 "could not destroy krb5 credential cache: "
689 "%s\n", error_message(krb5_ret)));
694 return NT_STATUS_OK;
696 failed:
698 /* we could have created a new credential cache with a valid tgt in it
699 * but we werent able to get or verify the service ticket for this
700 * local host and therefor didn't get the PAC, we need to remove that
701 * cache entirely now */
703 krb5_ret = ads_kdestroy(cc);
704 if (krb5_ret) {
705 DEBUG(3,("winbindd_raw_kerberos_login: "
706 "could not destroy krb5 credential cache: "
707 "%s\n", error_message(krb5_ret)));
710 if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
711 DEBUG(3,("winbindd_raw_kerberos_login: "
712 "could not remove ccache for user %s\n",
713 state->request.data.auth.user));
716 return result;
717 #else
718 return NT_STATUS_NOT_SUPPORTED;
719 #endif /* HAVE_KRB5 */
722 /****************************************************************
723 ****************************************************************/
725 static bool check_request_flags(uint32_t flags)
727 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
728 WBFLAG_PAM_INFO3_TEXT |
729 WBFLAG_PAM_INFO3_NDR;
731 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
732 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
733 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
734 !(flags & flags_edata) ) {
735 return true;
738 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags));
740 return false;
743 /****************************************************************
744 ****************************************************************/
746 static NTSTATUS append_data(struct winbindd_cli_state *state,
747 struct netr_SamInfo3 *info3,
748 const char *name_domain,
749 const char *name_user)
751 NTSTATUS result;
752 uint32_t flags = state->request.flags;
754 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
755 memcpy(state->response.data.auth.user_session_key,
756 info3->base.key.key,
757 sizeof(state->response.data.auth.user_session_key)
758 /* 16 */);
761 if (flags & WBFLAG_PAM_LMKEY) {
762 memcpy(state->response.data.auth.first_8_lm_hash,
763 info3->base.LMSessKey.key,
764 sizeof(state->response.data.auth.first_8_lm_hash)
765 /* 8 */);
768 if (flags & WBFLAG_PAM_INFO3_TEXT) {
769 result = append_info3_as_txt(state->mem_ctx, state, info3);
770 if (!NT_STATUS_IS_OK(result)) {
771 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
772 nt_errstr(result)));
773 return result;
777 /* currently, anything from here on potentially overwrites extra_data. */
779 if (flags & WBFLAG_PAM_INFO3_NDR) {
780 result = append_info3_as_ndr(state->mem_ctx, state, info3);
781 if (!NT_STATUS_IS_OK(result)) {
782 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
783 nt_errstr(result)));
784 return result;
788 if (flags & WBFLAG_PAM_UNIX_NAME) {
789 result = append_unix_username(state->mem_ctx, state, info3,
790 name_domain, name_user);
791 if (!NT_STATUS_IS_OK(result)) {
792 DEBUG(10,("Failed to append Unix Username: %s\n",
793 nt_errstr(result)));
794 return result;
798 if (flags & WBFLAG_PAM_AFS_TOKEN) {
799 result = append_afs_token(state->mem_ctx, state, info3,
800 name_domain, name_user);
801 if (!NT_STATUS_IS_OK(result)) {
802 DEBUG(10,("Failed to append AFS token: %s\n",
803 nt_errstr(result)));
804 return result;
808 return NT_STATUS_OK;
811 void winbindd_pam_auth(struct winbindd_cli_state *state)
813 struct winbindd_domain *domain;
814 fstring name_domain, name_user;
815 char *mapped_user = NULL;
816 NTSTATUS result;
817 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
819 /* Ensure null termination */
820 state->request.data.auth.user
821 [sizeof(state->request.data.auth.user)-1]='\0';
823 /* Ensure null termination */
824 state->request.data.auth.pass
825 [sizeof(state->request.data.auth.pass)-1]='\0';
827 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
828 state->request.data.auth.user));
830 if (!check_request_flags(state->request.flags)) {
831 result = NT_STATUS_INVALID_PARAMETER_MIX;
832 goto done;
835 /* Parse domain and username */
837 name_map_status = normalize_name_unmap(state->mem_ctx,
838 state->request.data.auth.user,
839 &mapped_user);
841 /* If the name normalization didnt' actually do anything,
842 just use the original name */
844 if (!NT_STATUS_IS_OK(name_map_status) &&
845 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
847 mapped_user = state->request.data.auth.user;
850 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
851 result = NT_STATUS_NO_SUCH_USER;
852 goto done;
855 domain = find_auth_domain(state, name_domain);
857 if (domain == NULL) {
858 result = NT_STATUS_NO_SUCH_USER;
859 goto done;
862 sendto_domain(state, domain);
863 return;
864 done:
865 set_auth_errors(&state->response, result);
866 DEBUG(5, ("Plain text authentication for %s returned %s "
867 "(PAM: %d)\n",
868 state->request.data.auth.user,
869 state->response.data.auth.nt_status_string,
870 state->response.data.auth.pam_error));
871 request_error(state);
874 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
875 struct winbindd_cli_state *state,
876 struct netr_SamInfo3 **info3)
878 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
879 uint16 max_allowed_bad_attempts;
880 fstring name_domain, name_user;
881 DOM_SID sid;
882 enum lsa_SidType type;
883 uchar new_nt_pass[NT_HASH_LEN];
884 const uint8 *cached_nt_pass;
885 const uint8 *cached_salt;
886 struct netr_SamInfo3 *my_info3;
887 time_t kickoff_time, must_change_time;
888 bool password_good = false;
889 #ifdef HAVE_KRB5
890 struct winbindd_tdc_domain *tdc_domain = NULL;
891 #endif
893 *info3 = NULL;
895 ZERO_STRUCTP(info3);
897 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
899 /* Parse domain and username */
901 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
904 if (!lookup_cached_name(state->mem_ctx,
905 name_domain,
906 name_user,
907 &sid,
908 &type)) {
909 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
910 return NT_STATUS_NO_SUCH_USER;
913 if (type != SID_NAME_USER) {
914 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
915 return NT_STATUS_LOGON_FAILURE;
918 result = winbindd_get_creds(domain,
919 state->mem_ctx,
920 &sid,
921 &my_info3,
922 &cached_nt_pass,
923 &cached_salt);
924 if (!NT_STATUS_IS_OK(result)) {
925 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
926 return result;
929 *info3 = my_info3;
931 E_md4hash(state->request.data.auth.pass, new_nt_pass);
933 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
934 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
935 if (cached_salt) {
936 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
939 if (cached_salt) {
940 /* In this case we didn't store the nt_hash itself,
941 but the MD5 combination of salt + nt_hash. */
942 uchar salted_hash[NT_HASH_LEN];
943 E_md5hash(cached_salt, new_nt_pass, salted_hash);
945 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
946 true : false;
947 } else {
948 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
949 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
950 true : false;
953 if (password_good) {
955 /* User *DOES* know the password, update logon_time and reset
956 * bad_pw_count */
958 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
960 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
961 return NT_STATUS_ACCOUNT_LOCKED_OUT;
964 if (my_info3->base.acct_flags & ACB_DISABLED) {
965 return NT_STATUS_ACCOUNT_DISABLED;
968 if (my_info3->base.acct_flags & ACB_WSTRUST) {
969 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
972 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
973 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
976 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
977 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
980 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
981 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
982 my_info3->base.acct_flags));
983 return NT_STATUS_LOGON_FAILURE;
986 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
987 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
988 return NT_STATUS_ACCOUNT_EXPIRED;
991 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
992 if (must_change_time != 0 && must_change_time < time(NULL)) {
993 /* we allow grace logons when the password has expired */
994 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
995 /* return NT_STATUS_PASSWORD_EXPIRED; */
996 goto success;
999 #ifdef HAVE_KRB5
1000 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
1001 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
1002 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
1004 uid_t uid = -1;
1005 const char *cc = NULL;
1006 char *realm = NULL;
1007 const char *principal_s = NULL;
1008 const char *service = NULL;
1009 bool internal_ccache = false;
1011 uid = get_uid_from_state(state);
1012 if (uid == -1) {
1013 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1014 return NT_STATUS_INVALID_PARAMETER;
1017 cc = generate_krb5_ccache(state->mem_ctx,
1018 state->request.data.auth.krb5_cc_type,
1019 state->request.data.auth.uid,
1020 &internal_ccache);
1021 if (cc == NULL) {
1022 return NT_STATUS_NO_MEMORY;
1025 realm = domain->alt_name;
1026 strupper_m(realm);
1028 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1029 if (principal_s == NULL) {
1030 return NT_STATUS_NO_MEMORY;
1033 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1034 if (service == NULL) {
1035 return NT_STATUS_NO_MEMORY;
1038 if (!internal_ccache) {
1040 setup_return_cc_name(state, cc);
1042 result = add_ccache_to_list(principal_s,
1044 service,
1045 state->request.data.auth.user,
1046 domain->alt_name,
1047 uid,
1048 time(NULL),
1049 time(NULL) + lp_winbind_cache_time(),
1050 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1051 true);
1053 if (!NT_STATUS_IS_OK(result)) {
1054 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1055 "to add ccache to list: %s\n",
1056 nt_errstr(result)));
1060 #endif /* HAVE_KRB5 */
1061 success:
1062 /* FIXME: we possibly should handle logon hours as well (does xp when
1063 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1065 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1066 my_info3->base.bad_password_count = 0;
1068 result = winbindd_update_creds_by_info3(domain,
1069 state->mem_ctx,
1070 state->request.data.auth.user,
1071 state->request.data.auth.pass,
1072 my_info3);
1073 if (!NT_STATUS_IS_OK(result)) {
1074 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1075 nt_errstr(result)));
1076 return result;
1079 return NT_STATUS_OK;
1083 /* User does *NOT* know the correct password, modify info3 accordingly */
1085 /* failure of this is not critical */
1086 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1087 if (!NT_STATUS_IS_OK(result)) {
1088 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1089 "Won't be able to honour account lockout policies\n"));
1092 /* increase counter */
1093 my_info3->base.bad_password_count++;
1095 if (max_allowed_bad_attempts == 0) {
1096 goto failed;
1099 /* lockout user */
1100 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1102 uint32 password_properties;
1104 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1105 if (!NT_STATUS_IS_OK(result)) {
1106 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1109 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1110 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1111 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1115 failed:
1116 result = winbindd_update_creds_by_info3(domain,
1117 state->mem_ctx,
1118 state->request.data.auth.user,
1119 NULL,
1120 my_info3);
1122 if (!NT_STATUS_IS_OK(result)) {
1123 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1124 nt_errstr(result)));
1127 return NT_STATUS_LOGON_FAILURE;
1130 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1131 struct winbindd_cli_state *state,
1132 struct netr_SamInfo3 **info3)
1134 struct winbindd_domain *contact_domain;
1135 fstring name_domain, name_user;
1136 NTSTATUS result;
1138 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1140 /* Parse domain and username */
1142 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1144 /* what domain should we contact? */
1146 if ( IS_DC ) {
1147 if (!(contact_domain = find_domain_from_name(name_domain))) {
1148 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1149 state->request.data.auth.user, name_domain, name_user, name_domain));
1150 result = NT_STATUS_NO_SUCH_USER;
1151 goto done;
1154 } else {
1155 if (is_myname(name_domain)) {
1156 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1157 result = NT_STATUS_NO_SUCH_USER;
1158 goto done;
1161 contact_domain = find_domain_from_name(name_domain);
1162 if (contact_domain == NULL) {
1163 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1164 state->request.data.auth.user, name_domain, name_user, name_domain));
1166 contact_domain = find_our_domain();
1170 if (contact_domain->initialized &&
1171 contact_domain->active_directory) {
1172 goto try_login;
1175 if (!contact_domain->initialized) {
1176 init_dc_connection(contact_domain);
1179 if (!contact_domain->active_directory) {
1180 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1181 return NT_STATUS_INVALID_LOGON_TYPE;
1183 try_login:
1184 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1185 done:
1186 return result;
1189 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1190 TALLOC_CTX *mem_ctx,
1191 uint32 logon_parameters,
1192 const char *server,
1193 const char *username,
1194 const char *domain,
1195 const char *workstation,
1196 const uint8 chal[8],
1197 DATA_BLOB lm_response,
1198 DATA_BLOB nt_response,
1199 struct netr_SamInfo3 **info3);
1201 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1202 struct winbindd_cli_state *state,
1203 struct netr_SamInfo3 **info3)
1206 struct rpc_pipe_client *netlogon_pipe;
1207 uchar chal[8];
1208 DATA_BLOB lm_resp;
1209 DATA_BLOB nt_resp;
1210 int attempts = 0;
1211 unsigned char local_lm_response[24];
1212 unsigned char local_nt_response[24];
1213 struct winbindd_domain *contact_domain;
1214 fstring name_domain, name_user;
1215 bool retry;
1216 NTSTATUS result;
1217 struct netr_SamInfo3 *my_info3 = NULL;
1219 *info3 = NULL;
1221 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1223 /* Parse domain and username */
1225 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1227 /* do password magic */
1230 generate_random_buffer(chal, 8);
1231 if (lp_client_ntlmv2_auth()) {
1232 DATA_BLOB server_chal;
1233 DATA_BLOB names_blob;
1234 DATA_BLOB nt_response;
1235 DATA_BLOB lm_response;
1236 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1238 /* note that the 'workgroup' here is a best guess - we don't know
1239 the server's domain at this point. The 'server name' is also
1240 dodgy...
1242 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1244 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1245 state->request.data.auth.pass,
1246 &server_chal,
1247 &names_blob,
1248 &lm_response, &nt_response, NULL, NULL)) {
1249 data_blob_free(&names_blob);
1250 data_blob_free(&server_chal);
1251 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1252 result = NT_STATUS_NO_MEMORY;
1253 goto done;
1255 data_blob_free(&names_blob);
1256 data_blob_free(&server_chal);
1257 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1258 lm_response.length);
1259 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1260 nt_response.length);
1261 data_blob_free(&lm_response);
1262 data_blob_free(&nt_response);
1264 } else {
1265 if (lp_client_lanman_auth()
1266 && SMBencrypt(state->request.data.auth.pass,
1267 chal,
1268 local_lm_response)) {
1269 lm_resp = data_blob_talloc(state->mem_ctx,
1270 local_lm_response,
1271 sizeof(local_lm_response));
1272 } else {
1273 lm_resp = data_blob_null;
1275 SMBNTencrypt(state->request.data.auth.pass,
1276 chal,
1277 local_nt_response);
1279 nt_resp = data_blob_talloc(state->mem_ctx,
1280 local_nt_response,
1281 sizeof(local_nt_response));
1284 /* what domain should we contact? */
1286 if ( IS_DC ) {
1287 if (!(contact_domain = find_domain_from_name(name_domain))) {
1288 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1289 state->request.data.auth.user, name_domain, name_user, name_domain));
1290 result = NT_STATUS_NO_SUCH_USER;
1291 goto done;
1294 } else {
1295 if (is_myname(name_domain)) {
1296 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1297 result = NT_STATUS_NO_SUCH_USER;
1298 goto done;
1301 contact_domain = find_our_domain();
1304 /* check authentication loop */
1306 do {
1307 netlogon_fn_t logon_fn;
1309 ZERO_STRUCTP(my_info3);
1310 retry = false;
1312 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1314 if (!NT_STATUS_IS_OK(result)) {
1315 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1316 goto done;
1319 /* It is really important to try SamLogonEx here,
1320 * because in a clustered environment, we want to use
1321 * one machine account from multiple physical
1322 * computers.
1324 * With a normal SamLogon call, we must keep the
1325 * credentials chain updated and intact between all
1326 * users of the machine account (which would imply
1327 * cross-node communication for every NTLM logon).
1329 * (The credentials chain is not per NETLOGON pipe
1330 * connection, but globally on the server/client pair
1331 * by machine name).
1333 * When using SamLogonEx, the credentials are not
1334 * supplied, but the session key is implied by the
1335 * wrapping SamLogon context.
1337 * -- abartlet 21 April 2008
1340 logon_fn = contact_domain->can_do_samlogon_ex
1341 ? rpccli_netlogon_sam_network_logon_ex
1342 : rpccli_netlogon_sam_network_logon;
1344 result = logon_fn(netlogon_pipe,
1345 state->mem_ctx,
1347 contact_domain->dcname, /* server name */
1348 name_user, /* user name */
1349 name_domain, /* target domain */
1350 global_myname(), /* workstation */
1351 chal,
1352 lm_resp,
1353 nt_resp,
1354 &my_info3);
1355 attempts += 1;
1357 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1358 && contact_domain->can_do_samlogon_ex) {
1359 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1360 "retrying with NetSamLogon\n"));
1361 contact_domain->can_do_samlogon_ex = false;
1362 retry = true;
1363 continue;
1366 /* We have to try a second time as cm_connect_netlogon
1367 might not yet have noticed that the DC has killed
1368 our connection. */
1370 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1371 retry = true;
1372 continue;
1375 /* if we get access denied, a possible cause was that we had
1376 and open connection to the DC, but someone changed our
1377 machine account password out from underneath us using 'net
1378 rpc changetrustpw' */
1380 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1381 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1382 "ACCESS_DENIED. Maybe the trust account "
1383 "password was changed and we didn't know it. "
1384 "Killing connections to domain %s\n",
1385 name_domain));
1386 invalidate_cm_connection(&contact_domain->conn);
1387 retry = true;
1390 } while ( (attempts < 2) && retry );
1392 /* handle the case where a NT4 DC does not fill in the acct_flags in
1393 * the samlogon reply info3. When accurate info3 is required by the
1394 * caller, we look up the account flags ourselve - gd */
1396 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1397 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1399 struct rpc_pipe_client *samr_pipe;
1400 struct policy_handle samr_domain_handle, user_pol;
1401 union samr_UserInfo *info = NULL;
1402 NTSTATUS status_tmp;
1403 uint32 acct_flags;
1405 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1406 &samr_pipe, &samr_domain_handle);
1408 if (!NT_STATUS_IS_OK(status_tmp)) {
1409 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1410 nt_errstr(status_tmp)));
1411 goto done;
1414 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1415 &samr_domain_handle,
1416 MAXIMUM_ALLOWED_ACCESS,
1417 my_info3->base.rid,
1418 &user_pol);
1420 if (!NT_STATUS_IS_OK(status_tmp)) {
1421 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1422 nt_errstr(status_tmp)));
1423 goto done;
1426 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1427 &user_pol,
1429 &info);
1431 if (!NT_STATUS_IS_OK(status_tmp)) {
1432 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1433 nt_errstr(status_tmp)));
1434 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1435 goto done;
1438 acct_flags = info->info16.acct_flags;
1440 if (acct_flags == 0) {
1441 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1442 goto done;
1445 my_info3->base.acct_flags = acct_flags;
1447 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1449 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1452 *info3 = my_info3;
1453 done:
1454 return result;
1457 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1458 struct winbindd_cli_state *state)
1460 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1461 NTSTATUS krb5_result = NT_STATUS_OK;
1462 fstring name_domain, name_user;
1463 char *mapped_user;
1464 fstring domain_user;
1465 struct netr_SamInfo3 *info3 = NULL;
1466 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1468 /* Ensure null termination */
1469 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1471 /* Ensure null termination */
1472 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1474 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1475 state->request.data.auth.user));
1477 if (!check_request_flags(state->request.flags)) {
1478 result = NT_STATUS_INVALID_PARAMETER_MIX;
1479 goto done;
1482 /* Parse domain and username */
1484 name_map_status = normalize_name_unmap(state->mem_ctx,
1485 state->request.data.auth.user,
1486 &mapped_user);
1488 /* If the name normalization didnt' actually do anything,
1489 just use the original name */
1491 if (!NT_STATUS_IS_OK(name_map_status) &&
1492 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1494 mapped_user = state->request.data.auth.user;
1497 parse_domain_user(mapped_user, name_domain, name_user);
1499 if ( mapped_user != state->request.data.auth.user ) {
1500 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1501 safe_strcpy( state->request.data.auth.user, domain_user,
1502 sizeof(state->request.data.auth.user)-1 );
1505 if (domain->online == false) {
1506 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1507 if (domain->startup) {
1508 /* Logons are very important to users. If we're offline and
1509 we get a request within the first 30 seconds of startup,
1510 try very hard to find a DC and go online. */
1512 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1513 "request in startup mode.\n", domain->name ));
1515 winbindd_flush_negative_conn_cache(domain);
1516 result = init_dc_connection(domain);
1520 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1522 /* Check for Kerberos authentication */
1523 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1525 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1526 /* save for later */
1527 krb5_result = result;
1530 if (NT_STATUS_IS_OK(result)) {
1531 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1532 goto process_result;
1533 } else {
1534 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1537 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1538 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1540 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1541 set_domain_offline( domain );
1542 goto cached_logon;
1545 /* there are quite some NT_STATUS errors where there is no
1546 * point in retrying with a samlogon, we explictly have to take
1547 * care not to increase the bad logon counter on the DC */
1549 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1550 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1551 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1552 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1553 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1554 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1555 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1556 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1557 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1558 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1559 goto process_result;
1562 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1563 DEBUG(3,("falling back to samlogon\n"));
1564 goto sam_logon;
1565 } else {
1566 goto cached_logon;
1570 sam_logon:
1571 /* Check for Samlogon authentication */
1572 if (domain->online) {
1573 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1575 if (NT_STATUS_IS_OK(result)) {
1576 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1577 /* add the Krb5 err if we have one */
1578 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1579 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1581 goto process_result;
1584 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1585 nt_errstr(result)));
1587 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1588 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1589 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1591 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1592 set_domain_offline( domain );
1593 goto cached_logon;
1596 if (domain->online) {
1597 /* We're still online - fail. */
1598 goto done;
1602 cached_logon:
1603 /* Check for Cached logons */
1604 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1605 lp_winbind_offline_logon()) {
1607 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1609 if (NT_STATUS_IS_OK(result)) {
1610 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1611 goto process_result;
1612 } else {
1613 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1614 goto done;
1618 process_result:
1620 if (NT_STATUS_IS_OK(result)) {
1622 DOM_SID user_sid;
1624 /* In all codepaths where result == NT_STATUS_OK info3 must have
1625 been initialized. */
1626 if (!info3) {
1627 result = NT_STATUS_INTERNAL_ERROR;
1628 goto done;
1631 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1632 netsamlogon_cache_store(name_user, info3);
1634 /* save name_to_sid info as early as possible (only if
1635 this is our primary domain so we don't invalidate
1636 the cache entry by storing the seq_num for the wrong
1637 domain). */
1638 if ( domain->primary ) {
1639 sid_compose(&user_sid, info3->base.domain_sid,
1640 info3->base.rid);
1641 cache_name2sid(domain, name_domain, name_user,
1642 SID_NAME_USER, &user_sid);
1645 /* Check if the user is in the right group */
1647 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1648 state->request.data.auth.require_membership_of_sid))) {
1649 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1650 state->request.data.auth.user,
1651 state->request.data.auth.require_membership_of_sid));
1652 goto done;
1655 result = append_data(state, info3, name_domain, name_user);
1656 if (!NT_STATUS_IS_OK(result)) {
1657 goto done;
1660 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1662 /* Store in-memory creds for single-signon using ntlm_auth. */
1663 result = winbindd_add_memory_creds(state->request.data.auth.user,
1664 get_uid_from_state(state),
1665 state->request.data.auth.pass);
1667 if (!NT_STATUS_IS_OK(result)) {
1668 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1669 goto done;
1672 if (lp_winbind_offline_logon()) {
1673 result = winbindd_store_creds(domain,
1674 state->mem_ctx,
1675 state->request.data.auth.user,
1676 state->request.data.auth.pass,
1677 info3, NULL);
1678 if (!NT_STATUS_IS_OK(result)) {
1680 /* Release refcount. */
1681 winbindd_delete_memory_creds(state->request.data.auth.user);
1683 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1684 goto done;
1690 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1691 struct winbindd_domain *our_domain = find_our_domain();
1693 /* This is not entirely correct I believe, but it is
1694 consistent. Only apply the password policy settings
1695 too warn users for our own domain. Cannot obtain these
1696 from trusted DCs all the time so don't do it at all.
1697 -- jerry */
1699 result = NT_STATUS_NOT_SUPPORTED;
1700 if (our_domain == domain ) {
1701 result = fillup_password_policy(our_domain, state);
1704 if (!NT_STATUS_IS_OK(result)
1705 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1707 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1708 domain->name, nt_errstr(result)));
1709 goto done;
1713 result = NT_STATUS_OK;
1716 done:
1717 /* give us a more useful (more correct?) error code */
1718 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1719 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1720 result = NT_STATUS_NO_LOGON_SERVERS;
1723 set_auth_errors(&state->response, result);
1725 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1726 state->request.data.auth.user,
1727 state->response.data.auth.nt_status_string,
1728 state->response.data.auth.pam_error));
1730 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1734 /**********************************************************************
1735 Challenge Response Authentication Protocol
1736 **********************************************************************/
1738 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1740 struct winbindd_domain *domain = NULL;
1741 const char *domain_name = NULL;
1742 NTSTATUS result;
1744 if (!check_request_flags(state->request.flags)) {
1745 result = NT_STATUS_INVALID_PARAMETER_MIX;
1746 goto done;
1749 if (!state->privileged) {
1750 char *error_string = NULL;
1751 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1752 "denied. !\n"));
1753 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1754 "on %s are set correctly.\n",
1755 get_winbind_priv_pipe_dir()));
1756 /* send a better message than ACCESS_DENIED */
1757 error_string = talloc_asprintf(state->mem_ctx,
1758 "winbind client not authorized "
1759 "to use winbindd_pam_auth_crap."
1760 " Ensure permissions on %s "
1761 "are set correctly.",
1762 get_winbind_priv_pipe_dir());
1763 fstrcpy(state->response.data.auth.error_string, error_string);
1764 result = NT_STATUS_ACCESS_DENIED;
1765 goto done;
1768 /* Ensure null termination */
1769 state->request.data.auth_crap.user
1770 [sizeof(state->request.data.auth_crap.user)-1]=0;
1771 state->request.data.auth_crap.domain
1772 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1774 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1775 (unsigned long)state->pid,
1776 state->request.data.auth_crap.domain,
1777 state->request.data.auth_crap.user));
1779 if (*state->request.data.auth_crap.domain != '\0') {
1780 domain_name = state->request.data.auth_crap.domain;
1781 } else if (lp_winbind_use_default_domain()) {
1782 domain_name = lp_workgroup();
1785 if (domain_name != NULL)
1786 domain = find_auth_domain(state, domain_name);
1788 if (domain != NULL) {
1789 sendto_domain(state, domain);
1790 return;
1793 result = NT_STATUS_NO_SUCH_USER;
1795 done:
1796 set_auth_errors(&state->response, result);
1797 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1798 state->request.data.auth_crap.domain,
1799 state->request.data.auth_crap.user,
1800 state->response.data.auth.nt_status_string,
1801 state->response.data.auth.pam_error));
1802 request_error(state);
1803 return;
1807 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1808 struct winbindd_cli_state *state)
1810 NTSTATUS result;
1811 struct netr_SamInfo3 *info3 = NULL;
1812 struct rpc_pipe_client *netlogon_pipe;
1813 const char *name_user = NULL;
1814 const char *name_domain = NULL;
1815 const char *workstation;
1816 struct winbindd_domain *contact_domain;
1817 int attempts = 0;
1818 bool retry;
1820 DATA_BLOB lm_resp, nt_resp;
1822 /* This is child-only, so no check for privileged access is needed
1823 anymore */
1825 /* Ensure null termination */
1826 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1827 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1829 if (!check_request_flags(state->request.flags)) {
1830 result = NT_STATUS_INVALID_PARAMETER_MIX;
1831 goto done;
1834 name_user = state->request.data.auth_crap.user;
1836 if (*state->request.data.auth_crap.domain) {
1837 name_domain = state->request.data.auth_crap.domain;
1838 } else if (lp_winbind_use_default_domain()) {
1839 name_domain = lp_workgroup();
1840 } else {
1841 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1842 name_user));
1843 result = NT_STATUS_NO_SUCH_USER;
1844 goto done;
1847 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1848 name_domain, name_user));
1850 if (*state->request.data.auth_crap.workstation) {
1851 workstation = state->request.data.auth_crap.workstation;
1852 } else {
1853 workstation = global_myname();
1856 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1857 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1858 if (!(state->request.flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1859 state->request.extra_len != state->request.data.auth_crap.nt_resp_len) {
1860 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1861 state->request.data.auth_crap.lm_resp_len,
1862 state->request.data.auth_crap.nt_resp_len));
1863 result = NT_STATUS_INVALID_PARAMETER;
1864 goto done;
1868 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1869 state->request.data.auth_crap.lm_resp_len);
1871 if (state->request.flags & WBFLAG_BIG_NTLMV2_BLOB) {
1872 nt_resp = data_blob_talloc(state->mem_ctx,
1873 state->request.extra_data.data,
1874 state->request.data.auth_crap.nt_resp_len);
1875 } else {
1876 nt_resp = data_blob_talloc(state->mem_ctx,
1877 state->request.data.auth_crap.nt_resp,
1878 state->request.data.auth_crap.nt_resp_len);
1881 /* what domain should we contact? */
1883 if ( IS_DC ) {
1884 if (!(contact_domain = find_domain_from_name(name_domain))) {
1885 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1886 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1887 result = NT_STATUS_NO_SUCH_USER;
1888 goto done;
1890 } else {
1891 if (is_myname(name_domain)) {
1892 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1893 result = NT_STATUS_NO_SUCH_USER;
1894 goto done;
1896 contact_domain = find_our_domain();
1899 do {
1900 netlogon_fn_t logon_fn;
1902 retry = false;
1904 netlogon_pipe = NULL;
1905 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1907 if (!NT_STATUS_IS_OK(result)) {
1908 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1909 nt_errstr(result)));
1910 goto done;
1913 logon_fn = contact_domain->can_do_samlogon_ex
1914 ? rpccli_netlogon_sam_network_logon_ex
1915 : rpccli_netlogon_sam_network_logon;
1917 result = logon_fn(netlogon_pipe,
1918 state->mem_ctx,
1919 state->request.data.auth_crap.logon_parameters,
1920 contact_domain->dcname,
1921 name_user,
1922 name_domain,
1923 /* Bug #3248 - found by Stefan Burkei. */
1924 workstation, /* We carefully set this above so use it... */
1925 state->request.data.auth_crap.chal,
1926 lm_resp,
1927 nt_resp,
1928 &info3);
1930 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1931 && contact_domain->can_do_samlogon_ex) {
1932 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1933 "retrying with NetSamLogon\n"));
1934 contact_domain->can_do_samlogon_ex = false;
1935 retry = true;
1936 continue;
1939 attempts += 1;
1941 /* We have to try a second time as cm_connect_netlogon
1942 might not yet have noticed that the DC has killed
1943 our connection. */
1945 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1946 retry = true;
1947 continue;
1950 /* if we get access denied, a possible cause was that we had and open
1951 connection to the DC, but someone changed our machine account password
1952 out from underneath us using 'net rpc changetrustpw' */
1954 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1955 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1956 "ACCESS_DENIED. Maybe the trust account "
1957 "password was changed and we didn't know it. "
1958 "Killing connections to domain %s\n",
1959 name_domain));
1960 invalidate_cm_connection(&contact_domain->conn);
1961 retry = true;
1964 } while ( (attempts < 2) && retry );
1966 if (NT_STATUS_IS_OK(result)) {
1968 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1969 netsamlogon_cache_store(name_user, info3);
1971 /* Check if the user is in the right group */
1973 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1974 state->request.data.auth_crap.require_membership_of_sid))) {
1975 DEBUG(3, ("User %s is not in the required group (%s), so "
1976 "crap authentication is rejected\n",
1977 state->request.data.auth_crap.user,
1978 state->request.data.auth_crap.require_membership_of_sid));
1979 goto done;
1982 result = append_data(state, info3, name_domain, name_user);
1983 if (!NT_STATUS_IS_OK(result)) {
1984 goto done;
1988 done:
1990 /* give us a more useful (more correct?) error code */
1991 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1992 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1993 result = NT_STATUS_NO_LOGON_SERVERS;
1996 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1997 result = nt_status_squash(result);
2000 set_auth_errors(&state->response, result);
2002 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2003 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2004 name_domain,
2005 name_user,
2006 state->response.data.auth.nt_status_string,
2007 state->response.data.auth.pam_error));
2009 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2012 /* Change a user password */
2014 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2016 fstring domain, user;
2017 char *mapped_user;
2018 struct winbindd_domain *contact_domain;
2019 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2021 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2022 state->request.data.chauthtok.user));
2024 /* Setup crap */
2026 nt_status = normalize_name_unmap(state->mem_ctx,
2027 state->request.data.chauthtok.user,
2028 &mapped_user);
2030 /* Update the chauthtok name if we did any mapping */
2032 if (NT_STATUS_IS_OK(nt_status) ||
2033 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2035 fstrcpy(state->request.data.chauthtok.user, mapped_user);
2038 /* Must pass in state->...chauthtok.user because
2039 canonicalize_username() assumes an fstring(). Since
2040 we have already copied it (if necessary), this is ok. */
2042 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
2043 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2044 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2045 "(PAM: %d)\n",
2046 state->request.data.auth.user,
2047 state->response.data.auth.nt_status_string,
2048 state->response.data.auth.pam_error));
2049 request_error(state);
2050 return;
2053 contact_domain = find_domain_from_name(domain);
2054 if (!contact_domain) {
2055 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2056 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2057 state->request.data.chauthtok.user, domain, user, domain));
2058 request_error(state);
2059 return;
2062 sendto_domain(state, contact_domain);
2065 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2066 struct winbindd_cli_state *state)
2068 char *oldpass;
2069 char *newpass = NULL;
2070 struct policy_handle dom_pol;
2071 struct rpc_pipe_client *cli;
2072 bool got_info = false;
2073 struct samr_DomInfo1 *info = NULL;
2074 struct samr_ChangeReject *reject = NULL;
2075 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2076 fstring domain, user;
2078 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2079 state->request.data.auth.user));
2081 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2082 goto done;
2085 /* Change password */
2087 oldpass = state->request.data.chauthtok.oldpass;
2088 newpass = state->request.data.chauthtok.newpass;
2090 /* Initialize reject reason */
2091 state->response.data.auth.reject_reason = Undefined;
2093 /* Get sam handle */
2095 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2096 &dom_pol);
2097 if (!NT_STATUS_IS_OK(result)) {
2098 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2099 goto done;
2102 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2103 user,
2104 newpass,
2105 oldpass,
2106 &info,
2107 &reject);
2109 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2111 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2113 fill_in_password_policy(&state->response, info);
2115 state->response.data.auth.reject_reason =
2116 reject->reason;
2118 got_info = true;
2121 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2122 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2123 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2124 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2126 /* only fallback when the chgpasswd_user3 call is not supported */
2127 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2128 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2129 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2130 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2132 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2133 nt_errstr(result)));
2135 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2137 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2138 Map to the same status code as Windows 2003. */
2140 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2141 result = NT_STATUS_PASSWORD_RESTRICTION;
2145 done:
2147 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2149 /* Update the single sign-on memory creds. */
2150 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2151 newpass);
2153 /* When we login from gdm or xdm and password expires,
2154 * we change password, but there are no memory crendentials
2155 * So, winbindd_replace_memory_creds() returns
2156 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2157 * --- BoYang
2158 * */
2159 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2160 result = NT_STATUS_OK;
2163 if (!NT_STATUS_IS_OK(result)) {
2164 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2165 goto process_result;
2168 if (lp_winbind_offline_logon()) {
2169 result = winbindd_update_creds_by_name(contact_domain,
2170 state->mem_ctx, user,
2171 newpass);
2172 /* Again, this happens when we login from gdm or xdm
2173 * and the password expires, *BUT* cached crendentials
2174 * doesn't exist. winbindd_update_creds_by_name()
2175 * returns NT_STATUS_NO_SUCH_USER.
2176 * This is not a failure.
2177 * --- BoYang
2178 * */
2179 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2180 result = NT_STATUS_OK;
2183 if (!NT_STATUS_IS_OK(result)) {
2184 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2185 goto process_result;
2190 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2192 NTSTATUS policy_ret;
2194 policy_ret = fillup_password_policy(contact_domain, state);
2196 /* failure of this is non critical, it will just provide no
2197 * additional information to the client why the change has
2198 * failed - Guenther */
2200 if (!NT_STATUS_IS_OK(policy_ret)) {
2201 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2202 goto process_result;
2206 process_result:
2208 set_auth_errors(&state->response, result);
2210 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2211 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2212 domain,
2213 user,
2214 state->response.data.auth.nt_status_string,
2215 state->response.data.auth.pam_error));
2217 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2220 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2222 struct winbindd_domain *domain;
2223 fstring name_domain, user;
2224 uid_t caller_uid = (uid_t)-1;
2225 uid_t request_uid = state->request.data.logoff.uid;
2227 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2228 state->request.data.logoff.user));
2230 /* Ensure null termination */
2231 state->request.data.logoff.user
2232 [sizeof(state->request.data.logoff.user)-1]='\0';
2234 state->request.data.logoff.krb5ccname
2235 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2237 if (request_uid == (gid_t)-1) {
2238 goto failed;
2241 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2242 goto failed;
2245 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2246 goto failed;
2249 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2250 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2251 strerror(errno)));
2252 goto failed;
2255 switch (caller_uid) {
2256 case -1:
2257 goto failed;
2258 case 0:
2259 /* root must be able to logoff any user - gd */
2260 state->request.data.logoff.uid = request_uid;
2261 break;
2262 default:
2263 if (caller_uid != request_uid) {
2264 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2265 goto failed;
2267 state->request.data.logoff.uid = caller_uid;
2268 break;
2271 sendto_domain(state, domain);
2272 return;
2274 failed:
2275 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2276 DEBUG(5, ("Pam Logoff for %s returned %s "
2277 "(PAM: %d)\n",
2278 state->request.data.logoff.user,
2279 state->response.data.auth.nt_status_string,
2280 state->response.data.auth.pam_error));
2281 request_error(state);
2282 return;
2285 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2286 struct winbindd_cli_state *state)
2288 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2290 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2291 state->request.data.logoff.user));
2293 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2294 result = NT_STATUS_OK;
2295 goto process_result;
2298 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2299 result = NT_STATUS_OK;
2300 goto process_result;
2303 #ifdef HAVE_KRB5
2305 if (state->request.data.logoff.uid < 0) {
2306 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2307 goto process_result;
2310 /* what we need here is to find the corresponding krb5 ccache name *we*
2311 * created for a given username and destroy it */
2313 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2314 result = NT_STATUS_OK;
2315 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2316 goto process_result;
2319 if (!ccache_entry_identical(state->request.data.logoff.user,
2320 state->request.data.logoff.uid,
2321 state->request.data.logoff.krb5ccname)) {
2322 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2323 goto process_result;
2326 result = remove_ccache(state->request.data.logoff.user);
2327 if (!NT_STATUS_IS_OK(result)) {
2328 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2329 nt_errstr(result)));
2330 goto process_result;
2333 #else
2334 result = NT_STATUS_NOT_SUPPORTED;
2335 #endif
2337 process_result:
2339 winbindd_delete_memory_creds(state->request.data.logoff.user);
2341 set_auth_errors(&state->response, result);
2343 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2346 /* Change user password with auth crap*/
2348 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2350 struct winbindd_domain *domain = NULL;
2351 const char *domain_name = NULL;
2353 /* Ensure null termination */
2354 state->request.data.chng_pswd_auth_crap.user[
2355 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2356 state->request.data.chng_pswd_auth_crap.domain[
2357 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2359 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2360 (unsigned long)state->pid,
2361 state->request.data.chng_pswd_auth_crap.domain,
2362 state->request.data.chng_pswd_auth_crap.user));
2364 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2365 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2366 } else if (lp_winbind_use_default_domain()) {
2367 domain_name = lp_workgroup();
2370 if (domain_name != NULL)
2371 domain = find_domain_from_name(domain_name);
2373 if (domain != NULL) {
2374 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2375 "%s\n", (unsigned long)state->pid,domain->name));
2376 sendto_domain(state, domain);
2377 return;
2380 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2381 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2382 state->request.data.chng_pswd_auth_crap.domain,
2383 state->request.data.chng_pswd_auth_crap.user,
2384 state->response.data.auth.nt_status_string,
2385 state->response.data.auth.pam_error));
2386 request_error(state);
2387 return;
2390 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2392 NTSTATUS result;
2393 DATA_BLOB new_nt_password;
2394 DATA_BLOB old_nt_hash_enc;
2395 DATA_BLOB new_lm_password;
2396 DATA_BLOB old_lm_hash_enc;
2397 fstring domain,user;
2398 struct policy_handle dom_pol;
2399 struct winbindd_domain *contact_domain = domainSt;
2400 struct rpc_pipe_client *cli;
2402 /* Ensure null termination */
2403 state->request.data.chng_pswd_auth_crap.user[
2404 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2405 state->request.data.chng_pswd_auth_crap.domain[
2406 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2407 *domain = 0;
2408 *user = 0;
2410 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2411 (unsigned long)state->pid,
2412 state->request.data.chng_pswd_auth_crap.domain,
2413 state->request.data.chng_pswd_auth_crap.user));
2415 if (lp_winbind_offline_logon()) {
2416 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2417 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2418 result = NT_STATUS_ACCESS_DENIED;
2419 goto done;
2422 if (*state->request.data.chng_pswd_auth_crap.domain) {
2423 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2424 } else {
2425 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2426 domain, user);
2428 if(!*domain) {
2429 DEBUG(3,("no domain specified with username (%s) - "
2430 "failing auth\n",
2431 state->request.data.chng_pswd_auth_crap.user));
2432 result = NT_STATUS_NO_SUCH_USER;
2433 goto done;
2437 if (!*domain && lp_winbind_use_default_domain()) {
2438 fstrcpy(domain,(char *)lp_workgroup());
2441 if(!*user) {
2442 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2445 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2446 (unsigned long)state->pid, domain, user));
2448 /* Change password */
2449 new_nt_password = data_blob_talloc(
2450 state->mem_ctx,
2451 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2452 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2454 old_nt_hash_enc = data_blob_talloc(
2455 state->mem_ctx,
2456 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2457 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2459 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2460 new_lm_password = data_blob_talloc(
2461 state->mem_ctx,
2462 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2463 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2465 old_lm_hash_enc = data_blob_talloc(
2466 state->mem_ctx,
2467 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2468 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2469 } else {
2470 new_lm_password.length = 0;
2471 old_lm_hash_enc.length = 0;
2474 /* Get sam handle */
2476 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2477 if (!NT_STATUS_IS_OK(result)) {
2478 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2479 goto done;
2482 result = rpccli_samr_chng_pswd_auth_crap(
2483 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2484 new_lm_password, old_lm_hash_enc);
2486 done:
2488 set_auth_errors(&state->response, result);
2490 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2491 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2492 domain, user,
2493 state->response.data.auth.nt_status_string,
2494 state->response.data.auth.pam_error));
2496 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;