s3: These assignments are overwritten immediately
[Samba/gbeck.git] / source3 / winbindd / winbindd_pam.c
blobc828935f2a8e2ec9ba683f51923a122ecc235afb
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 size_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 nt_user_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 nt_user_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 bool *internal_ccache)
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 *internal_ccache = true;
476 if (uid == -1) {
477 goto memory_ccache;
480 if (!type || type[0] == '\0') {
481 goto memory_ccache;
484 if (strequal(type, "FILE")) {
485 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
486 } else if (strequal(type, "WRFILE")) {
487 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
488 } else {
489 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
490 goto memory_ccache;
493 *internal_ccache = false;
494 goto done;
496 memory_ccache:
497 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
499 done:
500 if (gen_cc == NULL) {
501 DEBUG(0,("out of memory\n"));
502 return NULL;
505 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
507 return gen_cc;
510 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
512 const char *type = state->request->data.auth.krb5_cc_type;
514 state->response->data.auth.krb5ccname[0] = '\0';
516 if (type[0] == '\0') {
517 return;
520 if (!strequal(type, "FILE") &&
521 !strequal(type, "WRFILE")) {
522 DEBUG(10,("won't return krbccname for a %s type ccache\n",
523 type));
524 return;
527 fstrcpy(state->response->data.auth.krb5ccname, cc);
530 #endif
532 uid_t get_uid_from_request(struct winbindd_request *request)
534 uid_t uid;
536 uid = request->data.auth.uid;
538 if (uid < 0) {
539 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
540 return -1;
542 return uid;
545 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
547 return get_uid_from_request(state->request);
550 /**********************************************************************
551 Authenticate a user with a clear text password using Kerberos and fill up
552 ccache if required
553 **********************************************************************/
555 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
556 struct winbindd_cli_state *state,
557 struct netr_SamInfo3 **info3)
559 #ifdef HAVE_KRB5
560 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
561 krb5_error_code krb5_ret;
562 const char *cc = NULL;
563 const char *principal_s = NULL;
564 const char *service = NULL;
565 char *realm = NULL;
566 fstring name_domain, name_user;
567 time_t ticket_lifetime = 0;
568 time_t renewal_until = 0;
569 uid_t uid = -1;
570 ADS_STRUCT *ads;
571 time_t time_offset = 0;
572 bool internal_ccache = true;
573 struct PAC_LOGON_INFO *logon_info = NULL;
575 *info3 = NULL;
577 /* 1st step:
578 * prepare a krb5_cc_cache string for the user */
580 uid = get_uid_from_state(state);
581 if (uid == -1) {
582 DEBUG(0,("no valid uid\n"));
585 cc = generate_krb5_ccache(state->mem_ctx,
586 state->request->data.auth.krb5_cc_type,
587 state->request->data.auth.uid,
588 &internal_ccache);
589 if (cc == NULL) {
590 return NT_STATUS_NO_MEMORY;
594 /* 2nd step:
595 * get kerberos properties */
597 if (domain->private_data) {
598 ads = (ADS_STRUCT *)domain->private_data;
599 time_offset = ads->auth.time_offset;
603 /* 3rd step:
604 * do kerberos auth and setup ccache as the user */
606 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
608 realm = domain->alt_name;
609 strupper_m(realm);
611 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
612 if (principal_s == NULL) {
613 return NT_STATUS_NO_MEMORY;
616 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
617 if (service == NULL) {
618 return NT_STATUS_NO_MEMORY;
621 /* if this is a user ccache, we need to act as the user to let the krb5
622 * library handle the chown, etc. */
624 /************************ ENTERING NON-ROOT **********************/
626 if (!internal_ccache) {
627 set_effective_uid(uid);
628 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
631 result = kerberos_return_pac(state->mem_ctx,
632 principal_s,
633 state->request->data.auth.pass,
634 time_offset,
635 &ticket_lifetime,
636 &renewal_until,
638 true,
639 true,
640 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
641 NULL,
642 &logon_info);
643 if (!internal_ccache) {
644 gain_root_privilege();
647 /************************ RETURNED TO ROOT **********************/
649 if (!NT_STATUS_IS_OK(result)) {
650 goto failed;
653 *info3 = &logon_info->info3;
655 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
656 principal_s));
658 /* if we had a user's ccache then return that string for the pam
659 * environment */
661 if (!internal_ccache) {
663 setup_return_cc_name(state, cc);
665 result = add_ccache_to_list(principal_s,
667 service,
668 state->request->data.auth.user,
669 realm,
670 uid,
671 time(NULL),
672 ticket_lifetime,
673 renewal_until,
674 false);
676 if (!NT_STATUS_IS_OK(result)) {
677 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
678 nt_errstr(result)));
680 } else {
682 /* need to delete the memory cred cache, it is not used anymore */
684 krb5_ret = ads_kdestroy(cc);
685 if (krb5_ret) {
686 DEBUG(3,("winbindd_raw_kerberos_login: "
687 "could not destroy krb5 credential cache: "
688 "%s\n", error_message(krb5_ret)));
693 return NT_STATUS_OK;
695 failed:
697 /* we could have created a new credential cache with a valid tgt in it
698 * but we werent able to get or verify the service ticket for this
699 * local host and therefor didn't get the PAC, we need to remove that
700 * cache entirely now */
702 krb5_ret = ads_kdestroy(cc);
703 if (krb5_ret) {
704 DEBUG(3,("winbindd_raw_kerberos_login: "
705 "could not destroy krb5 credential cache: "
706 "%s\n", error_message(krb5_ret)));
709 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
710 DEBUG(3,("winbindd_raw_kerberos_login: "
711 "could not remove ccache for user %s\n",
712 state->request->data.auth.user));
715 return result;
716 #else
717 return NT_STATUS_NOT_SUPPORTED;
718 #endif /* HAVE_KRB5 */
721 /****************************************************************
722 ****************************************************************/
724 bool check_request_flags(uint32_t flags)
726 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
727 WBFLAG_PAM_INFO3_TEXT |
728 WBFLAG_PAM_INFO3_NDR;
730 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
731 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
732 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
733 !(flags & flags_edata) ) {
734 return true;
737 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
738 flags));
740 return false;
743 /****************************************************************
744 ****************************************************************/
746 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
747 struct netr_SamInfo3 *info3,
748 const char *name_domain,
749 const char *name_user)
751 NTSTATUS result;
752 uint32_t flags = state->request->flags;
754 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
755 memcpy(state->response->data.auth.user_session_key,
756 info3->base.key.key,
757 sizeof(state->response->data.auth.user_session_key)
758 /* 16 */);
761 if (flags & WBFLAG_PAM_LMKEY) {
762 memcpy(state->response->data.auth.first_8_lm_hash,
763 info3->base.LMSessKey.key,
764 sizeof(state->response->data.auth.first_8_lm_hash)
765 /* 8 */);
768 if (flags & WBFLAG_PAM_UNIX_NAME) {
769 result = append_unix_username(state->mem_ctx, state, info3,
770 name_domain, name_user);
771 if (!NT_STATUS_IS_OK(result)) {
772 DEBUG(10,("Failed to append Unix Username: %s\n",
773 nt_errstr(result)));
774 return result;
778 /* currently, anything from here on potentially overwrites extra_data. */
780 if (flags & WBFLAG_PAM_INFO3_NDR) {
781 result = append_info3_as_ndr(state->mem_ctx, state, info3);
782 if (!NT_STATUS_IS_OK(result)) {
783 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
784 nt_errstr(result)));
785 return result;
789 if (flags & WBFLAG_PAM_INFO3_TEXT) {
790 result = append_info3_as_txt(state->mem_ctx, state, info3);
791 if (!NT_STATUS_IS_OK(result)) {
792 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
793 nt_errstr(result)));
794 return result;
798 if (flags & WBFLAG_PAM_AFS_TOKEN) {
799 result = append_afs_token(state->mem_ctx, state, info3,
800 name_domain, name_user);
801 if (!NT_STATUS_IS_OK(result)) {
802 DEBUG(10,("Failed to append AFS token: %s\n",
803 nt_errstr(result)));
804 return result;
808 return NT_STATUS_OK;
811 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
812 struct winbindd_cli_state *state,
813 struct netr_SamInfo3 **info3)
815 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
816 uint16 max_allowed_bad_attempts;
817 fstring name_domain, name_user;
818 struct dom_sid sid;
819 enum lsa_SidType type;
820 uchar new_nt_pass[NT_HASH_LEN];
821 const uint8 *cached_nt_pass;
822 const uint8 *cached_salt;
823 struct netr_SamInfo3 *my_info3;
824 time_t kickoff_time, must_change_time;
825 bool password_good = false;
826 #ifdef HAVE_KRB5
827 struct winbindd_tdc_domain *tdc_domain = NULL;
828 #endif
830 *info3 = NULL;
832 ZERO_STRUCTP(info3);
834 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
836 /* Parse domain and username */
838 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
841 if (!lookup_cached_name(name_domain,
842 name_user,
843 &sid,
844 &type)) {
845 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
846 return NT_STATUS_NO_SUCH_USER;
849 if (type != SID_NAME_USER) {
850 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
851 return NT_STATUS_LOGON_FAILURE;
854 result = winbindd_get_creds(domain,
855 state->mem_ctx,
856 &sid,
857 &my_info3,
858 &cached_nt_pass,
859 &cached_salt);
860 if (!NT_STATUS_IS_OK(result)) {
861 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
862 return result;
865 *info3 = my_info3;
867 E_md4hash(state->request->data.auth.pass, new_nt_pass);
869 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
870 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
871 if (cached_salt) {
872 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
875 if (cached_salt) {
876 /* In this case we didn't store the nt_hash itself,
877 but the MD5 combination of salt + nt_hash. */
878 uchar salted_hash[NT_HASH_LEN];
879 E_md5hash(cached_salt, new_nt_pass, salted_hash);
881 password_good = (memcmp(cached_nt_pass, salted_hash,
882 NT_HASH_LEN) == 0);
883 } else {
884 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
885 password_good = (memcmp(cached_nt_pass, new_nt_pass,
886 NT_HASH_LEN) == 0);
889 if (password_good) {
891 /* User *DOES* know the password, update logon_time and reset
892 * bad_pw_count */
894 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
896 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
897 return NT_STATUS_ACCOUNT_LOCKED_OUT;
900 if (my_info3->base.acct_flags & ACB_DISABLED) {
901 return NT_STATUS_ACCOUNT_DISABLED;
904 if (my_info3->base.acct_flags & ACB_WSTRUST) {
905 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
908 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
909 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
912 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
913 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
916 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
917 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
918 my_info3->base.acct_flags));
919 return NT_STATUS_LOGON_FAILURE;
922 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
923 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
924 return NT_STATUS_ACCOUNT_EXPIRED;
927 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
928 if (must_change_time != 0 && must_change_time < time(NULL)) {
929 /* we allow grace logons when the password has expired */
930 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
931 /* return NT_STATUS_PASSWORD_EXPIRED; */
932 goto success;
935 #ifdef HAVE_KRB5
936 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
937 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
938 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
939 /* used to cope with the case winbindd starting without network. */
940 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
942 uid_t uid = -1;
943 const char *cc = NULL;
944 char *realm = NULL;
945 const char *principal_s = NULL;
946 const char *service = NULL;
947 bool internal_ccache = false;
949 uid = get_uid_from_state(state);
950 if (uid == -1) {
951 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
952 return NT_STATUS_INVALID_PARAMETER;
955 cc = generate_krb5_ccache(state->mem_ctx,
956 state->request->data.auth.krb5_cc_type,
957 state->request->data.auth.uid,
958 &internal_ccache);
959 if (cc == NULL) {
960 return NT_STATUS_NO_MEMORY;
963 realm = domain->alt_name;
964 strupper_m(realm);
966 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
967 if (principal_s == NULL) {
968 return NT_STATUS_NO_MEMORY;
971 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
972 if (service == NULL) {
973 return NT_STATUS_NO_MEMORY;
976 if (!internal_ccache) {
978 setup_return_cc_name(state, cc);
980 result = add_ccache_to_list(principal_s,
982 service,
983 state->request->data.auth.user,
984 domain->alt_name,
985 uid,
986 time(NULL),
987 time(NULL) + lp_winbind_cache_time(),
988 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
989 true);
991 if (!NT_STATUS_IS_OK(result)) {
992 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
993 "to add ccache to list: %s\n",
994 nt_errstr(result)));
998 #endif /* HAVE_KRB5 */
999 success:
1000 /* FIXME: we possibly should handle logon hours as well (does xp when
1001 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1003 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1004 my_info3->base.bad_password_count = 0;
1006 result = winbindd_update_creds_by_info3(domain,
1007 state->request->data.auth.user,
1008 state->request->data.auth.pass,
1009 my_info3);
1010 if (!NT_STATUS_IS_OK(result)) {
1011 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1012 nt_errstr(result)));
1013 return result;
1016 return NT_STATUS_OK;
1020 /* User does *NOT* know the correct password, modify info3 accordingly */
1022 /* failure of this is not critical */
1023 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1024 if (!NT_STATUS_IS_OK(result)) {
1025 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1026 "Won't be able to honour account lockout policies\n"));
1029 /* increase counter */
1030 my_info3->base.bad_password_count++;
1032 if (max_allowed_bad_attempts == 0) {
1033 goto failed;
1036 /* lockout user */
1037 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1039 uint32 password_properties;
1041 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1042 if (!NT_STATUS_IS_OK(result)) {
1043 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1046 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1047 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1048 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1052 failed:
1053 result = winbindd_update_creds_by_info3(domain,
1054 state->request->data.auth.user,
1055 NULL,
1056 my_info3);
1058 if (!NT_STATUS_IS_OK(result)) {
1059 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1060 nt_errstr(result)));
1063 return NT_STATUS_LOGON_FAILURE;
1066 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1067 struct winbindd_cli_state *state,
1068 struct netr_SamInfo3 **info3)
1070 struct winbindd_domain *contact_domain;
1071 fstring name_domain, name_user;
1072 NTSTATUS result;
1074 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1076 /* Parse domain and username */
1078 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1080 /* what domain should we contact? */
1082 if ( IS_DC ) {
1083 if (!(contact_domain = find_domain_from_name(name_domain))) {
1084 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1085 state->request->data.auth.user, name_domain, name_user, name_domain));
1086 result = NT_STATUS_NO_SUCH_USER;
1087 goto done;
1090 } else {
1091 if (is_myname(name_domain)) {
1092 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1093 result = NT_STATUS_NO_SUCH_USER;
1094 goto done;
1097 contact_domain = find_domain_from_name(name_domain);
1098 if (contact_domain == NULL) {
1099 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1100 state->request->data.auth.user, name_domain, name_user, name_domain));
1102 contact_domain = find_our_domain();
1106 if (contact_domain->initialized &&
1107 contact_domain->active_directory) {
1108 goto try_login;
1111 if (!contact_domain->initialized) {
1112 init_dc_connection(contact_domain);
1115 if (!contact_domain->active_directory) {
1116 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1117 return NT_STATUS_INVALID_LOGON_TYPE;
1119 try_login:
1120 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1121 done:
1122 return result;
1125 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1126 const char *domain, const char *user,
1127 const DATA_BLOB *challenge,
1128 const DATA_BLOB *lm_resp,
1129 const DATA_BLOB *nt_resp,
1130 struct netr_SamInfo3 **pinfo3)
1132 struct auth_usersupplied_info *user_info = NULL;
1133 NTSTATUS status;
1135 status = make_user_info(&user_info, user, user, domain, domain,
1136 global_myname(), lm_resp, nt_resp, NULL, NULL,
1137 NULL, AUTH_PASSWORD_RESPONSE);
1138 if (!NT_STATUS_IS_OK(status)) {
1139 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1140 return status;
1143 /* We don't want any more mapping of the username */
1144 user_info->mapped_state = True;
1146 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1147 pinfo3);
1148 free_user_info(&user_info);
1149 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1150 return NT_STATUS_OK;
1153 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1154 TALLOC_CTX *mem_ctx,
1155 uint32 logon_parameters,
1156 const char *server,
1157 const char *username,
1158 const char *domain,
1159 const char *workstation,
1160 const uint8 chal[8],
1161 DATA_BLOB lm_response,
1162 DATA_BLOB nt_response,
1163 struct netr_SamInfo3 **info3);
1165 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1166 struct winbindd_cli_state *state,
1167 struct netr_SamInfo3 **info3)
1170 struct rpc_pipe_client *netlogon_pipe;
1171 uchar chal[8];
1172 DATA_BLOB lm_resp;
1173 DATA_BLOB nt_resp;
1174 int attempts = 0;
1175 unsigned char local_lm_response[24];
1176 unsigned char local_nt_response[24];
1177 fstring name_domain, name_user;
1178 bool retry;
1179 NTSTATUS result;
1180 struct netr_SamInfo3 *my_info3 = NULL;
1182 *info3 = NULL;
1184 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1186 /* Parse domain and username */
1188 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1190 /* do password magic */
1192 generate_random_buffer(chal, sizeof(chal));
1194 if (lp_client_ntlmv2_auth()) {
1195 DATA_BLOB server_chal;
1196 DATA_BLOB names_blob;
1197 DATA_BLOB nt_response;
1198 DATA_BLOB lm_response;
1199 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1201 /* note that the 'workgroup' here is a best guess - we don't know
1202 the server's domain at this point. The 'server name' is also
1203 dodgy...
1205 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1207 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1208 state->request->data.auth.pass,
1209 &server_chal,
1210 &names_blob,
1211 &lm_response, &nt_response, NULL, NULL)) {
1212 data_blob_free(&names_blob);
1213 data_blob_free(&server_chal);
1214 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1215 result = NT_STATUS_NO_MEMORY;
1216 goto done;
1218 data_blob_free(&names_blob);
1219 data_blob_free(&server_chal);
1220 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1221 lm_response.length);
1222 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1223 nt_response.length);
1224 data_blob_free(&lm_response);
1225 data_blob_free(&nt_response);
1227 } else {
1228 if (lp_client_lanman_auth()
1229 && SMBencrypt(state->request->data.auth.pass,
1230 chal,
1231 local_lm_response)) {
1232 lm_resp = data_blob_talloc(state->mem_ctx,
1233 local_lm_response,
1234 sizeof(local_lm_response));
1235 } else {
1236 lm_resp = data_blob_null;
1238 SMBNTencrypt(state->request->data.auth.pass,
1239 chal,
1240 local_nt_response);
1242 nt_resp = data_blob_talloc(state->mem_ctx,
1243 local_nt_response,
1244 sizeof(local_nt_response));
1247 if (strequal(name_domain, get_global_sam_name())) {
1248 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1250 result = winbindd_dual_auth_passdb(
1251 state->mem_ctx, name_domain, name_user,
1252 &chal_blob, &lm_resp, &nt_resp, info3);
1253 goto done;
1256 /* check authentication loop */
1258 do {
1259 netlogon_fn_t logon_fn;
1261 ZERO_STRUCTP(my_info3);
1262 retry = false;
1264 result = cm_connect_netlogon(domain, &netlogon_pipe);
1266 if (!NT_STATUS_IS_OK(result)) {
1267 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1268 goto done;
1271 /* It is really important to try SamLogonEx here,
1272 * because in a clustered environment, we want to use
1273 * one machine account from multiple physical
1274 * computers.
1276 * With a normal SamLogon call, we must keep the
1277 * credentials chain updated and intact between all
1278 * users of the machine account (which would imply
1279 * cross-node communication for every NTLM logon).
1281 * (The credentials chain is not per NETLOGON pipe
1282 * connection, but globally on the server/client pair
1283 * by machine name).
1285 * When using SamLogonEx, the credentials are not
1286 * supplied, but the session key is implied by the
1287 * wrapping SamLogon context.
1289 * -- abartlet 21 April 2008
1292 logon_fn = domain->can_do_samlogon_ex
1293 ? rpccli_netlogon_sam_network_logon_ex
1294 : rpccli_netlogon_sam_network_logon;
1296 result = logon_fn(netlogon_pipe,
1297 state->mem_ctx,
1299 domain->dcname, /* server name */
1300 name_user, /* user name */
1301 name_domain, /* target domain */
1302 global_myname(), /* workstation */
1303 chal,
1304 lm_resp,
1305 nt_resp,
1306 &my_info3);
1307 attempts += 1;
1309 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1310 && domain->can_do_samlogon_ex) {
1311 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1312 "retrying with NetSamLogon\n"));
1313 domain->can_do_samlogon_ex = false;
1314 continue;
1317 /* We have to try a second time as cm_connect_netlogon
1318 might not yet have noticed that the DC has killed
1319 our connection. */
1321 if (!rpccli_is_connected(netlogon_pipe)) {
1322 continue;
1325 /* if we get access denied, a possible cause was that we had
1326 and open connection to the DC, but someone changed our
1327 machine account password out from underneath us using 'net
1328 rpc changetrustpw' */
1330 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1331 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1332 "ACCESS_DENIED. Maybe the trust account "
1333 "password was changed and we didn't know it. "
1334 "Killing connections to domain %s\n",
1335 name_domain));
1336 invalidate_cm_connection(&domain->conn);
1337 retry = true;
1340 } while ( (attempts < 2) && retry );
1342 /* handle the case where a NT4 DC does not fill in the acct_flags in
1343 * the samlogon reply info3. When accurate info3 is required by the
1344 * caller, we look up the account flags ourselve - gd */
1346 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1347 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1349 struct rpc_pipe_client *samr_pipe;
1350 struct policy_handle samr_domain_handle, user_pol;
1351 union samr_UserInfo *info = NULL;
1352 NTSTATUS status_tmp;
1353 uint32 acct_flags;
1355 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1356 &samr_pipe, &samr_domain_handle);
1358 if (!NT_STATUS_IS_OK(status_tmp)) {
1359 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1360 nt_errstr(status_tmp)));
1361 goto done;
1364 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1365 &samr_domain_handle,
1366 MAXIMUM_ALLOWED_ACCESS,
1367 my_info3->base.rid,
1368 &user_pol);
1370 if (!NT_STATUS_IS_OK(status_tmp)) {
1371 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1372 nt_errstr(status_tmp)));
1373 goto done;
1376 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1377 &user_pol,
1379 &info);
1381 if (!NT_STATUS_IS_OK(status_tmp)) {
1382 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1383 nt_errstr(status_tmp)));
1384 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1385 goto done;
1388 acct_flags = info->info16.acct_flags;
1390 if (acct_flags == 0) {
1391 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1392 goto done;
1395 my_info3->base.acct_flags = acct_flags;
1397 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1399 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1402 *info3 = my_info3;
1403 done:
1404 return result;
1407 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1408 struct winbindd_cli_state *state)
1410 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1411 NTSTATUS krb5_result = NT_STATUS_OK;
1412 fstring name_domain, name_user;
1413 char *mapped_user;
1414 fstring domain_user;
1415 struct netr_SamInfo3 *info3 = NULL;
1416 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1418 /* Ensure null termination */
1419 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1421 /* Ensure null termination */
1422 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1424 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1425 state->request->data.auth.user));
1427 /* Parse domain and username */
1429 name_map_status = normalize_name_unmap(state->mem_ctx,
1430 state->request->data.auth.user,
1431 &mapped_user);
1433 /* If the name normalization didnt' actually do anything,
1434 just use the original name */
1436 if (!NT_STATUS_IS_OK(name_map_status) &&
1437 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1439 mapped_user = state->request->data.auth.user;
1442 parse_domain_user(mapped_user, name_domain, name_user);
1444 if ( mapped_user != state->request->data.auth.user ) {
1445 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1446 *lp_winbind_separator(),
1447 name_user );
1448 safe_strcpy( state->request->data.auth.user, domain_user,
1449 sizeof(state->request->data.auth.user)-1 );
1452 if (!domain->online) {
1453 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1454 if (domain->startup) {
1455 /* Logons are very important to users. If we're offline and
1456 we get a request within the first 30 seconds of startup,
1457 try very hard to find a DC and go online. */
1459 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1460 "request in startup mode.\n", domain->name ));
1462 winbindd_flush_negative_conn_cache(domain);
1463 result = init_dc_connection(domain);
1467 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1469 /* Check for Kerberos authentication */
1470 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1472 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1473 /* save for later */
1474 krb5_result = result;
1477 if (NT_STATUS_IS_OK(result)) {
1478 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1479 goto process_result;
1480 } else {
1481 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1484 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1485 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1486 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1487 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1488 set_domain_offline( domain );
1489 goto cached_logon;
1492 /* there are quite some NT_STATUS errors where there is no
1493 * point in retrying with a samlogon, we explictly have to take
1494 * care not to increase the bad logon counter on the DC */
1496 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1497 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1498 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1499 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1500 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1501 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1502 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1503 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1504 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1505 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1506 goto done;
1509 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1510 DEBUG(3,("falling back to samlogon\n"));
1511 goto sam_logon;
1512 } else {
1513 goto cached_logon;
1517 sam_logon:
1518 /* Check for Samlogon authentication */
1519 if (domain->online) {
1520 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1522 if (NT_STATUS_IS_OK(result)) {
1523 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1524 /* add the Krb5 err if we have one */
1525 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1526 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1528 goto process_result;
1531 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1532 nt_errstr(result)));
1534 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1535 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1536 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1538 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1539 set_domain_offline( domain );
1540 goto cached_logon;
1543 if (domain->online) {
1544 /* We're still online - fail. */
1545 goto done;
1549 cached_logon:
1550 /* Check for Cached logons */
1551 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1552 lp_winbind_offline_logon()) {
1554 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1556 if (NT_STATUS_IS_OK(result)) {
1557 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1558 goto process_result;
1559 } else {
1560 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1561 goto done;
1565 process_result:
1567 if (NT_STATUS_IS_OK(result)) {
1569 struct dom_sid user_sid;
1571 /* In all codepaths where result == NT_STATUS_OK info3 must have
1572 been initialized. */
1573 if (!info3) {
1574 result = NT_STATUS_INTERNAL_ERROR;
1575 goto done;
1578 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1579 netsamlogon_cache_store(name_user, info3);
1581 /* save name_to_sid info as early as possible (only if
1582 this is our primary domain so we don't invalidate
1583 the cache entry by storing the seq_num for the wrong
1584 domain). */
1585 if ( domain->primary ) {
1586 sid_compose(&user_sid, info3->base.domain_sid,
1587 info3->base.rid);
1588 cache_name2sid(domain, name_domain, name_user,
1589 SID_NAME_USER, &user_sid);
1592 /* Check if the user is in the right group */
1594 result = check_info3_in_group(
1595 info3,
1596 state->request->data.auth.require_membership_of_sid);
1597 if (!NT_STATUS_IS_OK(result)) {
1598 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1599 state->request->data.auth.user,
1600 state->request->data.auth.require_membership_of_sid));
1601 goto done;
1604 result = append_auth_data(state, info3, name_domain,
1605 name_user);
1606 if (!NT_STATUS_IS_OK(result)) {
1607 goto done;
1610 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1611 && lp_winbind_offline_logon()) {
1613 result = winbindd_store_creds(domain,
1614 state->request->data.auth.user,
1615 state->request->data.auth.pass,
1616 info3);
1619 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1620 struct winbindd_domain *our_domain = find_our_domain();
1622 /* This is not entirely correct I believe, but it is
1623 consistent. Only apply the password policy settings
1624 too warn users for our own domain. Cannot obtain these
1625 from trusted DCs all the time so don't do it at all.
1626 -- jerry */
1628 result = NT_STATUS_NOT_SUPPORTED;
1629 if (our_domain == domain ) {
1630 result = fillup_password_policy(our_domain, state);
1633 if (!NT_STATUS_IS_OK(result)
1634 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1636 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1637 domain->name, nt_errstr(result)));
1638 goto done;
1642 result = NT_STATUS_OK;
1645 done:
1646 /* give us a more useful (more correct?) error code */
1647 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1648 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1649 result = NT_STATUS_NO_LOGON_SERVERS;
1652 set_auth_errors(state->response, result);
1654 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1655 state->request->data.auth.user,
1656 state->response->data.auth.nt_status_string,
1657 state->response->data.auth.pam_error));
1659 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1662 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1663 struct winbindd_cli_state *state)
1665 NTSTATUS result;
1666 struct netr_SamInfo3 *info3 = NULL;
1667 struct rpc_pipe_client *netlogon_pipe;
1668 const char *name_user = NULL;
1669 const char *name_domain = NULL;
1670 const char *workstation;
1671 int attempts = 0;
1672 bool retry;
1674 DATA_BLOB lm_resp, nt_resp;
1676 /* This is child-only, so no check for privileged access is needed
1677 anymore */
1679 /* Ensure null termination */
1680 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1681 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1683 name_user = state->request->data.auth_crap.user;
1684 name_domain = state->request->data.auth_crap.domain;
1685 workstation = state->request->data.auth_crap.workstation;
1687 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1688 name_domain, name_user));
1690 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1691 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1692 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1693 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1694 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1695 state->request->data.auth_crap.lm_resp_len,
1696 state->request->data.auth_crap.nt_resp_len));
1697 result = NT_STATUS_INVALID_PARAMETER;
1698 goto done;
1702 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1703 state->request->data.auth_crap.lm_resp_len);
1705 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1706 nt_resp = data_blob_talloc(state->mem_ctx,
1707 state->request->extra_data.data,
1708 state->request->data.auth_crap.nt_resp_len);
1709 } else {
1710 nt_resp = data_blob_talloc(state->mem_ctx,
1711 state->request->data.auth_crap.nt_resp,
1712 state->request->data.auth_crap.nt_resp_len);
1715 if (strequal(name_domain, get_global_sam_name())) {
1716 DATA_BLOB chal_blob = data_blob_const(
1717 state->request->data.auth_crap.chal,
1718 sizeof(state->request->data.auth_crap.chal));
1720 result = winbindd_dual_auth_passdb(
1721 state->mem_ctx, name_domain, name_user,
1722 &chal_blob, &lm_resp, &nt_resp, &info3);
1723 goto process_result;
1726 do {
1727 netlogon_fn_t logon_fn;
1729 retry = false;
1731 netlogon_pipe = NULL;
1732 result = cm_connect_netlogon(domain, &netlogon_pipe);
1734 if (!NT_STATUS_IS_OK(result)) {
1735 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1736 nt_errstr(result)));
1737 goto done;
1740 logon_fn = domain->can_do_samlogon_ex
1741 ? rpccli_netlogon_sam_network_logon_ex
1742 : rpccli_netlogon_sam_network_logon;
1744 result = logon_fn(netlogon_pipe,
1745 state->mem_ctx,
1746 state->request->data.auth_crap.logon_parameters,
1747 domain->dcname,
1748 name_user,
1749 name_domain,
1750 /* Bug #3248 - found by Stefan Burkei. */
1751 workstation, /* We carefully set this above so use it... */
1752 state->request->data.auth_crap.chal,
1753 lm_resp,
1754 nt_resp,
1755 &info3);
1757 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1758 && domain->can_do_samlogon_ex) {
1759 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1760 "retrying with NetSamLogon\n"));
1761 domain->can_do_samlogon_ex = false;
1762 continue;
1765 attempts += 1;
1767 /* We have to try a second time as cm_connect_netlogon
1768 might not yet have noticed that the DC has killed
1769 our connection. */
1771 if (!rpccli_is_connected(netlogon_pipe)) {
1772 continue;
1775 /* if we get access denied, a possible cause was that we had and open
1776 connection to the DC, but someone changed our machine account password
1777 out from underneath us using 'net rpc changetrustpw' */
1779 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1780 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1781 "ACCESS_DENIED. Maybe the trust account "
1782 "password was changed and we didn't know it. "
1783 "Killing connections to domain %s\n",
1784 name_domain));
1785 invalidate_cm_connection(&domain->conn);
1786 retry = true;
1789 } while ( (attempts < 2) && retry );
1791 process_result:
1793 if (NT_STATUS_IS_OK(result)) {
1795 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1796 netsamlogon_cache_store(name_user, info3);
1798 /* Check if the user is in the right group */
1800 result = check_info3_in_group(
1801 info3,
1802 state->request->data.auth_crap.require_membership_of_sid);
1803 if (!NT_STATUS_IS_OK(result)) {
1804 DEBUG(3, ("User %s is not in the required group (%s), so "
1805 "crap authentication is rejected\n",
1806 state->request->data.auth_crap.user,
1807 state->request->data.auth_crap.require_membership_of_sid));
1808 goto done;
1811 result = append_auth_data(state, info3, name_domain,
1812 name_user);
1813 if (!NT_STATUS_IS_OK(result)) {
1814 goto done;
1818 done:
1820 /* give us a more useful (more correct?) error code */
1821 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1822 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1823 result = NT_STATUS_NO_LOGON_SERVERS;
1826 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1827 result = nt_status_squash(result);
1830 set_auth_errors(state->response, result);
1832 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1833 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1834 name_domain,
1835 name_user,
1836 state->response->data.auth.nt_status_string,
1837 state->response->data.auth.pam_error));
1839 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1842 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1843 struct winbindd_cli_state *state)
1845 char *oldpass;
1846 char *newpass = NULL;
1847 struct policy_handle dom_pol;
1848 struct rpc_pipe_client *cli = NULL;
1849 bool got_info = false;
1850 struct samr_DomInfo1 *info = NULL;
1851 struct userPwdChangeFailureInformation *reject = NULL;
1852 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1853 fstring domain, user;
1855 ZERO_STRUCT(dom_pol);
1857 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1858 state->request->data.auth.user));
1860 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1861 goto done;
1864 /* Change password */
1866 oldpass = state->request->data.chauthtok.oldpass;
1867 newpass = state->request->data.chauthtok.newpass;
1869 /* Initialize reject reason */
1870 state->response->data.auth.reject_reason = Undefined;
1872 /* Get sam handle */
1874 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1875 &dom_pol);
1876 if (!NT_STATUS_IS_OK(result)) {
1877 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1878 goto done;
1881 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1882 user,
1883 newpass,
1884 oldpass,
1885 &info,
1886 &reject);
1888 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1890 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1892 fill_in_password_policy(state->response, info);
1894 state->response->data.auth.reject_reason =
1895 reject->extendedFailureReason;
1897 got_info = true;
1900 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1901 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1902 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1903 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1905 /* only fallback when the chgpasswd_user3 call is not supported */
1906 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1907 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1908 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1909 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1911 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1912 nt_errstr(result)));
1914 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1916 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1917 Map to the same status code as Windows 2003. */
1919 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1920 result = NT_STATUS_PASSWORD_RESTRICTION;
1924 done:
1926 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1927 if (lp_winbind_offline_logon()) {
1928 result = winbindd_update_creds_by_name(contact_domain,
1929 user, newpass);
1930 /* Again, this happens when we login from gdm or xdm
1931 * and the password expires, *BUT* cached crendentials
1932 * doesn't exist. winbindd_update_creds_by_name()
1933 * returns NT_STATUS_NO_SUCH_USER.
1934 * This is not a failure.
1935 * --- BoYang
1936 * */
1937 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1938 result = NT_STATUS_OK;
1941 if (!NT_STATUS_IS_OK(result)) {
1942 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1943 goto process_result;
1948 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1950 NTSTATUS policy_ret;
1952 policy_ret = fillup_password_policy(contact_domain, state);
1954 /* failure of this is non critical, it will just provide no
1955 * additional information to the client why the change has
1956 * failed - Guenther */
1958 if (!NT_STATUS_IS_OK(policy_ret)) {
1959 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1960 goto process_result;
1964 process_result:
1966 if (strequal(contact_domain->name, get_global_sam_name())) {
1967 /* FIXME: internal rpc pipe does not cache handles yet */
1968 if (cli) {
1969 if (is_valid_policy_hnd(&dom_pol)) {
1970 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1972 TALLOC_FREE(cli);
1976 set_auth_errors(state->response, result);
1978 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1979 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1980 domain,
1981 user,
1982 state->response->data.auth.nt_status_string,
1983 state->response->data.auth.pam_error));
1985 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1988 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1989 struct winbindd_cli_state *state)
1991 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1993 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1994 state->request->data.logoff.user));
1996 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1997 result = NT_STATUS_OK;
1998 goto process_result;
2001 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2002 result = NT_STATUS_OK;
2003 goto process_result;
2006 #ifdef HAVE_KRB5
2008 if (state->request->data.logoff.uid < 0) {
2009 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2010 goto process_result;
2013 /* what we need here is to find the corresponding krb5 ccache name *we*
2014 * created for a given username and destroy it */
2016 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2017 result = NT_STATUS_OK;
2018 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2019 goto process_result;
2022 if (!ccache_entry_identical(state->request->data.logoff.user,
2023 state->request->data.logoff.uid,
2024 state->request->data.logoff.krb5ccname)) {
2025 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2026 goto process_result;
2029 result = remove_ccache(state->request->data.logoff.user);
2030 if (!NT_STATUS_IS_OK(result)) {
2031 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2032 nt_errstr(result)));
2033 goto process_result;
2036 #else
2037 result = NT_STATUS_NOT_SUPPORTED;
2038 #endif
2040 process_result:
2043 set_auth_errors(state->response, result);
2045 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2048 /* Change user password with auth crap*/
2050 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2052 NTSTATUS result;
2053 DATA_BLOB new_nt_password;
2054 DATA_BLOB old_nt_hash_enc;
2055 DATA_BLOB new_lm_password;
2056 DATA_BLOB old_lm_hash_enc;
2057 fstring domain,user;
2058 struct policy_handle dom_pol;
2059 struct winbindd_domain *contact_domain = domainSt;
2060 struct rpc_pipe_client *cli = NULL;
2062 ZERO_STRUCT(dom_pol);
2064 /* Ensure null termination */
2065 state->request->data.chng_pswd_auth_crap.user[
2066 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2067 state->request->data.chng_pswd_auth_crap.domain[
2068 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2069 *domain = 0;
2070 *user = 0;
2072 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2073 (unsigned long)state->pid,
2074 state->request->data.chng_pswd_auth_crap.domain,
2075 state->request->data.chng_pswd_auth_crap.user));
2077 if (lp_winbind_offline_logon()) {
2078 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2079 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2080 result = NT_STATUS_ACCESS_DENIED;
2081 goto done;
2084 if (*state->request->data.chng_pswd_auth_crap.domain) {
2085 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2086 } else {
2087 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2088 domain, user);
2090 if(!*domain) {
2091 DEBUG(3,("no domain specified with username (%s) - "
2092 "failing auth\n",
2093 state->request->data.chng_pswd_auth_crap.user));
2094 result = NT_STATUS_NO_SUCH_USER;
2095 goto done;
2099 if (!*domain && lp_winbind_use_default_domain()) {
2100 fstrcpy(domain,(char *)lp_workgroup());
2103 if(!*user) {
2104 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2107 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2108 (unsigned long)state->pid, domain, user));
2110 /* Change password */
2111 new_nt_password = data_blob_const(
2112 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2113 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2115 old_nt_hash_enc = data_blob_const(
2116 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2117 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2119 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2120 new_lm_password = data_blob_const(
2121 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2122 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2124 old_lm_hash_enc = data_blob_const(
2125 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2126 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2127 } else {
2128 new_lm_password.length = 0;
2129 old_lm_hash_enc.length = 0;
2132 /* Get sam handle */
2134 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2135 if (!NT_STATUS_IS_OK(result)) {
2136 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2137 goto done;
2140 result = rpccli_samr_chng_pswd_auth_crap(
2141 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2142 new_lm_password, old_lm_hash_enc);
2144 done:
2146 if (strequal(contact_domain->name, get_global_sam_name())) {
2147 /* FIXME: internal rpc pipe does not cache handles yet */
2148 if (cli) {
2149 if (is_valid_policy_hnd(&dom_pol)) {
2150 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2152 TALLOC_FREE(cli);
2156 set_auth_errors(state->response, result);
2158 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2159 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2160 domain, user,
2161 state->response->data.auth.nt_status_string,
2162 state->response->data.auth.pam_error));
2164 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;