Change all uint32/16/8 to 32_t/16_t/8_t in winbindd.
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob864382e5df1b7272958cf79fe9af0e6d0d869d48
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"
44 #include "lib/afs/afs_funcs.h"
46 #undef DBGC_CLASS
47 #define DBGC_CLASS DBGC_WINBIND
49 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
51 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
52 struct winbindd_response *resp,
53 struct netr_SamInfo3 *info3)
55 char *ex;
56 uint32_t i;
58 resp->data.auth.info3.logon_time =
59 nt_time_to_unix(info3->base.logon_time);
60 resp->data.auth.info3.logoff_time =
61 nt_time_to_unix(info3->base.logoff_time);
62 resp->data.auth.info3.kickoff_time =
63 nt_time_to_unix(info3->base.kickoff_time);
64 resp->data.auth.info3.pass_last_set_time =
65 nt_time_to_unix(info3->base.last_password_change);
66 resp->data.auth.info3.pass_can_change_time =
67 nt_time_to_unix(info3->base.allow_password_change);
68 resp->data.auth.info3.pass_must_change_time =
69 nt_time_to_unix(info3->base.force_password_change);
71 resp->data.auth.info3.logon_count = info3->base.logon_count;
72 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
74 resp->data.auth.info3.user_rid = info3->base.rid;
75 resp->data.auth.info3.group_rid = info3->base.primary_gid;
76 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
78 resp->data.auth.info3.num_groups = info3->base.groups.count;
79 resp->data.auth.info3.user_flgs = info3->base.user_flags;
81 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
82 resp->data.auth.info3.num_other_sids = info3->sidcount;
84 fstrcpy(resp->data.auth.info3.user_name,
85 info3->base.account_name.string);
86 fstrcpy(resp->data.auth.info3.full_name,
87 info3->base.full_name.string);
88 fstrcpy(resp->data.auth.info3.logon_script,
89 info3->base.logon_script.string);
90 fstrcpy(resp->data.auth.info3.profile_path,
91 info3->base.profile_path.string);
92 fstrcpy(resp->data.auth.info3.home_dir,
93 info3->base.home_directory.string);
94 fstrcpy(resp->data.auth.info3.dir_drive,
95 info3->base.home_drive.string);
97 fstrcpy(resp->data.auth.info3.logon_srv,
98 info3->base.logon_server.string);
99 fstrcpy(resp->data.auth.info3.logon_dom,
100 info3->base.logon_domain.string);
102 ex = talloc_strdup(mem_ctx, "");
103 NT_STATUS_HAVE_NO_MEMORY(ex);
105 for (i=0; i < info3->base.groups.count; i++) {
106 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
107 info3->base.groups.rids[i].rid,
108 info3->base.groups.rids[i].attributes);
109 NT_STATUS_HAVE_NO_MEMORY(ex);
112 for (i=0; i < info3->sidcount; i++) {
113 char *sid;
115 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
116 NT_STATUS_HAVE_NO_MEMORY(sid);
118 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
119 sid,
120 info3->sids[i].attributes);
121 NT_STATUS_HAVE_NO_MEMORY(ex);
123 talloc_free(sid);
126 resp->extra_data.data = ex;
127 resp->length += talloc_get_size(ex);
129 return NT_STATUS_OK;
132 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
133 struct winbindd_response *resp,
134 struct netr_SamInfo3 *info3)
136 DATA_BLOB blob;
137 enum ndr_err_code ndr_err;
139 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
140 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
141 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
142 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
143 return ndr_map_error2ntstatus(ndr_err);
146 resp->extra_data.data = blob.data;
147 resp->length += blob.length;
149 return NT_STATUS_OK;
152 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
153 struct winbindd_response *resp,
154 const struct netr_SamInfo3 *info3,
155 const char *name_domain,
156 const char *name_user)
158 /* We've been asked to return the unix username, per
159 'winbind use default domain' settings and the like */
161 const char *nt_username, *nt_domain;
163 nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
164 if (!nt_domain) {
165 /* If the server didn't give us one, just use the one
166 * we sent them */
167 nt_domain = name_domain;
170 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
171 if (!nt_username) {
172 /* If the server didn't give us one, just use the one
173 * we sent them */
174 nt_username = name_user;
177 fill_domain_username(resp->data.auth.unix_username,
178 nt_domain, nt_username, true);
180 DEBUG(5, ("Setting unix username to [%s]\n",
181 resp->data.auth.unix_username));
183 return NT_STATUS_OK;
186 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
187 struct winbindd_response *resp,
188 const struct netr_SamInfo3 *info3,
189 const char *name_domain,
190 const char *name_user)
192 char *afsname = NULL;
193 char *cell;
194 char *token;
196 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
197 if (afsname == NULL) {
198 return NT_STATUS_NO_MEMORY;
201 afsname = talloc_string_sub(mem_ctx,
202 lp_afs_username_map(),
203 "%D", name_domain);
204 afsname = talloc_string_sub(mem_ctx, afsname,
205 "%u", name_user);
206 afsname = talloc_string_sub(mem_ctx, afsname,
207 "%U", name_user);
210 struct dom_sid user_sid;
211 fstring sidstr;
213 sid_compose(&user_sid, info3->base.domain_sid,
214 info3->base.rid);
215 sid_to_fstring(sidstr, &user_sid);
216 afsname = talloc_string_sub(mem_ctx, afsname,
217 "%s", sidstr);
220 if (afsname == NULL) {
221 return NT_STATUS_NO_MEMORY;
224 if (!strlower_m(afsname)) {
225 return NT_STATUS_INVALID_PARAMETER;
228 DEBUG(10, ("Generating token for user %s\n", afsname));
230 cell = strchr(afsname, '@');
232 if (cell == NULL) {
233 return NT_STATUS_NO_MEMORY;
236 *cell = '\0';
237 cell += 1;
239 token = afs_createtoken_str(afsname, cell);
240 if (token == NULL) {
241 return NT_STATUS_OK;
243 resp->extra_data.data = talloc_strdup(mem_ctx, token);
244 if (resp->extra_data.data == NULL) {
245 return NT_STATUS_NO_MEMORY;
247 resp->length += strlen((const char *)resp->extra_data.data)+1;
249 return NT_STATUS_OK;
252 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
253 const char *group_sid)
255 * Check whether a user belongs to a group or list of groups.
257 * @param mem_ctx talloc memory context.
258 * @param info3 user information, including group membership info.
259 * @param group_sid One or more groups , separated by commas.
261 * @return NT_STATUS_OK on success,
262 * NT_STATUS_LOGON_FAILURE if the user does not belong,
263 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
266 struct dom_sid *require_membership_of_sid;
267 uint32_t num_require_membership_of_sid;
268 char *req_sid;
269 const char *p;
270 struct dom_sid sid;
271 size_t i;
272 struct security_token *token;
273 TALLOC_CTX *frame = talloc_stackframe();
274 NTSTATUS status;
276 /* Parse the 'required group' SID */
278 if (!group_sid || !group_sid[0]) {
279 /* NO sid supplied, all users may access */
280 TALLOC_FREE(frame);
281 return NT_STATUS_OK;
284 token = talloc_zero(talloc_tos(), struct security_token);
285 if (token == NULL) {
286 DEBUG(0, ("talloc failed\n"));
287 TALLOC_FREE(frame);
288 return NT_STATUS_NO_MEMORY;
291 num_require_membership_of_sid = 0;
292 require_membership_of_sid = NULL;
294 p = group_sid;
296 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
297 if (!string_to_sid(&sid, req_sid)) {
298 DEBUG(0, ("check_info3_in_group: could not parse %s "
299 "as a SID!", req_sid));
300 TALLOC_FREE(frame);
301 return NT_STATUS_INVALID_PARAMETER;
304 status = add_sid_to_array(talloc_tos(), &sid,
305 &require_membership_of_sid,
306 &num_require_membership_of_sid);
307 if (!NT_STATUS_IS_OK(status)) {
308 DEBUG(0, ("add_sid_to_array failed\n"));
309 TALLOC_FREE(frame);
310 return status;
314 status = sid_array_from_info3(talloc_tos(), info3,
315 &token->sids,
316 &token->num_sids,
317 true);
318 if (!NT_STATUS_IS_OK(status)) {
319 TALLOC_FREE(frame);
320 return status;
323 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
324 token))
325 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
326 token))) {
327 DEBUG(3, ("could not add aliases: %s\n",
328 nt_errstr(status)));
329 TALLOC_FREE(frame);
330 return status;
333 security_token_debug(DBGC_CLASS, 10, token);
335 for (i=0; i<num_require_membership_of_sid; i++) {
336 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
337 &require_membership_of_sid[i])));
338 if (nt_token_check_sid(&require_membership_of_sid[i],
339 token)) {
340 DEBUG(10, ("Access ok\n"));
341 TALLOC_FREE(frame);
342 return NT_STATUS_OK;
346 /* Do not distinguish this error from a wrong username/pw */
348 TALLOC_FREE(frame);
349 return NT_STATUS_LOGON_FAILURE;
352 struct winbindd_domain *find_auth_domain(uint8_t flags,
353 const char *domain_name)
355 struct winbindd_domain *domain;
357 if (IS_DC) {
358 domain = find_domain_from_name_noinit(domain_name);
359 if (domain == NULL) {
360 DEBUG(3, ("Authentication for domain [%s] refused "
361 "as it is not a trusted domain\n",
362 domain_name));
364 return domain;
367 if (strequal(domain_name, get_global_sam_name())) {
368 return find_domain_from_name_noinit(domain_name);
371 /* we can auth against trusted domains */
372 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
373 domain = find_domain_from_name_noinit(domain_name);
374 if (domain == NULL) {
375 DEBUG(3, ("Authentication for domain [%s] skipped "
376 "as it is not a trusted domain\n",
377 domain_name));
378 } else {
379 return domain;
383 return find_our_domain();
386 static void fill_in_password_policy(struct winbindd_response *r,
387 const struct samr_DomInfo1 *p)
389 r->data.auth.policy.min_length_password =
390 p->min_password_length;
391 r->data.auth.policy.password_history =
392 p->password_history_length;
393 r->data.auth.policy.password_properties =
394 p->password_properties;
395 r->data.auth.policy.expire =
396 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
397 r->data.auth.policy.min_passwordage =
398 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
401 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
402 struct winbindd_response *response)
404 TALLOC_CTX *frame = talloc_stackframe();
405 struct winbindd_methods *methods;
406 NTSTATUS status;
407 struct samr_DomInfo1 password_policy;
409 if ( !winbindd_can_contact_domain( domain ) ) {
410 DEBUG(5,("fillup_password_policy: No inbound trust to "
411 "contact domain %s\n", domain->name));
412 status = NT_STATUS_NOT_SUPPORTED;
413 goto done;
416 methods = domain->methods;
418 status = methods->password_policy(domain, talloc_tos(), &password_policy);
419 if (NT_STATUS_IS_ERR(status)) {
420 goto done;
423 fill_in_password_policy(response, &password_policy);
425 done:
426 TALLOC_FREE(frame);
427 return NT_STATUS_OK;
430 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
431 TALLOC_CTX *mem_ctx,
432 uint16_t *lockout_threshold)
434 struct winbindd_methods *methods;
435 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436 struct samr_DomInfo12 lockout_policy;
438 *lockout_threshold = 0;
440 methods = domain->methods;
442 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
443 if (NT_STATUS_IS_ERR(status)) {
444 return status;
447 *lockout_threshold = lockout_policy.lockout_threshold;
449 return NT_STATUS_OK;
452 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
453 TALLOC_CTX *mem_ctx,
454 uint32_t *password_properties)
456 struct winbindd_methods *methods;
457 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
458 struct samr_DomInfo1 password_policy;
460 *password_properties = 0;
462 methods = domain->methods;
464 status = methods->password_policy(domain, mem_ctx, &password_policy);
465 if (NT_STATUS_IS_ERR(status)) {
466 return status;
469 *password_properties = password_policy.password_properties;
471 return NT_STATUS_OK;
474 #ifdef HAVE_KRB5
476 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
477 const char *type,
478 uid_t uid,
479 const char **user_ccache_file)
481 /* accept FILE and WRFILE as krb5_cc_type from the client and then
482 * build the full ccname string based on the user's uid here -
483 * Guenther*/
485 const char *gen_cc = NULL;
487 if (uid != -1) {
488 if (strequal(type, "FILE")) {
489 gen_cc = talloc_asprintf(
490 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
492 if (strequal(type, "WRFILE")) {
493 gen_cc = talloc_asprintf(
494 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
496 if (strequal(type, "KEYRING")) {
497 gen_cc = talloc_asprintf(
498 mem_ctx, "KEYRING:persistent:%d", uid);
501 if (strnequal(type, "FILE:/", 6) ||
502 strnequal(type, "WRFILE:/", 8) ||
503 strnequal(type, "DIR:/", 5)) {
505 /* we allow only one "%u" substitution */
507 char *p;
509 p = strchr(type, '%');
510 if (p != NULL) {
512 p++;
514 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
515 char uid_str[sizeof("18446744073709551615")];
517 snprintf(uid_str, sizeof(uid_str), "%u", uid);
519 gen_cc = talloc_string_sub2(mem_ctx,
520 type,
521 "%u",
522 uid_str,
523 /* remove_unsafe_characters */
524 false,
525 /* replace_once */
526 true,
527 /* allow_trailing_dollar */
528 false);
534 *user_ccache_file = gen_cc;
536 if (gen_cc == NULL) {
537 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
539 if (gen_cc == NULL) {
540 DEBUG(0,("out of memory\n"));
541 return NULL;
544 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
545 (*user_ccache_file == NULL) ? " (internal)":""));
547 return gen_cc;
550 #endif
552 uid_t get_uid_from_request(struct winbindd_request *request)
554 uid_t uid;
556 uid = request->data.auth.uid;
558 if (uid < 0) {
559 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
560 return -1;
562 return uid;
565 /**********************************************************************
566 Authenticate a user with a clear text password using Kerberos and fill up
567 ccache if required
568 **********************************************************************/
570 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
571 struct winbindd_domain *domain,
572 const char *user,
573 const char *pass,
574 const char *krb5_cc_type,
575 uid_t uid,
576 struct netr_SamInfo3 **info3,
577 fstring krb5ccname)
579 #ifdef HAVE_KRB5
580 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
581 krb5_error_code krb5_ret;
582 const char *cc = NULL;
583 const char *principal_s = NULL;
584 const char *service = NULL;
585 char *realm = NULL;
586 fstring name_domain, name_user;
587 time_t ticket_lifetime = 0;
588 time_t renewal_until = 0;
589 ADS_STRUCT *ads;
590 time_t time_offset = 0;
591 const char *user_ccache_file;
592 struct PAC_LOGON_INFO *logon_info = NULL;
593 struct PAC_DATA *pac_data = NULL;
594 struct PAC_DATA_CTR *pac_data_ctr = NULL;
595 const char *local_service;
596 int i;
598 *info3 = NULL;
600 if (domain->alt_name == NULL) {
601 return NT_STATUS_INVALID_PARAMETER;
604 /* 1st step:
605 * prepare a krb5_cc_cache string for the user */
607 if (uid == -1) {
608 DEBUG(0,("no valid uid\n"));
611 cc = generate_krb5_ccache(mem_ctx,
612 krb5_cc_type,
613 uid,
614 &user_ccache_file);
615 if (cc == NULL) {
616 return NT_STATUS_NO_MEMORY;
620 /* 2nd step:
621 * get kerberos properties */
623 if (domain->private_data) {
624 ads = (ADS_STRUCT *)domain->private_data;
625 time_offset = ads->auth.time_offset;
629 /* 3rd step:
630 * do kerberos auth and setup ccache as the user */
632 parse_domain_user(user, name_domain, name_user);
634 realm = talloc_strdup(mem_ctx, domain->alt_name);
635 if (realm == NULL) {
636 return NT_STATUS_NO_MEMORY;
639 if (!strupper_m(realm)) {
640 return NT_STATUS_INVALID_PARAMETER;
643 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
644 if (principal_s == NULL) {
645 return NT_STATUS_NO_MEMORY;
648 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
649 if (service == NULL) {
650 return NT_STATUS_NO_MEMORY;
653 local_service = talloc_asprintf(mem_ctx, "%s$@%s",
654 lp_netbios_name(), lp_realm());
655 if (local_service == NULL) {
656 return NT_STATUS_NO_MEMORY;
660 /* if this is a user ccache, we need to act as the user to let the krb5
661 * library handle the chown, etc. */
663 /************************ ENTERING NON-ROOT **********************/
665 if (user_ccache_file != NULL) {
666 set_effective_uid(uid);
667 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
670 result = kerberos_return_pac(mem_ctx,
671 principal_s,
672 pass,
673 time_offset,
674 &ticket_lifetime,
675 &renewal_until,
677 true,
678 true,
679 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
680 NULL,
681 local_service,
682 &pac_data_ctr);
683 if (user_ccache_file != NULL) {
684 gain_root_privilege();
687 /************************ RETURNED TO ROOT **********************/
689 if (!NT_STATUS_IS_OK(result)) {
690 goto failed;
693 if (pac_data_ctr == NULL) {
694 goto failed;
697 pac_data = pac_data_ctr->pac_data;
698 if (pac_data == NULL) {
699 goto failed;
702 for (i=0; i < pac_data->num_buffers; i++) {
704 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
705 continue;
708 logon_info = pac_data->buffers[i].info->logon_info.info;
709 if (!logon_info) {
710 return NT_STATUS_INVALID_PARAMETER;
713 break;
716 *info3 = &logon_info->info3;
718 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
719 principal_s));
721 /* if we had a user's ccache then return that string for the pam
722 * environment */
724 if (user_ccache_file != NULL) {
726 fstrcpy(krb5ccname, user_ccache_file);
728 result = add_ccache_to_list(principal_s,
730 service,
731 user,
732 pass,
733 realm,
734 uid,
735 time(NULL),
736 ticket_lifetime,
737 renewal_until,
738 false);
740 if (!NT_STATUS_IS_OK(result)) {
741 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
742 nt_errstr(result)));
744 } else {
746 /* need to delete the memory cred cache, it is not used anymore */
748 krb5_ret = ads_kdestroy(cc);
749 if (krb5_ret) {
750 DEBUG(3,("winbindd_raw_kerberos_login: "
751 "could not destroy krb5 credential cache: "
752 "%s\n", error_message(krb5_ret)));
757 return NT_STATUS_OK;
759 failed:
761 * Do not delete an existing valid credential cache, if the user
762 * e.g. enters a wrong password
764 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
765 && user_ccache_file != NULL) {
766 return result;
769 /* we could have created a new credential cache with a valid tgt in it
770 * but we werent able to get or verify the service ticket for this
771 * local host and therefor didn't get the PAC, we need to remove that
772 * cache entirely now */
774 krb5_ret = ads_kdestroy(cc);
775 if (krb5_ret) {
776 DEBUG(3,("winbindd_raw_kerberos_login: "
777 "could not destroy krb5 credential cache: "
778 "%s\n", error_message(krb5_ret)));
781 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
782 DEBUG(3,("winbindd_raw_kerberos_login: "
783 "could not remove ccache for user %s\n",
784 user));
787 return result;
788 #else
789 return NT_STATUS_NOT_SUPPORTED;
790 #endif /* HAVE_KRB5 */
793 /****************************************************************
794 ****************************************************************/
796 bool check_request_flags(uint32_t flags)
798 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
799 WBFLAG_PAM_INFO3_TEXT |
800 WBFLAG_PAM_INFO3_NDR;
802 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
803 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
804 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
805 !(flags & flags_edata) ) {
806 return true;
809 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
810 flags));
812 return false;
815 /****************************************************************
816 ****************************************************************/
818 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
819 struct winbindd_response *resp,
820 uint32_t request_flags,
821 struct netr_SamInfo3 *info3,
822 const char *name_domain,
823 const char *name_user)
825 NTSTATUS result;
827 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
828 memcpy(resp->data.auth.user_session_key,
829 info3->base.key.key,
830 sizeof(resp->data.auth.user_session_key)
831 /* 16 */);
834 if (request_flags & WBFLAG_PAM_LMKEY) {
835 memcpy(resp->data.auth.first_8_lm_hash,
836 info3->base.LMSessKey.key,
837 sizeof(resp->data.auth.first_8_lm_hash)
838 /* 8 */);
841 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
842 result = append_unix_username(mem_ctx, resp,
843 info3, name_domain, name_user);
844 if (!NT_STATUS_IS_OK(result)) {
845 DEBUG(10,("Failed to append Unix Username: %s\n",
846 nt_errstr(result)));
847 return result;
851 /* currently, anything from here on potentially overwrites extra_data. */
853 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
854 result = append_info3_as_ndr(mem_ctx, resp, info3);
855 if (!NT_STATUS_IS_OK(result)) {
856 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
857 nt_errstr(result)));
858 return result;
862 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
863 result = append_info3_as_txt(mem_ctx, resp, info3);
864 if (!NT_STATUS_IS_OK(result)) {
865 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
866 nt_errstr(result)));
867 return result;
871 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
872 result = append_afs_token(mem_ctx, resp,
873 info3, name_domain, name_user);
874 if (!NT_STATUS_IS_OK(result)) {
875 DEBUG(10,("Failed to append AFS token: %s\n",
876 nt_errstr(result)));
877 return result;
881 return NT_STATUS_OK;
884 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
885 struct winbindd_cli_state *state,
886 struct netr_SamInfo3 **info3)
888 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
889 uint16_t max_allowed_bad_attempts;
890 fstring name_domain, name_user;
891 struct dom_sid sid;
892 enum lsa_SidType type;
893 uchar new_nt_pass[NT_HASH_LEN];
894 const uint8_t *cached_nt_pass;
895 const uint8_t *cached_salt;
896 struct netr_SamInfo3 *my_info3;
897 time_t kickoff_time, must_change_time;
898 bool password_good = false;
899 #ifdef HAVE_KRB5
900 struct winbindd_tdc_domain *tdc_domain = NULL;
901 #endif
903 *info3 = NULL;
905 ZERO_STRUCTP(info3);
907 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
909 /* Parse domain and username */
911 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
914 if (!lookup_cached_name(name_domain,
915 name_user,
916 &sid,
917 &type)) {
918 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
919 return NT_STATUS_NO_SUCH_USER;
922 if (type != SID_NAME_USER) {
923 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
924 return NT_STATUS_LOGON_FAILURE;
927 result = winbindd_get_creds(domain,
928 state->mem_ctx,
929 &sid,
930 &my_info3,
931 &cached_nt_pass,
932 &cached_salt);
933 if (!NT_STATUS_IS_OK(result)) {
934 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
935 return result;
938 *info3 = my_info3;
940 E_md4hash(state->request->data.auth.pass, new_nt_pass);
942 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
943 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
944 if (cached_salt) {
945 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
948 if (cached_salt) {
949 /* In this case we didn't store the nt_hash itself,
950 but the MD5 combination of salt + nt_hash. */
951 uchar salted_hash[NT_HASH_LEN];
952 E_md5hash(cached_salt, new_nt_pass, salted_hash);
954 password_good = (memcmp(cached_nt_pass, salted_hash,
955 NT_HASH_LEN) == 0);
956 } else {
957 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
958 password_good = (memcmp(cached_nt_pass, new_nt_pass,
959 NT_HASH_LEN) == 0);
962 if (password_good) {
964 /* User *DOES* know the password, update logon_time and reset
965 * bad_pw_count */
967 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
969 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
970 return NT_STATUS_ACCOUNT_LOCKED_OUT;
973 if (my_info3->base.acct_flags & ACB_DISABLED) {
974 return NT_STATUS_ACCOUNT_DISABLED;
977 if (my_info3->base.acct_flags & ACB_WSTRUST) {
978 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
981 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
982 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
985 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
986 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
989 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
990 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
991 my_info3->base.acct_flags));
992 return NT_STATUS_LOGON_FAILURE;
995 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
996 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
997 return NT_STATUS_ACCOUNT_EXPIRED;
1000 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
1001 if (must_change_time != 0 && must_change_time < time(NULL)) {
1002 /* we allow grace logons when the password has expired */
1003 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
1004 /* return NT_STATUS_PASSWORD_EXPIRED; */
1005 goto success;
1008 #ifdef HAVE_KRB5
1009 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
1010 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
1011 ((tdc_domain->trust_type & LSA_TRUST_TYPE_UPLEVEL) ||
1012 /* used to cope with the case winbindd starting without network. */
1013 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
1015 uid_t uid = -1;
1016 const char *cc = NULL;
1017 char *realm = NULL;
1018 const char *principal_s = NULL;
1019 const char *service = NULL;
1020 const char *user_ccache_file;
1022 if (domain->alt_name == NULL) {
1023 return NT_STATUS_INVALID_PARAMETER;
1026 uid = get_uid_from_request(state->request);
1027 if (uid == -1) {
1028 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1029 return NT_STATUS_INVALID_PARAMETER;
1032 cc = generate_krb5_ccache(state->mem_ctx,
1033 state->request->data.auth.krb5_cc_type,
1034 state->request->data.auth.uid,
1035 &user_ccache_file);
1036 if (cc == NULL) {
1037 return NT_STATUS_NO_MEMORY;
1040 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
1041 if (realm == NULL) {
1042 return NT_STATUS_NO_MEMORY;
1045 if (!strupper_m(realm)) {
1046 return NT_STATUS_INVALID_PARAMETER;
1049 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1050 if (principal_s == NULL) {
1051 return NT_STATUS_NO_MEMORY;
1054 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1055 if (service == NULL) {
1056 return NT_STATUS_NO_MEMORY;
1059 if (user_ccache_file != NULL) {
1061 fstrcpy(state->response->data.auth.krb5ccname,
1062 user_ccache_file);
1064 result = add_ccache_to_list(principal_s,
1066 service,
1067 state->request->data.auth.user,
1068 state->request->data.auth.pass,
1069 realm,
1070 uid,
1071 time(NULL),
1072 time(NULL) + lp_winbind_cache_time(),
1073 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1074 true);
1076 if (!NT_STATUS_IS_OK(result)) {
1077 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1078 "to add ccache to list: %s\n",
1079 nt_errstr(result)));
1083 #endif /* HAVE_KRB5 */
1084 success:
1085 /* FIXME: we possibly should handle logon hours as well (does xp when
1086 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1088 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1089 my_info3->base.bad_password_count = 0;
1091 result = winbindd_update_creds_by_info3(domain,
1092 state->request->data.auth.user,
1093 state->request->data.auth.pass,
1094 my_info3);
1095 if (!NT_STATUS_IS_OK(result)) {
1096 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1097 nt_errstr(result)));
1098 return result;
1101 return NT_STATUS_OK;
1105 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1106 if (domain->online == false) {
1107 goto failed;
1110 /* failure of this is not critical */
1111 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1112 if (!NT_STATUS_IS_OK(result)) {
1113 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1114 "Won't be able to honour account lockout policies\n"));
1117 /* increase counter */
1118 my_info3->base.bad_password_count++;
1120 if (max_allowed_bad_attempts == 0) {
1121 goto failed;
1124 /* lockout user */
1125 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1127 uint32_t password_properties;
1129 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1130 if (!NT_STATUS_IS_OK(result)) {
1131 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1134 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1135 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1136 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1140 failed:
1141 result = winbindd_update_creds_by_info3(domain,
1142 state->request->data.auth.user,
1143 NULL,
1144 my_info3);
1146 if (!NT_STATUS_IS_OK(result)) {
1147 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1148 nt_errstr(result)));
1151 return NT_STATUS_LOGON_FAILURE;
1154 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1155 struct winbindd_cli_state *state,
1156 struct netr_SamInfo3 **info3)
1158 struct winbindd_domain *contact_domain;
1159 fstring name_domain, name_user;
1160 NTSTATUS result;
1162 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1164 /* Parse domain and username */
1166 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1168 /* what domain should we contact? */
1170 if ( IS_DC ) {
1171 if (!(contact_domain = find_domain_from_name(name_domain))) {
1172 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1173 state->request->data.auth.user, name_domain, name_user, name_domain));
1174 result = NT_STATUS_NO_SUCH_USER;
1175 goto done;
1178 } else {
1179 if (is_myname(name_domain)) {
1180 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1181 result = NT_STATUS_NO_SUCH_USER;
1182 goto done;
1185 contact_domain = find_domain_from_name(name_domain);
1186 if (contact_domain == NULL) {
1187 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1188 state->request->data.auth.user, name_domain, name_user, name_domain));
1190 result = NT_STATUS_NO_SUCH_USER;
1191 goto done;
1195 if (contact_domain->initialized &&
1196 contact_domain->active_directory) {
1197 goto try_login;
1200 if (!contact_domain->initialized) {
1201 init_dc_connection(contact_domain, false);
1204 if (!contact_domain->active_directory) {
1205 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1206 return NT_STATUS_INVALID_LOGON_TYPE;
1208 try_login:
1209 result = winbindd_raw_kerberos_login(
1210 state->mem_ctx, contact_domain,
1211 state->request->data.auth.user,
1212 state->request->data.auth.pass,
1213 state->request->data.auth.krb5_cc_type,
1214 get_uid_from_request(state->request),
1215 info3, state->response->data.auth.krb5ccname);
1216 done:
1217 return result;
1220 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1221 uint32_t logon_parameters,
1222 const char *domain, const char *user,
1223 const DATA_BLOB *challenge,
1224 const DATA_BLOB *lm_resp,
1225 const DATA_BLOB *nt_resp,
1226 struct netr_SamInfo3 **pinfo3)
1228 struct auth_context *auth_context;
1229 struct auth_serversupplied_info *server_info;
1230 struct auth_usersupplied_info *user_info = NULL;
1231 struct tsocket_address *local;
1232 struct netr_SamInfo3 *info3;
1233 NTSTATUS status;
1234 int rc;
1235 TALLOC_CTX *frame = talloc_stackframe();
1237 rc = tsocket_address_inet_from_strings(frame,
1238 "ip",
1239 "127.0.0.1",
1241 &local);
1242 if (rc < 0) {
1243 TALLOC_FREE(frame);
1244 return NT_STATUS_NO_MEMORY;
1246 status = make_user_info(frame, &user_info, user, user, domain, domain,
1247 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1248 NULL, AUTH_PASSWORD_RESPONSE);
1249 if (!NT_STATUS_IS_OK(status)) {
1250 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1251 TALLOC_FREE(frame);
1252 return status;
1255 user_info->logon_parameters = logon_parameters;
1257 /* We don't want any more mapping of the username */
1258 user_info->mapped_state = True;
1260 /* We don't want to come back to winbindd or to do PAM account checks */
1261 user_info->flags |= USER_INFO_LOCAL_SAM_ONLY | USER_INFO_INFO3_AND_NO_AUTHZ;
1263 status = make_auth_context_fixed(frame, &auth_context, challenge->data);
1265 if (!NT_STATUS_IS_OK(status)) {
1266 DEBUG(0, ("Failed to test authentication with check_sam_security_info3: %s\n", nt_errstr(status)));
1267 TALLOC_FREE(frame);
1268 return status;
1271 status = auth_check_ntlm_password(mem_ctx,
1272 auth_context,
1273 user_info,
1274 &server_info);
1276 if (!NT_STATUS_IS_OK(status)) {
1277 TALLOC_FREE(frame);
1278 return status;
1281 info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
1282 if (info3 == NULL) {
1283 TALLOC_FREE(frame);
1284 return NT_STATUS_NO_MEMORY;
1287 status = serverinfo_to_SamInfo3(server_info, info3);
1288 if (!NT_STATUS_IS_OK(status)) {
1289 TALLOC_FREE(frame);
1290 TALLOC_FREE(info3);
1291 DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
1292 nt_errstr(status)));
1293 return status;
1296 *pinfo3 = info3;
1297 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1298 user, nt_errstr(status)));
1299 TALLOC_FREE(frame);
1300 return status;
1303 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1304 TALLOC_CTX *mem_ctx,
1305 uint32_t logon_parameters,
1306 const char *server,
1307 const char *username,
1308 const char *password,
1309 const char *domainname,
1310 const char *workstation,
1311 const uint8_t chal[8],
1312 DATA_BLOB lm_response,
1313 DATA_BLOB nt_response,
1314 bool interactive,
1315 struct netr_SamInfo3 **info3)
1317 int attempts = 0;
1318 int netr_attempts = 0;
1319 bool retry = false;
1320 NTSTATUS result;
1322 do {
1323 struct rpc_pipe_client *netlogon_pipe;
1324 uint8_t authoritative = 0;
1325 uint32_t flags = 0;
1327 ZERO_STRUCTP(info3);
1328 retry = false;
1330 result = cm_connect_netlogon(domain, &netlogon_pipe);
1332 if (!NT_STATUS_IS_OK(result)) {
1333 DEBUG(3,("Could not open handle to NETLOGON pipe "
1334 "(error: %s, attempts: %d)\n",
1335 nt_errstr(result), netr_attempts));
1337 /* After the first retry always close the connection */
1338 if (netr_attempts > 0) {
1339 DEBUG(3, ("This is again a problem for this "
1340 "particular call, forcing the close "
1341 "of this connection\n"));
1342 invalidate_cm_connection(domain);
1345 /* After the second retry failover to the next DC */
1346 if (netr_attempts > 1) {
1348 * If the netlogon server is not reachable then
1349 * it is possible that the DC is rebuilding
1350 * sysvol and shutdown netlogon for that time.
1351 * We should failover to the next dc.
1353 DEBUG(3, ("This is the third problem for this "
1354 "particular call, adding DC to the "
1355 "negative cache list\n"));
1356 add_failed_connection_entry(domain->name,
1357 domain->dcname,
1358 result);
1359 saf_delete(domain->name);
1362 /* Only allow 3 retries */
1363 if (netr_attempts < 3) {
1364 DEBUG(3, ("The connection to netlogon "
1365 "failed, retrying\n"));
1366 netr_attempts++;
1367 retry = true;
1368 continue;
1370 return result;
1372 netr_attempts = 0;
1374 if (interactive && username != NULL && password != NULL) {
1375 result = rpccli_netlogon_password_logon(domain->conn.netlogon_creds,
1376 netlogon_pipe->binding_handle,
1377 mem_ctx,
1378 logon_parameters,
1379 domainname,
1380 username,
1381 password,
1382 workstation,
1383 NetlogonInteractiveInformation,
1384 info3);
1385 } else {
1386 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1387 netlogon_pipe->binding_handle,
1388 mem_ctx,
1389 logon_parameters,
1390 username,
1391 domainname,
1392 workstation,
1393 chal,
1394 lm_response,
1395 nt_response,
1396 &authoritative,
1397 &flags,
1398 info3);
1402 * we increment this after the "feature negotiation"
1403 * for can_do_samlogon_ex and can_do_validation6
1405 attempts += 1;
1407 /* We have to try a second time as cm_connect_netlogon
1408 might not yet have noticed that the DC has killed
1409 our connection. */
1411 if (!rpccli_is_connected(netlogon_pipe)) {
1412 retry = true;
1413 continue;
1416 /* if we get access denied, a possible cause was that we had
1417 and open connection to the DC, but someone changed our
1418 machine account password out from underneath us using 'net
1419 rpc changetrustpw' */
1421 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1422 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1423 "ACCESS_DENIED. Maybe the trust account "
1424 "password was changed and we didn't know it. "
1425 "Killing connections to domain %s\n",
1426 domainname));
1427 invalidate_cm_connection(domain);
1428 retry = true;
1431 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1433 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1434 * (no Ex). This happens against old Samba
1435 * DCs, if LogonSamLogonEx() fails with an error
1436 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1438 * The server will log something like this:
1439 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1441 * This sets the whole connection into a fault_state mode
1442 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1444 * This also happens to our retry with LogonSamLogonWithFlags()
1445 * and LogonSamLogon().
1447 * In order to recover from this situation, we need to
1448 * drop the connection.
1450 invalidate_cm_connection(domain);
1451 result = NT_STATUS_LOGON_FAILURE;
1452 break;
1455 } while ( (attempts < 2) && retry );
1457 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1458 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1459 "returned NT_STATUS_IO_TIMEOUT after the retry."
1460 "Killing connections to domain %s\n",
1461 domainname));
1462 invalidate_cm_connection(domain);
1464 return result;
1467 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1468 struct winbindd_domain *domain,
1469 const char *user,
1470 const char *pass,
1471 uint32_t request_flags,
1472 struct netr_SamInfo3 **info3)
1475 uchar chal[8];
1476 DATA_BLOB lm_resp;
1477 DATA_BLOB nt_resp;
1478 unsigned char local_nt_response[24];
1479 fstring name_domain, name_user;
1480 NTSTATUS result;
1481 struct netr_SamInfo3 *my_info3 = NULL;
1483 *info3 = NULL;
1485 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1487 /* Parse domain and username */
1489 parse_domain_user(user, name_domain, name_user);
1491 /* do password magic */
1493 generate_random_buffer(chal, sizeof(chal));
1495 if (lp_client_ntlmv2_auth()) {
1496 DATA_BLOB server_chal;
1497 DATA_BLOB names_blob;
1498 server_chal = data_blob_const(chal, 8);
1500 /* note that the 'workgroup' here is for the local
1501 machine. The 'server name' must match the
1502 'workstation' passed to the actual SamLogon call.
1504 names_blob = NTLMv2_generate_names_blob(
1505 mem_ctx, lp_netbios_name(), lp_workgroup());
1507 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1508 pass,
1509 &server_chal,
1510 &names_blob,
1511 &lm_resp, &nt_resp, NULL, NULL)) {
1512 data_blob_free(&names_blob);
1513 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1514 result = NT_STATUS_NO_MEMORY;
1515 goto done;
1517 data_blob_free(&names_blob);
1518 } else {
1519 lm_resp = data_blob_null;
1520 SMBNTencrypt(pass, chal, local_nt_response);
1522 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1523 sizeof(local_nt_response));
1526 if (strequal(name_domain, get_global_sam_name())) {
1527 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1529 result = winbindd_dual_auth_passdb(
1530 mem_ctx, 0, name_domain, name_user,
1531 &chal_blob, &lm_resp, &nt_resp, info3);
1534 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1536 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1537 goto done;
1541 /* check authentication loop */
1543 result = winbind_samlogon_retry_loop(domain,
1544 mem_ctx,
1546 domain->dcname,
1547 name_user,
1548 pass,
1549 name_domain,
1550 lp_netbios_name(),
1551 chal,
1552 lm_resp,
1553 nt_resp,
1554 true, /* interactive */
1555 &my_info3);
1556 if (!NT_STATUS_IS_OK(result)) {
1557 goto done;
1560 /* handle the case where a NT4 DC does not fill in the acct_flags in
1561 * the samlogon reply info3. When accurate info3 is required by the
1562 * caller, we look up the account flags ourselve - gd */
1564 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1565 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1567 struct rpc_pipe_client *samr_pipe;
1568 struct policy_handle samr_domain_handle, user_pol;
1569 union samr_UserInfo *info = NULL;
1570 NTSTATUS status_tmp, result_tmp;
1571 uint32_t acct_flags;
1572 struct dcerpc_binding_handle *b;
1574 status_tmp = cm_connect_sam(domain, mem_ctx, false,
1575 &samr_pipe, &samr_domain_handle);
1577 if (!NT_STATUS_IS_OK(status_tmp)) {
1578 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1579 nt_errstr(status_tmp)));
1580 goto done;
1583 b = samr_pipe->binding_handle;
1585 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1586 &samr_domain_handle,
1587 MAXIMUM_ALLOWED_ACCESS,
1588 my_info3->base.rid,
1589 &user_pol,
1590 &result_tmp);
1592 if (!NT_STATUS_IS_OK(status_tmp)) {
1593 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1594 nt_errstr(status_tmp)));
1595 goto done;
1597 if (!NT_STATUS_IS_OK(result_tmp)) {
1598 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1599 nt_errstr(result_tmp)));
1600 goto done;
1603 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1604 &user_pol,
1606 &info,
1607 &result_tmp);
1609 if (!NT_STATUS_IS_OK(status_tmp)) {
1610 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1611 nt_errstr(status_tmp)));
1612 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1613 goto done;
1615 if (!NT_STATUS_IS_OK(result_tmp)) {
1616 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1617 nt_errstr(result_tmp)));
1618 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1619 goto done;
1622 acct_flags = info->info16.acct_flags;
1624 if (acct_flags == 0) {
1625 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1626 goto done;
1629 my_info3->base.acct_flags = acct_flags;
1631 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1633 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1636 *info3 = my_info3;
1637 done:
1638 return result;
1641 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1642 struct winbindd_cli_state *state)
1644 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1645 NTSTATUS krb5_result = NT_STATUS_OK;
1646 fstring name_domain, name_user;
1647 char *mapped_user;
1648 fstring domain_user;
1649 struct netr_SamInfo3 *info3 = NULL;
1650 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1652 /* Ensure null termination */
1653 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1655 /* Ensure null termination */
1656 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1658 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1659 state->request->data.auth.user));
1661 /* Parse domain and username */
1663 name_map_status = normalize_name_unmap(state->mem_ctx,
1664 state->request->data.auth.user,
1665 &mapped_user);
1667 /* If the name normalization didnt' actually do anything,
1668 just use the original name */
1670 if (!NT_STATUS_IS_OK(name_map_status) &&
1671 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1673 mapped_user = state->request->data.auth.user;
1676 parse_domain_user(mapped_user, name_domain, name_user);
1678 if ( mapped_user != state->request->data.auth.user ) {
1679 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1680 *lp_winbind_separator(),
1681 name_user );
1682 strlcpy( state->request->data.auth.user, domain_user,
1683 sizeof(state->request->data.auth.user));
1686 if (!domain->online) {
1687 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1688 if (domain->startup) {
1689 /* Logons are very important to users. If we're offline and
1690 we get a request within the first 30 seconds of startup,
1691 try very hard to find a DC and go online. */
1693 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1694 "request in startup mode.\n", domain->name ));
1696 winbindd_flush_negative_conn_cache(domain);
1697 result = init_dc_connection(domain, false);
1701 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1703 /* Check for Kerberos authentication */
1704 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1706 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1707 /* save for later */
1708 krb5_result = result;
1711 if (NT_STATUS_IS_OK(result)) {
1712 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1713 goto process_result;
1714 } else {
1715 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1718 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1719 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1720 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1721 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1722 set_domain_offline( domain );
1723 goto cached_logon;
1726 /* there are quite some NT_STATUS errors where there is no
1727 * point in retrying with a samlogon, we explictly have to take
1728 * care not to increase the bad logon counter on the DC */
1730 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1731 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1732 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1733 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1734 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1735 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1736 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1737 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1738 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1739 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1740 goto done;
1743 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1744 DEBUG(3,("falling back to samlogon\n"));
1745 goto sam_logon;
1746 } else {
1747 goto cached_logon;
1751 sam_logon:
1752 /* Check for Samlogon authentication */
1753 if (domain->online) {
1754 result = winbindd_dual_pam_auth_samlogon(
1755 state->mem_ctx, domain,
1756 state->request->data.auth.user,
1757 state->request->data.auth.pass,
1758 state->request->flags,
1759 &info3);
1761 if (NT_STATUS_IS_OK(result)) {
1762 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1763 /* add the Krb5 err if we have one */
1764 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1765 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1767 goto process_result;
1770 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1771 nt_errstr(result)));
1773 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1774 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1775 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1777 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1778 set_domain_offline( domain );
1779 goto cached_logon;
1782 if (domain->online) {
1783 /* We're still online - fail. */
1784 goto done;
1788 cached_logon:
1789 /* Check for Cached logons */
1790 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1791 lp_winbind_offline_logon()) {
1793 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1795 if (NT_STATUS_IS_OK(result)) {
1796 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1797 goto process_result;
1798 } else {
1799 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1800 goto done;
1804 process_result:
1806 if (NT_STATUS_IS_OK(result)) {
1808 struct dom_sid user_sid;
1810 /* In all codepaths where result == NT_STATUS_OK info3 must have
1811 been initialized. */
1812 if (!info3) {
1813 result = NT_STATUS_INTERNAL_ERROR;
1814 goto done;
1817 sid_compose(&user_sid, info3->base.domain_sid,
1818 info3->base.rid);
1820 if (info3->base.full_name.string == NULL) {
1821 struct netr_SamInfo3 *cached_info3;
1823 cached_info3 = netsamlogon_cache_get(state->mem_ctx,
1824 &user_sid);
1825 if (cached_info3 != NULL &&
1826 cached_info3->base.full_name.string != NULL) {
1827 info3->base.full_name.string =
1828 talloc_strdup(info3,
1829 cached_info3->base.full_name.string);
1830 } else {
1832 /* this might fail so we dont check the return code */
1833 wcache_query_user_fullname(domain,
1834 info3,
1835 &user_sid,
1836 &info3->base.full_name.string);
1840 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1841 &user_sid);
1842 netsamlogon_cache_store(name_user, info3);
1844 /* save name_to_sid info as early as possible (only if
1845 this is our primary domain so we don't invalidate
1846 the cache entry by storing the seq_num for the wrong
1847 domain). */
1848 if ( domain->primary ) {
1849 cache_name2sid(domain, name_domain, name_user,
1850 SID_NAME_USER, &user_sid);
1853 /* Check if the user is in the right group */
1855 result = check_info3_in_group(
1856 info3,
1857 state->request->data.auth.require_membership_of_sid);
1858 if (!NT_STATUS_IS_OK(result)) {
1859 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1860 state->request->data.auth.user,
1861 state->request->data.auth.require_membership_of_sid));
1862 goto done;
1865 result = append_auth_data(state->mem_ctx, state->response,
1866 state->request->flags, info3,
1867 name_domain, name_user);
1868 if (!NT_STATUS_IS_OK(result)) {
1869 goto done;
1872 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1873 && lp_winbind_offline_logon()) {
1875 result = winbindd_store_creds(domain,
1876 state->request->data.auth.user,
1877 state->request->data.auth.pass,
1878 info3);
1881 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1882 struct winbindd_domain *our_domain = find_our_domain();
1884 /* This is not entirely correct I believe, but it is
1885 consistent. Only apply the password policy settings
1886 too warn users for our own domain. Cannot obtain these
1887 from trusted DCs all the time so don't do it at all.
1888 -- jerry */
1890 result = NT_STATUS_NOT_SUPPORTED;
1891 if (our_domain == domain ) {
1892 result = fillup_password_policy(
1893 our_domain, state->response);
1896 if (!NT_STATUS_IS_OK(result)
1897 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1899 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1900 domain->name, nt_errstr(result)));
1901 goto done;
1905 result = NT_STATUS_OK;
1908 done:
1909 /* give us a more useful (more correct?) error code */
1910 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1911 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1912 result = NT_STATUS_NO_LOGON_SERVERS;
1915 set_auth_errors(state->response, result);
1917 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1918 state->request->data.auth.user,
1919 state->response->data.auth.nt_status_string,
1920 state->response->data.auth.pam_error));
1922 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1925 NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
1926 TALLOC_CTX *mem_ctx,
1927 uint32_t logon_parameters,
1928 const char *name_user,
1929 const char *name_domain,
1930 const char *workstation,
1931 const uint8_t chal[8],
1932 DATA_BLOB lm_response,
1933 DATA_BLOB nt_response,
1934 struct netr_SamInfo3 **info3)
1936 NTSTATUS result;
1938 if (strequal(name_domain, get_global_sam_name())) {
1939 DATA_BLOB chal_blob = data_blob_const(
1940 chal, 8);
1942 result = winbindd_dual_auth_passdb(
1943 mem_ctx,
1944 logon_parameters,
1945 name_domain, name_user,
1946 &chal_blob, &lm_response, &nt_response, info3);
1949 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1951 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1952 goto process_result;
1956 result = winbind_samlogon_retry_loop(domain,
1957 mem_ctx,
1958 logon_parameters,
1959 domain->dcname,
1960 name_user,
1961 NULL, /* password */
1962 name_domain,
1963 /* Bug #3248 - found by Stefan Burkei. */
1964 workstation, /* We carefully set this above so use it... */
1965 chal,
1966 lm_response,
1967 nt_response,
1968 false, /* interactive */
1969 info3);
1970 if (!NT_STATUS_IS_OK(result)) {
1971 goto done;
1974 process_result:
1976 if (NT_STATUS_IS_OK(result)) {
1977 struct dom_sid user_sid;
1979 sid_compose(&user_sid, (*info3)->base.domain_sid,
1980 (*info3)->base.rid);
1982 if ((*info3)->base.full_name.string == NULL) {
1983 struct netr_SamInfo3 *cached_info3;
1985 cached_info3 = netsamlogon_cache_get(mem_ctx,
1986 &user_sid);
1987 if (cached_info3 != NULL &&
1988 cached_info3->base.full_name.string != NULL) {
1989 (*info3)->base.full_name.string =
1990 talloc_strdup(*info3,
1991 cached_info3->base.full_name.string);
1992 } else {
1994 /* this might fail so we dont check the return code */
1995 wcache_query_user_fullname(domain,
1996 *info3,
1997 &user_sid,
1998 &(*info3)->base.full_name.string);
2002 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
2003 &user_sid);
2004 netsamlogon_cache_store(name_user, *info3);
2007 done:
2009 /* give us a more useful (more correct?) error code */
2010 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2011 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2012 result = NT_STATUS_NO_LOGON_SERVERS;
2015 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2016 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
2017 name_domain,
2018 name_user,
2019 nt_errstr(result)));
2021 return result;
2024 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
2025 struct winbindd_cli_state *state)
2027 NTSTATUS result;
2028 struct netr_SamInfo3 *info3 = NULL;
2029 const char *name_user = NULL;
2030 const char *name_domain = NULL;
2031 const char *workstation;
2033 DATA_BLOB lm_resp, nt_resp;
2035 /* This is child-only, so no check for privileged access is needed
2036 anymore */
2038 /* Ensure null termination */
2039 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
2040 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
2042 name_user = state->request->data.auth_crap.user;
2043 name_domain = state->request->data.auth_crap.domain;
2044 workstation = state->request->data.auth_crap.workstation;
2046 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
2047 name_domain, name_user));
2049 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
2050 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
2051 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
2052 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
2053 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2054 state->request->data.auth_crap.lm_resp_len,
2055 state->request->data.auth_crap.nt_resp_len));
2056 result = NT_STATUS_INVALID_PARAMETER;
2057 goto done;
2061 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
2062 state->request->data.auth_crap.lm_resp_len);
2064 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
2065 nt_resp = data_blob_talloc(state->mem_ctx,
2066 state->request->extra_data.data,
2067 state->request->data.auth_crap.nt_resp_len);
2068 } else {
2069 nt_resp = data_blob_talloc(state->mem_ctx,
2070 state->request->data.auth_crap.nt_resp,
2071 state->request->data.auth_crap.nt_resp_len);
2074 result = winbind_dual_SamLogon(domain,
2075 state->mem_ctx,
2076 state->request->data.auth_crap.logon_parameters,
2077 name_user,
2078 name_domain,
2079 /* Bug #3248 - found by Stefan Burkei. */
2080 workstation, /* We carefully set this above so use it... */
2081 state->request->data.auth_crap.chal,
2082 lm_resp,
2083 nt_resp,
2084 &info3);
2085 if (!NT_STATUS_IS_OK(result)) {
2086 goto done;
2089 if (NT_STATUS_IS_OK(result)) {
2090 /* Check if the user is in the right group */
2092 result = check_info3_in_group(
2093 info3,
2094 state->request->data.auth_crap.require_membership_of_sid);
2095 if (!NT_STATUS_IS_OK(result)) {
2096 DEBUG(3, ("User %s is not in the required group (%s), so "
2097 "crap authentication is rejected\n",
2098 state->request->data.auth_crap.user,
2099 state->request->data.auth_crap.require_membership_of_sid));
2100 goto done;
2103 result = append_auth_data(state->mem_ctx, state->response,
2104 state->request->flags, info3,
2105 name_domain, name_user);
2106 if (!NT_STATUS_IS_OK(result)) {
2107 goto done;
2111 done:
2113 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2114 result = nt_status_squash(result);
2117 set_auth_errors(state->response, result);
2119 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2122 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2123 struct winbindd_cli_state *state)
2125 char *oldpass;
2126 char *newpass = NULL;
2127 struct policy_handle dom_pol;
2128 struct rpc_pipe_client *cli = NULL;
2129 bool got_info = false;
2130 struct samr_DomInfo1 *info = NULL;
2131 struct userPwdChangeFailureInformation *reject = NULL;
2132 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2133 fstring domain, user;
2134 struct dcerpc_binding_handle *b = NULL;
2136 ZERO_STRUCT(dom_pol);
2138 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2139 state->request->data.auth.user));
2141 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2142 goto done;
2145 /* Change password */
2147 oldpass = state->request->data.chauthtok.oldpass;
2148 newpass = state->request->data.chauthtok.newpass;
2150 /* Initialize reject reason */
2151 state->response->data.auth.reject_reason = Undefined;
2153 /* Get sam handle */
2155 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli,
2156 &dom_pol);
2157 if (!NT_STATUS_IS_OK(result)) {
2158 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2159 goto done;
2162 b = cli->binding_handle;
2164 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2165 user,
2166 newpass,
2167 oldpass,
2168 &info,
2169 &reject);
2171 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2173 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2175 fill_in_password_policy(state->response, info);
2177 state->response->data.auth.reject_reason =
2178 reject->extendedFailureReason;
2180 got_info = true;
2183 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2184 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2185 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2186 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2188 /* only fallback when the chgpasswd_user3 call is not supported */
2189 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2190 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2191 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2192 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2194 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2195 nt_errstr(result)));
2197 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2199 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2200 Map to the same status code as Windows 2003. */
2202 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2203 result = NT_STATUS_PASSWORD_RESTRICTION;
2207 done:
2209 if (NT_STATUS_IS_OK(result)
2210 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2211 && lp_winbind_offline_logon()) {
2212 result = winbindd_update_creds_by_name(contact_domain, user,
2213 newpass);
2214 /* Again, this happens when we login from gdm or xdm
2215 * and the password expires, *BUT* cached crendentials
2216 * doesn't exist. winbindd_update_creds_by_name()
2217 * returns NT_STATUS_NO_SUCH_USER.
2218 * This is not a failure.
2219 * --- BoYang
2220 * */
2221 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2222 result = NT_STATUS_OK;
2225 if (!NT_STATUS_IS_OK(result)) {
2226 DEBUG(10, ("Failed to store creds: %s\n",
2227 nt_errstr(result)));
2228 goto process_result;
2232 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2234 NTSTATUS policy_ret;
2236 policy_ret = fillup_password_policy(
2237 contact_domain, state->response);
2239 /* failure of this is non critical, it will just provide no
2240 * additional information to the client why the change has
2241 * failed - Guenther */
2243 if (!NT_STATUS_IS_OK(policy_ret)) {
2244 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2245 goto process_result;
2249 process_result:
2251 if (strequal(contact_domain->name, get_global_sam_name())) {
2252 /* FIXME: internal rpc pipe does not cache handles yet */
2253 if (b) {
2254 if (is_valid_policy_hnd(&dom_pol)) {
2255 NTSTATUS _result;
2256 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2258 TALLOC_FREE(cli);
2262 set_auth_errors(state->response, result);
2264 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2265 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2266 domain,
2267 user,
2268 state->response->data.auth.nt_status_string,
2269 state->response->data.auth.pam_error));
2271 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2274 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2275 struct winbindd_cli_state *state)
2277 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2279 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2280 state->request->data.logoff.user));
2282 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2283 result = NT_STATUS_OK;
2284 goto process_result;
2287 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2288 result = NT_STATUS_OK;
2289 goto process_result;
2292 #ifdef HAVE_KRB5
2294 if (state->request->data.logoff.uid < 0) {
2295 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2296 goto process_result;
2299 /* what we need here is to find the corresponding krb5 ccache name *we*
2300 * created for a given username and destroy it */
2302 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2303 result = NT_STATUS_OK;
2304 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2305 goto process_result;
2308 if (!ccache_entry_identical(state->request->data.logoff.user,
2309 state->request->data.logoff.uid,
2310 state->request->data.logoff.krb5ccname)) {
2311 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2312 goto process_result;
2315 result = remove_ccache(state->request->data.logoff.user);
2316 if (!NT_STATUS_IS_OK(result)) {
2317 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2318 nt_errstr(result)));
2319 goto process_result;
2323 * Remove any mlock'ed memory creds in the child
2324 * we might be using for krb5 ticket renewal.
2327 winbindd_delete_memory_creds(state->request->data.logoff.user);
2329 #else
2330 result = NT_STATUS_NOT_SUPPORTED;
2331 #endif
2333 process_result:
2336 set_auth_errors(state->response, result);
2338 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2341 /* Change user password with auth crap*/
2343 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2345 NTSTATUS result;
2346 DATA_BLOB new_nt_password;
2347 DATA_BLOB old_nt_hash_enc;
2348 DATA_BLOB new_lm_password;
2349 DATA_BLOB old_lm_hash_enc;
2350 fstring domain,user;
2351 struct policy_handle dom_pol;
2352 struct winbindd_domain *contact_domain = domainSt;
2353 struct rpc_pipe_client *cli = NULL;
2354 struct dcerpc_binding_handle *b = NULL;
2356 ZERO_STRUCT(dom_pol);
2358 /* Ensure null termination */
2359 state->request->data.chng_pswd_auth_crap.user[
2360 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2361 state->request->data.chng_pswd_auth_crap.domain[
2362 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2363 *domain = 0;
2364 *user = 0;
2366 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2367 (unsigned long)state->pid,
2368 state->request->data.chng_pswd_auth_crap.domain,
2369 state->request->data.chng_pswd_auth_crap.user));
2371 if (lp_winbind_offline_logon()) {
2372 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2373 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2374 result = NT_STATUS_ACCESS_DENIED;
2375 goto done;
2378 if (*state->request->data.chng_pswd_auth_crap.domain) {
2379 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2380 } else {
2381 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2382 domain, user);
2384 if(!*domain) {
2385 DEBUG(3,("no domain specified with username (%s) - "
2386 "failing auth\n",
2387 state->request->data.chng_pswd_auth_crap.user));
2388 result = NT_STATUS_NO_SUCH_USER;
2389 goto done;
2393 if (!*domain && lp_winbind_use_default_domain()) {
2394 fstrcpy(domain,lp_workgroup());
2397 if(!*user) {
2398 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2401 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2402 (unsigned long)state->pid, domain, user));
2404 /* Change password */
2405 new_nt_password = data_blob_const(
2406 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2407 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2409 old_nt_hash_enc = data_blob_const(
2410 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2411 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2413 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2414 new_lm_password = data_blob_const(
2415 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2416 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2418 old_lm_hash_enc = data_blob_const(
2419 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2420 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2421 } else {
2422 new_lm_password = data_blob_null;
2423 old_lm_hash_enc = data_blob_null;
2426 /* Get sam handle */
2428 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli, &dom_pol);
2429 if (!NT_STATUS_IS_OK(result)) {
2430 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2431 goto done;
2434 b = cli->binding_handle;
2436 result = rpccli_samr_chng_pswd_auth_crap(
2437 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2438 new_lm_password, old_lm_hash_enc);
2440 done:
2442 if (strequal(contact_domain->name, get_global_sam_name())) {
2443 /* FIXME: internal rpc pipe does not cache handles yet */
2444 if (b) {
2445 if (is_valid_policy_hnd(&dom_pol)) {
2446 NTSTATUS _result;
2447 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2449 TALLOC_FREE(cli);
2453 set_auth_errors(state->response, result);
2455 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2456 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2457 domain, user,
2458 state->response->data.auth.nt_status_string,
2459 state->response->data.auth.pam_error));
2461 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2464 #ifdef HAVE_KRB5
2465 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2466 struct PAC_LOGON_INFO **logon_info)
2468 krb5_context krbctx = NULL;
2469 krb5_error_code k5ret;
2470 krb5_keytab keytab;
2471 krb5_kt_cursor cursor;
2472 krb5_keytab_entry entry;
2473 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2475 ZERO_STRUCT(entry);
2476 ZERO_STRUCT(cursor);
2478 k5ret = krb5_init_context(&krbctx);
2479 if (k5ret) {
2480 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2481 error_message(k5ret)));
2482 status = krb5_to_nt_status(k5ret);
2483 goto out;
2486 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2487 if (k5ret) {
2488 DEBUG(1, ("Failed to get keytab: %s\n",
2489 error_message(k5ret)));
2490 status = krb5_to_nt_status(k5ret);
2491 goto out_free;
2494 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2495 if (k5ret) {
2496 DEBUG(1, ("Failed to start seq: %s\n",
2497 error_message(k5ret)));
2498 status = krb5_to_nt_status(k5ret);
2499 goto out_keytab;
2502 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2503 while (k5ret == 0) {
2504 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2505 krbctx, NULL,
2506 KRB5_KT_KEY(&entry), NULL, 0,
2507 logon_info);
2508 if (NT_STATUS_IS_OK(status)) {
2509 break;
2511 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2512 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2515 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2516 if (k5ret) {
2517 DEBUG(1, ("Failed to end seq: %s\n",
2518 error_message(k5ret)));
2520 out_keytab:
2521 k5ret = krb5_kt_close(krbctx, keytab);
2522 if (k5ret) {
2523 DEBUG(1, ("Failed to close keytab: %s\n",
2524 error_message(k5ret)));
2526 out_free:
2527 krb5_free_context(krbctx);
2528 out:
2529 return status;
2532 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2533 struct netr_SamInfo3 **info3)
2535 struct winbindd_request *req = state->request;
2536 DATA_BLOB pac_blob;
2537 struct PAC_LOGON_INFO *logon_info = NULL;
2538 struct netr_SamInfo3 *info3_copy = NULL;
2539 NTSTATUS result;
2541 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2542 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2543 if (!NT_STATUS_IS_OK(result) &&
2544 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2545 DEBUG(1, ("Error during PAC signature verification: %s\n",
2546 nt_errstr(result)));
2547 return result;
2550 if (logon_info) {
2551 /* Signature verification succeeded, trust the PAC */
2552 result = create_info3_from_pac_logon_info(state->mem_ctx,
2553 logon_info,
2554 &info3_copy);
2555 if (!NT_STATUS_IS_OK(result)) {
2556 return result;
2558 netsamlogon_cache_store(NULL, info3_copy);
2560 } else {
2561 /* Try without signature verification */
2562 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2563 NULL, NULL, NULL, 0,
2564 &logon_info);
2565 if (!NT_STATUS_IS_OK(result)) {
2566 DEBUG(10, ("Could not extract PAC: %s\n",
2567 nt_errstr(result)));
2568 return result;
2570 if (logon_info) {
2572 * Don't strictly need to copy here,
2573 * but it makes it explicit we're
2574 * returning a copy talloc'ed off
2575 * the state->mem_ctx.
2577 info3_copy = copy_netr_SamInfo3(state->mem_ctx,
2578 &logon_info->info3);
2579 if (info3_copy == NULL) {
2580 return NT_STATUS_NO_MEMORY;
2585 *info3 = info3_copy;
2587 return NT_STATUS_OK;
2589 #else /* HAVE_KRB5 */
2590 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2591 struct netr_SamInfo3 **info3)
2593 return NT_STATUS_NO_SUCH_USER;
2595 #endif /* HAVE_KRB5 */