libndr: add support for relative_rap_convert.
[Samba/ekacnet.git] / source3 / winbindd / winbindd_pam.c
blob9848bea6feefc823ad603e253ce2d665e62323b7
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/cli_samr.h"
29 #include "rpc_client/cli_samr.h"
30 #include "../librpc/gen_ndr/ndr_netlogon.h"
31 #include "rpc_client/cli_netlogon.h"
32 #include "smb_krb5.h"
33 #include "../lib/crypto/arcfour.h"
34 #include "rpc_server/srv_samr_util.h"
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_WINBIND
39 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
41 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
42 struct winbindd_cli_state *state,
43 struct netr_SamInfo3 *info3)
45 char *ex;
46 uint32_t i;
48 state->response->data.auth.info3.logon_time =
49 nt_time_to_unix(info3->base.last_logon);
50 state->response->data.auth.info3.logoff_time =
51 nt_time_to_unix(info3->base.last_logoff);
52 state->response->data.auth.info3.kickoff_time =
53 nt_time_to_unix(info3->base.acct_expiry);
54 state->response->data.auth.info3.pass_last_set_time =
55 nt_time_to_unix(info3->base.last_password_change);
56 state->response->data.auth.info3.pass_can_change_time =
57 nt_time_to_unix(info3->base.allow_password_change);
58 state->response->data.auth.info3.pass_must_change_time =
59 nt_time_to_unix(info3->base.force_password_change);
61 state->response->data.auth.info3.logon_count = info3->base.logon_count;
62 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
64 state->response->data.auth.info3.user_rid = info3->base.rid;
65 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
66 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
68 state->response->data.auth.info3.num_groups = info3->base.groups.count;
69 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
71 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
72 state->response->data.auth.info3.num_other_sids = info3->sidcount;
74 fstrcpy(state->response->data.auth.info3.user_name,
75 info3->base.account_name.string);
76 fstrcpy(state->response->data.auth.info3.full_name,
77 info3->base.full_name.string);
78 fstrcpy(state->response->data.auth.info3.logon_script,
79 info3->base.logon_script.string);
80 fstrcpy(state->response->data.auth.info3.profile_path,
81 info3->base.profile_path.string);
82 fstrcpy(state->response->data.auth.info3.home_dir,
83 info3->base.home_directory.string);
84 fstrcpy(state->response->data.auth.info3.dir_drive,
85 info3->base.home_drive.string);
87 fstrcpy(state->response->data.auth.info3.logon_srv,
88 info3->base.logon_server.string);
89 fstrcpy(state->response->data.auth.info3.logon_dom,
90 info3->base.domain.string);
92 ex = talloc_strdup(state->mem_ctx, "");
93 NT_STATUS_HAVE_NO_MEMORY(ex);
95 for (i=0; i < info3->base.groups.count; i++) {
96 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
97 info3->base.groups.rids[i].rid,
98 info3->base.groups.rids[i].attributes);
99 NT_STATUS_HAVE_NO_MEMORY(ex);
102 for (i=0; i < info3->sidcount; i++) {
103 char *sid;
105 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
106 NT_STATUS_HAVE_NO_MEMORY(sid);
108 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
109 sid,
110 info3->sids[i].attributes);
111 NT_STATUS_HAVE_NO_MEMORY(ex);
113 talloc_free(sid);
116 state->response->extra_data.data = ex;
117 state->response->length += talloc_get_size(ex);
119 return NT_STATUS_OK;
122 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
123 struct winbindd_cli_state *state,
124 struct netr_SamInfo3 *info3)
126 DATA_BLOB blob;
127 enum ndr_err_code ndr_err;
129 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
130 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
131 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
132 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
133 return ndr_map_error2ntstatus(ndr_err);
136 state->response->extra_data.data = blob.data;
137 state->response->length += blob.length;
139 return NT_STATUS_OK;
142 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
143 struct winbindd_cli_state *state,
144 const struct netr_SamInfo3 *info3,
145 const char *name_domain,
146 const char *name_user)
148 /* We've been asked to return the unix username, per
149 'winbind use default domain' settings and the like */
151 const char *nt_username, *nt_domain;
153 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
154 if (!nt_domain) {
155 /* If the server didn't give us one, just use the one
156 * we sent them */
157 nt_domain = name_domain;
160 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
161 if (!nt_username) {
162 /* If the server didn't give us one, just use the one
163 * we sent them */
164 nt_username = name_user;
167 fill_domain_username(state->response->data.auth.unix_username,
168 nt_domain, nt_username, true);
170 DEBUG(5,("Setting unix username to [%s]\n",
171 state->response->data.auth.unix_username));
173 return NT_STATUS_OK;
176 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
177 struct winbindd_cli_state *state,
178 const struct netr_SamInfo3 *info3,
179 const char *name_domain,
180 const char *name_user)
182 char *afsname = NULL;
183 char *cell;
184 char *token;
186 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
187 if (afsname == NULL) {
188 return NT_STATUS_NO_MEMORY;
191 afsname = talloc_string_sub(mem_ctx,
192 lp_afs_username_map(),
193 "%D", name_domain);
194 afsname = talloc_string_sub(mem_ctx, afsname,
195 "%u", name_user);
196 afsname = talloc_string_sub(mem_ctx, afsname,
197 "%U", name_user);
200 struct dom_sid user_sid;
201 fstring sidstr;
203 sid_compose(&user_sid, info3->base.domain_sid,
204 info3->base.rid);
205 sid_to_fstring(sidstr, &user_sid);
206 afsname = talloc_string_sub(mem_ctx, afsname,
207 "%s", sidstr);
210 if (afsname == NULL) {
211 return NT_STATUS_NO_MEMORY;
214 strlower_m(afsname);
216 DEBUG(10, ("Generating token for user %s\n", afsname));
218 cell = strchr(afsname, '@');
220 if (cell == NULL) {
221 return NT_STATUS_NO_MEMORY;
224 *cell = '\0';
225 cell += 1;
227 token = afs_createtoken_str(afsname, cell);
228 if (token == NULL) {
229 return NT_STATUS_OK;
231 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
232 token);
233 if (state->response->extra_data.data == NULL) {
234 return NT_STATUS_NO_MEMORY;
236 state->response->length +=
237 strlen((const char *)state->response->extra_data.data)+1;
239 return NT_STATUS_OK;
242 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
243 const char *group_sid)
245 * Check whether a user belongs to a group or list of groups.
247 * @param mem_ctx talloc memory context.
248 * @param info3 user information, including group membership info.
249 * @param group_sid One or more groups , separated by commas.
251 * @return NT_STATUS_OK on success,
252 * NT_STATUS_LOGON_FAILURE if the user does not belong,
253 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
256 struct dom_sid *require_membership_of_sid;
257 size_t num_require_membership_of_sid;
258 char *req_sid;
259 const char *p;
260 struct dom_sid sid;
261 size_t i;
262 struct nt_user_token *token;
263 TALLOC_CTX *frame = talloc_stackframe();
264 NTSTATUS status;
266 /* Parse the 'required group' SID */
268 if (!group_sid || !group_sid[0]) {
269 /* NO sid supplied, all users may access */
270 return NT_STATUS_OK;
273 token = talloc_zero(talloc_tos(), struct nt_user_token);
274 if (token == NULL) {
275 DEBUG(0, ("talloc failed\n"));
276 TALLOC_FREE(frame);
277 return NT_STATUS_NO_MEMORY;
280 num_require_membership_of_sid = 0;
281 require_membership_of_sid = NULL;
283 p = group_sid;
285 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
286 if (!string_to_sid(&sid, req_sid)) {
287 DEBUG(0, ("check_info3_in_group: could not parse %s "
288 "as a SID!", req_sid));
289 TALLOC_FREE(frame);
290 return NT_STATUS_INVALID_PARAMETER;
293 status = add_sid_to_array(talloc_tos(), &sid,
294 &require_membership_of_sid,
295 &num_require_membership_of_sid);
296 if (!NT_STATUS_IS_OK(status)) {
297 DEBUG(0, ("add_sid_to_array failed\n"));
298 TALLOC_FREE(frame);
299 return status;
303 status = sid_array_from_info3(talloc_tos(), info3,
304 &token->user_sids,
305 &token->num_sids,
306 true, false);
307 if (!NT_STATUS_IS_OK(status)) {
308 TALLOC_FREE(frame);
309 return status;
312 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
313 token))
314 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
315 token))) {
316 DEBUG(3, ("could not add aliases: %s\n",
317 nt_errstr(status)));
318 TALLOC_FREE(frame);
319 return status;
322 debug_nt_user_token(DBGC_CLASS, 10, token);
324 for (i=0; i<num_require_membership_of_sid; i++) {
325 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
326 &require_membership_of_sid[i])));
327 if (nt_token_check_sid(&require_membership_of_sid[i],
328 token)) {
329 DEBUG(10, ("Access ok\n"));
330 TALLOC_FREE(frame);
331 return NT_STATUS_OK;
335 /* Do not distinguish this error from a wrong username/pw */
337 TALLOC_FREE(frame);
338 return NT_STATUS_LOGON_FAILURE;
341 struct winbindd_domain *find_auth_domain(uint8_t flags,
342 const char *domain_name)
344 struct winbindd_domain *domain;
346 if (IS_DC) {
347 domain = find_domain_from_name_noinit(domain_name);
348 if (domain == NULL) {
349 DEBUG(3, ("Authentication for domain [%s] refused "
350 "as it is not a trusted domain\n",
351 domain_name));
353 return domain;
356 if (strequal(domain_name, get_global_sam_name())) {
357 return find_domain_from_name_noinit(domain_name);
360 /* we can auth against trusted domains */
361 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
362 domain = find_domain_from_name_noinit(domain_name);
363 if (domain == NULL) {
364 DEBUG(3, ("Authentication for domain [%s] skipped "
365 "as it is not a trusted domain\n",
366 domain_name));
367 } else {
368 return domain;
372 return find_our_domain();
375 static void fill_in_password_policy(struct winbindd_response *r,
376 const struct samr_DomInfo1 *p)
378 r->data.auth.policy.min_length_password =
379 p->min_password_length;
380 r->data.auth.policy.password_history =
381 p->password_history_length;
382 r->data.auth.policy.password_properties =
383 p->password_properties;
384 r->data.auth.policy.expire =
385 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
386 r->data.auth.policy.min_passwordage =
387 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
390 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
391 struct winbindd_cli_state *state)
393 struct winbindd_methods *methods;
394 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
395 struct samr_DomInfo1 password_policy;
397 if ( !winbindd_can_contact_domain( domain ) ) {
398 DEBUG(5,("fillup_password_policy: No inbound trust to "
399 "contact domain %s\n", domain->name));
400 return NT_STATUS_NOT_SUPPORTED;
403 methods = domain->methods;
405 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
406 if (NT_STATUS_IS_ERR(status)) {
407 return status;
410 fill_in_password_policy(state->response, &password_policy);
412 return NT_STATUS_OK;
415 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
416 TALLOC_CTX *mem_ctx,
417 uint16 *lockout_threshold)
419 struct winbindd_methods *methods;
420 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
421 struct samr_DomInfo12 lockout_policy;
423 *lockout_threshold = 0;
425 methods = domain->methods;
427 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
428 if (NT_STATUS_IS_ERR(status)) {
429 return status;
432 *lockout_threshold = lockout_policy.lockout_threshold;
434 return NT_STATUS_OK;
437 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
438 TALLOC_CTX *mem_ctx,
439 uint32 *password_properties)
441 struct winbindd_methods *methods;
442 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
443 struct samr_DomInfo1 password_policy;
445 *password_properties = 0;
447 methods = domain->methods;
449 status = methods->password_policy(domain, mem_ctx, &password_policy);
450 if (NT_STATUS_IS_ERR(status)) {
451 return status;
454 *password_properties = password_policy.password_properties;
456 return NT_STATUS_OK;
459 #ifdef HAVE_KRB5
461 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
462 const char *type,
463 uid_t uid,
464 bool *internal_ccache)
466 /* accept FILE and WRFILE as krb5_cc_type from the client and then
467 * build the full ccname string based on the user's uid here -
468 * Guenther*/
470 const char *gen_cc = NULL;
472 *internal_ccache = true;
474 if (uid == -1) {
475 goto memory_ccache;
478 if (!type || type[0] == '\0') {
479 goto memory_ccache;
482 if (strequal(type, "FILE")) {
483 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
484 } else if (strequal(type, "WRFILE")) {
485 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
486 } else {
487 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
488 goto memory_ccache;
491 *internal_ccache = false;
492 goto done;
494 memory_ccache:
495 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
497 done:
498 if (gen_cc == NULL) {
499 DEBUG(0,("out of memory\n"));
500 return NULL;
503 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
505 return gen_cc;
508 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
510 const char *type = state->request->data.auth.krb5_cc_type;
512 state->response->data.auth.krb5ccname[0] = '\0';
514 if (type[0] == '\0') {
515 return;
518 if (!strequal(type, "FILE") &&
519 !strequal(type, "WRFILE")) {
520 DEBUG(10,("won't return krbccname for a %s type ccache\n",
521 type));
522 return;
525 fstrcpy(state->response->data.auth.krb5ccname, cc);
528 #endif
530 uid_t get_uid_from_request(struct winbindd_request *request)
532 uid_t uid;
534 uid = request->data.auth.uid;
536 if (uid < 0) {
537 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
538 return -1;
540 return uid;
543 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
545 return get_uid_from_request(state->request);
548 /**********************************************************************
549 Authenticate a user with a clear text password using Kerberos and fill up
550 ccache if required
551 **********************************************************************/
553 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
554 struct winbindd_cli_state *state,
555 struct netr_SamInfo3 **info3)
557 #ifdef HAVE_KRB5
558 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
559 krb5_error_code krb5_ret;
560 const char *cc = NULL;
561 const char *principal_s = NULL;
562 const char *service = NULL;
563 char *realm = NULL;
564 fstring name_domain, name_user;
565 time_t ticket_lifetime = 0;
566 time_t renewal_until = 0;
567 uid_t uid = -1;
568 ADS_STRUCT *ads;
569 time_t time_offset = 0;
570 bool internal_ccache = true;
571 struct PAC_LOGON_INFO *logon_info = NULL;
573 *info3 = NULL;
575 /* 1st step:
576 * prepare a krb5_cc_cache string for the user */
578 uid = get_uid_from_state(state);
579 if (uid == -1) {
580 DEBUG(0,("no valid uid\n"));
583 cc = generate_krb5_ccache(state->mem_ctx,
584 state->request->data.auth.krb5_cc_type,
585 state->request->data.auth.uid,
586 &internal_ccache);
587 if (cc == NULL) {
588 return NT_STATUS_NO_MEMORY;
592 /* 2nd step:
593 * get kerberos properties */
595 if (domain->private_data) {
596 ads = (ADS_STRUCT *)domain->private_data;
597 time_offset = ads->auth.time_offset;
601 /* 3rd step:
602 * do kerberos auth and setup ccache as the user */
604 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
606 realm = domain->alt_name;
607 strupper_m(realm);
609 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
610 if (principal_s == NULL) {
611 return NT_STATUS_NO_MEMORY;
614 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
615 if (service == NULL) {
616 return NT_STATUS_NO_MEMORY;
619 /* if this is a user ccache, we need to act as the user to let the krb5
620 * library handle the chown, etc. */
622 /************************ ENTERING NON-ROOT **********************/
624 if (!internal_ccache) {
625 set_effective_uid(uid);
626 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
629 result = kerberos_return_pac(state->mem_ctx,
630 principal_s,
631 state->request->data.auth.pass,
632 time_offset,
633 &ticket_lifetime,
634 &renewal_until,
636 true,
637 true,
638 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
639 NULL,
640 &logon_info);
641 if (!internal_ccache) {
642 gain_root_privilege();
645 /************************ RETURNED TO ROOT **********************/
647 if (!NT_STATUS_IS_OK(result)) {
648 goto failed;
651 *info3 = &logon_info->info3;
653 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
654 principal_s));
656 /* if we had a user's ccache then return that string for the pam
657 * environment */
659 if (!internal_ccache) {
661 setup_return_cc_name(state, cc);
663 result = add_ccache_to_list(principal_s,
665 service,
666 state->request->data.auth.user,
667 realm,
668 uid,
669 time(NULL),
670 ticket_lifetime,
671 renewal_until,
672 false);
674 if (!NT_STATUS_IS_OK(result)) {
675 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
676 nt_errstr(result)));
678 } else {
680 /* need to delete the memory cred cache, it is not used anymore */
682 krb5_ret = ads_kdestroy(cc);
683 if (krb5_ret) {
684 DEBUG(3,("winbindd_raw_kerberos_login: "
685 "could not destroy krb5 credential cache: "
686 "%s\n", error_message(krb5_ret)));
691 return NT_STATUS_OK;
693 failed:
695 /* we could have created a new credential cache with a valid tgt in it
696 * but we werent able to get or verify the service ticket for this
697 * local host and therefor didn't get the PAC, we need to remove that
698 * cache entirely now */
700 krb5_ret = ads_kdestroy(cc);
701 if (krb5_ret) {
702 DEBUG(3,("winbindd_raw_kerberos_login: "
703 "could not destroy krb5 credential cache: "
704 "%s\n", error_message(krb5_ret)));
707 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
708 DEBUG(3,("winbindd_raw_kerberos_login: "
709 "could not remove ccache for user %s\n",
710 state->request->data.auth.user));
713 return result;
714 #else
715 return NT_STATUS_NOT_SUPPORTED;
716 #endif /* HAVE_KRB5 */
719 /****************************************************************
720 ****************************************************************/
722 bool check_request_flags(uint32_t flags)
724 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
725 WBFLAG_PAM_INFO3_TEXT |
726 WBFLAG_PAM_INFO3_NDR;
728 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
729 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
730 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
731 !(flags & flags_edata) ) {
732 return true;
735 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
736 flags));
738 return false;
741 /****************************************************************
742 ****************************************************************/
744 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
745 struct netr_SamInfo3 *info3,
746 const char *name_domain,
747 const char *name_user)
749 NTSTATUS result;
750 uint32_t flags = state->request->flags;
752 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
753 memcpy(state->response->data.auth.user_session_key,
754 info3->base.key.key,
755 sizeof(state->response->data.auth.user_session_key)
756 /* 16 */);
759 if (flags & WBFLAG_PAM_LMKEY) {
760 memcpy(state->response->data.auth.first_8_lm_hash,
761 info3->base.LMSessKey.key,
762 sizeof(state->response->data.auth.first_8_lm_hash)
763 /* 8 */);
766 if (flags & WBFLAG_PAM_UNIX_NAME) {
767 result = append_unix_username(state->mem_ctx, state, info3,
768 name_domain, name_user);
769 if (!NT_STATUS_IS_OK(result)) {
770 DEBUG(10,("Failed to append Unix Username: %s\n",
771 nt_errstr(result)));
772 return result;
776 /* currently, anything from here on potentially overwrites extra_data. */
778 if (flags & WBFLAG_PAM_INFO3_NDR) {
779 result = append_info3_as_ndr(state->mem_ctx, state, info3);
780 if (!NT_STATUS_IS_OK(result)) {
781 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
782 nt_errstr(result)));
783 return result;
787 if (flags & WBFLAG_PAM_INFO3_TEXT) {
788 result = append_info3_as_txt(state->mem_ctx, state, info3);
789 if (!NT_STATUS_IS_OK(result)) {
790 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
791 nt_errstr(result)));
792 return result;
796 if (flags & WBFLAG_PAM_AFS_TOKEN) {
797 result = append_afs_token(state->mem_ctx, state, info3,
798 name_domain, name_user);
799 if (!NT_STATUS_IS_OK(result)) {
800 DEBUG(10,("Failed to append AFS token: %s\n",
801 nt_errstr(result)));
802 return result;
806 return NT_STATUS_OK;
809 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
810 struct winbindd_cli_state *state,
811 struct netr_SamInfo3 **info3)
813 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
814 uint16 max_allowed_bad_attempts;
815 fstring name_domain, name_user;
816 struct dom_sid sid;
817 enum lsa_SidType type;
818 uchar new_nt_pass[NT_HASH_LEN];
819 const uint8 *cached_nt_pass;
820 const uint8 *cached_salt;
821 struct netr_SamInfo3 *my_info3;
822 time_t kickoff_time, must_change_time;
823 bool password_good = false;
824 #ifdef HAVE_KRB5
825 struct winbindd_tdc_domain *tdc_domain = NULL;
826 #endif
828 *info3 = NULL;
830 ZERO_STRUCTP(info3);
832 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
834 /* Parse domain and username */
836 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
839 if (!lookup_cached_name(state->mem_ctx,
840 name_domain,
841 name_user,
842 &sid,
843 &type)) {
844 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
845 return NT_STATUS_NO_SUCH_USER;
848 if (type != SID_NAME_USER) {
849 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
850 return NT_STATUS_LOGON_FAILURE;
853 result = winbindd_get_creds(domain,
854 state->mem_ctx,
855 &sid,
856 &my_info3,
857 &cached_nt_pass,
858 &cached_salt);
859 if (!NT_STATUS_IS_OK(result)) {
860 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
861 return result;
864 *info3 = my_info3;
866 E_md4hash(state->request->data.auth.pass, new_nt_pass);
868 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
869 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
870 if (cached_salt) {
871 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
874 if (cached_salt) {
875 /* In this case we didn't store the nt_hash itself,
876 but the MD5 combination of salt + nt_hash. */
877 uchar salted_hash[NT_HASH_LEN];
878 E_md5hash(cached_salt, new_nt_pass, salted_hash);
880 password_good = (memcmp(cached_nt_pass, salted_hash,
881 NT_HASH_LEN) == 0);
882 } else {
883 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
884 password_good = (memcmp(cached_nt_pass, new_nt_pass,
885 NT_HASH_LEN) == 0);
888 if (password_good) {
890 /* User *DOES* know the password, update logon_time and reset
891 * bad_pw_count */
893 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
895 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
896 return NT_STATUS_ACCOUNT_LOCKED_OUT;
899 if (my_info3->base.acct_flags & ACB_DISABLED) {
900 return NT_STATUS_ACCOUNT_DISABLED;
903 if (my_info3->base.acct_flags & ACB_WSTRUST) {
904 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
907 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
908 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
911 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
912 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
915 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
916 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
917 my_info3->base.acct_flags));
918 return NT_STATUS_LOGON_FAILURE;
921 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
922 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
923 return NT_STATUS_ACCOUNT_EXPIRED;
926 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
927 if (must_change_time != 0 && must_change_time < time(NULL)) {
928 /* we allow grace logons when the password has expired */
929 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
930 /* return NT_STATUS_PASSWORD_EXPIRED; */
931 goto success;
934 #ifdef HAVE_KRB5
935 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
936 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
937 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
938 /* used to cope with the case winbindd starting without network. */
939 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
941 uid_t uid = -1;
942 const char *cc = NULL;
943 char *realm = NULL;
944 const char *principal_s = NULL;
945 const char *service = NULL;
946 bool internal_ccache = false;
948 uid = get_uid_from_state(state);
949 if (uid == -1) {
950 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
951 return NT_STATUS_INVALID_PARAMETER;
954 cc = generate_krb5_ccache(state->mem_ctx,
955 state->request->data.auth.krb5_cc_type,
956 state->request->data.auth.uid,
957 &internal_ccache);
958 if (cc == NULL) {
959 return NT_STATUS_NO_MEMORY;
962 realm = domain->alt_name;
963 strupper_m(realm);
965 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
966 if (principal_s == NULL) {
967 return NT_STATUS_NO_MEMORY;
970 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
971 if (service == NULL) {
972 return NT_STATUS_NO_MEMORY;
975 if (!internal_ccache) {
977 setup_return_cc_name(state, cc);
979 result = add_ccache_to_list(principal_s,
981 service,
982 state->request->data.auth.user,
983 domain->alt_name,
984 uid,
985 time(NULL),
986 time(NULL) + lp_winbind_cache_time(),
987 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
988 true);
990 if (!NT_STATUS_IS_OK(result)) {
991 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
992 "to add ccache to list: %s\n",
993 nt_errstr(result)));
997 #endif /* HAVE_KRB5 */
998 success:
999 /* FIXME: we possibly should handle logon hours as well (does xp when
1000 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1002 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1003 my_info3->base.bad_password_count = 0;
1005 result = winbindd_update_creds_by_info3(domain,
1006 state->mem_ctx,
1007 state->request->data.auth.user,
1008 state->request->data.auth.pass,
1009 my_info3);
1010 if (!NT_STATUS_IS_OK(result)) {
1011 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1012 nt_errstr(result)));
1013 return result;
1016 return NT_STATUS_OK;
1020 /* User does *NOT* know the correct password, modify info3 accordingly */
1022 /* failure of this is not critical */
1023 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1024 if (!NT_STATUS_IS_OK(result)) {
1025 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1026 "Won't be able to honour account lockout policies\n"));
1029 /* increase counter */
1030 my_info3->base.bad_password_count++;
1032 if (max_allowed_bad_attempts == 0) {
1033 goto failed;
1036 /* lockout user */
1037 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1039 uint32 password_properties;
1041 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1042 if (!NT_STATUS_IS_OK(result)) {
1043 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1046 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1047 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1048 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1052 failed:
1053 result = winbindd_update_creds_by_info3(domain,
1054 state->mem_ctx,
1055 state->request->data.auth.user,
1056 NULL,
1057 my_info3);
1059 if (!NT_STATUS_IS_OK(result)) {
1060 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1061 nt_errstr(result)));
1064 return NT_STATUS_LOGON_FAILURE;
1067 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1068 struct winbindd_cli_state *state,
1069 struct netr_SamInfo3 **info3)
1071 struct winbindd_domain *contact_domain;
1072 fstring name_domain, name_user;
1073 NTSTATUS result;
1075 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1077 /* Parse domain and username */
1079 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1081 /* what domain should we contact? */
1083 if ( IS_DC ) {
1084 if (!(contact_domain = find_domain_from_name(name_domain))) {
1085 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1086 state->request->data.auth.user, name_domain, name_user, name_domain));
1087 result = NT_STATUS_NO_SUCH_USER;
1088 goto done;
1091 } else {
1092 if (is_myname(name_domain)) {
1093 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1094 result = NT_STATUS_NO_SUCH_USER;
1095 goto done;
1098 contact_domain = find_domain_from_name(name_domain);
1099 if (contact_domain == NULL) {
1100 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1101 state->request->data.auth.user, name_domain, name_user, name_domain));
1103 contact_domain = find_our_domain();
1107 if (contact_domain->initialized &&
1108 contact_domain->active_directory) {
1109 goto try_login;
1112 if (!contact_domain->initialized) {
1113 init_dc_connection(contact_domain);
1116 if (!contact_domain->active_directory) {
1117 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1118 return NT_STATUS_INVALID_LOGON_TYPE;
1120 try_login:
1121 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1122 done:
1123 return result;
1126 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1127 const char *domain, const char *user,
1128 const DATA_BLOB *challenge,
1129 const DATA_BLOB *lm_resp,
1130 const DATA_BLOB *nt_resp,
1131 struct netr_SamInfo3 **pinfo3)
1133 struct auth_usersupplied_info *user_info = NULL;
1134 struct auth_serversupplied_info *server_info = NULL;
1135 struct netr_SamInfo3 *info3;
1136 NTSTATUS status;
1138 status = make_user_info(&user_info, user, user, domain, domain,
1139 global_myname(), lm_resp, nt_resp, NULL, NULL,
1140 NULL, True);
1141 if (!NT_STATUS_IS_OK(status)) {
1142 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1143 return status;
1146 status = check_sam_security(challenge, talloc_tos(), user_info,
1147 &server_info);
1148 free_user_info(&user_info);
1150 if (!NT_STATUS_IS_OK(status)) {
1151 DEBUG(10, ("check_ntlm_password failed: %s\n",
1152 nt_errstr(status)));
1153 return status;
1156 info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1157 if (info3 == NULL) {
1158 return NT_STATUS_NO_MEMORY;
1161 status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1162 if (!NT_STATUS_IS_OK(status)) {
1163 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1164 nt_errstr(status)));
1165 return status;
1168 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1169 *pinfo3 = info3;
1170 return NT_STATUS_OK;
1173 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1174 TALLOC_CTX *mem_ctx,
1175 uint32 logon_parameters,
1176 const char *server,
1177 const char *username,
1178 const char *domain,
1179 const char *workstation,
1180 const uint8 chal[8],
1181 DATA_BLOB lm_response,
1182 DATA_BLOB nt_response,
1183 struct netr_SamInfo3 **info3);
1185 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1186 struct winbindd_cli_state *state,
1187 struct netr_SamInfo3 **info3)
1190 struct rpc_pipe_client *netlogon_pipe;
1191 uchar chal[8];
1192 DATA_BLOB lm_resp;
1193 DATA_BLOB nt_resp;
1194 int attempts = 0;
1195 unsigned char local_lm_response[24];
1196 unsigned char local_nt_response[24];
1197 fstring name_domain, name_user;
1198 bool retry;
1199 NTSTATUS result;
1200 struct netr_SamInfo3 *my_info3 = NULL;
1202 *info3 = NULL;
1204 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1206 /* Parse domain and username */
1208 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1210 /* do password magic */
1212 generate_random_buffer(chal, sizeof(chal));
1214 if (lp_client_ntlmv2_auth()) {
1215 DATA_BLOB server_chal;
1216 DATA_BLOB names_blob;
1217 DATA_BLOB nt_response;
1218 DATA_BLOB lm_response;
1219 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1221 /* note that the 'workgroup' here is a best guess - we don't know
1222 the server's domain at this point. The 'server name' is also
1223 dodgy...
1225 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1227 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1228 state->request->data.auth.pass,
1229 &server_chal,
1230 &names_blob,
1231 &lm_response, &nt_response, NULL, NULL)) {
1232 data_blob_free(&names_blob);
1233 data_blob_free(&server_chal);
1234 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1235 result = NT_STATUS_NO_MEMORY;
1236 goto done;
1238 data_blob_free(&names_blob);
1239 data_blob_free(&server_chal);
1240 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1241 lm_response.length);
1242 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1243 nt_response.length);
1244 data_blob_free(&lm_response);
1245 data_blob_free(&nt_response);
1247 } else {
1248 if (lp_client_lanman_auth()
1249 && SMBencrypt(state->request->data.auth.pass,
1250 chal,
1251 local_lm_response)) {
1252 lm_resp = data_blob_talloc(state->mem_ctx,
1253 local_lm_response,
1254 sizeof(local_lm_response));
1255 } else {
1256 lm_resp = data_blob_null;
1258 SMBNTencrypt(state->request->data.auth.pass,
1259 chal,
1260 local_nt_response);
1262 nt_resp = data_blob_talloc(state->mem_ctx,
1263 local_nt_response,
1264 sizeof(local_nt_response));
1267 if (strequal(name_domain, get_global_sam_name())) {
1268 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1270 result = winbindd_dual_auth_passdb(
1271 state->mem_ctx, name_domain, name_user,
1272 &chal_blob, &lm_resp, &nt_resp, info3);
1273 goto done;
1276 /* check authentication loop */
1278 do {
1279 netlogon_fn_t logon_fn;
1281 ZERO_STRUCTP(my_info3);
1282 retry = false;
1284 result = cm_connect_netlogon(domain, &netlogon_pipe);
1286 if (!NT_STATUS_IS_OK(result)) {
1287 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1288 goto done;
1291 /* It is really important to try SamLogonEx here,
1292 * because in a clustered environment, we want to use
1293 * one machine account from multiple physical
1294 * computers.
1296 * With a normal SamLogon call, we must keep the
1297 * credentials chain updated and intact between all
1298 * users of the machine account (which would imply
1299 * cross-node communication for every NTLM logon).
1301 * (The credentials chain is not per NETLOGON pipe
1302 * connection, but globally on the server/client pair
1303 * by machine name).
1305 * When using SamLogonEx, the credentials are not
1306 * supplied, but the session key is implied by the
1307 * wrapping SamLogon context.
1309 * -- abartlet 21 April 2008
1312 logon_fn = domain->can_do_samlogon_ex
1313 ? rpccli_netlogon_sam_network_logon_ex
1314 : rpccli_netlogon_sam_network_logon;
1316 result = logon_fn(netlogon_pipe,
1317 state->mem_ctx,
1319 domain->dcname, /* server name */
1320 name_user, /* user name */
1321 name_domain, /* target domain */
1322 global_myname(), /* workstation */
1323 chal,
1324 lm_resp,
1325 nt_resp,
1326 &my_info3);
1327 attempts += 1;
1329 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1330 && domain->can_do_samlogon_ex) {
1331 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1332 "retrying with NetSamLogon\n"));
1333 domain->can_do_samlogon_ex = false;
1334 retry = true;
1335 continue;
1338 /* We have to try a second time as cm_connect_netlogon
1339 might not yet have noticed that the DC has killed
1340 our connection. */
1342 if (!rpccli_is_connected(netlogon_pipe)) {
1343 retry = true;
1344 continue;
1347 /* if we get access denied, a possible cause was that we had
1348 and open connection to the DC, but someone changed our
1349 machine account password out from underneath us using 'net
1350 rpc changetrustpw' */
1352 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1353 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1354 "ACCESS_DENIED. Maybe the trust account "
1355 "password was changed and we didn't know it. "
1356 "Killing connections to domain %s\n",
1357 name_domain));
1358 invalidate_cm_connection(&domain->conn);
1359 retry = true;
1362 } while ( (attempts < 2) && retry );
1364 /* handle the case where a NT4 DC does not fill in the acct_flags in
1365 * the samlogon reply info3. When accurate info3 is required by the
1366 * caller, we look up the account flags ourselve - gd */
1368 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1369 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1371 struct rpc_pipe_client *samr_pipe;
1372 struct policy_handle samr_domain_handle, user_pol;
1373 union samr_UserInfo *info = NULL;
1374 NTSTATUS status_tmp;
1375 uint32 acct_flags;
1377 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1378 &samr_pipe, &samr_domain_handle);
1380 if (!NT_STATUS_IS_OK(status_tmp)) {
1381 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1382 nt_errstr(status_tmp)));
1383 goto done;
1386 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1387 &samr_domain_handle,
1388 MAXIMUM_ALLOWED_ACCESS,
1389 my_info3->base.rid,
1390 &user_pol);
1392 if (!NT_STATUS_IS_OK(status_tmp)) {
1393 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1394 nt_errstr(status_tmp)));
1395 goto done;
1398 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1399 &user_pol,
1401 &info);
1403 if (!NT_STATUS_IS_OK(status_tmp)) {
1404 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1405 nt_errstr(status_tmp)));
1406 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1407 goto done;
1410 acct_flags = info->info16.acct_flags;
1412 if (acct_flags == 0) {
1413 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1414 goto done;
1417 my_info3->base.acct_flags = acct_flags;
1419 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1421 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1424 *info3 = my_info3;
1425 done:
1426 return result;
1429 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1430 struct winbindd_cli_state *state)
1432 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1433 NTSTATUS krb5_result = NT_STATUS_OK;
1434 fstring name_domain, name_user;
1435 char *mapped_user;
1436 fstring domain_user;
1437 struct netr_SamInfo3 *info3 = NULL;
1438 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1440 /* Ensure null termination */
1441 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1443 /* Ensure null termination */
1444 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1446 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1447 state->request->data.auth.user));
1449 if (!check_request_flags(state->request->flags)) {
1450 result = NT_STATUS_INVALID_PARAMETER_MIX;
1451 goto done;
1454 /* Parse domain and username */
1456 name_map_status = normalize_name_unmap(state->mem_ctx,
1457 state->request->data.auth.user,
1458 &mapped_user);
1460 /* If the name normalization didnt' actually do anything,
1461 just use the original name */
1463 if (!NT_STATUS_IS_OK(name_map_status) &&
1464 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1466 mapped_user = state->request->data.auth.user;
1469 parse_domain_user(mapped_user, name_domain, name_user);
1471 if ( mapped_user != state->request->data.auth.user ) {
1472 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1473 safe_strcpy( state->request->data.auth.user, domain_user,
1474 sizeof(state->request->data.auth.user)-1 );
1477 if (domain->online == false) {
1478 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1479 if (domain->startup) {
1480 /* Logons are very important to users. If we're offline and
1481 we get a request within the first 30 seconds of startup,
1482 try very hard to find a DC and go online. */
1484 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1485 "request in startup mode.\n", domain->name ));
1487 winbindd_flush_negative_conn_cache(domain);
1488 result = init_dc_connection(domain);
1492 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1494 /* Check for Kerberos authentication */
1495 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1497 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1498 /* save for later */
1499 krb5_result = result;
1502 if (NT_STATUS_IS_OK(result)) {
1503 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1504 goto process_result;
1505 } else {
1506 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1509 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1510 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1511 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1512 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1513 set_domain_offline( domain );
1514 goto cached_logon;
1517 /* there are quite some NT_STATUS errors where there is no
1518 * point in retrying with a samlogon, we explictly have to take
1519 * care not to increase the bad logon counter on the DC */
1521 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1526 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1527 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1528 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1529 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1530 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1531 goto done;
1534 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1535 DEBUG(3,("falling back to samlogon\n"));
1536 goto sam_logon;
1537 } else {
1538 goto cached_logon;
1542 sam_logon:
1543 /* Check for Samlogon authentication */
1544 if (domain->online) {
1545 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1547 if (NT_STATUS_IS_OK(result)) {
1548 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1549 /* add the Krb5 err if we have one */
1550 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1551 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1553 goto process_result;
1556 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1557 nt_errstr(result)));
1559 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1560 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1561 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1563 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1564 set_domain_offline( domain );
1565 goto cached_logon;
1568 if (domain->online) {
1569 /* We're still online - fail. */
1570 goto done;
1574 cached_logon:
1575 /* Check for Cached logons */
1576 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1577 lp_winbind_offline_logon()) {
1579 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1581 if (NT_STATUS_IS_OK(result)) {
1582 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1583 goto process_result;
1584 } else {
1585 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1586 goto done;
1590 process_result:
1592 if (NT_STATUS_IS_OK(result)) {
1594 struct dom_sid user_sid;
1596 /* In all codepaths where result == NT_STATUS_OK info3 must have
1597 been initialized. */
1598 if (!info3) {
1599 result = NT_STATUS_INTERNAL_ERROR;
1600 goto done;
1603 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1604 netsamlogon_cache_store(name_user, info3);
1606 /* save name_to_sid info as early as possible (only if
1607 this is our primary domain so we don't invalidate
1608 the cache entry by storing the seq_num for the wrong
1609 domain). */
1610 if ( domain->primary ) {
1611 sid_compose(&user_sid, info3->base.domain_sid,
1612 info3->base.rid);
1613 cache_name2sid(domain, name_domain, name_user,
1614 SID_NAME_USER, &user_sid);
1617 /* Check if the user is in the right group */
1619 result = check_info3_in_group(
1620 info3,
1621 state->request->data.auth.require_membership_of_sid);
1622 if (!NT_STATUS_IS_OK(result)) {
1623 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1624 state->request->data.auth.user,
1625 state->request->data.auth.require_membership_of_sid));
1626 goto done;
1629 result = append_auth_data(state, info3, name_domain,
1630 name_user);
1631 if (!NT_STATUS_IS_OK(result)) {
1632 goto done;
1635 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1637 if (lp_winbind_offline_logon()) {
1638 result = winbindd_store_creds(domain,
1639 state->mem_ctx,
1640 state->request->data.auth.user,
1641 state->request->data.auth.pass,
1642 info3, NULL);
1647 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1648 struct winbindd_domain *our_domain = find_our_domain();
1650 /* This is not entirely correct I believe, but it is
1651 consistent. Only apply the password policy settings
1652 too warn users for our own domain. Cannot obtain these
1653 from trusted DCs all the time so don't do it at all.
1654 -- jerry */
1656 result = NT_STATUS_NOT_SUPPORTED;
1657 if (our_domain == domain ) {
1658 result = fillup_password_policy(our_domain, state);
1661 if (!NT_STATUS_IS_OK(result)
1662 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1664 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1665 domain->name, nt_errstr(result)));
1666 goto done;
1670 result = NT_STATUS_OK;
1673 done:
1674 /* give us a more useful (more correct?) error code */
1675 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1676 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1677 result = NT_STATUS_NO_LOGON_SERVERS;
1680 set_auth_errors(state->response, result);
1682 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1683 state->request->data.auth.user,
1684 state->response->data.auth.nt_status_string,
1685 state->response->data.auth.pam_error));
1687 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1690 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1691 struct winbindd_cli_state *state)
1693 NTSTATUS result;
1694 struct netr_SamInfo3 *info3 = NULL;
1695 struct rpc_pipe_client *netlogon_pipe;
1696 const char *name_user = NULL;
1697 const char *name_domain = NULL;
1698 const char *workstation;
1699 int attempts = 0;
1700 bool retry;
1702 DATA_BLOB lm_resp, nt_resp;
1704 /* This is child-only, so no check for privileged access is needed
1705 anymore */
1707 /* Ensure null termination */
1708 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1709 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1711 if (!check_request_flags(state->request->flags)) {
1712 result = NT_STATUS_INVALID_PARAMETER_MIX;
1713 goto done;
1716 name_user = state->request->data.auth_crap.user;
1718 if (*state->request->data.auth_crap.domain) {
1719 name_domain = state->request->data.auth_crap.domain;
1720 } else if (lp_winbind_use_default_domain()) {
1721 name_domain = lp_workgroup();
1722 } else {
1723 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1724 name_user));
1725 result = NT_STATUS_NO_SUCH_USER;
1726 goto done;
1729 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1730 name_domain, name_user));
1732 if (*state->request->data.auth_crap.workstation) {
1733 workstation = state->request->data.auth_crap.workstation;
1734 } else {
1735 workstation = global_myname();
1738 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1739 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1740 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1741 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1742 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1743 state->request->data.auth_crap.lm_resp_len,
1744 state->request->data.auth_crap.nt_resp_len));
1745 result = NT_STATUS_INVALID_PARAMETER;
1746 goto done;
1750 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1751 state->request->data.auth_crap.lm_resp_len);
1753 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1754 nt_resp = data_blob_talloc(state->mem_ctx,
1755 state->request->extra_data.data,
1756 state->request->data.auth_crap.nt_resp_len);
1757 } else {
1758 nt_resp = data_blob_talloc(state->mem_ctx,
1759 state->request->data.auth_crap.nt_resp,
1760 state->request->data.auth_crap.nt_resp_len);
1763 if (strequal(name_domain, get_global_sam_name())) {
1764 DATA_BLOB chal_blob = data_blob_const(
1765 state->request->data.auth_crap.chal,
1766 sizeof(state->request->data.auth_crap.chal));
1768 result = winbindd_dual_auth_passdb(
1769 state->mem_ctx, name_domain, name_user,
1770 &chal_blob, &lm_resp, &nt_resp, &info3);
1771 goto process_result;
1774 do {
1775 netlogon_fn_t logon_fn;
1777 retry = false;
1779 netlogon_pipe = NULL;
1780 result = cm_connect_netlogon(domain, &netlogon_pipe);
1782 if (!NT_STATUS_IS_OK(result)) {
1783 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1784 nt_errstr(result)));
1785 goto done;
1788 logon_fn = domain->can_do_samlogon_ex
1789 ? rpccli_netlogon_sam_network_logon_ex
1790 : rpccli_netlogon_sam_network_logon;
1792 result = logon_fn(netlogon_pipe,
1793 state->mem_ctx,
1794 state->request->data.auth_crap.logon_parameters,
1795 domain->dcname,
1796 name_user,
1797 name_domain,
1798 /* Bug #3248 - found by Stefan Burkei. */
1799 workstation, /* We carefully set this above so use it... */
1800 state->request->data.auth_crap.chal,
1801 lm_resp,
1802 nt_resp,
1803 &info3);
1805 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1806 && domain->can_do_samlogon_ex) {
1807 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1808 "retrying with NetSamLogon\n"));
1809 domain->can_do_samlogon_ex = false;
1810 retry = true;
1811 continue;
1814 attempts += 1;
1816 /* We have to try a second time as cm_connect_netlogon
1817 might not yet have noticed that the DC has killed
1818 our connection. */
1820 if (!rpccli_is_connected(netlogon_pipe)) {
1821 retry = true;
1822 continue;
1825 /* if we get access denied, a possible cause was that we had and open
1826 connection to the DC, but someone changed our machine account password
1827 out from underneath us using 'net rpc changetrustpw' */
1829 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1830 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1831 "ACCESS_DENIED. Maybe the trust account "
1832 "password was changed and we didn't know it. "
1833 "Killing connections to domain %s\n",
1834 name_domain));
1835 invalidate_cm_connection(&domain->conn);
1836 retry = true;
1839 } while ( (attempts < 2) && retry );
1841 process_result:
1843 if (NT_STATUS_IS_OK(result)) {
1845 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1846 netsamlogon_cache_store(name_user, info3);
1848 /* Check if the user is in the right group */
1850 result = check_info3_in_group(
1851 info3,
1852 state->request->data.auth_crap.require_membership_of_sid);
1853 if (!NT_STATUS_IS_OK(result)) {
1854 DEBUG(3, ("User %s is not in the required group (%s), so "
1855 "crap authentication is rejected\n",
1856 state->request->data.auth_crap.user,
1857 state->request->data.auth_crap.require_membership_of_sid));
1858 goto done;
1861 result = append_auth_data(state, info3, name_domain,
1862 name_user);
1863 if (!NT_STATUS_IS_OK(result)) {
1864 goto done;
1868 done:
1870 /* give us a more useful (more correct?) error code */
1871 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1872 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1873 result = NT_STATUS_NO_LOGON_SERVERS;
1876 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1877 result = nt_status_squash(result);
1880 set_auth_errors(state->response, result);
1882 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1883 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1884 name_domain,
1885 name_user,
1886 state->response->data.auth.nt_status_string,
1887 state->response->data.auth.pam_error));
1889 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1892 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1893 struct winbindd_cli_state *state)
1895 char *oldpass;
1896 char *newpass = NULL;
1897 struct policy_handle dom_pol;
1898 struct rpc_pipe_client *cli;
1899 bool got_info = false;
1900 struct samr_DomInfo1 *info = NULL;
1901 struct userPwdChangeFailureInformation *reject = NULL;
1902 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1903 fstring domain, user;
1905 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1906 state->request->data.auth.user));
1908 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1909 goto done;
1912 /* Change password */
1914 oldpass = state->request->data.chauthtok.oldpass;
1915 newpass = state->request->data.chauthtok.newpass;
1917 /* Initialize reject reason */
1918 state->response->data.auth.reject_reason = Undefined;
1920 if (strequal(domain, get_global_sam_name())) {
1921 struct samr_CryptPassword new_nt_password;
1922 struct samr_CryptPassword new_lm_password;
1923 struct samr_Password old_nt_hash_enc;
1924 struct samr_Password old_lanman_hash_enc;
1925 enum samPwdChangeReason rejectReason;
1927 uchar old_nt_hash[16];
1928 uchar old_lanman_hash[16];
1929 uchar new_nt_hash[16];
1930 uchar new_lanman_hash[16];
1932 contact_domain = NULL;
1934 E_md4hash(oldpass, old_nt_hash);
1935 E_md4hash(newpass, new_nt_hash);
1937 if (lp_client_lanman_auth() &&
1938 E_deshash(newpass, new_lanman_hash) &&
1939 E_deshash(oldpass, old_lanman_hash)) {
1941 /* E_deshash returns false for 'long' passwords (> 14
1942 DOS chars). This allows us to match Win2k, which
1943 does not store a LM hash for these passwords (which
1944 would reduce the effective password length to 14) */
1946 encode_pw_buffer(new_lm_password.data, newpass, STR_UNICODE);
1947 arcfour_crypt(new_lm_password.data, old_nt_hash, 516);
1948 E_old_pw_hash(new_nt_hash, old_lanman_hash, old_lanman_hash_enc.hash);
1949 } else {
1950 ZERO_STRUCT(new_lm_password);
1951 ZERO_STRUCT(old_lanman_hash_enc);
1954 encode_pw_buffer(new_nt_password.data, newpass, STR_UNICODE);
1956 arcfour_crypt(new_nt_password.data, old_nt_hash, 516);
1957 E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
1959 result = pass_oem_change(
1960 user,
1961 new_lm_password.data, old_lanman_hash_enc.hash,
1962 new_nt_password.data, old_nt_hash_enc.hash,
1963 &rejectReason);
1964 goto done;
1967 /* Get sam handle */
1969 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1970 &dom_pol);
1971 if (!NT_STATUS_IS_OK(result)) {
1972 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1973 goto done;
1976 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1977 user,
1978 newpass,
1979 oldpass,
1980 &info,
1981 &reject);
1983 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1985 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1987 fill_in_password_policy(state->response, info);
1989 state->response->data.auth.reject_reason =
1990 reject->extendedFailureReason;
1992 got_info = true;
1995 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1996 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1997 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1998 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2000 /* only fallback when the chgpasswd_user3 call is not supported */
2001 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2002 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2003 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2004 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2006 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2007 nt_errstr(result)));
2009 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2011 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2012 Map to the same status code as Windows 2003. */
2014 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2015 result = NT_STATUS_PASSWORD_RESTRICTION;
2019 done:
2021 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2022 if (lp_winbind_offline_logon()) {
2023 result = winbindd_update_creds_by_name(contact_domain,
2024 state->mem_ctx, user,
2025 newpass);
2026 /* Again, this happens when we login from gdm or xdm
2027 * and the password expires, *BUT* cached crendentials
2028 * doesn't exist. winbindd_update_creds_by_name()
2029 * returns NT_STATUS_NO_SUCH_USER.
2030 * This is not a failure.
2031 * --- BoYang
2032 * */
2033 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2034 result = NT_STATUS_OK;
2037 if (!NT_STATUS_IS_OK(result)) {
2038 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2039 goto process_result;
2044 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2046 NTSTATUS policy_ret;
2048 policy_ret = fillup_password_policy(contact_domain, state);
2050 /* failure of this is non critical, it will just provide no
2051 * additional information to the client why the change has
2052 * failed - Guenther */
2054 if (!NT_STATUS_IS_OK(policy_ret)) {
2055 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2056 goto process_result;
2060 process_result:
2062 set_auth_errors(state->response, result);
2064 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2065 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2066 domain,
2067 user,
2068 state->response->data.auth.nt_status_string,
2069 state->response->data.auth.pam_error));
2071 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2074 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2075 struct winbindd_cli_state *state)
2077 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2079 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2080 state->request->data.logoff.user));
2082 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2083 result = NT_STATUS_OK;
2084 goto process_result;
2087 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2088 result = NT_STATUS_OK;
2089 goto process_result;
2092 #ifdef HAVE_KRB5
2094 if (state->request->data.logoff.uid < 0) {
2095 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2096 goto process_result;
2099 /* what we need here is to find the corresponding krb5 ccache name *we*
2100 * created for a given username and destroy it */
2102 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2103 result = NT_STATUS_OK;
2104 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2105 goto process_result;
2108 if (!ccache_entry_identical(state->request->data.logoff.user,
2109 state->request->data.logoff.uid,
2110 state->request->data.logoff.krb5ccname)) {
2111 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2112 goto process_result;
2115 result = remove_ccache(state->request->data.logoff.user);
2116 if (!NT_STATUS_IS_OK(result)) {
2117 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2118 nt_errstr(result)));
2119 goto process_result;
2122 #else
2123 result = NT_STATUS_NOT_SUPPORTED;
2124 #endif
2126 process_result:
2129 set_auth_errors(state->response, result);
2131 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2134 /* Change user password with auth crap*/
2136 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2138 NTSTATUS result;
2139 DATA_BLOB new_nt_password;
2140 DATA_BLOB old_nt_hash_enc;
2141 DATA_BLOB new_lm_password;
2142 DATA_BLOB old_lm_hash_enc;
2143 fstring domain,user;
2144 struct policy_handle dom_pol;
2145 struct winbindd_domain *contact_domain = domainSt;
2146 struct rpc_pipe_client *cli;
2148 /* Ensure null termination */
2149 state->request->data.chng_pswd_auth_crap.user[
2150 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2151 state->request->data.chng_pswd_auth_crap.domain[
2152 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2153 *domain = 0;
2154 *user = 0;
2156 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2157 (unsigned long)state->pid,
2158 state->request->data.chng_pswd_auth_crap.domain,
2159 state->request->data.chng_pswd_auth_crap.user));
2161 if (lp_winbind_offline_logon()) {
2162 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2163 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2164 result = NT_STATUS_ACCESS_DENIED;
2165 goto done;
2168 if (*state->request->data.chng_pswd_auth_crap.domain) {
2169 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2170 } else {
2171 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2172 domain, user);
2174 if(!*domain) {
2175 DEBUG(3,("no domain specified with username (%s) - "
2176 "failing auth\n",
2177 state->request->data.chng_pswd_auth_crap.user));
2178 result = NT_STATUS_NO_SUCH_USER;
2179 goto done;
2183 if (!*domain && lp_winbind_use_default_domain()) {
2184 fstrcpy(domain,(char *)lp_workgroup());
2187 if(!*user) {
2188 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2191 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2192 (unsigned long)state->pid, domain, user));
2194 if (strequal(domain, get_global_sam_name())) {
2195 enum samPwdChangeReason reject_reason;
2197 result = pass_oem_change(
2198 user,
2199 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2200 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2201 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2202 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2203 &reject_reason);
2204 DEBUG(10, ("pass_oem_change returned %s\n",
2205 nt_errstr(result)));
2206 goto done;
2209 /* Change password */
2210 new_nt_password = data_blob_const(
2211 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2212 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2214 old_nt_hash_enc = data_blob_const(
2215 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2216 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2218 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2219 new_lm_password = data_blob_const(
2220 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2221 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2223 old_lm_hash_enc = data_blob_const(
2224 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2225 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2226 } else {
2227 new_lm_password.length = 0;
2228 old_lm_hash_enc.length = 0;
2231 /* Get sam handle */
2233 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2234 if (!NT_STATUS_IS_OK(result)) {
2235 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2236 goto done;
2239 result = rpccli_samr_chng_pswd_auth_crap(
2240 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2241 new_lm_password, old_lm_hash_enc);
2243 done:
2245 set_auth_errors(state->response, result);
2247 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2248 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2249 domain, user,
2250 state->response->data.auth.nt_status_string,
2251 state->response->data.auth.pam_error));
2253 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;