tdb: fix non-WAF build, commit 1.2.6 ABI file.
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob968ffcd9ca39a366b62561593b30d3b2fc4a7c62
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/dom_sid.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_cli_state *state,
45 struct netr_SamInfo3 *info3)
47 char *ex;
48 uint32_t i;
50 state->response->data.auth.info3.logon_time =
51 nt_time_to_unix(info3->base.last_logon);
52 state->response->data.auth.info3.logoff_time =
53 nt_time_to_unix(info3->base.last_logoff);
54 state->response->data.auth.info3.kickoff_time =
55 nt_time_to_unix(info3->base.acct_expiry);
56 state->response->data.auth.info3.pass_last_set_time =
57 nt_time_to_unix(info3->base.last_password_change);
58 state->response->data.auth.info3.pass_can_change_time =
59 nt_time_to_unix(info3->base.allow_password_change);
60 state->response->data.auth.info3.pass_must_change_time =
61 nt_time_to_unix(info3->base.force_password_change);
63 state->response->data.auth.info3.logon_count = info3->base.logon_count;
64 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
66 state->response->data.auth.info3.user_rid = info3->base.rid;
67 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
68 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
70 state->response->data.auth.info3.num_groups = info3->base.groups.count;
71 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
73 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
74 state->response->data.auth.info3.num_other_sids = info3->sidcount;
76 fstrcpy(state->response->data.auth.info3.user_name,
77 info3->base.account_name.string);
78 fstrcpy(state->response->data.auth.info3.full_name,
79 info3->base.full_name.string);
80 fstrcpy(state->response->data.auth.info3.logon_script,
81 info3->base.logon_script.string);
82 fstrcpy(state->response->data.auth.info3.profile_path,
83 info3->base.profile_path.string);
84 fstrcpy(state->response->data.auth.info3.home_dir,
85 info3->base.home_directory.string);
86 fstrcpy(state->response->data.auth.info3.dir_drive,
87 info3->base.home_drive.string);
89 fstrcpy(state->response->data.auth.info3.logon_srv,
90 info3->base.logon_server.string);
91 fstrcpy(state->response->data.auth.info3.logon_dom,
92 info3->base.domain.string);
94 ex = talloc_strdup(state->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 state->response->extra_data.data = ex;
119 state->response->length += talloc_get_size(ex);
121 return NT_STATUS_OK;
124 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
125 struct winbindd_cli_state *state,
126 struct netr_SamInfo3 *info3)
128 DATA_BLOB blob;
129 enum ndr_err_code ndr_err;
131 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, 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 state->response->extra_data.data = blob.data;
139 state->response->length += blob.length;
141 return NT_STATUS_OK;
144 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
145 struct winbindd_cli_state *state,
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(state->response->data.auth.unix_username,
170 nt_domain, nt_username, true);
172 DEBUG(5,("Setting unix username to [%s]\n",
173 state->response->data.auth.unix_username));
175 return NT_STATUS_OK;
178 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
179 struct winbindd_cli_state *state,
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 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
234 token);
235 if (state->response->extra_data.data == NULL) {
236 return NT_STATUS_NO_MEMORY;
238 state->response->length +=
239 strlen((const char *)state->response->extra_data.data)+1;
241 return NT_STATUS_OK;
244 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
245 const char *group_sid)
247 * Check whether a user belongs to a group or list of groups.
249 * @param mem_ctx talloc memory context.
250 * @param info3 user information, including group membership info.
251 * @param group_sid One or more groups , separated by commas.
253 * @return NT_STATUS_OK on success,
254 * NT_STATUS_LOGON_FAILURE if the user does not belong,
255 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
258 struct dom_sid *require_membership_of_sid;
259 uint32_t num_require_membership_of_sid;
260 char *req_sid;
261 const char *p;
262 struct dom_sid sid;
263 size_t i;
264 struct security_token *token;
265 TALLOC_CTX *frame = talloc_stackframe();
266 NTSTATUS status;
268 /* Parse the 'required group' SID */
270 if (!group_sid || !group_sid[0]) {
271 /* NO sid supplied, all users may access */
272 return NT_STATUS_OK;
275 token = talloc_zero(talloc_tos(), struct security_token);
276 if (token == NULL) {
277 DEBUG(0, ("talloc failed\n"));
278 TALLOC_FREE(frame);
279 return NT_STATUS_NO_MEMORY;
282 num_require_membership_of_sid = 0;
283 require_membership_of_sid = NULL;
285 p = group_sid;
287 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
288 if (!string_to_sid(&sid, req_sid)) {
289 DEBUG(0, ("check_info3_in_group: could not parse %s "
290 "as a SID!", req_sid));
291 TALLOC_FREE(frame);
292 return NT_STATUS_INVALID_PARAMETER;
295 status = add_sid_to_array(talloc_tos(), &sid,
296 &require_membership_of_sid,
297 &num_require_membership_of_sid);
298 if (!NT_STATUS_IS_OK(status)) {
299 DEBUG(0, ("add_sid_to_array failed\n"));
300 TALLOC_FREE(frame);
301 return status;
305 status = sid_array_from_info3(talloc_tos(), info3,
306 &token->sids,
307 &token->num_sids,
308 true, false);
309 if (!NT_STATUS_IS_OK(status)) {
310 TALLOC_FREE(frame);
311 return status;
314 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
315 token))
316 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
317 token))) {
318 DEBUG(3, ("could not add aliases: %s\n",
319 nt_errstr(status)));
320 TALLOC_FREE(frame);
321 return status;
324 debug_nt_user_token(DBGC_CLASS, 10, token);
326 for (i=0; i<num_require_membership_of_sid; i++) {
327 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
328 &require_membership_of_sid[i])));
329 if (nt_token_check_sid(&require_membership_of_sid[i],
330 token)) {
331 DEBUG(10, ("Access ok\n"));
332 TALLOC_FREE(frame);
333 return NT_STATUS_OK;
337 /* Do not distinguish this error from a wrong username/pw */
339 TALLOC_FREE(frame);
340 return NT_STATUS_LOGON_FAILURE;
343 struct winbindd_domain *find_auth_domain(uint8_t flags,
344 const char *domain_name)
346 struct winbindd_domain *domain;
348 if (IS_DC) {
349 domain = find_domain_from_name_noinit(domain_name);
350 if (domain == NULL) {
351 DEBUG(3, ("Authentication for domain [%s] refused "
352 "as it is not a trusted domain\n",
353 domain_name));
355 return domain;
358 if (strequal(domain_name, get_global_sam_name())) {
359 return find_domain_from_name_noinit(domain_name);
362 /* we can auth against trusted domains */
363 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
364 domain = find_domain_from_name_noinit(domain_name);
365 if (domain == NULL) {
366 DEBUG(3, ("Authentication for domain [%s] skipped "
367 "as it is not a trusted domain\n",
368 domain_name));
369 } else {
370 return domain;
374 return find_our_domain();
377 static void fill_in_password_policy(struct winbindd_response *r,
378 const struct samr_DomInfo1 *p)
380 r->data.auth.policy.min_length_password =
381 p->min_password_length;
382 r->data.auth.policy.password_history =
383 p->password_history_length;
384 r->data.auth.policy.password_properties =
385 p->password_properties;
386 r->data.auth.policy.expire =
387 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
388 r->data.auth.policy.min_passwordage =
389 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
392 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
393 struct winbindd_cli_state *state)
395 struct winbindd_methods *methods;
396 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
397 struct samr_DomInfo1 password_policy;
399 if ( !winbindd_can_contact_domain( domain ) ) {
400 DEBUG(5,("fillup_password_policy: No inbound trust to "
401 "contact domain %s\n", domain->name));
402 return NT_STATUS_NOT_SUPPORTED;
405 methods = domain->methods;
407 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
408 if (NT_STATUS_IS_ERR(status)) {
409 return status;
412 fill_in_password_policy(state->response, &password_policy);
414 return NT_STATUS_OK;
417 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
418 TALLOC_CTX *mem_ctx,
419 uint16 *lockout_threshold)
421 struct winbindd_methods *methods;
422 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
423 struct samr_DomInfo12 lockout_policy;
425 *lockout_threshold = 0;
427 methods = domain->methods;
429 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
430 if (NT_STATUS_IS_ERR(status)) {
431 return status;
434 *lockout_threshold = lockout_policy.lockout_threshold;
436 return NT_STATUS_OK;
439 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
440 TALLOC_CTX *mem_ctx,
441 uint32 *password_properties)
443 struct winbindd_methods *methods;
444 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
445 struct samr_DomInfo1 password_policy;
447 *password_properties = 0;
449 methods = domain->methods;
451 status = methods->password_policy(domain, mem_ctx, &password_policy);
452 if (NT_STATUS_IS_ERR(status)) {
453 return status;
456 *password_properties = password_policy.password_properties;
458 return NT_STATUS_OK;
461 #ifdef HAVE_KRB5
463 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
464 const char *type,
465 uid_t uid,
466 const char **user_ccache_file)
468 /* accept FILE and WRFILE as krb5_cc_type from the client and then
469 * build the full ccname string based on the user's uid here -
470 * Guenther*/
472 const char *gen_cc = NULL;
474 if (uid != -1) {
475 if (strequal(type, "FILE")) {
476 gen_cc = talloc_asprintf(
477 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
479 if (strequal(type, "WRFILE")) {
480 gen_cc = talloc_asprintf(
481 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
485 *user_ccache_file = gen_cc;
487 if (gen_cc == NULL) {
488 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
490 if (gen_cc == NULL) {
491 DEBUG(0,("out of memory\n"));
492 return NULL;
495 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
496 (*user_ccache_file == NULL) ? " (internal)":""));
498 return gen_cc;
501 #endif
503 uid_t get_uid_from_request(struct winbindd_request *request)
505 uid_t uid;
507 uid = request->data.auth.uid;
509 if (uid < 0) {
510 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
511 return -1;
513 return uid;
516 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
518 return get_uid_from_request(state->request);
521 /**********************************************************************
522 Authenticate a user with a clear text password using Kerberos and fill up
523 ccache if required
524 **********************************************************************/
526 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
527 struct winbindd_cli_state *state,
528 struct netr_SamInfo3 **info3)
530 #ifdef HAVE_KRB5
531 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
532 krb5_error_code krb5_ret;
533 const char *cc = NULL;
534 const char *principal_s = NULL;
535 const char *service = NULL;
536 char *realm = NULL;
537 fstring name_domain, name_user;
538 time_t ticket_lifetime = 0;
539 time_t renewal_until = 0;
540 uid_t uid = -1;
541 ADS_STRUCT *ads;
542 time_t time_offset = 0;
543 const char *user_ccache_file;
544 struct PAC_LOGON_INFO *logon_info = NULL;
546 *info3 = NULL;
548 /* 1st step:
549 * prepare a krb5_cc_cache string for the user */
551 uid = get_uid_from_state(state);
552 if (uid == -1) {
553 DEBUG(0,("no valid uid\n"));
556 cc = generate_krb5_ccache(state->mem_ctx,
557 state->request->data.auth.krb5_cc_type,
558 state->request->data.auth.uid,
559 &user_ccache_file);
560 if (cc == NULL) {
561 return NT_STATUS_NO_MEMORY;
565 /* 2nd step:
566 * get kerberos properties */
568 if (domain->private_data) {
569 ads = (ADS_STRUCT *)domain->private_data;
570 time_offset = ads->auth.time_offset;
574 /* 3rd step:
575 * do kerberos auth and setup ccache as the user */
577 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
579 realm = domain->alt_name;
580 strupper_m(realm);
582 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
583 if (principal_s == NULL) {
584 return NT_STATUS_NO_MEMORY;
587 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
588 if (service == NULL) {
589 return NT_STATUS_NO_MEMORY;
592 /* if this is a user ccache, we need to act as the user to let the krb5
593 * library handle the chown, etc. */
595 /************************ ENTERING NON-ROOT **********************/
597 if (user_ccache_file != NULL) {
598 set_effective_uid(uid);
599 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
602 result = kerberos_return_pac(state->mem_ctx,
603 principal_s,
604 state->request->data.auth.pass,
605 time_offset,
606 &ticket_lifetime,
607 &renewal_until,
609 true,
610 true,
611 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
612 NULL,
613 &logon_info);
614 if (user_ccache_file != NULL) {
615 gain_root_privilege();
618 /************************ RETURNED TO ROOT **********************/
620 if (!NT_STATUS_IS_OK(result)) {
621 goto failed;
624 *info3 = &logon_info->info3;
626 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
627 principal_s));
629 /* if we had a user's ccache then return that string for the pam
630 * environment */
632 if (user_ccache_file != NULL) {
634 fstrcpy(state->response->data.auth.krb5ccname,
635 user_ccache_file);
637 result = add_ccache_to_list(principal_s,
639 service,
640 state->request->data.auth.user,
641 realm,
642 uid,
643 time(NULL),
644 ticket_lifetime,
645 renewal_until,
646 false);
648 if (!NT_STATUS_IS_OK(result)) {
649 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
650 nt_errstr(result)));
652 } else {
654 /* need to delete the memory cred cache, it is not used anymore */
656 krb5_ret = ads_kdestroy(cc);
657 if (krb5_ret) {
658 DEBUG(3,("winbindd_raw_kerberos_login: "
659 "could not destroy krb5 credential cache: "
660 "%s\n", error_message(krb5_ret)));
665 return NT_STATUS_OK;
667 failed:
669 /* we could have created a new credential cache with a valid tgt in it
670 * but we werent able to get or verify the service ticket for this
671 * local host and therefor didn't get the PAC, we need to remove that
672 * cache entirely now */
674 krb5_ret = ads_kdestroy(cc);
675 if (krb5_ret) {
676 DEBUG(3,("winbindd_raw_kerberos_login: "
677 "could not destroy krb5 credential cache: "
678 "%s\n", error_message(krb5_ret)));
681 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
682 DEBUG(3,("winbindd_raw_kerberos_login: "
683 "could not remove ccache for user %s\n",
684 state->request->data.auth.user));
687 return result;
688 #else
689 return NT_STATUS_NOT_SUPPORTED;
690 #endif /* HAVE_KRB5 */
693 /****************************************************************
694 ****************************************************************/
696 bool check_request_flags(uint32_t flags)
698 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
699 WBFLAG_PAM_INFO3_TEXT |
700 WBFLAG_PAM_INFO3_NDR;
702 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
703 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
704 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
705 !(flags & flags_edata) ) {
706 return true;
709 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
710 flags));
712 return false;
715 /****************************************************************
716 ****************************************************************/
718 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
719 struct netr_SamInfo3 *info3,
720 const char *name_domain,
721 const char *name_user)
723 NTSTATUS result;
724 uint32_t flags = state->request->flags;
726 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
727 memcpy(state->response->data.auth.user_session_key,
728 info3->base.key.key,
729 sizeof(state->response->data.auth.user_session_key)
730 /* 16 */);
733 if (flags & WBFLAG_PAM_LMKEY) {
734 memcpy(state->response->data.auth.first_8_lm_hash,
735 info3->base.LMSessKey.key,
736 sizeof(state->response->data.auth.first_8_lm_hash)
737 /* 8 */);
740 if (flags & WBFLAG_PAM_UNIX_NAME) {
741 result = append_unix_username(state->mem_ctx, state, info3,
742 name_domain, name_user);
743 if (!NT_STATUS_IS_OK(result)) {
744 DEBUG(10,("Failed to append Unix Username: %s\n",
745 nt_errstr(result)));
746 return result;
750 /* currently, anything from here on potentially overwrites extra_data. */
752 if (flags & WBFLAG_PAM_INFO3_NDR) {
753 result = append_info3_as_ndr(state->mem_ctx, state, info3);
754 if (!NT_STATUS_IS_OK(result)) {
755 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
756 nt_errstr(result)));
757 return result;
761 if (flags & WBFLAG_PAM_INFO3_TEXT) {
762 result = append_info3_as_txt(state->mem_ctx, state, info3);
763 if (!NT_STATUS_IS_OK(result)) {
764 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
765 nt_errstr(result)));
766 return result;
770 if (flags & WBFLAG_PAM_AFS_TOKEN) {
771 result = append_afs_token(state->mem_ctx, state, info3,
772 name_domain, name_user);
773 if (!NT_STATUS_IS_OK(result)) {
774 DEBUG(10,("Failed to append AFS token: %s\n",
775 nt_errstr(result)));
776 return result;
780 return NT_STATUS_OK;
783 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
784 struct winbindd_cli_state *state,
785 struct netr_SamInfo3 **info3)
787 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
788 uint16 max_allowed_bad_attempts;
789 fstring name_domain, name_user;
790 struct dom_sid sid;
791 enum lsa_SidType type;
792 uchar new_nt_pass[NT_HASH_LEN];
793 const uint8 *cached_nt_pass;
794 const uint8 *cached_salt;
795 struct netr_SamInfo3 *my_info3;
796 time_t kickoff_time, must_change_time;
797 bool password_good = false;
798 #ifdef HAVE_KRB5
799 struct winbindd_tdc_domain *tdc_domain = NULL;
800 #endif
802 *info3 = NULL;
804 ZERO_STRUCTP(info3);
806 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
808 /* Parse domain and username */
810 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
813 if (!lookup_cached_name(name_domain,
814 name_user,
815 &sid,
816 &type)) {
817 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
818 return NT_STATUS_NO_SUCH_USER;
821 if (type != SID_NAME_USER) {
822 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
823 return NT_STATUS_LOGON_FAILURE;
826 result = winbindd_get_creds(domain,
827 state->mem_ctx,
828 &sid,
829 &my_info3,
830 &cached_nt_pass,
831 &cached_salt);
832 if (!NT_STATUS_IS_OK(result)) {
833 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
834 return result;
837 *info3 = my_info3;
839 E_md4hash(state->request->data.auth.pass, new_nt_pass);
841 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
842 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
843 if (cached_salt) {
844 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
847 if (cached_salt) {
848 /* In this case we didn't store the nt_hash itself,
849 but the MD5 combination of salt + nt_hash. */
850 uchar salted_hash[NT_HASH_LEN];
851 E_md5hash(cached_salt, new_nt_pass, salted_hash);
853 password_good = (memcmp(cached_nt_pass, salted_hash,
854 NT_HASH_LEN) == 0);
855 } else {
856 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
857 password_good = (memcmp(cached_nt_pass, new_nt_pass,
858 NT_HASH_LEN) == 0);
861 if (password_good) {
863 /* User *DOES* know the password, update logon_time and reset
864 * bad_pw_count */
866 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
868 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
869 return NT_STATUS_ACCOUNT_LOCKED_OUT;
872 if (my_info3->base.acct_flags & ACB_DISABLED) {
873 return NT_STATUS_ACCOUNT_DISABLED;
876 if (my_info3->base.acct_flags & ACB_WSTRUST) {
877 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
880 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
881 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
884 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
885 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
888 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
889 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
890 my_info3->base.acct_flags));
891 return NT_STATUS_LOGON_FAILURE;
894 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
895 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
896 return NT_STATUS_ACCOUNT_EXPIRED;
899 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
900 if (must_change_time != 0 && must_change_time < time(NULL)) {
901 /* we allow grace logons when the password has expired */
902 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
903 /* return NT_STATUS_PASSWORD_EXPIRED; */
904 goto success;
907 #ifdef HAVE_KRB5
908 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
909 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
910 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
911 /* used to cope with the case winbindd starting without network. */
912 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
914 uid_t uid = -1;
915 const char *cc = NULL;
916 char *realm = NULL;
917 const char *principal_s = NULL;
918 const char *service = NULL;
919 const char *user_ccache_file;
921 uid = get_uid_from_state(state);
922 if (uid == -1) {
923 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
924 return NT_STATUS_INVALID_PARAMETER;
927 cc = generate_krb5_ccache(state->mem_ctx,
928 state->request->data.auth.krb5_cc_type,
929 state->request->data.auth.uid,
930 &user_ccache_file);
931 if (cc == NULL) {
932 return NT_STATUS_NO_MEMORY;
935 realm = domain->alt_name;
936 strupper_m(realm);
938 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
939 if (principal_s == NULL) {
940 return NT_STATUS_NO_MEMORY;
943 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
944 if (service == NULL) {
945 return NT_STATUS_NO_MEMORY;
948 if (user_ccache_file != NULL) {
950 fstrcpy(state->response->data.auth.krb5ccname,
951 user_ccache_file);
953 result = add_ccache_to_list(principal_s,
955 service,
956 state->request->data.auth.user,
957 domain->alt_name,
958 uid,
959 time(NULL),
960 time(NULL) + lp_winbind_cache_time(),
961 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
962 true);
964 if (!NT_STATUS_IS_OK(result)) {
965 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
966 "to add ccache to list: %s\n",
967 nt_errstr(result)));
971 #endif /* HAVE_KRB5 */
972 success:
973 /* FIXME: we possibly should handle logon hours as well (does xp when
974 * offline?) see auth/auth_sam.c:sam_account_ok for details */
976 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
977 my_info3->base.bad_password_count = 0;
979 result = winbindd_update_creds_by_info3(domain,
980 state->request->data.auth.user,
981 state->request->data.auth.pass,
982 my_info3);
983 if (!NT_STATUS_IS_OK(result)) {
984 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
985 nt_errstr(result)));
986 return result;
989 return NT_STATUS_OK;
993 /* User does *NOT* know the correct password, modify info3 accordingly */
995 /* failure of this is not critical */
996 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
997 if (!NT_STATUS_IS_OK(result)) {
998 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
999 "Won't be able to honour account lockout policies\n"));
1002 /* increase counter */
1003 my_info3->base.bad_password_count++;
1005 if (max_allowed_bad_attempts == 0) {
1006 goto failed;
1009 /* lockout user */
1010 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1012 uint32 password_properties;
1014 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1015 if (!NT_STATUS_IS_OK(result)) {
1016 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1019 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1020 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1021 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1025 failed:
1026 result = winbindd_update_creds_by_info3(domain,
1027 state->request->data.auth.user,
1028 NULL,
1029 my_info3);
1031 if (!NT_STATUS_IS_OK(result)) {
1032 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1033 nt_errstr(result)));
1036 return NT_STATUS_LOGON_FAILURE;
1039 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1040 struct winbindd_cli_state *state,
1041 struct netr_SamInfo3 **info3)
1043 struct winbindd_domain *contact_domain;
1044 fstring name_domain, name_user;
1045 NTSTATUS result;
1047 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1049 /* Parse domain and username */
1051 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1053 /* what domain should we contact? */
1055 if ( IS_DC ) {
1056 if (!(contact_domain = find_domain_from_name(name_domain))) {
1057 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1058 state->request->data.auth.user, name_domain, name_user, name_domain));
1059 result = NT_STATUS_NO_SUCH_USER;
1060 goto done;
1063 } else {
1064 if (is_myname(name_domain)) {
1065 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1066 result = NT_STATUS_NO_SUCH_USER;
1067 goto done;
1070 contact_domain = find_domain_from_name(name_domain);
1071 if (contact_domain == NULL) {
1072 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1073 state->request->data.auth.user, name_domain, name_user, name_domain));
1075 contact_domain = find_our_domain();
1079 if (contact_domain->initialized &&
1080 contact_domain->active_directory) {
1081 goto try_login;
1084 if (!contact_domain->initialized) {
1085 init_dc_connection(contact_domain);
1088 if (!contact_domain->active_directory) {
1089 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1090 return NT_STATUS_INVALID_LOGON_TYPE;
1092 try_login:
1093 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1094 done:
1095 return result;
1098 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1099 const char *domain, const char *user,
1100 const DATA_BLOB *challenge,
1101 const DATA_BLOB *lm_resp,
1102 const DATA_BLOB *nt_resp,
1103 struct netr_SamInfo3 **pinfo3)
1105 struct auth_usersupplied_info *user_info = NULL;
1106 NTSTATUS status;
1108 status = make_user_info(&user_info, user, user, domain, domain,
1109 global_myname(), lm_resp, nt_resp, NULL, NULL,
1110 NULL, AUTH_PASSWORD_RESPONSE);
1111 if (!NT_STATUS_IS_OK(status)) {
1112 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1113 return status;
1116 /* We don't want any more mapping of the username */
1117 user_info->mapped_state = True;
1119 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1120 pinfo3);
1121 free_user_info(&user_info);
1122 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1123 return NT_STATUS_OK;
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_lm_response[24];
1149 unsigned char local_nt_response[24];
1150 fstring name_domain, name_user;
1151 bool retry;
1152 NTSTATUS result;
1153 struct netr_SamInfo3 *my_info3 = NULL;
1155 *info3 = NULL;
1157 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1159 /* Parse domain and username */
1161 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1163 /* do password magic */
1165 generate_random_buffer(chal, sizeof(chal));
1167 if (lp_client_ntlmv2_auth()) {
1168 DATA_BLOB server_chal;
1169 DATA_BLOB names_blob;
1170 DATA_BLOB nt_response;
1171 DATA_BLOB lm_response;
1172 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1174 /* note that the 'workgroup' here is a best guess - we don't know
1175 the server's domain at this point. The 'server name' is also
1176 dodgy...
1178 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1180 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1181 state->request->data.auth.pass,
1182 &server_chal,
1183 &names_blob,
1184 &lm_response, &nt_response, NULL, NULL)) {
1185 data_blob_free(&names_blob);
1186 data_blob_free(&server_chal);
1187 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1188 result = NT_STATUS_NO_MEMORY;
1189 goto done;
1191 data_blob_free(&names_blob);
1192 data_blob_free(&server_chal);
1193 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1194 lm_response.length);
1195 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1196 nt_response.length);
1197 data_blob_free(&lm_response);
1198 data_blob_free(&nt_response);
1200 } else {
1201 if (lp_client_lanman_auth()
1202 && SMBencrypt(state->request->data.auth.pass,
1203 chal,
1204 local_lm_response)) {
1205 lm_resp = data_blob_talloc(state->mem_ctx,
1206 local_lm_response,
1207 sizeof(local_lm_response));
1208 } else {
1209 lm_resp = data_blob_null;
1211 SMBNTencrypt(state->request->data.auth.pass,
1212 chal,
1213 local_nt_response);
1215 nt_resp = data_blob_talloc(state->mem_ctx,
1216 local_nt_response,
1217 sizeof(local_nt_response));
1220 if (strequal(name_domain, get_global_sam_name())) {
1221 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1223 result = winbindd_dual_auth_passdb(
1224 state->mem_ctx, name_domain, name_user,
1225 &chal_blob, &lm_resp, &nt_resp, info3);
1226 goto done;
1229 /* check authentication loop */
1231 do {
1232 netlogon_fn_t logon_fn;
1234 ZERO_STRUCTP(my_info3);
1235 retry = false;
1237 result = cm_connect_netlogon(domain, &netlogon_pipe);
1239 if (!NT_STATUS_IS_OK(result)) {
1240 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1241 goto done;
1244 /* It is really important to try SamLogonEx here,
1245 * because in a clustered environment, we want to use
1246 * one machine account from multiple physical
1247 * computers.
1249 * With a normal SamLogon call, we must keep the
1250 * credentials chain updated and intact between all
1251 * users of the machine account (which would imply
1252 * cross-node communication for every NTLM logon).
1254 * (The credentials chain is not per NETLOGON pipe
1255 * connection, but globally on the server/client pair
1256 * by machine name).
1258 * When using SamLogonEx, the credentials are not
1259 * supplied, but the session key is implied by the
1260 * wrapping SamLogon context.
1262 * -- abartlet 21 April 2008
1265 logon_fn = domain->can_do_samlogon_ex
1266 ? rpccli_netlogon_sam_network_logon_ex
1267 : rpccli_netlogon_sam_network_logon;
1269 result = logon_fn(netlogon_pipe,
1270 state->mem_ctx,
1272 domain->dcname, /* server name */
1273 name_user, /* user name */
1274 name_domain, /* target domain */
1275 global_myname(), /* workstation */
1276 chal,
1277 lm_resp,
1278 nt_resp,
1279 &my_info3);
1280 attempts += 1;
1282 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1283 && domain->can_do_samlogon_ex) {
1284 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1285 "retrying with NetSamLogon\n"));
1286 domain->can_do_samlogon_ex = false;
1287 continue;
1290 /* We have to try a second time as cm_connect_netlogon
1291 might not yet have noticed that the DC has killed
1292 our connection. */
1294 if (!rpccli_is_connected(netlogon_pipe)) {
1295 continue;
1298 /* if we get access denied, a possible cause was that we had
1299 and open connection to the DC, but someone changed our
1300 machine account password out from underneath us using 'net
1301 rpc changetrustpw' */
1303 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1304 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1305 "ACCESS_DENIED. Maybe the trust account "
1306 "password was changed and we didn't know it. "
1307 "Killing connections to domain %s\n",
1308 name_domain));
1309 invalidate_cm_connection(&domain->conn);
1310 retry = true;
1313 } while ( (attempts < 2) && retry );
1315 /* handle the case where a NT4 DC does not fill in the acct_flags in
1316 * the samlogon reply info3. When accurate info3 is required by the
1317 * caller, we look up the account flags ourselve - gd */
1319 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1320 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1322 struct rpc_pipe_client *samr_pipe;
1323 struct policy_handle samr_domain_handle, user_pol;
1324 union samr_UserInfo *info = NULL;
1325 NTSTATUS status_tmp;
1326 uint32 acct_flags;
1328 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1329 &samr_pipe, &samr_domain_handle);
1331 if (!NT_STATUS_IS_OK(status_tmp)) {
1332 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1333 nt_errstr(status_tmp)));
1334 goto done;
1337 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1338 &samr_domain_handle,
1339 MAXIMUM_ALLOWED_ACCESS,
1340 my_info3->base.rid,
1341 &user_pol);
1343 if (!NT_STATUS_IS_OK(status_tmp)) {
1344 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1345 nt_errstr(status_tmp)));
1346 goto done;
1349 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1350 &user_pol,
1352 &info);
1354 if (!NT_STATUS_IS_OK(status_tmp)) {
1355 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1356 nt_errstr(status_tmp)));
1357 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1358 goto done;
1361 acct_flags = info->info16.acct_flags;
1363 if (acct_flags == 0) {
1364 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1365 goto done;
1368 my_info3->base.acct_flags = acct_flags;
1370 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1372 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1375 *info3 = my_info3;
1376 done:
1377 return result;
1380 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1381 struct winbindd_cli_state *state)
1383 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1384 NTSTATUS krb5_result = NT_STATUS_OK;
1385 fstring name_domain, name_user;
1386 char *mapped_user;
1387 fstring domain_user;
1388 struct netr_SamInfo3 *info3 = NULL;
1389 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1391 /* Ensure null termination */
1392 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1394 /* Ensure null termination */
1395 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1397 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1398 state->request->data.auth.user));
1400 /* Parse domain and username */
1402 name_map_status = normalize_name_unmap(state->mem_ctx,
1403 state->request->data.auth.user,
1404 &mapped_user);
1406 /* If the name normalization didnt' actually do anything,
1407 just use the original name */
1409 if (!NT_STATUS_IS_OK(name_map_status) &&
1410 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1412 mapped_user = state->request->data.auth.user;
1415 parse_domain_user(mapped_user, name_domain, name_user);
1417 if ( mapped_user != state->request->data.auth.user ) {
1418 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1419 *lp_winbind_separator(),
1420 name_user );
1421 safe_strcpy( state->request->data.auth.user, domain_user,
1422 sizeof(state->request->data.auth.user)-1 );
1425 if (!domain->online) {
1426 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1427 if (domain->startup) {
1428 /* Logons are very important to users. If we're offline and
1429 we get a request within the first 30 seconds of startup,
1430 try very hard to find a DC and go online. */
1432 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1433 "request in startup mode.\n", domain->name ));
1435 winbindd_flush_negative_conn_cache(domain);
1436 result = init_dc_connection(domain);
1440 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1442 /* Check for Kerberos authentication */
1443 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1445 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1446 /* save for later */
1447 krb5_result = result;
1450 if (NT_STATUS_IS_OK(result)) {
1451 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1452 goto process_result;
1453 } else {
1454 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1457 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1458 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1459 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1460 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1461 set_domain_offline( domain );
1462 goto cached_logon;
1465 /* there are quite some NT_STATUS errors where there is no
1466 * point in retrying with a samlogon, we explictly have to take
1467 * care not to increase the bad logon counter on the DC */
1469 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1470 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1471 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1472 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1473 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1474 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1475 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1476 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1477 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1478 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1479 goto done;
1482 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1483 DEBUG(3,("falling back to samlogon\n"));
1484 goto sam_logon;
1485 } else {
1486 goto cached_logon;
1490 sam_logon:
1491 /* Check for Samlogon authentication */
1492 if (domain->online) {
1493 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1495 if (NT_STATUS_IS_OK(result)) {
1496 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1497 /* add the Krb5 err if we have one */
1498 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1499 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1501 goto process_result;
1504 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1505 nt_errstr(result)));
1507 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1508 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1509 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1511 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1512 set_domain_offline( domain );
1513 goto cached_logon;
1516 if (domain->online) {
1517 /* We're still online - fail. */
1518 goto done;
1522 cached_logon:
1523 /* Check for Cached logons */
1524 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1525 lp_winbind_offline_logon()) {
1527 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1529 if (NT_STATUS_IS_OK(result)) {
1530 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1531 goto process_result;
1532 } else {
1533 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1534 goto done;
1538 process_result:
1540 if (NT_STATUS_IS_OK(result)) {
1542 struct dom_sid user_sid;
1544 /* In all codepaths where result == NT_STATUS_OK info3 must have
1545 been initialized. */
1546 if (!info3) {
1547 result = NT_STATUS_INTERNAL_ERROR;
1548 goto done;
1551 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1552 netsamlogon_cache_store(name_user, info3);
1554 /* save name_to_sid info as early as possible (only if
1555 this is our primary domain so we don't invalidate
1556 the cache entry by storing the seq_num for the wrong
1557 domain). */
1558 if ( domain->primary ) {
1559 sid_compose(&user_sid, info3->base.domain_sid,
1560 info3->base.rid);
1561 cache_name2sid(domain, name_domain, name_user,
1562 SID_NAME_USER, &user_sid);
1565 /* Check if the user is in the right group */
1567 result = check_info3_in_group(
1568 info3,
1569 state->request->data.auth.require_membership_of_sid);
1570 if (!NT_STATUS_IS_OK(result)) {
1571 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1572 state->request->data.auth.user,
1573 state->request->data.auth.require_membership_of_sid));
1574 goto done;
1577 result = append_auth_data(state, info3, name_domain,
1578 name_user);
1579 if (!NT_STATUS_IS_OK(result)) {
1580 goto done;
1583 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1584 && lp_winbind_offline_logon()) {
1586 result = winbindd_store_creds(domain,
1587 state->request->data.auth.user,
1588 state->request->data.auth.pass,
1589 info3);
1592 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1593 struct winbindd_domain *our_domain = find_our_domain();
1595 /* This is not entirely correct I believe, but it is
1596 consistent. Only apply the password policy settings
1597 too warn users for our own domain. Cannot obtain these
1598 from trusted DCs all the time so don't do it at all.
1599 -- jerry */
1601 result = NT_STATUS_NOT_SUPPORTED;
1602 if (our_domain == domain ) {
1603 result = fillup_password_policy(our_domain, state);
1606 if (!NT_STATUS_IS_OK(result)
1607 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1609 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1610 domain->name, nt_errstr(result)));
1611 goto done;
1615 result = NT_STATUS_OK;
1618 done:
1619 /* give us a more useful (more correct?) error code */
1620 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1621 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1622 result = NT_STATUS_NO_LOGON_SERVERS;
1625 set_auth_errors(state->response, result);
1627 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1628 state->request->data.auth.user,
1629 state->response->data.auth.nt_status_string,
1630 state->response->data.auth.pam_error));
1632 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1635 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1636 struct winbindd_cli_state *state)
1638 NTSTATUS result;
1639 struct netr_SamInfo3 *info3 = NULL;
1640 struct rpc_pipe_client *netlogon_pipe;
1641 const char *name_user = NULL;
1642 const char *name_domain = NULL;
1643 const char *workstation;
1644 int attempts = 0;
1645 bool retry;
1647 DATA_BLOB lm_resp, nt_resp;
1649 /* This is child-only, so no check for privileged access is needed
1650 anymore */
1652 /* Ensure null termination */
1653 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1654 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1656 name_user = state->request->data.auth_crap.user;
1657 name_domain = state->request->data.auth_crap.domain;
1658 workstation = state->request->data.auth_crap.workstation;
1660 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1661 name_domain, name_user));
1663 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1664 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1665 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1666 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1667 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1668 state->request->data.auth_crap.lm_resp_len,
1669 state->request->data.auth_crap.nt_resp_len));
1670 result = NT_STATUS_INVALID_PARAMETER;
1671 goto done;
1675 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1676 state->request->data.auth_crap.lm_resp_len);
1678 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1679 nt_resp = data_blob_talloc(state->mem_ctx,
1680 state->request->extra_data.data,
1681 state->request->data.auth_crap.nt_resp_len);
1682 } else {
1683 nt_resp = data_blob_talloc(state->mem_ctx,
1684 state->request->data.auth_crap.nt_resp,
1685 state->request->data.auth_crap.nt_resp_len);
1688 if (strequal(name_domain, get_global_sam_name())) {
1689 DATA_BLOB chal_blob = data_blob_const(
1690 state->request->data.auth_crap.chal,
1691 sizeof(state->request->data.auth_crap.chal));
1693 result = winbindd_dual_auth_passdb(
1694 state->mem_ctx, name_domain, name_user,
1695 &chal_blob, &lm_resp, &nt_resp, &info3);
1696 goto process_result;
1699 do {
1700 netlogon_fn_t logon_fn;
1702 retry = false;
1704 netlogon_pipe = NULL;
1705 result = cm_connect_netlogon(domain, &netlogon_pipe);
1707 if (!NT_STATUS_IS_OK(result)) {
1708 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1709 nt_errstr(result)));
1710 goto done;
1713 logon_fn = domain->can_do_samlogon_ex
1714 ? rpccli_netlogon_sam_network_logon_ex
1715 : rpccli_netlogon_sam_network_logon;
1717 result = logon_fn(netlogon_pipe,
1718 state->mem_ctx,
1719 state->request->data.auth_crap.logon_parameters,
1720 domain->dcname,
1721 name_user,
1722 name_domain,
1723 /* Bug #3248 - found by Stefan Burkei. */
1724 workstation, /* We carefully set this above so use it... */
1725 state->request->data.auth_crap.chal,
1726 lm_resp,
1727 nt_resp,
1728 &info3);
1730 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1731 && domain->can_do_samlogon_ex) {
1732 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1733 "retrying with NetSamLogon\n"));
1734 domain->can_do_samlogon_ex = false;
1735 continue;
1738 attempts += 1;
1740 /* We have to try a second time as cm_connect_netlogon
1741 might not yet have noticed that the DC has killed
1742 our connection. */
1744 if (!rpccli_is_connected(netlogon_pipe)) {
1745 continue;
1748 /* if we get access denied, a possible cause was that we had and open
1749 connection to the DC, but someone changed our machine account password
1750 out from underneath us using 'net rpc changetrustpw' */
1752 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1753 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1754 "ACCESS_DENIED. Maybe the trust account "
1755 "password was changed and we didn't know it. "
1756 "Killing connections to domain %s\n",
1757 name_domain));
1758 invalidate_cm_connection(&domain->conn);
1759 retry = true;
1762 } while ( (attempts < 2) && retry );
1764 process_result:
1766 if (NT_STATUS_IS_OK(result)) {
1768 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1769 netsamlogon_cache_store(name_user, info3);
1771 /* Check if the user is in the right group */
1773 result = check_info3_in_group(
1774 info3,
1775 state->request->data.auth_crap.require_membership_of_sid);
1776 if (!NT_STATUS_IS_OK(result)) {
1777 DEBUG(3, ("User %s is not in the required group (%s), so "
1778 "crap authentication is rejected\n",
1779 state->request->data.auth_crap.user,
1780 state->request->data.auth_crap.require_membership_of_sid));
1781 goto done;
1784 result = append_auth_data(state, info3, name_domain,
1785 name_user);
1786 if (!NT_STATUS_IS_OK(result)) {
1787 goto done;
1791 done:
1793 /* give us a more useful (more correct?) error code */
1794 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1795 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1796 result = NT_STATUS_NO_LOGON_SERVERS;
1799 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1800 result = nt_status_squash(result);
1803 set_auth_errors(state->response, result);
1805 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1806 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1807 name_domain,
1808 name_user,
1809 state->response->data.auth.nt_status_string,
1810 state->response->data.auth.pam_error));
1812 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1815 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1816 struct winbindd_cli_state *state)
1818 char *oldpass;
1819 char *newpass = NULL;
1820 struct policy_handle dom_pol;
1821 struct rpc_pipe_client *cli = NULL;
1822 bool got_info = false;
1823 struct samr_DomInfo1 *info = NULL;
1824 struct userPwdChangeFailureInformation *reject = NULL;
1825 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1826 fstring domain, user;
1828 ZERO_STRUCT(dom_pol);
1830 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1831 state->request->data.auth.user));
1833 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1834 goto done;
1837 /* Change password */
1839 oldpass = state->request->data.chauthtok.oldpass;
1840 newpass = state->request->data.chauthtok.newpass;
1842 /* Initialize reject reason */
1843 state->response->data.auth.reject_reason = Undefined;
1845 /* Get sam handle */
1847 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1848 &dom_pol);
1849 if (!NT_STATUS_IS_OK(result)) {
1850 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1851 goto done;
1854 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1855 user,
1856 newpass,
1857 oldpass,
1858 &info,
1859 &reject);
1861 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1863 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1865 fill_in_password_policy(state->response, info);
1867 state->response->data.auth.reject_reason =
1868 reject->extendedFailureReason;
1870 got_info = true;
1873 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1874 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1875 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1876 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1878 /* only fallback when the chgpasswd_user3 call is not supported */
1879 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1880 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1881 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1882 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1884 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1885 nt_errstr(result)));
1887 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1889 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1890 Map to the same status code as Windows 2003. */
1892 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1893 result = NT_STATUS_PASSWORD_RESTRICTION;
1897 done:
1899 if (NT_STATUS_IS_OK(result)
1900 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1901 && lp_winbind_offline_logon()) {
1902 result = winbindd_update_creds_by_name(contact_domain, user,
1903 newpass);
1904 /* Again, this happens when we login from gdm or xdm
1905 * and the password expires, *BUT* cached crendentials
1906 * doesn't exist. winbindd_update_creds_by_name()
1907 * returns NT_STATUS_NO_SUCH_USER.
1908 * This is not a failure.
1909 * --- BoYang
1910 * */
1911 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1912 result = NT_STATUS_OK;
1915 if (!NT_STATUS_IS_OK(result)) {
1916 DEBUG(10, ("Failed to store creds: %s\n",
1917 nt_errstr(result)));
1918 goto process_result;
1922 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1924 NTSTATUS policy_ret;
1926 policy_ret = fillup_password_policy(contact_domain, state);
1928 /* failure of this is non critical, it will just provide no
1929 * additional information to the client why the change has
1930 * failed - Guenther */
1932 if (!NT_STATUS_IS_OK(policy_ret)) {
1933 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1934 goto process_result;
1938 process_result:
1940 if (strequal(contact_domain->name, get_global_sam_name())) {
1941 /* FIXME: internal rpc pipe does not cache handles yet */
1942 if (cli) {
1943 if (is_valid_policy_hnd(&dom_pol)) {
1944 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1946 TALLOC_FREE(cli);
1950 set_auth_errors(state->response, result);
1952 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1953 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1954 domain,
1955 user,
1956 state->response->data.auth.nt_status_string,
1957 state->response->data.auth.pam_error));
1959 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1962 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1963 struct winbindd_cli_state *state)
1965 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1967 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1968 state->request->data.logoff.user));
1970 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1971 result = NT_STATUS_OK;
1972 goto process_result;
1975 if (state->request->data.logoff.krb5ccname[0] == '\0') {
1976 result = NT_STATUS_OK;
1977 goto process_result;
1980 #ifdef HAVE_KRB5
1982 if (state->request->data.logoff.uid < 0) {
1983 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1984 goto process_result;
1987 /* what we need here is to find the corresponding krb5 ccache name *we*
1988 * created for a given username and destroy it */
1990 if (!ccache_entry_exists(state->request->data.logoff.user)) {
1991 result = NT_STATUS_OK;
1992 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
1993 goto process_result;
1996 if (!ccache_entry_identical(state->request->data.logoff.user,
1997 state->request->data.logoff.uid,
1998 state->request->data.logoff.krb5ccname)) {
1999 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2000 goto process_result;
2003 result = remove_ccache(state->request->data.logoff.user);
2004 if (!NT_STATUS_IS_OK(result)) {
2005 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2006 nt_errstr(result)));
2007 goto process_result;
2010 #else
2011 result = NT_STATUS_NOT_SUPPORTED;
2012 #endif
2014 process_result:
2017 set_auth_errors(state->response, result);
2019 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2022 /* Change user password with auth crap*/
2024 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2026 NTSTATUS result;
2027 DATA_BLOB new_nt_password;
2028 DATA_BLOB old_nt_hash_enc;
2029 DATA_BLOB new_lm_password;
2030 DATA_BLOB old_lm_hash_enc;
2031 fstring domain,user;
2032 struct policy_handle dom_pol;
2033 struct winbindd_domain *contact_domain = domainSt;
2034 struct rpc_pipe_client *cli = NULL;
2036 ZERO_STRUCT(dom_pol);
2038 /* Ensure null termination */
2039 state->request->data.chng_pswd_auth_crap.user[
2040 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2041 state->request->data.chng_pswd_auth_crap.domain[
2042 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2043 *domain = 0;
2044 *user = 0;
2046 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2047 (unsigned long)state->pid,
2048 state->request->data.chng_pswd_auth_crap.domain,
2049 state->request->data.chng_pswd_auth_crap.user));
2051 if (lp_winbind_offline_logon()) {
2052 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2053 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2054 result = NT_STATUS_ACCESS_DENIED;
2055 goto done;
2058 if (*state->request->data.chng_pswd_auth_crap.domain) {
2059 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2060 } else {
2061 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2062 domain, user);
2064 if(!*domain) {
2065 DEBUG(3,("no domain specified with username (%s) - "
2066 "failing auth\n",
2067 state->request->data.chng_pswd_auth_crap.user));
2068 result = NT_STATUS_NO_SUCH_USER;
2069 goto done;
2073 if (!*domain && lp_winbind_use_default_domain()) {
2074 fstrcpy(domain,(char *)lp_workgroup());
2077 if(!*user) {
2078 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2081 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2082 (unsigned long)state->pid, domain, user));
2084 /* Change password */
2085 new_nt_password = data_blob_const(
2086 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2087 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2089 old_nt_hash_enc = data_blob_const(
2090 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2091 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2093 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2094 new_lm_password = data_blob_const(
2095 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2096 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2098 old_lm_hash_enc = data_blob_const(
2099 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2100 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2101 } else {
2102 new_lm_password.length = 0;
2103 old_lm_hash_enc.length = 0;
2106 /* Get sam handle */
2108 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2109 if (!NT_STATUS_IS_OK(result)) {
2110 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2111 goto done;
2114 result = rpccli_samr_chng_pswd_auth_crap(
2115 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2116 new_lm_password, old_lm_hash_enc);
2118 done:
2120 if (strequal(contact_domain->name, get_global_sam_name())) {
2121 /* FIXME: internal rpc pipe does not cache handles yet */
2122 if (cli) {
2123 if (is_valid_policy_hnd(&dom_pol)) {
2124 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2126 TALLOC_FREE(cli);
2130 set_auth_errors(state->response, result);
2132 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2133 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2134 domain, user,
2135 state->response->data.auth.nt_status_string,
2136 state->response->data.auth.pam_error));
2138 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;