s3: "== false" looks wrong :-)
[Samba/wip.git] / source3 / winbindd / winbindd_pam.c
blob86d9e6d0c388dddd6a8bd11d3f45ee693cfc4fcf
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(state->mem_ctx,
842 name_domain,
843 name_user,
844 &sid,
845 &type)) {
846 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
847 return NT_STATUS_NO_SUCH_USER;
850 if (type != SID_NAME_USER) {
851 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
852 return NT_STATUS_LOGON_FAILURE;
855 result = winbindd_get_creds(domain,
856 state->mem_ctx,
857 &sid,
858 &my_info3,
859 &cached_nt_pass,
860 &cached_salt);
861 if (!NT_STATUS_IS_OK(result)) {
862 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
863 return result;
866 *info3 = my_info3;
868 E_md4hash(state->request->data.auth.pass, new_nt_pass);
870 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
871 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
872 if (cached_salt) {
873 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
876 if (cached_salt) {
877 /* In this case we didn't store the nt_hash itself,
878 but the MD5 combination of salt + nt_hash. */
879 uchar salted_hash[NT_HASH_LEN];
880 E_md5hash(cached_salt, new_nt_pass, salted_hash);
882 password_good = (memcmp(cached_nt_pass, salted_hash,
883 NT_HASH_LEN) == 0);
884 } else {
885 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
886 password_good = (memcmp(cached_nt_pass, new_nt_pass,
887 NT_HASH_LEN) == 0);
890 if (password_good) {
892 /* User *DOES* know the password, update logon_time and reset
893 * bad_pw_count */
895 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
897 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
898 return NT_STATUS_ACCOUNT_LOCKED_OUT;
901 if (my_info3->base.acct_flags & ACB_DISABLED) {
902 return NT_STATUS_ACCOUNT_DISABLED;
905 if (my_info3->base.acct_flags & ACB_WSTRUST) {
906 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
909 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
910 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
913 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
914 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
917 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
918 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
919 my_info3->base.acct_flags));
920 return NT_STATUS_LOGON_FAILURE;
923 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
924 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
925 return NT_STATUS_ACCOUNT_EXPIRED;
928 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
929 if (must_change_time != 0 && must_change_time < time(NULL)) {
930 /* we allow grace logons when the password has expired */
931 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
932 /* return NT_STATUS_PASSWORD_EXPIRED; */
933 goto success;
936 #ifdef HAVE_KRB5
937 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
938 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
939 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
940 /* used to cope with the case winbindd starting without network. */
941 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
943 uid_t uid = -1;
944 const char *cc = NULL;
945 char *realm = NULL;
946 const char *principal_s = NULL;
947 const char *service = NULL;
948 bool internal_ccache = false;
950 uid = get_uid_from_state(state);
951 if (uid == -1) {
952 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
953 return NT_STATUS_INVALID_PARAMETER;
956 cc = generate_krb5_ccache(state->mem_ctx,
957 state->request->data.auth.krb5_cc_type,
958 state->request->data.auth.uid,
959 &internal_ccache);
960 if (cc == NULL) {
961 return NT_STATUS_NO_MEMORY;
964 realm = domain->alt_name;
965 strupper_m(realm);
967 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
968 if (principal_s == NULL) {
969 return NT_STATUS_NO_MEMORY;
972 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
973 if (service == NULL) {
974 return NT_STATUS_NO_MEMORY;
977 if (!internal_ccache) {
979 setup_return_cc_name(state, cc);
981 result = add_ccache_to_list(principal_s,
983 service,
984 state->request->data.auth.user,
985 domain->alt_name,
986 uid,
987 time(NULL),
988 time(NULL) + lp_winbind_cache_time(),
989 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
990 true);
992 if (!NT_STATUS_IS_OK(result)) {
993 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
994 "to add ccache to list: %s\n",
995 nt_errstr(result)));
999 #endif /* HAVE_KRB5 */
1000 success:
1001 /* FIXME: we possibly should handle logon hours as well (does xp when
1002 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1004 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1005 my_info3->base.bad_password_count = 0;
1007 result = winbindd_update_creds_by_info3(domain,
1008 state->mem_ctx,
1009 state->request->data.auth.user,
1010 state->request->data.auth.pass,
1011 my_info3);
1012 if (!NT_STATUS_IS_OK(result)) {
1013 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1014 nt_errstr(result)));
1015 return result;
1018 return NT_STATUS_OK;
1022 /* User does *NOT* know the correct password, modify info3 accordingly */
1024 /* failure of this is not critical */
1025 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1026 if (!NT_STATUS_IS_OK(result)) {
1027 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1028 "Won't be able to honour account lockout policies\n"));
1031 /* increase counter */
1032 my_info3->base.bad_password_count++;
1034 if (max_allowed_bad_attempts == 0) {
1035 goto failed;
1038 /* lockout user */
1039 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1041 uint32 password_properties;
1043 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1044 if (!NT_STATUS_IS_OK(result)) {
1045 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1048 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1049 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1050 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1054 failed:
1055 result = winbindd_update_creds_by_info3(domain,
1056 state->mem_ctx,
1057 state->request->data.auth.user,
1058 NULL,
1059 my_info3);
1061 if (!NT_STATUS_IS_OK(result)) {
1062 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1063 nt_errstr(result)));
1066 return NT_STATUS_LOGON_FAILURE;
1069 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1070 struct winbindd_cli_state *state,
1071 struct netr_SamInfo3 **info3)
1073 struct winbindd_domain *contact_domain;
1074 fstring name_domain, name_user;
1075 NTSTATUS result;
1077 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1079 /* Parse domain and username */
1081 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1083 /* what domain should we contact? */
1085 if ( IS_DC ) {
1086 if (!(contact_domain = find_domain_from_name(name_domain))) {
1087 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1088 state->request->data.auth.user, name_domain, name_user, name_domain));
1089 result = NT_STATUS_NO_SUCH_USER;
1090 goto done;
1093 } else {
1094 if (is_myname(name_domain)) {
1095 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1096 result = NT_STATUS_NO_SUCH_USER;
1097 goto done;
1100 contact_domain = find_domain_from_name(name_domain);
1101 if (contact_domain == NULL) {
1102 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1103 state->request->data.auth.user, name_domain, name_user, name_domain));
1105 contact_domain = find_our_domain();
1109 if (contact_domain->initialized &&
1110 contact_domain->active_directory) {
1111 goto try_login;
1114 if (!contact_domain->initialized) {
1115 init_dc_connection(contact_domain);
1118 if (!contact_domain->active_directory) {
1119 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1120 return NT_STATUS_INVALID_LOGON_TYPE;
1122 try_login:
1123 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1124 done:
1125 return result;
1128 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1129 const char *domain, const char *user,
1130 const DATA_BLOB *challenge,
1131 const DATA_BLOB *lm_resp,
1132 const DATA_BLOB *nt_resp,
1133 struct netr_SamInfo3 **pinfo3)
1135 struct auth_usersupplied_info *user_info = NULL;
1136 NTSTATUS status;
1138 status = make_user_info(&user_info, user, user, domain, domain,
1139 global_myname(), lm_resp, nt_resp, NULL, NULL,
1140 NULL, AUTH_PASSWORD_RESPONSE);
1141 if (!NT_STATUS_IS_OK(status)) {
1142 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1143 return status;
1146 /* We don't want any more mapping of the username */
1147 user_info->mapped_state = True;
1149 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1150 pinfo3);
1151 free_user_info(&user_info);
1152 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1153 return NT_STATUS_OK;
1156 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1157 TALLOC_CTX *mem_ctx,
1158 uint32 logon_parameters,
1159 const char *server,
1160 const char *username,
1161 const char *domain,
1162 const char *workstation,
1163 const uint8 chal[8],
1164 DATA_BLOB lm_response,
1165 DATA_BLOB nt_response,
1166 struct netr_SamInfo3 **info3);
1168 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1169 struct winbindd_cli_state *state,
1170 struct netr_SamInfo3 **info3)
1173 struct rpc_pipe_client *netlogon_pipe;
1174 uchar chal[8];
1175 DATA_BLOB lm_resp;
1176 DATA_BLOB nt_resp;
1177 int attempts = 0;
1178 unsigned char local_lm_response[24];
1179 unsigned char local_nt_response[24];
1180 fstring name_domain, name_user;
1181 bool retry;
1182 NTSTATUS result;
1183 struct netr_SamInfo3 *my_info3 = NULL;
1185 *info3 = NULL;
1187 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1189 /* Parse domain and username */
1191 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1193 /* do password magic */
1195 generate_random_buffer(chal, sizeof(chal));
1197 if (lp_client_ntlmv2_auth()) {
1198 DATA_BLOB server_chal;
1199 DATA_BLOB names_blob;
1200 DATA_BLOB nt_response;
1201 DATA_BLOB lm_response;
1202 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1204 /* note that the 'workgroup' here is a best guess - we don't know
1205 the server's domain at this point. The 'server name' is also
1206 dodgy...
1208 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1210 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1211 state->request->data.auth.pass,
1212 &server_chal,
1213 &names_blob,
1214 &lm_response, &nt_response, NULL, NULL)) {
1215 data_blob_free(&names_blob);
1216 data_blob_free(&server_chal);
1217 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1218 result = NT_STATUS_NO_MEMORY;
1219 goto done;
1221 data_blob_free(&names_blob);
1222 data_blob_free(&server_chal);
1223 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1224 lm_response.length);
1225 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1226 nt_response.length);
1227 data_blob_free(&lm_response);
1228 data_blob_free(&nt_response);
1230 } else {
1231 if (lp_client_lanman_auth()
1232 && SMBencrypt(state->request->data.auth.pass,
1233 chal,
1234 local_lm_response)) {
1235 lm_resp = data_blob_talloc(state->mem_ctx,
1236 local_lm_response,
1237 sizeof(local_lm_response));
1238 } else {
1239 lm_resp = data_blob_null;
1241 SMBNTencrypt(state->request->data.auth.pass,
1242 chal,
1243 local_nt_response);
1245 nt_resp = data_blob_talloc(state->mem_ctx,
1246 local_nt_response,
1247 sizeof(local_nt_response));
1250 if (strequal(name_domain, get_global_sam_name())) {
1251 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1253 result = winbindd_dual_auth_passdb(
1254 state->mem_ctx, name_domain, name_user,
1255 &chal_blob, &lm_resp, &nt_resp, info3);
1256 goto done;
1259 /* check authentication loop */
1261 do {
1262 netlogon_fn_t logon_fn;
1264 ZERO_STRUCTP(my_info3);
1265 retry = false;
1267 result = cm_connect_netlogon(domain, &netlogon_pipe);
1269 if (!NT_STATUS_IS_OK(result)) {
1270 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1271 goto done;
1274 /* It is really important to try SamLogonEx here,
1275 * because in a clustered environment, we want to use
1276 * one machine account from multiple physical
1277 * computers.
1279 * With a normal SamLogon call, we must keep the
1280 * credentials chain updated and intact between all
1281 * users of the machine account (which would imply
1282 * cross-node communication for every NTLM logon).
1284 * (The credentials chain is not per NETLOGON pipe
1285 * connection, but globally on the server/client pair
1286 * by machine name).
1288 * When using SamLogonEx, the credentials are not
1289 * supplied, but the session key is implied by the
1290 * wrapping SamLogon context.
1292 * -- abartlet 21 April 2008
1295 logon_fn = domain->can_do_samlogon_ex
1296 ? rpccli_netlogon_sam_network_logon_ex
1297 : rpccli_netlogon_sam_network_logon;
1299 result = logon_fn(netlogon_pipe,
1300 state->mem_ctx,
1302 domain->dcname, /* server name */
1303 name_user, /* user name */
1304 name_domain, /* target domain */
1305 global_myname(), /* workstation */
1306 chal,
1307 lm_resp,
1308 nt_resp,
1309 &my_info3);
1310 attempts += 1;
1312 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1313 && domain->can_do_samlogon_ex) {
1314 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1315 "retrying with NetSamLogon\n"));
1316 domain->can_do_samlogon_ex = false;
1317 retry = true;
1318 continue;
1321 /* We have to try a second time as cm_connect_netlogon
1322 might not yet have noticed that the DC has killed
1323 our connection. */
1325 if (!rpccli_is_connected(netlogon_pipe)) {
1326 retry = true;
1327 continue;
1330 /* if we get access denied, a possible cause was that we had
1331 and open connection to the DC, but someone changed our
1332 machine account password out from underneath us using 'net
1333 rpc changetrustpw' */
1335 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1336 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1337 "ACCESS_DENIED. Maybe the trust account "
1338 "password was changed and we didn't know it. "
1339 "Killing connections to domain %s\n",
1340 name_domain));
1341 invalidate_cm_connection(&domain->conn);
1342 retry = true;
1345 } while ( (attempts < 2) && retry );
1347 /* handle the case where a NT4 DC does not fill in the acct_flags in
1348 * the samlogon reply info3. When accurate info3 is required by the
1349 * caller, we look up the account flags ourselve - gd */
1351 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1352 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1354 struct rpc_pipe_client *samr_pipe;
1355 struct policy_handle samr_domain_handle, user_pol;
1356 union samr_UserInfo *info = NULL;
1357 NTSTATUS status_tmp;
1358 uint32 acct_flags;
1360 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1361 &samr_pipe, &samr_domain_handle);
1363 if (!NT_STATUS_IS_OK(status_tmp)) {
1364 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1365 nt_errstr(status_tmp)));
1366 goto done;
1369 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1370 &samr_domain_handle,
1371 MAXIMUM_ALLOWED_ACCESS,
1372 my_info3->base.rid,
1373 &user_pol);
1375 if (!NT_STATUS_IS_OK(status_tmp)) {
1376 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1377 nt_errstr(status_tmp)));
1378 goto done;
1381 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1382 &user_pol,
1384 &info);
1386 if (!NT_STATUS_IS_OK(status_tmp)) {
1387 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1388 nt_errstr(status_tmp)));
1389 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1390 goto done;
1393 acct_flags = info->info16.acct_flags;
1395 if (acct_flags == 0) {
1396 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1397 goto done;
1400 my_info3->base.acct_flags = acct_flags;
1402 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1404 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1407 *info3 = my_info3;
1408 done:
1409 return result;
1412 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1413 struct winbindd_cli_state *state)
1415 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1416 NTSTATUS krb5_result = NT_STATUS_OK;
1417 fstring name_domain, name_user;
1418 char *mapped_user;
1419 fstring domain_user;
1420 struct netr_SamInfo3 *info3 = NULL;
1421 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1423 /* Ensure null termination */
1424 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1426 /* Ensure null termination */
1427 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1429 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1430 state->request->data.auth.user));
1432 if (!check_request_flags(state->request->flags)) {
1433 result = NT_STATUS_INVALID_PARAMETER_MIX;
1434 goto done;
1437 /* Parse domain and username */
1439 name_map_status = normalize_name_unmap(state->mem_ctx,
1440 state->request->data.auth.user,
1441 &mapped_user);
1443 /* If the name normalization didnt' actually do anything,
1444 just use the original name */
1446 if (!NT_STATUS_IS_OK(name_map_status) &&
1447 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1449 mapped_user = state->request->data.auth.user;
1452 parse_domain_user(mapped_user, name_domain, name_user);
1454 if ( mapped_user != state->request->data.auth.user ) {
1455 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1456 *lp_winbind_separator(),
1457 name_user );
1458 safe_strcpy( state->request->data.auth.user, domain_user,
1459 sizeof(state->request->data.auth.user)-1 );
1462 if (!domain->online) {
1463 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1464 if (domain->startup) {
1465 /* Logons are very important to users. If we're offline and
1466 we get a request within the first 30 seconds of startup,
1467 try very hard to find a DC and go online. */
1469 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1470 "request in startup mode.\n", domain->name ));
1472 winbindd_flush_negative_conn_cache(domain);
1473 result = init_dc_connection(domain);
1477 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1479 /* Check for Kerberos authentication */
1480 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1482 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1483 /* save for later */
1484 krb5_result = result;
1487 if (NT_STATUS_IS_OK(result)) {
1488 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1489 goto process_result;
1490 } else {
1491 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1494 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1495 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1496 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1497 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1498 set_domain_offline( domain );
1499 goto cached_logon;
1502 /* there are quite some NT_STATUS errors where there is no
1503 * point in retrying with a samlogon, we explictly have to take
1504 * care not to increase the bad logon counter on the DC */
1506 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1507 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1508 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1509 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1510 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1511 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1512 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1513 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1514 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1515 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1516 goto done;
1519 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1520 DEBUG(3,("falling back to samlogon\n"));
1521 goto sam_logon;
1522 } else {
1523 goto cached_logon;
1527 sam_logon:
1528 /* Check for Samlogon authentication */
1529 if (domain->online) {
1530 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1532 if (NT_STATUS_IS_OK(result)) {
1533 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1534 /* add the Krb5 err if we have one */
1535 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1536 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1538 goto process_result;
1541 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1542 nt_errstr(result)));
1544 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1545 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1546 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1548 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1549 set_domain_offline( domain );
1550 goto cached_logon;
1553 if (domain->online) {
1554 /* We're still online - fail. */
1555 goto done;
1559 cached_logon:
1560 /* Check for Cached logons */
1561 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1562 lp_winbind_offline_logon()) {
1564 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1566 if (NT_STATUS_IS_OK(result)) {
1567 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1568 goto process_result;
1569 } else {
1570 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1571 goto done;
1575 process_result:
1577 if (NT_STATUS_IS_OK(result)) {
1579 struct dom_sid user_sid;
1581 /* In all codepaths where result == NT_STATUS_OK info3 must have
1582 been initialized. */
1583 if (!info3) {
1584 result = NT_STATUS_INTERNAL_ERROR;
1585 goto done;
1588 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1589 netsamlogon_cache_store(name_user, info3);
1591 /* save name_to_sid info as early as possible (only if
1592 this is our primary domain so we don't invalidate
1593 the cache entry by storing the seq_num for the wrong
1594 domain). */
1595 if ( domain->primary ) {
1596 sid_compose(&user_sid, info3->base.domain_sid,
1597 info3->base.rid);
1598 cache_name2sid(domain, name_domain, name_user,
1599 SID_NAME_USER, &user_sid);
1602 /* Check if the user is in the right group */
1604 result = check_info3_in_group(
1605 info3,
1606 state->request->data.auth.require_membership_of_sid);
1607 if (!NT_STATUS_IS_OK(result)) {
1608 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1609 state->request->data.auth.user,
1610 state->request->data.auth.require_membership_of_sid));
1611 goto done;
1614 result = append_auth_data(state, info3, name_domain,
1615 name_user);
1616 if (!NT_STATUS_IS_OK(result)) {
1617 goto done;
1620 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1622 if (lp_winbind_offline_logon()) {
1623 result = winbindd_store_creds(domain,
1624 state->mem_ctx,
1625 state->request->data.auth.user,
1626 state->request->data.auth.pass,
1627 info3, NULL);
1632 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1633 struct winbindd_domain *our_domain = find_our_domain();
1635 /* This is not entirely correct I believe, but it is
1636 consistent. Only apply the password policy settings
1637 too warn users for our own domain. Cannot obtain these
1638 from trusted DCs all the time so don't do it at all.
1639 -- jerry */
1641 result = NT_STATUS_NOT_SUPPORTED;
1642 if (our_domain == domain ) {
1643 result = fillup_password_policy(our_domain, state);
1646 if (!NT_STATUS_IS_OK(result)
1647 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1649 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1650 domain->name, nt_errstr(result)));
1651 goto done;
1655 result = NT_STATUS_OK;
1658 done:
1659 /* give us a more useful (more correct?) error code */
1660 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1661 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1662 result = NT_STATUS_NO_LOGON_SERVERS;
1665 set_auth_errors(state->response, result);
1667 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1668 state->request->data.auth.user,
1669 state->response->data.auth.nt_status_string,
1670 state->response->data.auth.pam_error));
1672 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1675 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1676 struct winbindd_cli_state *state)
1678 NTSTATUS result;
1679 struct netr_SamInfo3 *info3 = NULL;
1680 struct rpc_pipe_client *netlogon_pipe;
1681 const char *name_user = NULL;
1682 const char *name_domain = NULL;
1683 const char *workstation;
1684 int attempts = 0;
1685 bool retry;
1687 DATA_BLOB lm_resp, nt_resp;
1689 /* This is child-only, so no check for privileged access is needed
1690 anymore */
1692 /* Ensure null termination */
1693 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1694 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1696 if (!check_request_flags(state->request->flags)) {
1697 result = NT_STATUS_INVALID_PARAMETER_MIX;
1698 goto done;
1701 name_user = state->request->data.auth_crap.user;
1703 if (*state->request->data.auth_crap.domain) {
1704 name_domain = state->request->data.auth_crap.domain;
1705 } else if (lp_winbind_use_default_domain()) {
1706 name_domain = lp_workgroup();
1707 } else {
1708 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1709 name_user));
1710 result = NT_STATUS_NO_SUCH_USER;
1711 goto done;
1714 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1715 name_domain, name_user));
1717 if (*state->request->data.auth_crap.workstation) {
1718 workstation = state->request->data.auth_crap.workstation;
1719 } else {
1720 workstation = global_myname();
1723 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1724 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1725 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1726 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1727 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1728 state->request->data.auth_crap.lm_resp_len,
1729 state->request->data.auth_crap.nt_resp_len));
1730 result = NT_STATUS_INVALID_PARAMETER;
1731 goto done;
1735 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1736 state->request->data.auth_crap.lm_resp_len);
1738 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1739 nt_resp = data_blob_talloc(state->mem_ctx,
1740 state->request->extra_data.data,
1741 state->request->data.auth_crap.nt_resp_len);
1742 } else {
1743 nt_resp = data_blob_talloc(state->mem_ctx,
1744 state->request->data.auth_crap.nt_resp,
1745 state->request->data.auth_crap.nt_resp_len);
1748 if (strequal(name_domain, get_global_sam_name())) {
1749 DATA_BLOB chal_blob = data_blob_const(
1750 state->request->data.auth_crap.chal,
1751 sizeof(state->request->data.auth_crap.chal));
1753 result = winbindd_dual_auth_passdb(
1754 state->mem_ctx, name_domain, name_user,
1755 &chal_blob, &lm_resp, &nt_resp, &info3);
1756 goto process_result;
1759 do {
1760 netlogon_fn_t logon_fn;
1762 retry = false;
1764 netlogon_pipe = NULL;
1765 result = cm_connect_netlogon(domain, &netlogon_pipe);
1767 if (!NT_STATUS_IS_OK(result)) {
1768 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1769 nt_errstr(result)));
1770 goto done;
1773 logon_fn = domain->can_do_samlogon_ex
1774 ? rpccli_netlogon_sam_network_logon_ex
1775 : rpccli_netlogon_sam_network_logon;
1777 result = logon_fn(netlogon_pipe,
1778 state->mem_ctx,
1779 state->request->data.auth_crap.logon_parameters,
1780 domain->dcname,
1781 name_user,
1782 name_domain,
1783 /* Bug #3248 - found by Stefan Burkei. */
1784 workstation, /* We carefully set this above so use it... */
1785 state->request->data.auth_crap.chal,
1786 lm_resp,
1787 nt_resp,
1788 &info3);
1790 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1791 && domain->can_do_samlogon_ex) {
1792 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1793 "retrying with NetSamLogon\n"));
1794 domain->can_do_samlogon_ex = false;
1795 retry = true;
1796 continue;
1799 attempts += 1;
1801 /* We have to try a second time as cm_connect_netlogon
1802 might not yet have noticed that the DC has killed
1803 our connection. */
1805 if (!rpccli_is_connected(netlogon_pipe)) {
1806 retry = true;
1807 continue;
1810 /* if we get access denied, a possible cause was that we had and open
1811 connection to the DC, but someone changed our machine account password
1812 out from underneath us using 'net rpc changetrustpw' */
1814 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1815 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1816 "ACCESS_DENIED. Maybe the trust account "
1817 "password was changed and we didn't know it. "
1818 "Killing connections to domain %s\n",
1819 name_domain));
1820 invalidate_cm_connection(&domain->conn);
1821 retry = true;
1824 } while ( (attempts < 2) && retry );
1826 process_result:
1828 if (NT_STATUS_IS_OK(result)) {
1830 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1831 netsamlogon_cache_store(name_user, info3);
1833 /* Check if the user is in the right group */
1835 result = check_info3_in_group(
1836 info3,
1837 state->request->data.auth_crap.require_membership_of_sid);
1838 if (!NT_STATUS_IS_OK(result)) {
1839 DEBUG(3, ("User %s is not in the required group (%s), so "
1840 "crap authentication is rejected\n",
1841 state->request->data.auth_crap.user,
1842 state->request->data.auth_crap.require_membership_of_sid));
1843 goto done;
1846 result = append_auth_data(state, info3, name_domain,
1847 name_user);
1848 if (!NT_STATUS_IS_OK(result)) {
1849 goto done;
1853 done:
1855 /* give us a more useful (more correct?) error code */
1856 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1857 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1858 result = NT_STATUS_NO_LOGON_SERVERS;
1861 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1862 result = nt_status_squash(result);
1865 set_auth_errors(state->response, result);
1867 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1868 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1869 name_domain,
1870 name_user,
1871 state->response->data.auth.nt_status_string,
1872 state->response->data.auth.pam_error));
1874 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1877 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1878 struct winbindd_cli_state *state)
1880 char *oldpass;
1881 char *newpass = NULL;
1882 struct policy_handle dom_pol;
1883 struct rpc_pipe_client *cli = NULL;
1884 bool got_info = false;
1885 struct samr_DomInfo1 *info = NULL;
1886 struct userPwdChangeFailureInformation *reject = NULL;
1887 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1888 fstring domain, user;
1890 ZERO_STRUCT(dom_pol);
1892 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1893 state->request->data.auth.user));
1895 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1896 goto done;
1899 /* Change password */
1901 oldpass = state->request->data.chauthtok.oldpass;
1902 newpass = state->request->data.chauthtok.newpass;
1904 /* Initialize reject reason */
1905 state->response->data.auth.reject_reason = Undefined;
1907 /* Get sam handle */
1909 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1910 &dom_pol);
1911 if (!NT_STATUS_IS_OK(result)) {
1912 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1913 goto done;
1916 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1917 user,
1918 newpass,
1919 oldpass,
1920 &info,
1921 &reject);
1923 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1925 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1927 fill_in_password_policy(state->response, info);
1929 state->response->data.auth.reject_reason =
1930 reject->extendedFailureReason;
1932 got_info = true;
1935 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1936 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1937 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1938 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1940 /* only fallback when the chgpasswd_user3 call is not supported */
1941 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1942 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1943 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1944 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1946 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1947 nt_errstr(result)));
1949 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1951 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1952 Map to the same status code as Windows 2003. */
1954 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1955 result = NT_STATUS_PASSWORD_RESTRICTION;
1959 done:
1961 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1962 if (lp_winbind_offline_logon()) {
1963 result = winbindd_update_creds_by_name(contact_domain,
1964 state->mem_ctx, user,
1965 newpass);
1966 /* Again, this happens when we login from gdm or xdm
1967 * and the password expires, *BUT* cached crendentials
1968 * doesn't exist. winbindd_update_creds_by_name()
1969 * returns NT_STATUS_NO_SUCH_USER.
1970 * This is not a failure.
1971 * --- BoYang
1972 * */
1973 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1974 result = NT_STATUS_OK;
1977 if (!NT_STATUS_IS_OK(result)) {
1978 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1979 goto process_result;
1984 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1986 NTSTATUS policy_ret;
1988 policy_ret = fillup_password_policy(contact_domain, state);
1990 /* failure of this is non critical, it will just provide no
1991 * additional information to the client why the change has
1992 * failed - Guenther */
1994 if (!NT_STATUS_IS_OK(policy_ret)) {
1995 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1996 goto process_result;
2000 process_result:
2002 if (strequal(contact_domain->name, get_global_sam_name())) {
2003 /* FIXME: internal rpc pipe does not cache handles yet */
2004 if (cli) {
2005 if (is_valid_policy_hnd(&dom_pol)) {
2006 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2008 TALLOC_FREE(cli);
2012 set_auth_errors(state->response, result);
2014 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2015 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2016 domain,
2017 user,
2018 state->response->data.auth.nt_status_string,
2019 state->response->data.auth.pam_error));
2021 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2024 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2025 struct winbindd_cli_state *state)
2027 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2029 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2030 state->request->data.logoff.user));
2032 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2033 result = NT_STATUS_OK;
2034 goto process_result;
2037 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2038 result = NT_STATUS_OK;
2039 goto process_result;
2042 #ifdef HAVE_KRB5
2044 if (state->request->data.logoff.uid < 0) {
2045 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2046 goto process_result;
2049 /* what we need here is to find the corresponding krb5 ccache name *we*
2050 * created for a given username and destroy it */
2052 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2053 result = NT_STATUS_OK;
2054 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2055 goto process_result;
2058 if (!ccache_entry_identical(state->request->data.logoff.user,
2059 state->request->data.logoff.uid,
2060 state->request->data.logoff.krb5ccname)) {
2061 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2062 goto process_result;
2065 result = remove_ccache(state->request->data.logoff.user);
2066 if (!NT_STATUS_IS_OK(result)) {
2067 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2068 nt_errstr(result)));
2069 goto process_result;
2072 #else
2073 result = NT_STATUS_NOT_SUPPORTED;
2074 #endif
2076 process_result:
2079 set_auth_errors(state->response, result);
2081 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2084 /* Change user password with auth crap*/
2086 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2088 NTSTATUS result;
2089 DATA_BLOB new_nt_password;
2090 DATA_BLOB old_nt_hash_enc;
2091 DATA_BLOB new_lm_password;
2092 DATA_BLOB old_lm_hash_enc;
2093 fstring domain,user;
2094 struct policy_handle dom_pol;
2095 struct winbindd_domain *contact_domain = domainSt;
2096 struct rpc_pipe_client *cli = NULL;
2098 ZERO_STRUCT(dom_pol);
2100 /* Ensure null termination */
2101 state->request->data.chng_pswd_auth_crap.user[
2102 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2103 state->request->data.chng_pswd_auth_crap.domain[
2104 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2105 *domain = 0;
2106 *user = 0;
2108 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2109 (unsigned long)state->pid,
2110 state->request->data.chng_pswd_auth_crap.domain,
2111 state->request->data.chng_pswd_auth_crap.user));
2113 if (lp_winbind_offline_logon()) {
2114 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2115 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2116 result = NT_STATUS_ACCESS_DENIED;
2117 goto done;
2120 if (*state->request->data.chng_pswd_auth_crap.domain) {
2121 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2122 } else {
2123 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2124 domain, user);
2126 if(!*domain) {
2127 DEBUG(3,("no domain specified with username (%s) - "
2128 "failing auth\n",
2129 state->request->data.chng_pswd_auth_crap.user));
2130 result = NT_STATUS_NO_SUCH_USER;
2131 goto done;
2135 if (!*domain && lp_winbind_use_default_domain()) {
2136 fstrcpy(domain,(char *)lp_workgroup());
2139 if(!*user) {
2140 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2143 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2144 (unsigned long)state->pid, domain, user));
2146 /* Change password */
2147 new_nt_password = data_blob_const(
2148 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2149 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2151 old_nt_hash_enc = data_blob_const(
2152 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2153 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2155 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2156 new_lm_password = data_blob_const(
2157 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2158 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2160 old_lm_hash_enc = data_blob_const(
2161 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2162 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2163 } else {
2164 new_lm_password.length = 0;
2165 old_lm_hash_enc.length = 0;
2168 /* Get sam handle */
2170 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2171 if (!NT_STATUS_IS_OK(result)) {
2172 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2173 goto done;
2176 result = rpccli_samr_chng_pswd_auth_crap(
2177 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2178 new_lm_password, old_lm_hash_enc);
2180 done:
2182 if (strequal(contact_domain->name, get_global_sam_name())) {
2183 /* FIXME: internal rpc pipe does not cache handles yet */
2184 if (cli) {
2185 if (is_valid_policy_hnd(&dom_pol)) {
2186 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2188 TALLOC_FREE(cli);
2192 set_auth_errors(state->response, result);
2194 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2195 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2196 domain, user,
2197 state->response->data.auth.nt_status_string,
2198 state->response->data.auth.pam_error));
2200 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;