tdb/tools: add -l option to tdbtool
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob415dc79974c90f7818ca7a026d30414ae824da49
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/ndr_samr_c.h"
29 #include "rpc_client/cli_pipe.h"
30 #include "rpc_client/cli_samr.h"
31 #include "../librpc/gen_ndr/ndr_netlogon.h"
32 #include "rpc_client/cli_netlogon.h"
33 #include "smb_krb5.h"
34 #include "../lib/crypto/arcfour.h"
35 #include "../libcli/security/security.h"
36 #include "ads.h"
37 #include "../librpc/gen_ndr/krb5pac.h"
38 #include "passdb/machine_sid.h"
39 #include "auth.h"
40 #include "../lib/tsocket/tsocket.h"
41 #include "auth/kerberos/pac_utils.h"
42 #include "auth/gensec/gensec.h"
43 #include "librpc/crypto/gse_krb5.h"
45 #undef DBGC_CLASS
46 #define DBGC_CLASS DBGC_WINBIND
48 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
50 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
51 struct winbindd_response *resp,
52 struct netr_SamInfo3 *info3)
54 char *ex;
55 uint32_t i;
57 resp->data.auth.info3.logon_time =
58 nt_time_to_unix(info3->base.logon_time);
59 resp->data.auth.info3.logoff_time =
60 nt_time_to_unix(info3->base.logoff_time);
61 resp->data.auth.info3.kickoff_time =
62 nt_time_to_unix(info3->base.kickoff_time);
63 resp->data.auth.info3.pass_last_set_time =
64 nt_time_to_unix(info3->base.last_password_change);
65 resp->data.auth.info3.pass_can_change_time =
66 nt_time_to_unix(info3->base.allow_password_change);
67 resp->data.auth.info3.pass_must_change_time =
68 nt_time_to_unix(info3->base.force_password_change);
70 resp->data.auth.info3.logon_count = info3->base.logon_count;
71 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
73 resp->data.auth.info3.user_rid = info3->base.rid;
74 resp->data.auth.info3.group_rid = info3->base.primary_gid;
75 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
77 resp->data.auth.info3.num_groups = info3->base.groups.count;
78 resp->data.auth.info3.user_flgs = info3->base.user_flags;
80 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
81 resp->data.auth.info3.num_other_sids = info3->sidcount;
83 fstrcpy(resp->data.auth.info3.user_name,
84 info3->base.account_name.string);
85 fstrcpy(resp->data.auth.info3.full_name,
86 info3->base.full_name.string);
87 fstrcpy(resp->data.auth.info3.logon_script,
88 info3->base.logon_script.string);
89 fstrcpy(resp->data.auth.info3.profile_path,
90 info3->base.profile_path.string);
91 fstrcpy(resp->data.auth.info3.home_dir,
92 info3->base.home_directory.string);
93 fstrcpy(resp->data.auth.info3.dir_drive,
94 info3->base.home_drive.string);
96 fstrcpy(resp->data.auth.info3.logon_srv,
97 info3->base.logon_server.string);
98 fstrcpy(resp->data.auth.info3.logon_dom,
99 info3->base.logon_domain.string);
101 ex = talloc_strdup(mem_ctx, "");
102 NT_STATUS_HAVE_NO_MEMORY(ex);
104 for (i=0; i < info3->base.groups.count; i++) {
105 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
106 info3->base.groups.rids[i].rid,
107 info3->base.groups.rids[i].attributes);
108 NT_STATUS_HAVE_NO_MEMORY(ex);
111 for (i=0; i < info3->sidcount; i++) {
112 char *sid;
114 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
115 NT_STATUS_HAVE_NO_MEMORY(sid);
117 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
118 sid,
119 info3->sids[i].attributes);
120 NT_STATUS_HAVE_NO_MEMORY(ex);
122 talloc_free(sid);
125 resp->extra_data.data = ex;
126 resp->length += talloc_get_size(ex);
128 return NT_STATUS_OK;
131 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
132 struct winbindd_response *resp,
133 struct netr_SamInfo3 *info3)
135 DATA_BLOB blob;
136 enum ndr_err_code ndr_err;
138 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
139 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
140 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
141 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
142 return ndr_map_error2ntstatus(ndr_err);
145 resp->extra_data.data = blob.data;
146 resp->length += blob.length;
148 return NT_STATUS_OK;
151 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
152 struct winbindd_response *resp,
153 const struct netr_SamInfo3 *info3,
154 const char *name_domain,
155 const char *name_user)
157 /* We've been asked to return the unix username, per
158 'winbind use default domain' settings and the like */
160 const char *nt_username, *nt_domain;
162 nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
163 if (!nt_domain) {
164 /* If the server didn't give us one, just use the one
165 * we sent them */
166 nt_domain = name_domain;
169 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
170 if (!nt_username) {
171 /* If the server didn't give us one, just use the one
172 * we sent them */
173 nt_username = name_user;
176 fill_domain_username(resp->data.auth.unix_username,
177 nt_domain, nt_username, true);
179 DEBUG(5, ("Setting unix username to [%s]\n",
180 resp->data.auth.unix_username));
182 return NT_STATUS_OK;
185 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
186 struct winbindd_response *resp,
187 const struct netr_SamInfo3 *info3,
188 const char *name_domain,
189 const char *name_user)
191 char *afsname = NULL;
192 char *cell;
193 char *token;
195 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
196 if (afsname == NULL) {
197 return NT_STATUS_NO_MEMORY;
200 afsname = talloc_string_sub(mem_ctx,
201 lp_afs_username_map(),
202 "%D", name_domain);
203 afsname = talloc_string_sub(mem_ctx, afsname,
204 "%u", name_user);
205 afsname = talloc_string_sub(mem_ctx, afsname,
206 "%U", name_user);
209 struct dom_sid user_sid;
210 fstring sidstr;
212 sid_compose(&user_sid, info3->base.domain_sid,
213 info3->base.rid);
214 sid_to_fstring(sidstr, &user_sid);
215 afsname = talloc_string_sub(mem_ctx, afsname,
216 "%s", sidstr);
219 if (afsname == NULL) {
220 return NT_STATUS_NO_MEMORY;
223 if (!strlower_m(afsname)) {
224 return NT_STATUS_INVALID_PARAMETER;
227 DEBUG(10, ("Generating token for user %s\n", afsname));
229 cell = strchr(afsname, '@');
231 if (cell == NULL) {
232 return NT_STATUS_NO_MEMORY;
235 *cell = '\0';
236 cell += 1;
238 token = afs_createtoken_str(afsname, cell);
239 if (token == NULL) {
240 return NT_STATUS_OK;
242 resp->extra_data.data = talloc_strdup(mem_ctx, token);
243 if (resp->extra_data.data == NULL) {
244 return NT_STATUS_NO_MEMORY;
246 resp->length += strlen((const char *)resp->extra_data.data)+1;
248 return NT_STATUS_OK;
251 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
252 const char *group_sid)
254 * Check whether a user belongs to a group or list of groups.
256 * @param mem_ctx talloc memory context.
257 * @param info3 user information, including group membership info.
258 * @param group_sid One or more groups , separated by commas.
260 * @return NT_STATUS_OK on success,
261 * NT_STATUS_LOGON_FAILURE if the user does not belong,
262 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
265 struct dom_sid *require_membership_of_sid;
266 uint32_t num_require_membership_of_sid;
267 char *req_sid;
268 const char *p;
269 struct dom_sid sid;
270 size_t i;
271 struct security_token *token;
272 TALLOC_CTX *frame = talloc_stackframe();
273 NTSTATUS status;
275 /* Parse the 'required group' SID */
277 if (!group_sid || !group_sid[0]) {
278 /* NO sid supplied, all users may access */
279 TALLOC_FREE(frame);
280 return NT_STATUS_OK;
283 token = talloc_zero(talloc_tos(), struct security_token);
284 if (token == NULL) {
285 DEBUG(0, ("talloc failed\n"));
286 TALLOC_FREE(frame);
287 return NT_STATUS_NO_MEMORY;
290 num_require_membership_of_sid = 0;
291 require_membership_of_sid = NULL;
293 p = group_sid;
295 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
296 if (!string_to_sid(&sid, req_sid)) {
297 DEBUG(0, ("check_info3_in_group: could not parse %s "
298 "as a SID!", req_sid));
299 TALLOC_FREE(frame);
300 return NT_STATUS_INVALID_PARAMETER;
303 status = add_sid_to_array(talloc_tos(), &sid,
304 &require_membership_of_sid,
305 &num_require_membership_of_sid);
306 if (!NT_STATUS_IS_OK(status)) {
307 DEBUG(0, ("add_sid_to_array failed\n"));
308 TALLOC_FREE(frame);
309 return status;
313 status = sid_array_from_info3(talloc_tos(), info3,
314 &token->sids,
315 &token->num_sids,
316 true);
317 if (!NT_STATUS_IS_OK(status)) {
318 TALLOC_FREE(frame);
319 return status;
322 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
323 token))
324 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
325 token))) {
326 DEBUG(3, ("could not add aliases: %s\n",
327 nt_errstr(status)));
328 TALLOC_FREE(frame);
329 return status;
332 security_token_debug(DBGC_CLASS, 10, token);
334 for (i=0; i<num_require_membership_of_sid; i++) {
335 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
336 &require_membership_of_sid[i])));
337 if (nt_token_check_sid(&require_membership_of_sid[i],
338 token)) {
339 DEBUG(10, ("Access ok\n"));
340 TALLOC_FREE(frame);
341 return NT_STATUS_OK;
345 /* Do not distinguish this error from a wrong username/pw */
347 TALLOC_FREE(frame);
348 return NT_STATUS_LOGON_FAILURE;
351 struct winbindd_domain *find_auth_domain(uint8_t flags,
352 const char *domain_name)
354 struct winbindd_domain *domain;
356 if (IS_DC) {
357 domain = find_domain_from_name_noinit(domain_name);
358 if (domain == NULL) {
359 DEBUG(3, ("Authentication for domain [%s] refused "
360 "as it is not a trusted domain\n",
361 domain_name));
363 return domain;
366 if (strequal(domain_name, get_global_sam_name())) {
367 return find_domain_from_name_noinit(domain_name);
370 /* we can auth against trusted domains */
371 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
372 domain = find_domain_from_name_noinit(domain_name);
373 if (domain == NULL) {
374 DEBUG(3, ("Authentication for domain [%s] skipped "
375 "as it is not a trusted domain\n",
376 domain_name));
377 } else {
378 return domain;
382 return find_our_domain();
385 static void fill_in_password_policy(struct winbindd_response *r,
386 const struct samr_DomInfo1 *p)
388 r->data.auth.policy.min_length_password =
389 p->min_password_length;
390 r->data.auth.policy.password_history =
391 p->password_history_length;
392 r->data.auth.policy.password_properties =
393 p->password_properties;
394 r->data.auth.policy.expire =
395 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
396 r->data.auth.policy.min_passwordage =
397 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
400 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
401 struct winbindd_response *response)
403 TALLOC_CTX *frame = talloc_stackframe();
404 struct winbindd_methods *methods;
405 NTSTATUS status;
406 struct samr_DomInfo1 password_policy;
408 if ( !winbindd_can_contact_domain( domain ) ) {
409 DEBUG(5,("fillup_password_policy: No inbound trust to "
410 "contact domain %s\n", domain->name));
411 status = NT_STATUS_NOT_SUPPORTED;
412 goto done;
415 methods = domain->methods;
417 status = methods->password_policy(domain, talloc_tos(), &password_policy);
418 if (NT_STATUS_IS_ERR(status)) {
419 goto done;
422 fill_in_password_policy(response, &password_policy);
424 done:
425 TALLOC_FREE(frame);
426 return NT_STATUS_OK;
429 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
430 TALLOC_CTX *mem_ctx,
431 uint16 *lockout_threshold)
433 struct winbindd_methods *methods;
434 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
435 struct samr_DomInfo12 lockout_policy;
437 *lockout_threshold = 0;
439 methods = domain->methods;
441 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
442 if (NT_STATUS_IS_ERR(status)) {
443 return status;
446 *lockout_threshold = lockout_policy.lockout_threshold;
448 return NT_STATUS_OK;
451 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
452 TALLOC_CTX *mem_ctx,
453 uint32 *password_properties)
455 struct winbindd_methods *methods;
456 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
457 struct samr_DomInfo1 password_policy;
459 *password_properties = 0;
461 methods = domain->methods;
463 status = methods->password_policy(domain, mem_ctx, &password_policy);
464 if (NT_STATUS_IS_ERR(status)) {
465 return status;
468 *password_properties = password_policy.password_properties;
470 return NT_STATUS_OK;
473 #ifdef HAVE_KRB5
475 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
476 const char *type,
477 uid_t uid,
478 const char **user_ccache_file)
480 /* accept FILE and WRFILE as krb5_cc_type from the client and then
481 * build the full ccname string based on the user's uid here -
482 * Guenther*/
484 const char *gen_cc = NULL;
486 if (uid != -1) {
487 if (strequal(type, "FILE")) {
488 gen_cc = talloc_asprintf(
489 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
491 if (strequal(type, "WRFILE")) {
492 gen_cc = talloc_asprintf(
493 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
495 if (strequal(type, "KEYRING")) {
496 gen_cc = talloc_asprintf(
497 mem_ctx, "KEYRING:persistent:%d", uid);
500 if (strnequal(type, "FILE:/", 6) ||
501 strnequal(type, "WRFILE:/", 8) ||
502 strnequal(type, "DIR:/", 5)) {
504 /* we allow only one "%u" substitution */
506 char *p;
508 p = strchr(type, '%');
509 if (p != NULL) {
511 p++;
513 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
514 gen_cc = talloc_asprintf(mem_ctx, type, uid);
520 *user_ccache_file = gen_cc;
522 if (gen_cc == NULL) {
523 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
525 if (gen_cc == NULL) {
526 DEBUG(0,("out of memory\n"));
527 return NULL;
530 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
531 (*user_ccache_file == NULL) ? " (internal)":""));
533 return gen_cc;
536 #endif
538 uid_t get_uid_from_request(struct winbindd_request *request)
540 uid_t uid;
542 uid = request->data.auth.uid;
544 if (uid < 0) {
545 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
546 return -1;
548 return uid;
551 /**********************************************************************
552 Authenticate a user with a clear text password using Kerberos and fill up
553 ccache if required
554 **********************************************************************/
556 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
557 struct winbindd_domain *domain,
558 const char *user,
559 const char *pass,
560 const char *krb5_cc_type,
561 uid_t uid,
562 struct netr_SamInfo3 **info3,
563 fstring krb5ccname)
565 #ifdef HAVE_KRB5
566 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
567 krb5_error_code krb5_ret;
568 const char *cc = NULL;
569 const char *principal_s = NULL;
570 const char *service = NULL;
571 char *realm = NULL;
572 fstring name_domain, name_user;
573 time_t ticket_lifetime = 0;
574 time_t renewal_until = 0;
575 ADS_STRUCT *ads;
576 time_t time_offset = 0;
577 const char *user_ccache_file;
578 struct PAC_LOGON_INFO *logon_info = NULL;
579 struct PAC_DATA *pac_data = NULL;
580 struct PAC_DATA_CTR *pac_data_ctr = NULL;
581 const char *local_service;
582 int i;
584 *info3 = NULL;
586 if (domain->alt_name == NULL) {
587 return NT_STATUS_INVALID_PARAMETER;
590 /* 1st step:
591 * prepare a krb5_cc_cache string for the user */
593 if (uid == -1) {
594 DEBUG(0,("no valid uid\n"));
597 cc = generate_krb5_ccache(mem_ctx,
598 krb5_cc_type,
599 uid,
600 &user_ccache_file);
601 if (cc == NULL) {
602 return NT_STATUS_NO_MEMORY;
606 /* 2nd step:
607 * get kerberos properties */
609 if (domain->private_data) {
610 ads = (ADS_STRUCT *)domain->private_data;
611 time_offset = ads->auth.time_offset;
615 /* 3rd step:
616 * do kerberos auth and setup ccache as the user */
618 parse_domain_user(user, name_domain, name_user);
620 realm = talloc_strdup(mem_ctx, domain->alt_name);
621 if (realm == NULL) {
622 return NT_STATUS_NO_MEMORY;
625 if (!strupper_m(realm)) {
626 return NT_STATUS_INVALID_PARAMETER;
629 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
630 if (principal_s == NULL) {
631 return NT_STATUS_NO_MEMORY;
634 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
635 if (service == NULL) {
636 return NT_STATUS_NO_MEMORY;
639 local_service = talloc_asprintf(mem_ctx, "%s$@%s",
640 lp_netbios_name(), lp_realm());
641 if (local_service == NULL) {
642 return NT_STATUS_NO_MEMORY;
646 /* if this is a user ccache, we need to act as the user to let the krb5
647 * library handle the chown, etc. */
649 /************************ ENTERING NON-ROOT **********************/
651 if (user_ccache_file != NULL) {
652 set_effective_uid(uid);
653 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
656 result = kerberos_return_pac(mem_ctx,
657 principal_s,
658 pass,
659 time_offset,
660 &ticket_lifetime,
661 &renewal_until,
663 true,
664 true,
665 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
666 NULL,
667 local_service,
668 &pac_data_ctr);
669 if (user_ccache_file != NULL) {
670 gain_root_privilege();
673 /************************ RETURNED TO ROOT **********************/
675 if (!NT_STATUS_IS_OK(result)) {
676 goto failed;
679 if (pac_data_ctr == NULL) {
680 goto failed;
683 pac_data = pac_data_ctr->pac_data;
684 if (pac_data == NULL) {
685 goto failed;
688 for (i=0; i < pac_data->num_buffers; i++) {
690 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
691 continue;
694 logon_info = pac_data->buffers[i].info->logon_info.info;
695 if (!logon_info) {
696 return NT_STATUS_INVALID_PARAMETER;
699 break;
702 *info3 = &logon_info->info3;
704 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
705 principal_s));
707 /* if we had a user's ccache then return that string for the pam
708 * environment */
710 if (user_ccache_file != NULL) {
712 fstrcpy(krb5ccname, user_ccache_file);
714 result = add_ccache_to_list(principal_s,
716 service,
717 user,
718 pass,
719 realm,
720 uid,
721 time(NULL),
722 ticket_lifetime,
723 renewal_until,
724 false);
726 if (!NT_STATUS_IS_OK(result)) {
727 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
728 nt_errstr(result)));
730 } else {
732 /* need to delete the memory cred cache, it is not used anymore */
734 krb5_ret = ads_kdestroy(cc);
735 if (krb5_ret) {
736 DEBUG(3,("winbindd_raw_kerberos_login: "
737 "could not destroy krb5 credential cache: "
738 "%s\n", error_message(krb5_ret)));
743 return NT_STATUS_OK;
745 failed:
747 * Do not delete an existing valid credential cache, if the user
748 * e.g. enters a wrong password
750 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
751 && user_ccache_file != NULL) {
752 return result;
755 /* we could have created a new credential cache with a valid tgt in it
756 * but we werent able to get or verify the service ticket for this
757 * local host and therefor didn't get the PAC, we need to remove that
758 * cache entirely now */
760 krb5_ret = ads_kdestroy(cc);
761 if (krb5_ret) {
762 DEBUG(3,("winbindd_raw_kerberos_login: "
763 "could not destroy krb5 credential cache: "
764 "%s\n", error_message(krb5_ret)));
767 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
768 DEBUG(3,("winbindd_raw_kerberos_login: "
769 "could not remove ccache for user %s\n",
770 user));
773 return result;
774 #else
775 return NT_STATUS_NOT_SUPPORTED;
776 #endif /* HAVE_KRB5 */
779 /****************************************************************
780 ****************************************************************/
782 bool check_request_flags(uint32_t flags)
784 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
785 WBFLAG_PAM_INFO3_TEXT |
786 WBFLAG_PAM_INFO3_NDR;
788 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
789 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
790 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
791 !(flags & flags_edata) ) {
792 return true;
795 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
796 flags));
798 return false;
801 /****************************************************************
802 ****************************************************************/
804 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
805 struct winbindd_response *resp,
806 uint32_t request_flags,
807 struct netr_SamInfo3 *info3,
808 const char *name_domain,
809 const char *name_user)
811 NTSTATUS result;
813 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
814 memcpy(resp->data.auth.user_session_key,
815 info3->base.key.key,
816 sizeof(resp->data.auth.user_session_key)
817 /* 16 */);
820 if (request_flags & WBFLAG_PAM_LMKEY) {
821 memcpy(resp->data.auth.first_8_lm_hash,
822 info3->base.LMSessKey.key,
823 sizeof(resp->data.auth.first_8_lm_hash)
824 /* 8 */);
827 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
828 result = append_unix_username(mem_ctx, resp,
829 info3, name_domain, name_user);
830 if (!NT_STATUS_IS_OK(result)) {
831 DEBUG(10,("Failed to append Unix Username: %s\n",
832 nt_errstr(result)));
833 return result;
837 /* currently, anything from here on potentially overwrites extra_data. */
839 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
840 result = append_info3_as_ndr(mem_ctx, resp, info3);
841 if (!NT_STATUS_IS_OK(result)) {
842 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
843 nt_errstr(result)));
844 return result;
848 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
849 result = append_info3_as_txt(mem_ctx, resp, info3);
850 if (!NT_STATUS_IS_OK(result)) {
851 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
852 nt_errstr(result)));
853 return result;
857 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
858 result = append_afs_token(mem_ctx, resp,
859 info3, name_domain, name_user);
860 if (!NT_STATUS_IS_OK(result)) {
861 DEBUG(10,("Failed to append AFS token: %s\n",
862 nt_errstr(result)));
863 return result;
867 return NT_STATUS_OK;
870 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
871 struct winbindd_cli_state *state,
872 struct netr_SamInfo3 **info3)
874 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
875 uint16 max_allowed_bad_attempts;
876 fstring name_domain, name_user;
877 struct dom_sid sid;
878 enum lsa_SidType type;
879 uchar new_nt_pass[NT_HASH_LEN];
880 const uint8 *cached_nt_pass;
881 const uint8 *cached_salt;
882 struct netr_SamInfo3 *my_info3;
883 time_t kickoff_time, must_change_time;
884 bool password_good = false;
885 #ifdef HAVE_KRB5
886 struct winbindd_tdc_domain *tdc_domain = NULL;
887 #endif
889 *info3 = NULL;
891 ZERO_STRUCTP(info3);
893 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
895 /* Parse domain and username */
897 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
900 if (!lookup_cached_name(name_domain,
901 name_user,
902 &sid,
903 &type)) {
904 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
905 return NT_STATUS_NO_SUCH_USER;
908 if (type != SID_NAME_USER) {
909 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
910 return NT_STATUS_LOGON_FAILURE;
913 result = winbindd_get_creds(domain,
914 state->mem_ctx,
915 &sid,
916 &my_info3,
917 &cached_nt_pass,
918 &cached_salt);
919 if (!NT_STATUS_IS_OK(result)) {
920 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
921 return result;
924 *info3 = my_info3;
926 E_md4hash(state->request->data.auth.pass, new_nt_pass);
928 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
929 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
930 if (cached_salt) {
931 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
934 if (cached_salt) {
935 /* In this case we didn't store the nt_hash itself,
936 but the MD5 combination of salt + nt_hash. */
937 uchar salted_hash[NT_HASH_LEN];
938 E_md5hash(cached_salt, new_nt_pass, salted_hash);
940 password_good = (memcmp(cached_nt_pass, salted_hash,
941 NT_HASH_LEN) == 0);
942 } else {
943 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
944 password_good = (memcmp(cached_nt_pass, new_nt_pass,
945 NT_HASH_LEN) == 0);
948 if (password_good) {
950 /* User *DOES* know the password, update logon_time and reset
951 * bad_pw_count */
953 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
955 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
956 return NT_STATUS_ACCOUNT_LOCKED_OUT;
959 if (my_info3->base.acct_flags & ACB_DISABLED) {
960 return NT_STATUS_ACCOUNT_DISABLED;
963 if (my_info3->base.acct_flags & ACB_WSTRUST) {
964 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
967 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
968 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
971 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
972 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
975 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
976 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
977 my_info3->base.acct_flags));
978 return NT_STATUS_LOGON_FAILURE;
981 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
982 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
983 return NT_STATUS_ACCOUNT_EXPIRED;
986 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
987 if (must_change_time != 0 && must_change_time < time(NULL)) {
988 /* we allow grace logons when the password has expired */
989 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
990 /* return NT_STATUS_PASSWORD_EXPIRED; */
991 goto success;
994 #ifdef HAVE_KRB5
995 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
996 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
997 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
998 /* used to cope with the case winbindd starting without network. */
999 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
1001 uid_t uid = -1;
1002 const char *cc = NULL;
1003 char *realm = NULL;
1004 const char *principal_s = NULL;
1005 const char *service = NULL;
1006 const char *user_ccache_file;
1008 if (domain->alt_name == NULL) {
1009 return NT_STATUS_INVALID_PARAMETER;
1012 uid = get_uid_from_request(state->request);
1013 if (uid == -1) {
1014 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1015 return NT_STATUS_INVALID_PARAMETER;
1018 cc = generate_krb5_ccache(state->mem_ctx,
1019 state->request->data.auth.krb5_cc_type,
1020 state->request->data.auth.uid,
1021 &user_ccache_file);
1022 if (cc == NULL) {
1023 return NT_STATUS_NO_MEMORY;
1026 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
1027 if (realm == NULL) {
1028 return NT_STATUS_NO_MEMORY;
1031 if (!strupper_m(realm)) {
1032 return NT_STATUS_INVALID_PARAMETER;
1035 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1036 if (principal_s == NULL) {
1037 return NT_STATUS_NO_MEMORY;
1040 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1041 if (service == NULL) {
1042 return NT_STATUS_NO_MEMORY;
1045 if (user_ccache_file != NULL) {
1047 fstrcpy(state->response->data.auth.krb5ccname,
1048 user_ccache_file);
1050 result = add_ccache_to_list(principal_s,
1052 service,
1053 state->request->data.auth.user,
1054 state->request->data.auth.pass,
1055 realm,
1056 uid,
1057 time(NULL),
1058 time(NULL) + lp_winbind_cache_time(),
1059 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1060 true);
1062 if (!NT_STATUS_IS_OK(result)) {
1063 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1064 "to add ccache to list: %s\n",
1065 nt_errstr(result)));
1069 #endif /* HAVE_KRB5 */
1070 success:
1071 /* FIXME: we possibly should handle logon hours as well (does xp when
1072 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1074 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1075 my_info3->base.bad_password_count = 0;
1077 result = winbindd_update_creds_by_info3(domain,
1078 state->request->data.auth.user,
1079 state->request->data.auth.pass,
1080 my_info3);
1081 if (!NT_STATUS_IS_OK(result)) {
1082 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1083 nt_errstr(result)));
1084 return result;
1087 return NT_STATUS_OK;
1091 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1092 if (domain->online == false) {
1093 goto failed;
1096 /* failure of this is not critical */
1097 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1098 if (!NT_STATUS_IS_OK(result)) {
1099 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1100 "Won't be able to honour account lockout policies\n"));
1103 /* increase counter */
1104 my_info3->base.bad_password_count++;
1106 if (max_allowed_bad_attempts == 0) {
1107 goto failed;
1110 /* lockout user */
1111 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1113 uint32 password_properties;
1115 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1116 if (!NT_STATUS_IS_OK(result)) {
1117 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1120 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1121 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1122 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1126 failed:
1127 result = winbindd_update_creds_by_info3(domain,
1128 state->request->data.auth.user,
1129 NULL,
1130 my_info3);
1132 if (!NT_STATUS_IS_OK(result)) {
1133 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1134 nt_errstr(result)));
1137 return NT_STATUS_LOGON_FAILURE;
1140 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1141 struct winbindd_cli_state *state,
1142 struct netr_SamInfo3 **info3)
1144 struct winbindd_domain *contact_domain;
1145 fstring name_domain, name_user;
1146 NTSTATUS result;
1148 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1150 /* Parse domain and username */
1152 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1154 /* what domain should we contact? */
1156 if ( IS_DC ) {
1157 if (!(contact_domain = find_domain_from_name(name_domain))) {
1158 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1159 state->request->data.auth.user, name_domain, name_user, name_domain));
1160 result = NT_STATUS_NO_SUCH_USER;
1161 goto done;
1164 } else {
1165 if (is_myname(name_domain)) {
1166 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1167 result = NT_STATUS_NO_SUCH_USER;
1168 goto done;
1171 contact_domain = find_domain_from_name(name_domain);
1172 if (contact_domain == NULL) {
1173 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1174 state->request->data.auth.user, name_domain, name_user, name_domain));
1176 result = NT_STATUS_NO_SUCH_USER;
1177 goto done;
1181 if (contact_domain->initialized &&
1182 contact_domain->active_directory) {
1183 goto try_login;
1186 if (!contact_domain->initialized) {
1187 init_dc_connection(contact_domain);
1190 if (!contact_domain->active_directory) {
1191 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1192 return NT_STATUS_INVALID_LOGON_TYPE;
1194 try_login:
1195 result = winbindd_raw_kerberos_login(
1196 state->mem_ctx, contact_domain,
1197 state->request->data.auth.user,
1198 state->request->data.auth.pass,
1199 state->request->data.auth.krb5_cc_type,
1200 get_uid_from_request(state->request),
1201 info3, state->response->data.auth.krb5ccname);
1202 done:
1203 return result;
1206 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1207 uint32_t logon_parameters,
1208 const char *domain, const char *user,
1209 const DATA_BLOB *challenge,
1210 const DATA_BLOB *lm_resp,
1211 const DATA_BLOB *nt_resp,
1212 struct netr_SamInfo3 **pinfo3)
1214 struct auth_usersupplied_info *user_info = NULL;
1215 struct tsocket_address *local;
1216 NTSTATUS status;
1217 int rc;
1218 TALLOC_CTX *frame = talloc_stackframe();
1220 rc = tsocket_address_inet_from_strings(mem_ctx,
1221 "ip",
1222 "127.0.0.1",
1224 &local);
1225 if (rc < 0) {
1226 TALLOC_FREE(frame);
1227 return NT_STATUS_NO_MEMORY;
1229 status = make_user_info(frame, &user_info, user, user, domain, domain,
1230 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1231 NULL, AUTH_PASSWORD_RESPONSE);
1232 if (!NT_STATUS_IS_OK(status)) {
1233 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1234 TALLOC_FREE(frame);
1235 return status;
1237 user_info->logon_parameters = logon_parameters;
1239 /* We don't want any more mapping of the username */
1240 user_info->mapped_state = True;
1242 status = check_sam_security_info3(challenge, mem_ctx, user_info,
1243 pinfo3);
1244 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1245 user, nt_errstr(status)));
1246 TALLOC_FREE(frame);
1247 return status;
1250 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1251 TALLOC_CTX *mem_ctx,
1252 uint32_t logon_parameters,
1253 const char *server,
1254 const char *username,
1255 const char *domainname,
1256 const char *workstation,
1257 const uint8_t chal[8],
1258 DATA_BLOB lm_response,
1259 DATA_BLOB nt_response,
1260 struct netr_SamInfo3 **info3)
1262 int attempts = 0;
1263 int netr_attempts = 0;
1264 bool retry = false;
1265 NTSTATUS result;
1267 do {
1268 struct rpc_pipe_client *netlogon_pipe;
1269 uint8_t authoritative = 0;
1270 uint32_t flags = 0;
1272 ZERO_STRUCTP(info3);
1273 retry = false;
1275 result = cm_connect_netlogon(domain, &netlogon_pipe);
1277 if (!NT_STATUS_IS_OK(result)) {
1278 DEBUG(3,("Could not open handle to NETLOGON pipe "
1279 "(error: %s, attempts: %d)\n",
1280 nt_errstr(result), netr_attempts));
1282 /* After the first retry always close the connection */
1283 if (netr_attempts > 0) {
1284 DEBUG(3, ("This is again a problem for this "
1285 "particular call, forcing the close "
1286 "of this connection\n"));
1287 invalidate_cm_connection(&domain->conn);
1290 /* After the second retry failover to the next DC */
1291 if (netr_attempts > 1) {
1293 * If the netlogon server is not reachable then
1294 * it is possible that the DC is rebuilding
1295 * sysvol and shutdown netlogon for that time.
1296 * We should failover to the next dc.
1298 DEBUG(3, ("This is the third problem for this "
1299 "particular call, adding DC to the "
1300 "negative cache list\n"));
1301 add_failed_connection_entry(domain->name,
1302 domain->dcname,
1303 result);
1304 saf_delete(domain->name);
1307 /* Only allow 3 retries */
1308 if (netr_attempts < 3) {
1309 DEBUG(3, ("The connection to netlogon "
1310 "failed, retrying\n"));
1311 netr_attempts++;
1312 retry = true;
1313 continue;
1315 return result;
1317 netr_attempts = 0;
1319 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1320 netlogon_pipe->binding_handle,
1321 mem_ctx,
1322 logon_parameters,
1323 username,
1324 domainname,
1325 workstation,
1326 chal,
1327 lm_response,
1328 nt_response,
1329 &authoritative,
1330 &flags,
1331 info3);
1334 * we increment this after the "feature negotiation"
1335 * for can_do_samlogon_ex and can_do_validation6
1337 attempts += 1;
1339 /* We have to try a second time as cm_connect_netlogon
1340 might not yet have noticed that the DC has killed
1341 our connection. */
1343 if (!rpccli_is_connected(netlogon_pipe)) {
1344 retry = true;
1345 continue;
1348 /* if we get access denied, a possible cause was that we had
1349 and open connection to the DC, but someone changed our
1350 machine account password out from underneath us using 'net
1351 rpc changetrustpw' */
1353 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1354 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1355 "ACCESS_DENIED. Maybe the trust account "
1356 "password was changed and we didn't know it. "
1357 "Killing connections to domain %s\n",
1358 domainname));
1359 invalidate_cm_connection(&domain->conn);
1360 retry = true;
1363 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1365 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1366 * (no Ex). This happens against old Samba
1367 * DCs, if LogonSamLogonEx() fails with an error
1368 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1370 * The server will log something like this:
1371 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1373 * This sets the whole connection into a fault_state mode
1374 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1376 * This also happens to our retry with LogonSamLogonWithFlags()
1377 * and LogonSamLogon().
1379 * In order to recover from this situation, we need to
1380 * drop the connection.
1382 invalidate_cm_connection(&domain->conn);
1383 result = NT_STATUS_LOGON_FAILURE;
1384 break;
1387 } while ( (attempts < 2) && retry );
1389 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1390 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1391 "returned NT_STATUS_IO_TIMEOUT after the retry."
1392 "Killing connections to domain %s\n",
1393 domainname));
1394 invalidate_cm_connection(&domain->conn);
1396 return result;
1399 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1400 struct winbindd_domain *domain,
1401 const char *user,
1402 const char *pass,
1403 uint32_t request_flags,
1404 struct netr_SamInfo3 **info3)
1407 uchar chal[8];
1408 DATA_BLOB lm_resp;
1409 DATA_BLOB nt_resp;
1410 unsigned char local_nt_response[24];
1411 fstring name_domain, name_user;
1412 NTSTATUS result;
1413 struct netr_SamInfo3 *my_info3 = NULL;
1415 *info3 = NULL;
1417 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1419 /* Parse domain and username */
1421 parse_domain_user(user, name_domain, name_user);
1423 /* do password magic */
1425 generate_random_buffer(chal, sizeof(chal));
1427 if (lp_client_ntlmv2_auth()) {
1428 DATA_BLOB server_chal;
1429 DATA_BLOB names_blob;
1430 server_chal = data_blob_const(chal, 8);
1432 /* note that the 'workgroup' here is for the local
1433 machine. The 'server name' must match the
1434 'workstation' passed to the actual SamLogon call.
1436 names_blob = NTLMv2_generate_names_blob(
1437 mem_ctx, lp_netbios_name(), lp_workgroup());
1439 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1440 pass,
1441 &server_chal,
1442 &names_blob,
1443 &lm_resp, &nt_resp, NULL, NULL)) {
1444 data_blob_free(&names_blob);
1445 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1446 result = NT_STATUS_NO_MEMORY;
1447 goto done;
1449 data_blob_free(&names_blob);
1450 } else {
1451 lm_resp = data_blob_null;
1452 SMBNTencrypt(pass, chal, local_nt_response);
1454 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1455 sizeof(local_nt_response));
1458 if (strequal(name_domain, get_global_sam_name())) {
1459 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1461 result = winbindd_dual_auth_passdb(
1462 mem_ctx, 0, name_domain, name_user,
1463 &chal_blob, &lm_resp, &nt_resp, info3);
1464 goto done;
1467 /* check authentication loop */
1469 result = winbind_samlogon_retry_loop(domain,
1470 mem_ctx,
1472 domain->dcname,
1473 name_user,
1474 name_domain,
1475 lp_netbios_name(),
1476 chal,
1477 lm_resp,
1478 nt_resp,
1479 &my_info3);
1480 if (!NT_STATUS_IS_OK(result)) {
1481 goto done;
1484 /* handle the case where a NT4 DC does not fill in the acct_flags in
1485 * the samlogon reply info3. When accurate info3 is required by the
1486 * caller, we look up the account flags ourselve - gd */
1488 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1489 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1491 struct rpc_pipe_client *samr_pipe;
1492 struct policy_handle samr_domain_handle, user_pol;
1493 union samr_UserInfo *info = NULL;
1494 NTSTATUS status_tmp, result_tmp;
1495 uint32 acct_flags;
1496 struct dcerpc_binding_handle *b;
1498 status_tmp = cm_connect_sam(domain, mem_ctx,
1499 &samr_pipe, &samr_domain_handle);
1501 if (!NT_STATUS_IS_OK(status_tmp)) {
1502 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1503 nt_errstr(status_tmp)));
1504 goto done;
1507 b = samr_pipe->binding_handle;
1509 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1510 &samr_domain_handle,
1511 MAXIMUM_ALLOWED_ACCESS,
1512 my_info3->base.rid,
1513 &user_pol,
1514 &result_tmp);
1516 if (!NT_STATUS_IS_OK(status_tmp)) {
1517 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1518 nt_errstr(status_tmp)));
1519 goto done;
1521 if (!NT_STATUS_IS_OK(result_tmp)) {
1522 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1523 nt_errstr(result_tmp)));
1524 goto done;
1527 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1528 &user_pol,
1530 &info,
1531 &result_tmp);
1533 if (!NT_STATUS_IS_OK(status_tmp)) {
1534 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1535 nt_errstr(status_tmp)));
1536 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1537 goto done;
1539 if (!NT_STATUS_IS_OK(result_tmp)) {
1540 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1541 nt_errstr(result_tmp)));
1542 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1543 goto done;
1546 acct_flags = info->info16.acct_flags;
1548 if (acct_flags == 0) {
1549 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1550 goto done;
1553 my_info3->base.acct_flags = acct_flags;
1555 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1557 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1560 *info3 = my_info3;
1561 done:
1562 return result;
1565 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1566 struct winbindd_cli_state *state)
1568 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1569 NTSTATUS krb5_result = NT_STATUS_OK;
1570 fstring name_domain, name_user;
1571 char *mapped_user;
1572 fstring domain_user;
1573 struct netr_SamInfo3 *info3 = NULL;
1574 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1576 /* Ensure null termination */
1577 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1579 /* Ensure null termination */
1580 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1582 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1583 state->request->data.auth.user));
1585 /* Parse domain and username */
1587 name_map_status = normalize_name_unmap(state->mem_ctx,
1588 state->request->data.auth.user,
1589 &mapped_user);
1591 /* If the name normalization didnt' actually do anything,
1592 just use the original name */
1594 if (!NT_STATUS_IS_OK(name_map_status) &&
1595 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1597 mapped_user = state->request->data.auth.user;
1600 parse_domain_user(mapped_user, name_domain, name_user);
1602 if ( mapped_user != state->request->data.auth.user ) {
1603 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1604 *lp_winbind_separator(),
1605 name_user );
1606 strlcpy( state->request->data.auth.user, domain_user,
1607 sizeof(state->request->data.auth.user));
1610 if (!domain->online) {
1611 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1612 if (domain->startup) {
1613 /* Logons are very important to users. If we're offline and
1614 we get a request within the first 30 seconds of startup,
1615 try very hard to find a DC and go online. */
1617 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1618 "request in startup mode.\n", domain->name ));
1620 winbindd_flush_negative_conn_cache(domain);
1621 result = init_dc_connection(domain);
1625 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1627 /* Check for Kerberos authentication */
1628 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1630 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1631 /* save for later */
1632 krb5_result = result;
1635 if (NT_STATUS_IS_OK(result)) {
1636 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1637 goto process_result;
1638 } else {
1639 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1642 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1643 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1644 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1645 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1646 set_domain_offline( domain );
1647 goto cached_logon;
1650 /* there are quite some NT_STATUS errors where there is no
1651 * point in retrying with a samlogon, we explictly have to take
1652 * care not to increase the bad logon counter on the DC */
1654 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1655 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1656 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1657 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1658 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1659 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1660 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1661 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1662 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1663 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1664 goto done;
1667 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1668 DEBUG(3,("falling back to samlogon\n"));
1669 goto sam_logon;
1670 } else {
1671 goto cached_logon;
1675 sam_logon:
1676 /* Check for Samlogon authentication */
1677 if (domain->online) {
1678 result = winbindd_dual_pam_auth_samlogon(
1679 state->mem_ctx, domain,
1680 state->request->data.auth.user,
1681 state->request->data.auth.pass,
1682 state->request->flags,
1683 &info3);
1685 if (NT_STATUS_IS_OK(result)) {
1686 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1687 /* add the Krb5 err if we have one */
1688 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1689 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1691 goto process_result;
1694 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1695 nt_errstr(result)));
1697 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1698 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1699 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1701 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1702 set_domain_offline( domain );
1703 goto cached_logon;
1706 if (domain->online) {
1707 /* We're still online - fail. */
1708 goto done;
1712 cached_logon:
1713 /* Check for Cached logons */
1714 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1715 lp_winbind_offline_logon()) {
1717 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1719 if (NT_STATUS_IS_OK(result)) {
1720 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1721 goto process_result;
1722 } else {
1723 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1724 goto done;
1728 process_result:
1730 if (NT_STATUS_IS_OK(result)) {
1732 struct dom_sid user_sid;
1734 /* In all codepaths where result == NT_STATUS_OK info3 must have
1735 been initialized. */
1736 if (!info3) {
1737 result = NT_STATUS_INTERNAL_ERROR;
1738 goto done;
1741 sid_compose(&user_sid, info3->base.domain_sid,
1742 info3->base.rid);
1744 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1745 &user_sid);
1746 netsamlogon_cache_store(name_user, info3);
1748 /* save name_to_sid info as early as possible (only if
1749 this is our primary domain so we don't invalidate
1750 the cache entry by storing the seq_num for the wrong
1751 domain). */
1752 if ( domain->primary ) {
1753 cache_name2sid(domain, name_domain, name_user,
1754 SID_NAME_USER, &user_sid);
1757 /* Check if the user is in the right group */
1759 result = check_info3_in_group(
1760 info3,
1761 state->request->data.auth.require_membership_of_sid);
1762 if (!NT_STATUS_IS_OK(result)) {
1763 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1764 state->request->data.auth.user,
1765 state->request->data.auth.require_membership_of_sid));
1766 goto done;
1769 result = append_auth_data(state->mem_ctx, state->response,
1770 state->request->flags, info3,
1771 name_domain, name_user);
1772 if (!NT_STATUS_IS_OK(result)) {
1773 goto done;
1776 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1777 && lp_winbind_offline_logon()) {
1779 result = winbindd_store_creds(domain,
1780 state->request->data.auth.user,
1781 state->request->data.auth.pass,
1782 info3);
1785 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1786 struct winbindd_domain *our_domain = find_our_domain();
1788 /* This is not entirely correct I believe, but it is
1789 consistent. Only apply the password policy settings
1790 too warn users for our own domain. Cannot obtain these
1791 from trusted DCs all the time so don't do it at all.
1792 -- jerry */
1794 result = NT_STATUS_NOT_SUPPORTED;
1795 if (our_domain == domain ) {
1796 result = fillup_password_policy(
1797 our_domain, state->response);
1800 if (!NT_STATUS_IS_OK(result)
1801 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1803 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1804 domain->name, nt_errstr(result)));
1805 goto done;
1809 result = NT_STATUS_OK;
1812 done:
1813 /* give us a more useful (more correct?) error code */
1814 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1815 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1816 result = NT_STATUS_NO_LOGON_SERVERS;
1819 set_auth_errors(state->response, result);
1821 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1822 state->request->data.auth.user,
1823 state->response->data.auth.nt_status_string,
1824 state->response->data.auth.pam_error));
1826 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1829 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1830 struct winbindd_cli_state *state)
1832 NTSTATUS result;
1833 struct netr_SamInfo3 *info3 = NULL;
1834 const char *name_user = NULL;
1835 const char *name_domain = NULL;
1836 const char *workstation;
1838 DATA_BLOB lm_resp, nt_resp;
1840 /* This is child-only, so no check for privileged access is needed
1841 anymore */
1843 /* Ensure null termination */
1844 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1845 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1847 name_user = state->request->data.auth_crap.user;
1848 name_domain = state->request->data.auth_crap.domain;
1849 workstation = state->request->data.auth_crap.workstation;
1851 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1852 name_domain, name_user));
1854 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1855 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1856 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1857 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1858 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1859 state->request->data.auth_crap.lm_resp_len,
1860 state->request->data.auth_crap.nt_resp_len));
1861 result = NT_STATUS_INVALID_PARAMETER;
1862 goto done;
1866 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1867 state->request->data.auth_crap.lm_resp_len);
1869 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1870 nt_resp = data_blob_talloc(state->mem_ctx,
1871 state->request->extra_data.data,
1872 state->request->data.auth_crap.nt_resp_len);
1873 } else {
1874 nt_resp = data_blob_talloc(state->mem_ctx,
1875 state->request->data.auth_crap.nt_resp,
1876 state->request->data.auth_crap.nt_resp_len);
1879 if (strequal(name_domain, get_global_sam_name())) {
1880 DATA_BLOB chal_blob = data_blob_const(
1881 state->request->data.auth_crap.chal,
1882 sizeof(state->request->data.auth_crap.chal));
1884 result = winbindd_dual_auth_passdb(
1885 state->mem_ctx,
1886 state->request->data.auth_crap.logon_parameters,
1887 name_domain, name_user,
1888 &chal_blob, &lm_resp, &nt_resp, &info3);
1889 goto process_result;
1892 result = winbind_samlogon_retry_loop(domain,
1893 state->mem_ctx,
1894 state->request->data.auth_crap.logon_parameters,
1895 domain->dcname,
1896 name_user,
1897 name_domain,
1898 /* Bug #3248 - found by Stefan Burkei. */
1899 workstation, /* We carefully set this above so use it... */
1900 state->request->data.auth_crap.chal,
1901 lm_resp,
1902 nt_resp,
1903 &info3);
1904 if (!NT_STATUS_IS_OK(result)) {
1905 goto done;
1908 process_result:
1910 if (NT_STATUS_IS_OK(result)) {
1911 struct dom_sid user_sid;
1913 sid_compose(&user_sid, info3->base.domain_sid,
1914 info3->base.rid);
1915 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1916 &user_sid);
1917 netsamlogon_cache_store(name_user, info3);
1919 /* Check if the user is in the right group */
1921 result = check_info3_in_group(
1922 info3,
1923 state->request->data.auth_crap.require_membership_of_sid);
1924 if (!NT_STATUS_IS_OK(result)) {
1925 DEBUG(3, ("User %s is not in the required group (%s), so "
1926 "crap authentication is rejected\n",
1927 state->request->data.auth_crap.user,
1928 state->request->data.auth_crap.require_membership_of_sid));
1929 goto done;
1932 result = append_auth_data(state->mem_ctx, state->response,
1933 state->request->flags, info3,
1934 name_domain, name_user);
1935 if (!NT_STATUS_IS_OK(result)) {
1936 goto done;
1940 done:
1942 /* give us a more useful (more correct?) error code */
1943 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1944 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1945 result = NT_STATUS_NO_LOGON_SERVERS;
1948 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1949 result = nt_status_squash(result);
1952 set_auth_errors(state->response, result);
1954 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1955 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1956 name_domain,
1957 name_user,
1958 state->response->data.auth.nt_status_string,
1959 state->response->data.auth.pam_error));
1961 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1964 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1965 struct winbindd_cli_state *state)
1967 char *oldpass;
1968 char *newpass = NULL;
1969 struct policy_handle dom_pol;
1970 struct rpc_pipe_client *cli = NULL;
1971 bool got_info = false;
1972 struct samr_DomInfo1 *info = NULL;
1973 struct userPwdChangeFailureInformation *reject = NULL;
1974 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1975 fstring domain, user;
1976 struct dcerpc_binding_handle *b = NULL;
1978 ZERO_STRUCT(dom_pol);
1980 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1981 state->request->data.auth.user));
1983 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1984 goto done;
1987 /* Change password */
1989 oldpass = state->request->data.chauthtok.oldpass;
1990 newpass = state->request->data.chauthtok.newpass;
1992 /* Initialize reject reason */
1993 state->response->data.auth.reject_reason = Undefined;
1995 /* Get sam handle */
1997 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1998 &dom_pol);
1999 if (!NT_STATUS_IS_OK(result)) {
2000 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2001 goto done;
2004 b = cli->binding_handle;
2006 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2007 user,
2008 newpass,
2009 oldpass,
2010 &info,
2011 &reject);
2013 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2015 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2017 fill_in_password_policy(state->response, info);
2019 state->response->data.auth.reject_reason =
2020 reject->extendedFailureReason;
2022 got_info = true;
2025 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2026 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2027 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2028 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2030 /* only fallback when the chgpasswd_user3 call is not supported */
2031 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2032 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2033 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2034 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2036 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2037 nt_errstr(result)));
2039 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2041 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2042 Map to the same status code as Windows 2003. */
2044 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2045 result = NT_STATUS_PASSWORD_RESTRICTION;
2049 done:
2051 if (NT_STATUS_IS_OK(result)
2052 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2053 && lp_winbind_offline_logon()) {
2054 result = winbindd_update_creds_by_name(contact_domain, user,
2055 newpass);
2056 /* Again, this happens when we login from gdm or xdm
2057 * and the password expires, *BUT* cached crendentials
2058 * doesn't exist. winbindd_update_creds_by_name()
2059 * returns NT_STATUS_NO_SUCH_USER.
2060 * This is not a failure.
2061 * --- BoYang
2062 * */
2063 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2064 result = NT_STATUS_OK;
2067 if (!NT_STATUS_IS_OK(result)) {
2068 DEBUG(10, ("Failed to store creds: %s\n",
2069 nt_errstr(result)));
2070 goto process_result;
2074 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2076 NTSTATUS policy_ret;
2078 policy_ret = fillup_password_policy(
2079 contact_domain, state->response);
2081 /* failure of this is non critical, it will just provide no
2082 * additional information to the client why the change has
2083 * failed - Guenther */
2085 if (!NT_STATUS_IS_OK(policy_ret)) {
2086 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2087 goto process_result;
2091 process_result:
2093 if (strequal(contact_domain->name, get_global_sam_name())) {
2094 /* FIXME: internal rpc pipe does not cache handles yet */
2095 if (b) {
2096 if (is_valid_policy_hnd(&dom_pol)) {
2097 NTSTATUS _result;
2098 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2100 TALLOC_FREE(cli);
2104 set_auth_errors(state->response, result);
2106 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2107 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2108 domain,
2109 user,
2110 state->response->data.auth.nt_status_string,
2111 state->response->data.auth.pam_error));
2113 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2116 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2117 struct winbindd_cli_state *state)
2119 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2121 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2122 state->request->data.logoff.user));
2124 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2125 result = NT_STATUS_OK;
2126 goto process_result;
2129 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2130 result = NT_STATUS_OK;
2131 goto process_result;
2134 #ifdef HAVE_KRB5
2136 if (state->request->data.logoff.uid < 0) {
2137 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2138 goto process_result;
2141 /* what we need here is to find the corresponding krb5 ccache name *we*
2142 * created for a given username and destroy it */
2144 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2145 result = NT_STATUS_OK;
2146 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2147 goto process_result;
2150 if (!ccache_entry_identical(state->request->data.logoff.user,
2151 state->request->data.logoff.uid,
2152 state->request->data.logoff.krb5ccname)) {
2153 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2154 goto process_result;
2157 result = remove_ccache(state->request->data.logoff.user);
2158 if (!NT_STATUS_IS_OK(result)) {
2159 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2160 nt_errstr(result)));
2161 goto process_result;
2165 * Remove any mlock'ed memory creds in the child
2166 * we might be using for krb5 ticket renewal.
2169 winbindd_delete_memory_creds(state->request->data.logoff.user);
2171 #else
2172 result = NT_STATUS_NOT_SUPPORTED;
2173 #endif
2175 process_result:
2178 set_auth_errors(state->response, result);
2180 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2183 /* Change user password with auth crap*/
2185 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2187 NTSTATUS result;
2188 DATA_BLOB new_nt_password;
2189 DATA_BLOB old_nt_hash_enc;
2190 DATA_BLOB new_lm_password;
2191 DATA_BLOB old_lm_hash_enc;
2192 fstring domain,user;
2193 struct policy_handle dom_pol;
2194 struct winbindd_domain *contact_domain = domainSt;
2195 struct rpc_pipe_client *cli = NULL;
2196 struct dcerpc_binding_handle *b = NULL;
2198 ZERO_STRUCT(dom_pol);
2200 /* Ensure null termination */
2201 state->request->data.chng_pswd_auth_crap.user[
2202 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2203 state->request->data.chng_pswd_auth_crap.domain[
2204 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2205 *domain = 0;
2206 *user = 0;
2208 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2209 (unsigned long)state->pid,
2210 state->request->data.chng_pswd_auth_crap.domain,
2211 state->request->data.chng_pswd_auth_crap.user));
2213 if (lp_winbind_offline_logon()) {
2214 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2215 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2216 result = NT_STATUS_ACCESS_DENIED;
2217 goto done;
2220 if (*state->request->data.chng_pswd_auth_crap.domain) {
2221 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2222 } else {
2223 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2224 domain, user);
2226 if(!*domain) {
2227 DEBUG(3,("no domain specified with username (%s) - "
2228 "failing auth\n",
2229 state->request->data.chng_pswd_auth_crap.user));
2230 result = NT_STATUS_NO_SUCH_USER;
2231 goto done;
2235 if (!*domain && lp_winbind_use_default_domain()) {
2236 fstrcpy(domain,lp_workgroup());
2239 if(!*user) {
2240 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2243 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2244 (unsigned long)state->pid, domain, user));
2246 /* Change password */
2247 new_nt_password = data_blob_const(
2248 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2249 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2251 old_nt_hash_enc = data_blob_const(
2252 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2253 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2255 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2256 new_lm_password = data_blob_const(
2257 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2258 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2260 old_lm_hash_enc = data_blob_const(
2261 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2262 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2263 } else {
2264 new_lm_password = data_blob_null;
2265 old_lm_hash_enc = data_blob_null;
2268 /* Get sam handle */
2270 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2271 if (!NT_STATUS_IS_OK(result)) {
2272 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2273 goto done;
2276 b = cli->binding_handle;
2278 result = rpccli_samr_chng_pswd_auth_crap(
2279 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2280 new_lm_password, old_lm_hash_enc);
2282 done:
2284 if (strequal(contact_domain->name, get_global_sam_name())) {
2285 /* FIXME: internal rpc pipe does not cache handles yet */
2286 if (b) {
2287 if (is_valid_policy_hnd(&dom_pol)) {
2288 NTSTATUS _result;
2289 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2291 TALLOC_FREE(cli);
2295 set_auth_errors(state->response, result);
2297 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2298 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2299 domain, user,
2300 state->response->data.auth.nt_status_string,
2301 state->response->data.auth.pam_error));
2303 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2306 #ifdef HAVE_KRB5
2307 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2308 struct PAC_LOGON_INFO **logon_info)
2310 krb5_context krbctx = NULL;
2311 krb5_error_code k5ret;
2312 krb5_keytab keytab;
2313 krb5_kt_cursor cursor;
2314 krb5_keytab_entry entry;
2315 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2317 ZERO_STRUCT(entry);
2318 ZERO_STRUCT(cursor);
2320 k5ret = krb5_init_context(&krbctx);
2321 if (k5ret) {
2322 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2323 error_message(k5ret)));
2324 status = krb5_to_nt_status(k5ret);
2325 goto out;
2328 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2329 if (k5ret) {
2330 DEBUG(1, ("Failed to get keytab: %s\n",
2331 error_message(k5ret)));
2332 status = krb5_to_nt_status(k5ret);
2333 goto out_free;
2336 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2337 if (k5ret) {
2338 DEBUG(1, ("Failed to start seq: %s\n",
2339 error_message(k5ret)));
2340 status = krb5_to_nt_status(k5ret);
2341 goto out_keytab;
2344 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2345 while (k5ret == 0) {
2346 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2347 krbctx, NULL,
2348 KRB5_KT_KEY(&entry), NULL, 0,
2349 logon_info);
2350 if (NT_STATUS_IS_OK(status)) {
2351 break;
2353 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2354 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2357 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2358 if (k5ret) {
2359 DEBUG(1, ("Failed to end seq: %s\n",
2360 error_message(k5ret)));
2362 out_keytab:
2363 k5ret = krb5_kt_close(krbctx, keytab);
2364 if (k5ret) {
2365 DEBUG(1, ("Failed to close keytab: %s\n",
2366 error_message(k5ret)));
2368 out_free:
2369 krb5_free_context(krbctx);
2370 out:
2371 return status;
2374 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2375 struct netr_SamInfo3 **info3)
2377 struct winbindd_request *req = state->request;
2378 DATA_BLOB pac_blob;
2379 struct PAC_LOGON_INFO *logon_info = NULL;
2380 NTSTATUS result;
2382 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2383 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2384 if (!NT_STATUS_IS_OK(result) &&
2385 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2386 DEBUG(1, ("Error during PAC signature verification: %s\n",
2387 nt_errstr(result)));
2388 return result;
2391 if (logon_info) {
2392 /* Signature verification succeeded, trust the PAC */
2393 netsamlogon_cache_store(NULL, &logon_info->info3);
2395 } else {
2396 /* Try without signature verification */
2397 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2398 NULL, NULL, NULL, 0,
2399 &logon_info);
2400 if (!NT_STATUS_IS_OK(result)) {
2401 DEBUG(10, ("Could not extract PAC: %s\n",
2402 nt_errstr(result)));
2403 return result;
2407 *info3 = &logon_info->info3;
2409 return NT_STATUS_OK;
2411 #else /* HAVE_KRB5 */
2412 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2413 struct netr_SamInfo3 **info3)
2415 return NT_STATUS_NO_SUCH_USER;
2417 #endif /* HAVE_KRB5 */