s3: wcache_invalidate_samlogon only needs the SID
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob5347c1cd453df34cb7d3ffbb08bddfc847669b23
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 "../libcli/security/security.h"
35 #include "ads.h"
36 #include "../librpc/gen_ndr/krb5pac.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
43 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
44 struct winbindd_response *resp,
45 struct netr_SamInfo3 *info3)
47 char *ex;
48 uint32_t i;
50 resp->data.auth.info3.logon_time =
51 nt_time_to_unix(info3->base.last_logon);
52 resp->data.auth.info3.logoff_time =
53 nt_time_to_unix(info3->base.last_logoff);
54 resp->data.auth.info3.kickoff_time =
55 nt_time_to_unix(info3->base.acct_expiry);
56 resp->data.auth.info3.pass_last_set_time =
57 nt_time_to_unix(info3->base.last_password_change);
58 resp->data.auth.info3.pass_can_change_time =
59 nt_time_to_unix(info3->base.allow_password_change);
60 resp->data.auth.info3.pass_must_change_time =
61 nt_time_to_unix(info3->base.force_password_change);
63 resp->data.auth.info3.logon_count = info3->base.logon_count;
64 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
66 resp->data.auth.info3.user_rid = info3->base.rid;
67 resp->data.auth.info3.group_rid = info3->base.primary_gid;
68 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
70 resp->data.auth.info3.num_groups = info3->base.groups.count;
71 resp->data.auth.info3.user_flgs = info3->base.user_flags;
73 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
74 resp->data.auth.info3.num_other_sids = info3->sidcount;
76 fstrcpy(resp->data.auth.info3.user_name,
77 info3->base.account_name.string);
78 fstrcpy(resp->data.auth.info3.full_name,
79 info3->base.full_name.string);
80 fstrcpy(resp->data.auth.info3.logon_script,
81 info3->base.logon_script.string);
82 fstrcpy(resp->data.auth.info3.profile_path,
83 info3->base.profile_path.string);
84 fstrcpy(resp->data.auth.info3.home_dir,
85 info3->base.home_directory.string);
86 fstrcpy(resp->data.auth.info3.dir_drive,
87 info3->base.home_drive.string);
89 fstrcpy(resp->data.auth.info3.logon_srv,
90 info3->base.logon_server.string);
91 fstrcpy(resp->data.auth.info3.logon_dom,
92 info3->base.domain.string);
94 ex = talloc_strdup(mem_ctx, "");
95 NT_STATUS_HAVE_NO_MEMORY(ex);
97 for (i=0; i < info3->base.groups.count; i++) {
98 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
99 info3->base.groups.rids[i].rid,
100 info3->base.groups.rids[i].attributes);
101 NT_STATUS_HAVE_NO_MEMORY(ex);
104 for (i=0; i < info3->sidcount; i++) {
105 char *sid;
107 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
108 NT_STATUS_HAVE_NO_MEMORY(sid);
110 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
111 sid,
112 info3->sids[i].attributes);
113 NT_STATUS_HAVE_NO_MEMORY(ex);
115 talloc_free(sid);
118 resp->extra_data.data = ex;
119 resp->length += talloc_get_size(ex);
121 return NT_STATUS_OK;
124 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
125 struct winbindd_response *resp,
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, 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 resp->extra_data.data = blob.data;
139 resp->length += blob.length;
141 return NT_STATUS_OK;
144 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
145 struct winbindd_response *resp,
146 const struct netr_SamInfo3 *info3,
147 const char *name_domain,
148 const char *name_user)
150 /* We've been asked to return the unix username, per
151 'winbind use default domain' settings and the like */
153 const char *nt_username, *nt_domain;
155 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
156 if (!nt_domain) {
157 /* If the server didn't give us one, just use the one
158 * we sent them */
159 nt_domain = name_domain;
162 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
163 if (!nt_username) {
164 /* If the server didn't give us one, just use the one
165 * we sent them */
166 nt_username = name_user;
169 fill_domain_username(resp->data.auth.unix_username,
170 nt_domain, nt_username, true);
172 DEBUG(5, ("Setting unix username to [%s]\n",
173 resp->data.auth.unix_username));
175 return NT_STATUS_OK;
178 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
179 struct winbindd_response *resp,
180 const struct netr_SamInfo3 *info3,
181 const char *name_domain,
182 const char *name_user)
184 char *afsname = NULL;
185 char *cell;
186 char *token;
188 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
189 if (afsname == NULL) {
190 return NT_STATUS_NO_MEMORY;
193 afsname = talloc_string_sub(mem_ctx,
194 lp_afs_username_map(),
195 "%D", name_domain);
196 afsname = talloc_string_sub(mem_ctx, afsname,
197 "%u", name_user);
198 afsname = talloc_string_sub(mem_ctx, afsname,
199 "%U", name_user);
202 struct dom_sid user_sid;
203 fstring sidstr;
205 sid_compose(&user_sid, info3->base.domain_sid,
206 info3->base.rid);
207 sid_to_fstring(sidstr, &user_sid);
208 afsname = talloc_string_sub(mem_ctx, afsname,
209 "%s", sidstr);
212 if (afsname == NULL) {
213 return NT_STATUS_NO_MEMORY;
216 strlower_m(afsname);
218 DEBUG(10, ("Generating token for user %s\n", afsname));
220 cell = strchr(afsname, '@');
222 if (cell == NULL) {
223 return NT_STATUS_NO_MEMORY;
226 *cell = '\0';
227 cell += 1;
229 token = afs_createtoken_str(afsname, cell);
230 if (token == NULL) {
231 return NT_STATUS_OK;
233 resp->extra_data.data = talloc_strdup(mem_ctx, token);
234 if (resp->extra_data.data == NULL) {
235 return NT_STATUS_NO_MEMORY;
237 resp->length += strlen((const char *)resp->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 uint32_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 security_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 security_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->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 security_token_debug(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 const char **user_ccache_file)
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 if (uid != -1) {
473 if (strequal(type, "FILE")) {
474 gen_cc = talloc_asprintf(
475 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
477 if (strequal(type, "WRFILE")) {
478 gen_cc = talloc_asprintf(
479 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
483 *user_ccache_file = gen_cc;
485 if (gen_cc == NULL) {
486 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
488 if (gen_cc == NULL) {
489 DEBUG(0,("out of memory\n"));
490 return NULL;
493 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
494 (*user_ccache_file == NULL) ? " (internal)":""));
496 return gen_cc;
499 #endif
501 uid_t get_uid_from_request(struct winbindd_request *request)
503 uid_t uid;
505 uid = request->data.auth.uid;
507 if (uid < 0) {
508 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
509 return -1;
511 return uid;
514 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
516 return get_uid_from_request(state->request);
519 /**********************************************************************
520 Authenticate a user with a clear text password using Kerberos and fill up
521 ccache if required
522 **********************************************************************/
524 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
525 struct winbindd_cli_state *state,
526 struct netr_SamInfo3 **info3)
528 #ifdef HAVE_KRB5
529 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
530 krb5_error_code krb5_ret;
531 const char *cc = NULL;
532 const char *principal_s = NULL;
533 const char *service = NULL;
534 char *realm = NULL;
535 fstring name_domain, name_user;
536 time_t ticket_lifetime = 0;
537 time_t renewal_until = 0;
538 uid_t uid = -1;
539 ADS_STRUCT *ads;
540 time_t time_offset = 0;
541 const char *user_ccache_file;
542 struct PAC_LOGON_INFO *logon_info = NULL;
544 *info3 = NULL;
546 /* 1st step:
547 * prepare a krb5_cc_cache string for the user */
549 uid = get_uid_from_state(state);
550 if (uid == -1) {
551 DEBUG(0,("no valid uid\n"));
554 cc = generate_krb5_ccache(state->mem_ctx,
555 state->request->data.auth.krb5_cc_type,
556 state->request->data.auth.uid,
557 &user_ccache_file);
558 if (cc == NULL) {
559 return NT_STATUS_NO_MEMORY;
563 /* 2nd step:
564 * get kerberos properties */
566 if (domain->private_data) {
567 ads = (ADS_STRUCT *)domain->private_data;
568 time_offset = ads->auth.time_offset;
572 /* 3rd step:
573 * do kerberos auth and setup ccache as the user */
575 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
577 realm = domain->alt_name;
578 strupper_m(realm);
580 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
581 if (principal_s == NULL) {
582 return NT_STATUS_NO_MEMORY;
585 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
586 if (service == NULL) {
587 return NT_STATUS_NO_MEMORY;
590 /* if this is a user ccache, we need to act as the user to let the krb5
591 * library handle the chown, etc. */
593 /************************ ENTERING NON-ROOT **********************/
595 if (user_ccache_file != NULL) {
596 set_effective_uid(uid);
597 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
600 result = kerberos_return_pac(state->mem_ctx,
601 principal_s,
602 state->request->data.auth.pass,
603 time_offset,
604 &ticket_lifetime,
605 &renewal_until,
607 true,
608 true,
609 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
610 NULL,
611 &logon_info);
612 if (user_ccache_file != NULL) {
613 gain_root_privilege();
616 /************************ RETURNED TO ROOT **********************/
618 if (!NT_STATUS_IS_OK(result)) {
619 goto failed;
622 *info3 = &logon_info->info3;
624 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
625 principal_s));
627 /* if we had a user's ccache then return that string for the pam
628 * environment */
630 if (user_ccache_file != NULL) {
632 fstrcpy(state->response->data.auth.krb5ccname,
633 user_ccache_file);
635 result = add_ccache_to_list(principal_s,
637 service,
638 state->request->data.auth.user,
639 realm,
640 uid,
641 time(NULL),
642 ticket_lifetime,
643 renewal_until,
644 false);
646 if (!NT_STATUS_IS_OK(result)) {
647 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
648 nt_errstr(result)));
650 } else {
652 /* need to delete the memory cred cache, it is not used anymore */
654 krb5_ret = ads_kdestroy(cc);
655 if (krb5_ret) {
656 DEBUG(3,("winbindd_raw_kerberos_login: "
657 "could not destroy krb5 credential cache: "
658 "%s\n", error_message(krb5_ret)));
663 return NT_STATUS_OK;
665 failed:
667 /* we could have created a new credential cache with a valid tgt in it
668 * but we werent able to get or verify the service ticket for this
669 * local host and therefor didn't get the PAC, we need to remove that
670 * cache entirely now */
672 krb5_ret = ads_kdestroy(cc);
673 if (krb5_ret) {
674 DEBUG(3,("winbindd_raw_kerberos_login: "
675 "could not destroy krb5 credential cache: "
676 "%s\n", error_message(krb5_ret)));
679 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
680 DEBUG(3,("winbindd_raw_kerberos_login: "
681 "could not remove ccache for user %s\n",
682 state->request->data.auth.user));
685 return result;
686 #else
687 return NT_STATUS_NOT_SUPPORTED;
688 #endif /* HAVE_KRB5 */
691 /****************************************************************
692 ****************************************************************/
694 bool check_request_flags(uint32_t flags)
696 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
697 WBFLAG_PAM_INFO3_TEXT |
698 WBFLAG_PAM_INFO3_NDR;
700 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
701 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
702 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
703 !(flags & flags_edata) ) {
704 return true;
707 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
708 flags));
710 return false;
713 /****************************************************************
714 ****************************************************************/
716 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
717 struct winbindd_response *resp,
718 uint32_t request_flags,
719 struct netr_SamInfo3 *info3,
720 const char *name_domain,
721 const char *name_user)
723 NTSTATUS result;
725 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
726 memcpy(resp->data.auth.user_session_key,
727 info3->base.key.key,
728 sizeof(resp->data.auth.user_session_key)
729 /* 16 */);
732 if (request_flags & WBFLAG_PAM_LMKEY) {
733 memcpy(resp->data.auth.first_8_lm_hash,
734 info3->base.LMSessKey.key,
735 sizeof(resp->data.auth.first_8_lm_hash)
736 /* 8 */);
739 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
740 result = append_unix_username(mem_ctx, resp,
741 info3, name_domain, name_user);
742 if (!NT_STATUS_IS_OK(result)) {
743 DEBUG(10,("Failed to append Unix Username: %s\n",
744 nt_errstr(result)));
745 return result;
749 /* currently, anything from here on potentially overwrites extra_data. */
751 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
752 result = append_info3_as_ndr(mem_ctx, resp, info3);
753 if (!NT_STATUS_IS_OK(result)) {
754 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
755 nt_errstr(result)));
756 return result;
760 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
761 result = append_info3_as_txt(mem_ctx, resp, info3);
762 if (!NT_STATUS_IS_OK(result)) {
763 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
764 nt_errstr(result)));
765 return result;
769 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
770 result = append_afs_token(mem_ctx, resp,
771 info3, name_domain, name_user);
772 if (!NT_STATUS_IS_OK(result)) {
773 DEBUG(10,("Failed to append AFS token: %s\n",
774 nt_errstr(result)));
775 return result;
779 return NT_STATUS_OK;
782 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
783 struct winbindd_cli_state *state,
784 struct netr_SamInfo3 **info3)
786 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
787 uint16 max_allowed_bad_attempts;
788 fstring name_domain, name_user;
789 struct dom_sid sid;
790 enum lsa_SidType type;
791 uchar new_nt_pass[NT_HASH_LEN];
792 const uint8 *cached_nt_pass;
793 const uint8 *cached_salt;
794 struct netr_SamInfo3 *my_info3;
795 time_t kickoff_time, must_change_time;
796 bool password_good = false;
797 #ifdef HAVE_KRB5
798 struct winbindd_tdc_domain *tdc_domain = NULL;
799 #endif
801 *info3 = NULL;
803 ZERO_STRUCTP(info3);
805 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
807 /* Parse domain and username */
809 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
812 if (!lookup_cached_name(name_domain,
813 name_user,
814 &sid,
815 &type)) {
816 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
817 return NT_STATUS_NO_SUCH_USER;
820 if (type != SID_NAME_USER) {
821 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
822 return NT_STATUS_LOGON_FAILURE;
825 result = winbindd_get_creds(domain,
826 state->mem_ctx,
827 &sid,
828 &my_info3,
829 &cached_nt_pass,
830 &cached_salt);
831 if (!NT_STATUS_IS_OK(result)) {
832 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
833 return result;
836 *info3 = my_info3;
838 E_md4hash(state->request->data.auth.pass, new_nt_pass);
840 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
841 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
842 if (cached_salt) {
843 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
846 if (cached_salt) {
847 /* In this case we didn't store the nt_hash itself,
848 but the MD5 combination of salt + nt_hash. */
849 uchar salted_hash[NT_HASH_LEN];
850 E_md5hash(cached_salt, new_nt_pass, salted_hash);
852 password_good = (memcmp(cached_nt_pass, salted_hash,
853 NT_HASH_LEN) == 0);
854 } else {
855 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
856 password_good = (memcmp(cached_nt_pass, new_nt_pass,
857 NT_HASH_LEN) == 0);
860 if (password_good) {
862 /* User *DOES* know the password, update logon_time and reset
863 * bad_pw_count */
865 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
867 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
868 return NT_STATUS_ACCOUNT_LOCKED_OUT;
871 if (my_info3->base.acct_flags & ACB_DISABLED) {
872 return NT_STATUS_ACCOUNT_DISABLED;
875 if (my_info3->base.acct_flags & ACB_WSTRUST) {
876 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
879 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
880 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
883 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
884 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
887 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
888 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
889 my_info3->base.acct_flags));
890 return NT_STATUS_LOGON_FAILURE;
893 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
894 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
895 return NT_STATUS_ACCOUNT_EXPIRED;
898 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
899 if (must_change_time != 0 && must_change_time < time(NULL)) {
900 /* we allow grace logons when the password has expired */
901 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
902 /* return NT_STATUS_PASSWORD_EXPIRED; */
903 goto success;
906 #ifdef HAVE_KRB5
907 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
908 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
909 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
910 /* used to cope with the case winbindd starting without network. */
911 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
913 uid_t uid = -1;
914 const char *cc = NULL;
915 char *realm = NULL;
916 const char *principal_s = NULL;
917 const char *service = NULL;
918 const char *user_ccache_file;
920 uid = get_uid_from_state(state);
921 if (uid == -1) {
922 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
923 return NT_STATUS_INVALID_PARAMETER;
926 cc = generate_krb5_ccache(state->mem_ctx,
927 state->request->data.auth.krb5_cc_type,
928 state->request->data.auth.uid,
929 &user_ccache_file);
930 if (cc == NULL) {
931 return NT_STATUS_NO_MEMORY;
934 realm = domain->alt_name;
935 strupper_m(realm);
937 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
938 if (principal_s == NULL) {
939 return NT_STATUS_NO_MEMORY;
942 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
943 if (service == NULL) {
944 return NT_STATUS_NO_MEMORY;
947 if (user_ccache_file != NULL) {
949 fstrcpy(state->response->data.auth.krb5ccname,
950 user_ccache_file);
952 result = add_ccache_to_list(principal_s,
954 service,
955 state->request->data.auth.user,
956 domain->alt_name,
957 uid,
958 time(NULL),
959 time(NULL) + lp_winbind_cache_time(),
960 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
961 true);
963 if (!NT_STATUS_IS_OK(result)) {
964 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
965 "to add ccache to list: %s\n",
966 nt_errstr(result)));
970 #endif /* HAVE_KRB5 */
971 success:
972 /* FIXME: we possibly should handle logon hours as well (does xp when
973 * offline?) see auth/auth_sam.c:sam_account_ok for details */
975 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
976 my_info3->base.bad_password_count = 0;
978 result = winbindd_update_creds_by_info3(domain,
979 state->request->data.auth.user,
980 state->request->data.auth.pass,
981 my_info3);
982 if (!NT_STATUS_IS_OK(result)) {
983 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
984 nt_errstr(result)));
985 return result;
988 return NT_STATUS_OK;
992 /* User does *NOT* know the correct password, modify info3 accordingly */
994 /* failure of this is not critical */
995 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
996 if (!NT_STATUS_IS_OK(result)) {
997 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
998 "Won't be able to honour account lockout policies\n"));
1001 /* increase counter */
1002 my_info3->base.bad_password_count++;
1004 if (max_allowed_bad_attempts == 0) {
1005 goto failed;
1008 /* lockout user */
1009 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1011 uint32 password_properties;
1013 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1014 if (!NT_STATUS_IS_OK(result)) {
1015 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1018 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1019 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1020 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1024 failed:
1025 result = winbindd_update_creds_by_info3(domain,
1026 state->request->data.auth.user,
1027 NULL,
1028 my_info3);
1030 if (!NT_STATUS_IS_OK(result)) {
1031 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1032 nt_errstr(result)));
1035 return NT_STATUS_LOGON_FAILURE;
1038 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1039 struct winbindd_cli_state *state,
1040 struct netr_SamInfo3 **info3)
1042 struct winbindd_domain *contact_domain;
1043 fstring name_domain, name_user;
1044 NTSTATUS result;
1046 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1048 /* Parse domain and username */
1050 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1052 /* what domain should we contact? */
1054 if ( IS_DC ) {
1055 if (!(contact_domain = find_domain_from_name(name_domain))) {
1056 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1057 state->request->data.auth.user, name_domain, name_user, name_domain));
1058 result = NT_STATUS_NO_SUCH_USER;
1059 goto done;
1062 } else {
1063 if (is_myname(name_domain)) {
1064 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1065 result = NT_STATUS_NO_SUCH_USER;
1066 goto done;
1069 contact_domain = find_domain_from_name(name_domain);
1070 if (contact_domain == NULL) {
1071 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1072 state->request->data.auth.user, name_domain, name_user, name_domain));
1074 contact_domain = find_our_domain();
1078 if (contact_domain->initialized &&
1079 contact_domain->active_directory) {
1080 goto try_login;
1083 if (!contact_domain->initialized) {
1084 init_dc_connection(contact_domain);
1087 if (!contact_domain->active_directory) {
1088 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1089 return NT_STATUS_INVALID_LOGON_TYPE;
1091 try_login:
1092 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1093 done:
1094 return result;
1097 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1098 const char *domain, const char *user,
1099 const DATA_BLOB *challenge,
1100 const DATA_BLOB *lm_resp,
1101 const DATA_BLOB *nt_resp,
1102 struct netr_SamInfo3 **pinfo3)
1104 struct auth_usersupplied_info *user_info = NULL;
1105 NTSTATUS status;
1107 status = make_user_info(&user_info, user, user, domain, domain,
1108 global_myname(), lm_resp, nt_resp, NULL, NULL,
1109 NULL, AUTH_PASSWORD_RESPONSE);
1110 if (!NT_STATUS_IS_OK(status)) {
1111 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1112 return status;
1115 /* We don't want any more mapping of the username */
1116 user_info->mapped_state = True;
1118 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1119 pinfo3);
1120 free_user_info(&user_info);
1121 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1122 user, nt_errstr(status)));
1123 return status;
1126 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1127 TALLOC_CTX *mem_ctx,
1128 uint32 logon_parameters,
1129 const char *server,
1130 const char *username,
1131 const char *domain,
1132 const char *workstation,
1133 const uint8 chal[8],
1134 DATA_BLOB lm_response,
1135 DATA_BLOB nt_response,
1136 struct netr_SamInfo3 **info3);
1138 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1139 struct winbindd_cli_state *state,
1140 struct netr_SamInfo3 **info3)
1143 struct rpc_pipe_client *netlogon_pipe;
1144 uchar chal[8];
1145 DATA_BLOB lm_resp;
1146 DATA_BLOB nt_resp;
1147 int attempts = 0;
1148 unsigned char local_nt_response[24];
1149 fstring name_domain, name_user;
1150 bool retry;
1151 NTSTATUS result;
1152 struct netr_SamInfo3 *my_info3 = NULL;
1154 *info3 = NULL;
1156 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1158 /* Parse domain and username */
1160 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1162 /* do password magic */
1164 generate_random_buffer(chal, sizeof(chal));
1166 if (lp_client_ntlmv2_auth()) {
1167 DATA_BLOB server_chal;
1168 DATA_BLOB names_blob;
1169 server_chal = data_blob_const(chal, 8);
1171 /* note that the 'workgroup' here is for the local
1172 machine. The 'server name' must match the
1173 'workstation' passed to the actual SamLogon call.
1175 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1177 if (!SMBNTLMv2encrypt(state->mem_ctx, name_user, name_domain,
1178 state->request->data.auth.pass,
1179 &server_chal,
1180 &names_blob,
1181 &lm_resp, &nt_resp, NULL, NULL)) {
1182 data_blob_free(&names_blob);
1183 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1184 result = NT_STATUS_NO_MEMORY;
1185 goto done;
1187 data_blob_free(&names_blob);
1188 } else {
1189 lm_resp = data_blob_null;
1190 SMBNTencrypt(state->request->data.auth.pass,
1191 chal,
1192 local_nt_response);
1194 nt_resp = data_blob_talloc(state->mem_ctx,
1195 local_nt_response,
1196 sizeof(local_nt_response));
1199 if (strequal(name_domain, get_global_sam_name())) {
1200 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1202 result = winbindd_dual_auth_passdb(
1203 state->mem_ctx, name_domain, name_user,
1204 &chal_blob, &lm_resp, &nt_resp, info3);
1205 goto done;
1208 /* check authentication loop */
1210 do {
1211 netlogon_fn_t logon_fn;
1213 ZERO_STRUCTP(my_info3);
1214 retry = false;
1216 result = cm_connect_netlogon(domain, &netlogon_pipe);
1218 if (!NT_STATUS_IS_OK(result)) {
1219 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1220 goto done;
1223 /* It is really important to try SamLogonEx here,
1224 * because in a clustered environment, we want to use
1225 * one machine account from multiple physical
1226 * computers.
1228 * With a normal SamLogon call, we must keep the
1229 * credentials chain updated and intact between all
1230 * users of the machine account (which would imply
1231 * cross-node communication for every NTLM logon).
1233 * (The credentials chain is not per NETLOGON pipe
1234 * connection, but globally on the server/client pair
1235 * by machine name).
1237 * When using SamLogonEx, the credentials are not
1238 * supplied, but the session key is implied by the
1239 * wrapping SamLogon context.
1241 * -- abartlet 21 April 2008
1244 logon_fn = domain->can_do_samlogon_ex
1245 ? rpccli_netlogon_sam_network_logon_ex
1246 : rpccli_netlogon_sam_network_logon;
1248 result = logon_fn(netlogon_pipe,
1249 state->mem_ctx,
1251 domain->dcname, /* server name */
1252 name_user, /* user name */
1253 name_domain, /* target domain */
1254 global_myname(), /* workstation */
1255 chal,
1256 lm_resp,
1257 nt_resp,
1258 &my_info3);
1259 attempts += 1;
1261 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1262 && domain->can_do_samlogon_ex) {
1263 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1264 "retrying with NetSamLogon\n"));
1265 domain->can_do_samlogon_ex = false;
1266 continue;
1269 /* We have to try a second time as cm_connect_netlogon
1270 might not yet have noticed that the DC has killed
1271 our connection. */
1273 if (!rpccli_is_connected(netlogon_pipe)) {
1274 continue;
1277 /* if we get access denied, a possible cause was that we had
1278 and open connection to the DC, but someone changed our
1279 machine account password out from underneath us using 'net
1280 rpc changetrustpw' */
1282 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1283 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1284 "ACCESS_DENIED. Maybe the trust account "
1285 "password was changed and we didn't know it. "
1286 "Killing connections to domain %s\n",
1287 name_domain));
1288 invalidate_cm_connection(&domain->conn);
1289 retry = true;
1292 } while ( (attempts < 2) && retry );
1294 /* handle the case where a NT4 DC does not fill in the acct_flags in
1295 * the samlogon reply info3. When accurate info3 is required by the
1296 * caller, we look up the account flags ourselve - gd */
1298 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1299 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1301 struct rpc_pipe_client *samr_pipe;
1302 struct policy_handle samr_domain_handle, user_pol;
1303 union samr_UserInfo *info = NULL;
1304 NTSTATUS status_tmp;
1305 uint32 acct_flags;
1307 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1308 &samr_pipe, &samr_domain_handle);
1310 if (!NT_STATUS_IS_OK(status_tmp)) {
1311 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1312 nt_errstr(status_tmp)));
1313 goto done;
1316 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1317 &samr_domain_handle,
1318 MAXIMUM_ALLOWED_ACCESS,
1319 my_info3->base.rid,
1320 &user_pol);
1322 if (!NT_STATUS_IS_OK(status_tmp)) {
1323 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1324 nt_errstr(status_tmp)));
1325 goto done;
1328 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1329 &user_pol,
1331 &info);
1333 if (!NT_STATUS_IS_OK(status_tmp)) {
1334 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1335 nt_errstr(status_tmp)));
1336 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1337 goto done;
1340 acct_flags = info->info16.acct_flags;
1342 if (acct_flags == 0) {
1343 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1344 goto done;
1347 my_info3->base.acct_flags = acct_flags;
1349 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1351 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1354 *info3 = my_info3;
1355 done:
1356 return result;
1359 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1360 struct winbindd_cli_state *state)
1362 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1363 NTSTATUS krb5_result = NT_STATUS_OK;
1364 fstring name_domain, name_user;
1365 char *mapped_user;
1366 fstring domain_user;
1367 struct netr_SamInfo3 *info3 = NULL;
1368 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1370 /* Ensure null termination */
1371 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1373 /* Ensure null termination */
1374 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1376 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1377 state->request->data.auth.user));
1379 /* Parse domain and username */
1381 name_map_status = normalize_name_unmap(state->mem_ctx,
1382 state->request->data.auth.user,
1383 &mapped_user);
1385 /* If the name normalization didnt' actually do anything,
1386 just use the original name */
1388 if (!NT_STATUS_IS_OK(name_map_status) &&
1389 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1391 mapped_user = state->request->data.auth.user;
1394 parse_domain_user(mapped_user, name_domain, name_user);
1396 if ( mapped_user != state->request->data.auth.user ) {
1397 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1398 *lp_winbind_separator(),
1399 name_user );
1400 safe_strcpy( state->request->data.auth.user, domain_user,
1401 sizeof(state->request->data.auth.user)-1 );
1404 if (!domain->online) {
1405 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1406 if (domain->startup) {
1407 /* Logons are very important to users. If we're offline and
1408 we get a request within the first 30 seconds of startup,
1409 try very hard to find a DC and go online. */
1411 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1412 "request in startup mode.\n", domain->name ));
1414 winbindd_flush_negative_conn_cache(domain);
1415 result = init_dc_connection(domain);
1419 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1421 /* Check for Kerberos authentication */
1422 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1424 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1425 /* save for later */
1426 krb5_result = result;
1429 if (NT_STATUS_IS_OK(result)) {
1430 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1431 goto process_result;
1432 } else {
1433 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1436 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1437 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1438 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1439 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1440 set_domain_offline( domain );
1441 goto cached_logon;
1444 /* there are quite some NT_STATUS errors where there is no
1445 * point in retrying with a samlogon, we explictly have to take
1446 * care not to increase the bad logon counter on the DC */
1448 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1449 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1450 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1451 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1452 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1453 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1454 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1455 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1456 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1457 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1458 goto done;
1461 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1462 DEBUG(3,("falling back to samlogon\n"));
1463 goto sam_logon;
1464 } else {
1465 goto cached_logon;
1469 sam_logon:
1470 /* Check for Samlogon authentication */
1471 if (domain->online) {
1472 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1474 if (NT_STATUS_IS_OK(result)) {
1475 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1476 /* add the Krb5 err if we have one */
1477 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1478 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1480 goto process_result;
1483 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1484 nt_errstr(result)));
1486 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1487 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1488 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1490 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1491 set_domain_offline( domain );
1492 goto cached_logon;
1495 if (domain->online) {
1496 /* We're still online - fail. */
1497 goto done;
1501 cached_logon:
1502 /* Check for Cached logons */
1503 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1504 lp_winbind_offline_logon()) {
1506 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1508 if (NT_STATUS_IS_OK(result)) {
1509 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1510 goto process_result;
1511 } else {
1512 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1513 goto done;
1517 process_result:
1519 if (NT_STATUS_IS_OK(result)) {
1521 struct dom_sid user_sid;
1523 /* In all codepaths where result == NT_STATUS_OK info3 must have
1524 been initialized. */
1525 if (!info3) {
1526 result = NT_STATUS_INTERNAL_ERROR;
1527 goto done;
1530 sid_compose(&user_sid, info3->base.domain_sid,
1531 info3->base.rid);
1533 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1534 &user_sid);
1535 netsamlogon_cache_store(name_user, info3);
1537 /* save name_to_sid info as early as possible (only if
1538 this is our primary domain so we don't invalidate
1539 the cache entry by storing the seq_num for the wrong
1540 domain). */
1541 if ( domain->primary ) {
1542 cache_name2sid(domain, name_domain, name_user,
1543 SID_NAME_USER, &user_sid);
1546 /* Check if the user is in the right group */
1548 result = check_info3_in_group(
1549 info3,
1550 state->request->data.auth.require_membership_of_sid);
1551 if (!NT_STATUS_IS_OK(result)) {
1552 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1553 state->request->data.auth.user,
1554 state->request->data.auth.require_membership_of_sid));
1555 goto done;
1558 result = append_auth_data(state->mem_ctx, state->response,
1559 state->request->flags, info3,
1560 name_domain, name_user);
1561 if (!NT_STATUS_IS_OK(result)) {
1562 goto done;
1565 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1566 && lp_winbind_offline_logon()) {
1568 result = winbindd_store_creds(domain,
1569 state->request->data.auth.user,
1570 state->request->data.auth.pass,
1571 info3);
1574 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1575 struct winbindd_domain *our_domain = find_our_domain();
1577 /* This is not entirely correct I believe, but it is
1578 consistent. Only apply the password policy settings
1579 too warn users for our own domain. Cannot obtain these
1580 from trusted DCs all the time so don't do it at all.
1581 -- jerry */
1583 result = NT_STATUS_NOT_SUPPORTED;
1584 if (our_domain == domain ) {
1585 result = fillup_password_policy(our_domain, state);
1588 if (!NT_STATUS_IS_OK(result)
1589 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1591 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1592 domain->name, nt_errstr(result)));
1593 goto done;
1597 result = NT_STATUS_OK;
1600 done:
1601 /* give us a more useful (more correct?) error code */
1602 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1603 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1604 result = NT_STATUS_NO_LOGON_SERVERS;
1607 set_auth_errors(state->response, result);
1609 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1610 state->request->data.auth.user,
1611 state->response->data.auth.nt_status_string,
1612 state->response->data.auth.pam_error));
1614 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1617 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1618 struct winbindd_cli_state *state)
1620 NTSTATUS result;
1621 struct netr_SamInfo3 *info3 = NULL;
1622 struct rpc_pipe_client *netlogon_pipe;
1623 const char *name_user = NULL;
1624 const char *name_domain = NULL;
1625 const char *workstation;
1626 int attempts = 0;
1627 bool retry;
1629 DATA_BLOB lm_resp, nt_resp;
1631 /* This is child-only, so no check for privileged access is needed
1632 anymore */
1634 /* Ensure null termination */
1635 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1636 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1638 name_user = state->request->data.auth_crap.user;
1639 name_domain = state->request->data.auth_crap.domain;
1640 workstation = state->request->data.auth_crap.workstation;
1642 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1643 name_domain, name_user));
1645 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1646 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1647 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1648 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1649 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1650 state->request->data.auth_crap.lm_resp_len,
1651 state->request->data.auth_crap.nt_resp_len));
1652 result = NT_STATUS_INVALID_PARAMETER;
1653 goto done;
1657 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1658 state->request->data.auth_crap.lm_resp_len);
1660 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1661 nt_resp = data_blob_talloc(state->mem_ctx,
1662 state->request->extra_data.data,
1663 state->request->data.auth_crap.nt_resp_len);
1664 } else {
1665 nt_resp = data_blob_talloc(state->mem_ctx,
1666 state->request->data.auth_crap.nt_resp,
1667 state->request->data.auth_crap.nt_resp_len);
1670 if (strequal(name_domain, get_global_sam_name())) {
1671 DATA_BLOB chal_blob = data_blob_const(
1672 state->request->data.auth_crap.chal,
1673 sizeof(state->request->data.auth_crap.chal));
1675 result = winbindd_dual_auth_passdb(
1676 state->mem_ctx, name_domain, name_user,
1677 &chal_blob, &lm_resp, &nt_resp, &info3);
1678 goto process_result;
1681 do {
1682 netlogon_fn_t logon_fn;
1684 retry = false;
1686 netlogon_pipe = NULL;
1687 result = cm_connect_netlogon(domain, &netlogon_pipe);
1689 if (!NT_STATUS_IS_OK(result)) {
1690 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1691 nt_errstr(result)));
1692 goto done;
1695 logon_fn = domain->can_do_samlogon_ex
1696 ? rpccli_netlogon_sam_network_logon_ex
1697 : rpccli_netlogon_sam_network_logon;
1699 result = logon_fn(netlogon_pipe,
1700 state->mem_ctx,
1701 state->request->data.auth_crap.logon_parameters,
1702 domain->dcname,
1703 name_user,
1704 name_domain,
1705 /* Bug #3248 - found by Stefan Burkei. */
1706 workstation, /* We carefully set this above so use it... */
1707 state->request->data.auth_crap.chal,
1708 lm_resp,
1709 nt_resp,
1710 &info3);
1712 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1713 && domain->can_do_samlogon_ex) {
1714 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1715 "retrying with NetSamLogon\n"));
1716 domain->can_do_samlogon_ex = false;
1717 continue;
1720 attempts += 1;
1722 /* We have to try a second time as cm_connect_netlogon
1723 might not yet have noticed that the DC has killed
1724 our connection. */
1726 if (!rpccli_is_connected(netlogon_pipe)) {
1727 continue;
1730 /* if we get access denied, a possible cause was that we had and open
1731 connection to the DC, but someone changed our machine account password
1732 out from underneath us using 'net rpc changetrustpw' */
1734 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1735 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1736 "ACCESS_DENIED. Maybe the trust account "
1737 "password was changed and we didn't know it. "
1738 "Killing connections to domain %s\n",
1739 name_domain));
1740 invalidate_cm_connection(&domain->conn);
1741 retry = true;
1744 } while ( (attempts < 2) && retry );
1746 process_result:
1748 if (NT_STATUS_IS_OK(result)) {
1749 struct dom_sid user_sid;
1751 sid_compose(&user_sid, info3->base.domain_sid,
1752 info3->base.rid);
1753 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1754 &user_sid);
1755 netsamlogon_cache_store(name_user, info3);
1757 /* Check if the user is in the right group */
1759 result = check_info3_in_group(
1760 info3,
1761 state->request->data.auth_crap.require_membership_of_sid);
1762 if (!NT_STATUS_IS_OK(result)) {
1763 DEBUG(3, ("User %s is not in the required group (%s), so "
1764 "crap authentication is rejected\n",
1765 state->request->data.auth_crap.user,
1766 state->request->data.auth_crap.require_membership_of_sid));
1767 goto done;
1770 result = append_auth_data(state->mem_ctx, state->response,
1771 state->request->flags, info3,
1772 name_domain, name_user);
1773 if (!NT_STATUS_IS_OK(result)) {
1774 goto done;
1778 done:
1780 /* give us a more useful (more correct?) error code */
1781 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1782 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1783 result = NT_STATUS_NO_LOGON_SERVERS;
1786 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1787 result = nt_status_squash(result);
1790 set_auth_errors(state->response, result);
1792 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1793 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1794 name_domain,
1795 name_user,
1796 state->response->data.auth.nt_status_string,
1797 state->response->data.auth.pam_error));
1799 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1802 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1803 struct winbindd_cli_state *state)
1805 char *oldpass;
1806 char *newpass = NULL;
1807 struct policy_handle dom_pol;
1808 struct rpc_pipe_client *cli = NULL;
1809 bool got_info = false;
1810 struct samr_DomInfo1 *info = NULL;
1811 struct userPwdChangeFailureInformation *reject = NULL;
1812 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1813 fstring domain, user;
1815 ZERO_STRUCT(dom_pol);
1817 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1818 state->request->data.auth.user));
1820 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1821 goto done;
1824 /* Change password */
1826 oldpass = state->request->data.chauthtok.oldpass;
1827 newpass = state->request->data.chauthtok.newpass;
1829 /* Initialize reject reason */
1830 state->response->data.auth.reject_reason = Undefined;
1832 /* Get sam handle */
1834 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1835 &dom_pol);
1836 if (!NT_STATUS_IS_OK(result)) {
1837 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1838 goto done;
1841 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1842 user,
1843 newpass,
1844 oldpass,
1845 &info,
1846 &reject);
1848 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1850 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1852 fill_in_password_policy(state->response, info);
1854 state->response->data.auth.reject_reason =
1855 reject->extendedFailureReason;
1857 got_info = true;
1860 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1861 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1862 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1863 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1865 /* only fallback when the chgpasswd_user3 call is not supported */
1866 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1867 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1868 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1869 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1871 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1872 nt_errstr(result)));
1874 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1876 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1877 Map to the same status code as Windows 2003. */
1879 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1880 result = NT_STATUS_PASSWORD_RESTRICTION;
1884 done:
1886 if (NT_STATUS_IS_OK(result)
1887 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1888 && lp_winbind_offline_logon()) {
1889 result = winbindd_update_creds_by_name(contact_domain, user,
1890 newpass);
1891 /* Again, this happens when we login from gdm or xdm
1892 * and the password expires, *BUT* cached crendentials
1893 * doesn't exist. winbindd_update_creds_by_name()
1894 * returns NT_STATUS_NO_SUCH_USER.
1895 * This is not a failure.
1896 * --- BoYang
1897 * */
1898 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1899 result = NT_STATUS_OK;
1902 if (!NT_STATUS_IS_OK(result)) {
1903 DEBUG(10, ("Failed to store creds: %s\n",
1904 nt_errstr(result)));
1905 goto process_result;
1909 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1911 NTSTATUS policy_ret;
1913 policy_ret = fillup_password_policy(contact_domain, state);
1915 /* failure of this is non critical, it will just provide no
1916 * additional information to the client why the change has
1917 * failed - Guenther */
1919 if (!NT_STATUS_IS_OK(policy_ret)) {
1920 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1921 goto process_result;
1925 process_result:
1927 if (strequal(contact_domain->name, get_global_sam_name())) {
1928 /* FIXME: internal rpc pipe does not cache handles yet */
1929 if (cli) {
1930 if (is_valid_policy_hnd(&dom_pol)) {
1931 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1933 TALLOC_FREE(cli);
1937 set_auth_errors(state->response, result);
1939 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1940 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1941 domain,
1942 user,
1943 state->response->data.auth.nt_status_string,
1944 state->response->data.auth.pam_error));
1946 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1949 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1950 struct winbindd_cli_state *state)
1952 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1954 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1955 state->request->data.logoff.user));
1957 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1958 result = NT_STATUS_OK;
1959 goto process_result;
1962 if (state->request->data.logoff.krb5ccname[0] == '\0') {
1963 result = NT_STATUS_OK;
1964 goto process_result;
1967 #ifdef HAVE_KRB5
1969 if (state->request->data.logoff.uid < 0) {
1970 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1971 goto process_result;
1974 /* what we need here is to find the corresponding krb5 ccache name *we*
1975 * created for a given username and destroy it */
1977 if (!ccache_entry_exists(state->request->data.logoff.user)) {
1978 result = NT_STATUS_OK;
1979 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
1980 goto process_result;
1983 if (!ccache_entry_identical(state->request->data.logoff.user,
1984 state->request->data.logoff.uid,
1985 state->request->data.logoff.krb5ccname)) {
1986 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
1987 goto process_result;
1990 result = remove_ccache(state->request->data.logoff.user);
1991 if (!NT_STATUS_IS_OK(result)) {
1992 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
1993 nt_errstr(result)));
1994 goto process_result;
1997 #else
1998 result = NT_STATUS_NOT_SUPPORTED;
1999 #endif
2001 process_result:
2004 set_auth_errors(state->response, result);
2006 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2009 /* Change user password with auth crap*/
2011 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2013 NTSTATUS result;
2014 DATA_BLOB new_nt_password;
2015 DATA_BLOB old_nt_hash_enc;
2016 DATA_BLOB new_lm_password;
2017 DATA_BLOB old_lm_hash_enc;
2018 fstring domain,user;
2019 struct policy_handle dom_pol;
2020 struct winbindd_domain *contact_domain = domainSt;
2021 struct rpc_pipe_client *cli = NULL;
2023 ZERO_STRUCT(dom_pol);
2025 /* Ensure null termination */
2026 state->request->data.chng_pswd_auth_crap.user[
2027 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2028 state->request->data.chng_pswd_auth_crap.domain[
2029 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2030 *domain = 0;
2031 *user = 0;
2033 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2034 (unsigned long)state->pid,
2035 state->request->data.chng_pswd_auth_crap.domain,
2036 state->request->data.chng_pswd_auth_crap.user));
2038 if (lp_winbind_offline_logon()) {
2039 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2040 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2041 result = NT_STATUS_ACCESS_DENIED;
2042 goto done;
2045 if (*state->request->data.chng_pswd_auth_crap.domain) {
2046 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2047 } else {
2048 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2049 domain, user);
2051 if(!*domain) {
2052 DEBUG(3,("no domain specified with username (%s) - "
2053 "failing auth\n",
2054 state->request->data.chng_pswd_auth_crap.user));
2055 result = NT_STATUS_NO_SUCH_USER;
2056 goto done;
2060 if (!*domain && lp_winbind_use_default_domain()) {
2061 fstrcpy(domain,(char *)lp_workgroup());
2064 if(!*user) {
2065 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2068 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2069 (unsigned long)state->pid, domain, user));
2071 /* Change password */
2072 new_nt_password = data_blob_const(
2073 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2074 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2076 old_nt_hash_enc = data_blob_const(
2077 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2078 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2080 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2081 new_lm_password = data_blob_const(
2082 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2083 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2085 old_lm_hash_enc = data_blob_const(
2086 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2087 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2088 } else {
2089 new_lm_password.length = 0;
2090 old_lm_hash_enc.length = 0;
2093 /* Get sam handle */
2095 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2096 if (!NT_STATUS_IS_OK(result)) {
2097 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2098 goto done;
2101 result = rpccli_samr_chng_pswd_auth_crap(
2102 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2103 new_lm_password, old_lm_hash_enc);
2105 done:
2107 if (strequal(contact_domain->name, get_global_sam_name())) {
2108 /* FIXME: internal rpc pipe does not cache handles yet */
2109 if (cli) {
2110 if (is_valid_policy_hnd(&dom_pol)) {
2111 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2113 TALLOC_FREE(cli);
2117 set_auth_errors(state->response, result);
2119 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2120 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2121 domain, user,
2122 state->response->data.auth.nt_status_string,
2123 state->response->data.auth.pam_error));
2125 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;