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/>.
28 #define DBGC_CLASS DBGC_WINBIND
30 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
32 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
33 struct winbindd_cli_state *state,
34 struct netr_SamInfo3 *info3)
40 state->response.data.auth.info3.logon_time =
41 nt_time_to_unix(info3->base.last_logon);
42 state->response.data.auth.info3.logoff_time =
43 nt_time_to_unix(info3->base.last_logoff);
44 state->response.data.auth.info3.kickoff_time =
45 nt_time_to_unix(info3->base.acct_expiry);
46 state->response.data.auth.info3.pass_last_set_time =
47 nt_time_to_unix(info3->base.last_password_change);
48 state->response.data.auth.info3.pass_can_change_time =
49 nt_time_to_unix(info3->base.allow_password_change);
50 state->response.data.auth.info3.pass_must_change_time =
51 nt_time_to_unix(info3->base.force_password_change);
53 state->response.data.auth.info3.logon_count = info3->base.logon_count;
54 state->response.data.auth.info3.bad_pw_count = info3->base.bad_password_count;
56 state->response.data.auth.info3.user_rid = info3->base.rid;
57 state->response.data.auth.info3.group_rid = info3->base.primary_gid;
58 sid_to_fstring(state->response.data.auth.info3.dom_sid, info3->base.domain_sid);
60 state->response.data.auth.info3.num_groups = info3->base.groups.count;
61 state->response.data.auth.info3.user_flgs = info3->base.user_flags;
63 state->response.data.auth.info3.acct_flags = info3->base.acct_flags;
64 state->response.data.auth.info3.num_other_sids = info3->sidcount;
66 fstrcpy(state->response.data.auth.info3.user_name,
67 info3->base.account_name.string);
68 fstrcpy(state->response.data.auth.info3.full_name,
69 info3->base.full_name.string);
70 fstrcpy(state->response.data.auth.info3.logon_script,
71 info3->base.logon_script.string);
72 fstrcpy(state->response.data.auth.info3.profile_path,
73 info3->base.profile_path.string);
74 fstrcpy(state->response.data.auth.info3.home_dir,
75 info3->base.home_directory.string);
76 fstrcpy(state->response.data.auth.info3.dir_drive,
77 info3->base.home_drive.string);
79 fstrcpy(state->response.data.auth.info3.logon_srv,
80 info3->base.logon_server.string);
81 fstrcpy(state->response.data.auth.info3.logon_dom,
82 info3->base.domain.string);
84 ex = talloc_strdup(mem_ctx, "");
85 NT_STATUS_HAVE_NO_MEMORY(ex);
87 for (i=0; i < info3->base.groups.count; i++) {
88 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
89 info3->base.groups.rids[i].rid,
90 info3->base.groups.rids[i].attributes);
91 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->sidcount; i++) {
97 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
98 NT_STATUS_HAVE_NO_MEMORY(sid);
100 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
102 info3->sids[i].attributes);
103 NT_STATUS_HAVE_NO_MEMORY(ex);
108 size = talloc_get_size(ex);
110 SAFE_FREE(state->response.extra_data.data);
111 state->response.extra_data.data = SMB_MALLOC(size);
112 if (!state->response.extra_data.data) {
113 return NT_STATUS_NO_MEMORY;
115 memcpy(state->response.extra_data.data, ex, size);
118 state->response.length += size;
123 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
124 struct winbindd_cli_state *state,
125 struct netr_SamInfo3 *info3)
128 enum ndr_err_code ndr_err;
130 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
131 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
132 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
133 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
134 return ndr_map_error2ntstatus(ndr_err);
137 SAFE_FREE(state->response.extra_data.data);
138 state->response.extra_data.data = SMB_MALLOC(blob.length);
139 if (!state->response.extra_data.data) {
140 data_blob_free(&blob);
141 return NT_STATUS_NO_MEMORY;
144 memset(state->response.extra_data.data, '\0', blob.length);
145 memcpy(state->response.extra_data.data, blob.data, blob.length);
146 state->response.length += blob.length;
148 data_blob_free(&blob);
153 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
154 struct winbindd_cli_state *state,
155 const struct netr_SamInfo3 *info3,
156 const char *name_domain,
157 const char *name_user)
159 /* We've been asked to return the unix username, per
160 'winbind use default domain' settings and the like */
162 const char *nt_username, *nt_domain;
164 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
166 /* If the server didn't give us one, just use the one
168 nt_domain = name_domain;
171 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
173 /* If the server didn't give us one, just use the one
175 nt_username = name_user;
178 fill_domain_username(state->response.data.auth.unix_username,
179 nt_domain, nt_username, True);
181 DEBUG(5,("Setting unix username to [%s]\n",
182 state->response.data.auth.unix_username));
187 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
188 struct winbindd_cli_state *state,
189 const struct netr_SamInfo3 *info3,
190 const char *name_domain,
191 const char *name_user)
193 char *afsname = NULL;
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(),
204 afsname = talloc_string_sub(mem_ctx, afsname,
206 afsname = talloc_string_sub(mem_ctx, afsname,
213 sid_copy(&user_sid, info3->base.domain_sid);
214 sid_append_rid(&user_sid, info3->base.rid);
215 sid_to_fstring(sidstr, &user_sid);
216 afsname = talloc_string_sub(mem_ctx, afsname,
220 if (afsname == NULL) {
221 return NT_STATUS_NO_MEMORY;
226 DEBUG(10, ("Generating token for user %s\n", afsname));
228 cell = strchr(afsname, '@');
231 return NT_STATUS_NO_MEMORY;
237 /* Append an AFS token string */
238 SAFE_FREE(state->response.extra_data.data);
239 state->response.extra_data.data =
240 afs_createtoken_str(afsname, cell);
242 if (state->response.extra_data.data != NULL) {
243 state->response.length +=
244 strlen((const char *)state->response.extra_data.data)+1;
250 static NTSTATUS check_info3_in_groups(TALLOC_CTX *mem_ctx,
251 struct netr_SamInfo3 *info3,
252 uint32_t num_require_membership_of_sids,
253 struct dom_sid *require_membership_of_sids)
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.
267 struct nt_user_token *token;
270 /* Parse the 'required group' SID */
272 if (num_require_membership_of_sids == 0) {
273 /* NO sid supplied, all users may access */
277 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
278 DEBUG(0, ("talloc failed\n"));
279 return NT_STATUS_NO_MEMORY;
282 status = sid_array_from_info3(mem_ctx, info3,
286 if (!NT_STATUS_IS_OK(status)) {
290 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
292 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
294 DEBUG(3, ("could not add aliases: %s\n",
299 debug_nt_user_token(DBGC_CLASS, 10, token);
301 for (i=0; i<num_require_membership_of_sids; i++) {
302 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
303 &require_membership_of_sids[i])));
304 if (nt_token_check_sid(&require_membership_of_sids[i],
306 DEBUG(10, ("Access ok\n"));
311 /* Do not distinguish this error from a wrong username/pw */
313 return NT_STATUS_LOGON_FAILURE;
316 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
317 struct netr_SamInfo3 *info3,
318 const char *group_sid)
320 * Check whether a user belongs to a group or list of groups.
322 * @param mem_ctx talloc memory context.
323 * @param info3 user information, including group membership info.
324 * @param group_sid One or more groups , separated by commas.
326 * @return NT_STATUS_OK on success,
327 * NT_STATUS_LOGON_FAILURE if the user does not belong,
328 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
331 DOM_SID *require_membership_of_sid;
332 size_t num_require_membership_of_sid;
336 TALLOC_CTX *frame = NULL;
339 /* Parse the 'required group' SID */
341 if (!group_sid || !group_sid[0]) {
342 /* NO sid supplied, all users may access */
346 num_require_membership_of_sid = 0;
347 require_membership_of_sid = NULL;
351 frame = talloc_stackframe();
352 while (next_token_talloc(frame, &p, &req_sid, ",")) {
353 if (!string_to_sid(&sid, req_sid)) {
354 DEBUG(0, ("check_info3_in_group: could not parse %s "
355 "as a SID!", req_sid));
357 return NT_STATUS_INVALID_PARAMETER;
360 status = add_sid_to_array(mem_ctx, &sid,
361 &require_membership_of_sid,
362 &num_require_membership_of_sid);
363 if (!NT_STATUS_IS_OK(status)) {
364 DEBUG(0, ("add_sid_to_array failed\n"));
372 return check_info3_in_groups(mem_ctx, info3,
373 num_require_membership_of_sid,
374 require_membership_of_sid);
377 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
378 const char *domain_name)
380 struct winbindd_domain *domain;
383 domain = find_domain_from_name_noinit(domain_name);
384 if (domain == NULL) {
385 DEBUG(3, ("Authentication for domain [%s] refused "
386 "as it is not a trusted domain\n",
392 if (is_myname(domain_name)) {
393 DEBUG(3, ("Authentication for domain %s (local domain "
394 "to this server) not supported at this "
395 "stage\n", domain_name));
399 /* we can auth against trusted domains */
400 if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
401 domain = find_domain_from_name_noinit(domain_name);
402 if (domain == NULL) {
403 DEBUG(3, ("Authentication for domain [%s] skipped "
404 "as it is not a trusted domain\n",
411 return find_our_domain();
414 static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
416 resp->data.auth.nt_status = NT_STATUS_V(result);
417 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
419 /* we might have given a more useful error above */
420 if (*resp->data.auth.error_string == '\0')
421 fstrcpy(resp->data.auth.error_string,
422 get_friendly_nt_error_msg(result));
423 resp->data.auth.pam_error = nt_status_to_pam(result);
426 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
427 struct winbindd_cli_state *state)
429 struct winbindd_methods *methods;
430 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
431 struct samr_DomInfo1 password_policy;
433 if ( !winbindd_can_contact_domain( domain ) ) {
434 DEBUG(5,("fillup_password_policy: No inbound trust to "
435 "contact domain %s\n", domain->name));
436 return NT_STATUS_NOT_SUPPORTED;
439 methods = domain->methods;
441 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
442 if (NT_STATUS_IS_ERR(status)) {
446 state->response.data.auth.policy.min_length_password =
447 password_policy.min_password_length;
448 state->response.data.auth.policy.password_history =
449 password_policy.password_history_length;
450 state->response.data.auth.policy.password_properties =
451 password_policy.password_properties;
452 state->response.data.auth.policy.expire =
453 nt_time_to_unix_abs((NTTIME *)&(password_policy.max_password_age));
454 state->response.data.auth.policy.min_passwordage =
455 nt_time_to_unix_abs((NTTIME *)&(password_policy.min_password_age));
460 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
462 uint16 *lockout_threshold)
464 struct winbindd_methods *methods;
465 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
466 struct samr_DomInfo12 lockout_policy;
468 *lockout_threshold = 0;
470 methods = domain->methods;
472 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
473 if (NT_STATUS_IS_ERR(status)) {
477 *lockout_threshold = lockout_policy.lockout_threshold;
482 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
484 uint32 *password_properties)
486 struct winbindd_methods *methods;
487 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
488 struct samr_DomInfo1 password_policy;
490 *password_properties = 0;
492 methods = domain->methods;
494 status = methods->password_policy(domain, mem_ctx, &password_policy);
495 if (NT_STATUS_IS_ERR(status)) {
499 *password_properties = password_policy.password_properties;
506 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
509 bool *internal_ccache)
511 /* accept FILE and WRFILE as krb5_cc_type from the client and then
512 * build the full ccname string based on the user's uid here -
515 const char *gen_cc = NULL;
517 *internal_ccache = True;
523 if (!type || type[0] == '\0') {
527 if (strequal(type, "FILE")) {
528 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
529 } else if (strequal(type, "WRFILE")) {
530 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
532 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
536 *internal_ccache = False;
540 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
543 if (gen_cc == NULL) {
544 DEBUG(0,("out of memory\n"));
548 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
553 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
555 const char *type = state->request.data.auth.krb5_cc_type;
557 state->response.data.auth.krb5ccname[0] = '\0';
559 if (type[0] == '\0') {
563 if (!strequal(type, "FILE") &&
564 !strequal(type, "WRFILE")) {
565 DEBUG(10,("won't return krbccname for a %s type ccache\n",
570 fstrcpy(state->response.data.auth.krb5ccname, cc);
575 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
579 uid = state->request.data.auth.uid;
582 DEBUG(1,("invalid uid: '%d'\n", uid));
588 /**********************************************************************
589 Authenticate a user with a clear text password using Kerberos and fill up
591 **********************************************************************/
593 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
594 struct winbindd_cli_state *state,
595 struct netr_SamInfo3 **info3)
598 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
599 krb5_error_code krb5_ret;
600 const char *cc = NULL;
601 const char *principal_s = NULL;
602 const char *service = NULL;
604 fstring name_domain, name_user;
605 time_t ticket_lifetime = 0;
606 time_t renewal_until = 0;
609 time_t time_offset = 0;
610 bool internal_ccache = True;
617 * prepare a krb5_cc_cache string for the user */
619 uid = get_uid_from_state(state);
621 DEBUG(0,("no valid uid\n"));
624 cc = generate_krb5_ccache(state->mem_ctx,
625 state->request.data.auth.krb5_cc_type,
626 state->request.data.auth.uid,
629 return NT_STATUS_NO_MEMORY;
634 * get kerberos properties */
636 if (domain->private_data) {
637 ads = (ADS_STRUCT *)domain->private_data;
638 time_offset = ads->auth.time_offset;
643 * do kerberos auth and setup ccache as the user */
645 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
647 realm = domain->alt_name;
650 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
651 if (principal_s == NULL) {
652 return NT_STATUS_NO_MEMORY;
655 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
656 if (service == NULL) {
657 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 (!internal_ccache) {
666 set_effective_uid(uid);
667 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
670 result = kerberos_return_info3_from_pac(state->mem_ctx,
672 state->request.data.auth.pass,
679 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
681 if (!internal_ccache) {
682 gain_root_privilege();
685 /************************ RETURNED TO ROOT **********************/
687 if (!NT_STATUS_IS_OK(result)) {
691 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
694 /* if we had a user's ccache then return that string for the pam
697 if (!internal_ccache) {
699 setup_return_cc_name(state, cc);
701 result = add_ccache_to_list(principal_s,
704 state->request.data.auth.user,
712 if (!NT_STATUS_IS_OK(result)) {
713 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
718 /* need to delete the memory cred cache, it is not used anymore */
720 krb5_ret = ads_kdestroy(cc);
722 DEBUG(3,("winbindd_raw_kerberos_login: "
723 "could not destroy krb5 credential cache: "
724 "%s\n", error_message(krb5_ret)));
733 /* we could have created a new credential cache with a valid tgt in it
734 * but we werent able to get or verify the service ticket for this
735 * local host and therefor didn't get the PAC, we need to remove that
736 * cache entirely now */
738 krb5_ret = ads_kdestroy(cc);
740 DEBUG(3,("winbindd_raw_kerberos_login: "
741 "could not destroy krb5 credential cache: "
742 "%s\n", error_message(krb5_ret)));
745 if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
746 DEBUG(3,("winbindd_raw_kerberos_login: "
747 "could not remove ccache for user %s\n",
748 state->request.data.auth.user));
753 return NT_STATUS_NOT_SUPPORTED;
754 #endif /* HAVE_KRB5 */
757 /****************************************************************
758 ****************************************************************/
760 static bool check_request_flags(uint32_t flags)
762 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
763 WBFLAG_PAM_INFO3_TEXT |
764 WBFLAG_PAM_INFO3_NDR;
766 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
767 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
768 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
769 !(flags & flags_edata) ) {
773 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags));
778 /****************************************************************
779 ****************************************************************/
781 static NTSTATUS append_data(struct winbindd_cli_state *state,
782 struct netr_SamInfo3 *info3,
783 const char *name_domain,
784 const char *name_user)
787 uint32_t flags = state->request.flags;
789 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
790 memcpy(state->response.data.auth.user_session_key,
792 sizeof(state->response.data.auth.user_session_key)
796 if (flags & WBFLAG_PAM_LMKEY) {
797 memcpy(state->response.data.auth.first_8_lm_hash,
798 info3->base.LMSessKey.key,
799 sizeof(state->response.data.auth.first_8_lm_hash)
803 if (flags & WBFLAG_PAM_INFO3_TEXT) {
804 result = append_info3_as_txt(state->mem_ctx, state, info3);
805 if (!NT_STATUS_IS_OK(result)) {
806 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
812 /* currently, anything from here on potentially overwrites extra_data. */
814 if (flags & WBFLAG_PAM_INFO3_NDR) {
815 result = append_info3_as_ndr(state->mem_ctx, state, info3);
816 if (!NT_STATUS_IS_OK(result)) {
817 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
823 if (flags & WBFLAG_PAM_UNIX_NAME) {
824 result = append_unix_username(state->mem_ctx, state, info3,
825 name_domain, name_user);
826 if (!NT_STATUS_IS_OK(result)) {
827 DEBUG(10,("Failed to append Unix Username: %s\n",
833 if (flags & WBFLAG_PAM_AFS_TOKEN) {
834 result = append_afs_token(state->mem_ctx, state, info3,
835 name_domain, name_user);
836 if (!NT_STATUS_IS_OK(result)) {
837 DEBUG(10,("Failed to append AFS token: %s\n",
846 void winbindd_pam_auth(struct winbindd_cli_state *state)
848 struct winbindd_domain *domain;
849 fstring name_domain, name_user;
852 /* Ensure null termination */
853 state->request.data.auth.user
854 [sizeof(state->request.data.auth.user)-1]='\0';
856 /* Ensure null termination */
857 state->request.data.auth.pass
858 [sizeof(state->request.data.auth.pass)-1]='\0';
860 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
861 state->request.data.auth.user));
863 if (!check_request_flags(state->request.flags)) {
864 result = NT_STATUS_INVALID_PARAMETER_MIX;
868 /* Parse domain and username */
870 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
872 if (!canonicalize_username(state->request.data.auth.user,
873 name_domain, name_user)) {
874 result = NT_STATUS_NO_SUCH_USER;
878 domain = find_auth_domain(state, name_domain);
880 if (domain == NULL) {
881 result = NT_STATUS_NO_SUCH_USER;
885 sendto_domain(state, domain);
888 set_auth_errors(&state->response, result);
889 DEBUG(5, ("Plain text authentication for %s returned %s "
891 state->request.data.auth.user,
892 state->response.data.auth.nt_status_string,
893 state->response.data.auth.pam_error));
894 request_error(state);
897 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
898 struct winbindd_cli_state *state,
899 struct netr_SamInfo3 **info3)
901 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
902 uint16 max_allowed_bad_attempts;
903 fstring name_domain, name_user;
905 enum lsa_SidType type;
906 uchar new_nt_pass[NT_HASH_LEN];
907 const uint8 *cached_nt_pass;
908 const uint8 *cached_salt;
909 struct netr_SamInfo3 *my_info3;
910 time_t kickoff_time, must_change_time;
911 bool password_good = False;
913 struct winbindd_tdc_domain *tdc_domain = NULL;
920 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
922 /* Parse domain and username */
924 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
927 if (!lookup_cached_name(state->mem_ctx,
932 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
933 return NT_STATUS_NO_SUCH_USER;
936 if (type != SID_NAME_USER) {
937 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
938 return NT_STATUS_LOGON_FAILURE;
941 result = winbindd_get_creds(domain,
947 if (!NT_STATUS_IS_OK(result)) {
948 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
954 E_md4hash(state->request.data.auth.pass, new_nt_pass);
956 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
957 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
959 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
963 /* In this case we didn't store the nt_hash itself,
964 but the MD5 combination of salt + nt_hash. */
965 uchar salted_hash[NT_HASH_LEN];
966 E_md5hash(cached_salt, new_nt_pass, salted_hash);
968 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
971 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
972 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
978 /* User *DOES* know the password, update logon_time and reset
981 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
983 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
984 return NT_STATUS_ACCOUNT_LOCKED_OUT;
987 if (my_info3->base.acct_flags & ACB_DISABLED) {
988 return NT_STATUS_ACCOUNT_DISABLED;
991 if (my_info3->base.acct_flags & ACB_WSTRUST) {
992 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
995 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
996 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
999 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
1000 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
1003 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
1004 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
1005 my_info3->base.acct_flags));
1006 return NT_STATUS_LOGON_FAILURE;
1009 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
1010 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
1011 return NT_STATUS_ACCOUNT_EXPIRED;
1014 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
1015 if (must_change_time != 0 && must_change_time < time(NULL)) {
1016 /* we allow grace logons when the password has expired */
1017 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
1018 /* return NT_STATUS_PASSWORD_EXPIRED; */
1023 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
1024 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
1025 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
1028 const char *cc = NULL;
1030 const char *principal_s = NULL;
1031 const char *service = NULL;
1032 bool internal_ccache = False;
1034 uid = get_uid_from_state(state);
1036 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1037 return NT_STATUS_INVALID_PARAMETER;
1040 cc = generate_krb5_ccache(state->mem_ctx,
1041 state->request.data.auth.krb5_cc_type,
1042 state->request.data.auth.uid,
1045 return NT_STATUS_NO_MEMORY;
1048 realm = domain->alt_name;
1051 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1052 if (principal_s == NULL) {
1053 return NT_STATUS_NO_MEMORY;
1056 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1057 if (service == NULL) {
1058 return NT_STATUS_NO_MEMORY;
1061 if (!internal_ccache) {
1063 setup_return_cc_name(state, cc);
1065 result = add_ccache_to_list(principal_s,
1068 state->request.data.auth.user,
1072 time(NULL) + lp_winbind_cache_time(),
1073 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
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 */
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.last_logon, time(NULL));
1089 my_info3->base.bad_password_count = 0;
1091 result = winbindd_update_creds_by_info3(domain,
1093 state->request.data.auth.user,
1094 state->request.data.auth.pass,
1096 if (!NT_STATUS_IS_OK(result)) {
1097 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1098 nt_errstr(result)));
1102 return NT_STATUS_OK;
1106 /* User does *NOT* know the correct password, modify info3 accordingly */
1108 /* failure of this is not critical */
1109 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1110 if (!NT_STATUS_IS_OK(result)) {
1111 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1112 "Won't be able to honour account lockout policies\n"));
1115 /* increase counter */
1116 my_info3->base.bad_password_count++;
1118 if (max_allowed_bad_attempts == 0) {
1123 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1125 uint32 password_properties;
1127 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1128 if (!NT_STATUS_IS_OK(result)) {
1129 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1132 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1133 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1134 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1139 result = winbindd_update_creds_by_info3(domain,
1141 state->request.data.auth.user,
1145 if (!NT_STATUS_IS_OK(result)) {
1146 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1147 nt_errstr(result)));
1150 return NT_STATUS_LOGON_FAILURE;
1153 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1154 struct winbindd_cli_state *state,
1155 struct netr_SamInfo3 **info3)
1157 struct winbindd_domain *contact_domain;
1158 fstring name_domain, name_user;
1161 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1163 /* Parse domain and username */
1165 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1167 /* what domain should we contact? */
1170 if (!(contact_domain = find_domain_from_name(name_domain))) {
1171 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1172 state->request.data.auth.user, name_domain, name_user, name_domain));
1173 result = NT_STATUS_NO_SUCH_USER;
1178 if (is_myname(name_domain)) {
1179 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1180 result = NT_STATUS_NO_SUCH_USER;
1184 contact_domain = find_domain_from_name(name_domain);
1185 if (contact_domain == NULL) {
1186 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1187 state->request.data.auth.user, name_domain, name_user, name_domain));
1189 contact_domain = find_our_domain();
1193 if (contact_domain->initialized &&
1194 contact_domain->active_directory) {
1198 if (!contact_domain->initialized) {
1199 init_dc_connection(contact_domain);
1202 if (!contact_domain->active_directory) {
1203 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1204 return NT_STATUS_INVALID_LOGON_TYPE;
1207 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1212 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1213 struct winbindd_cli_state *state,
1214 struct netr_SamInfo3 **info3)
1217 struct rpc_pipe_client *netlogon_pipe;
1222 unsigned char local_lm_response[24];
1223 unsigned char local_nt_response[24];
1224 struct winbindd_domain *contact_domain;
1225 fstring name_domain, name_user;
1228 struct netr_SamInfo3 *my_info3 = NULL;
1232 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1234 /* Parse domain and username */
1236 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1238 /* do password magic */
1241 generate_random_buffer(chal, 8);
1242 if (lp_client_ntlmv2_auth()) {
1243 DATA_BLOB server_chal;
1244 DATA_BLOB names_blob;
1245 DATA_BLOB nt_response;
1246 DATA_BLOB lm_response;
1247 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1249 /* note that the 'workgroup' here is a best guess - we don't know
1250 the server's domain at this point. The 'server name' is also
1253 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1255 if (!SMBNTLMv2encrypt(name_user, name_domain,
1256 state->request.data.auth.pass,
1259 &lm_response, &nt_response, NULL)) {
1260 data_blob_free(&names_blob);
1261 data_blob_free(&server_chal);
1262 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1263 result = NT_STATUS_NO_MEMORY;
1266 data_blob_free(&names_blob);
1267 data_blob_free(&server_chal);
1268 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1269 lm_response.length);
1270 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1271 nt_response.length);
1272 data_blob_free(&lm_response);
1273 data_blob_free(&nt_response);
1276 if (lp_client_lanman_auth()
1277 && SMBencrypt(state->request.data.auth.pass,
1279 local_lm_response)) {
1280 lm_resp = data_blob_talloc(state->mem_ctx,
1282 sizeof(local_lm_response));
1284 lm_resp = data_blob_null;
1286 SMBNTencrypt(state->request.data.auth.pass,
1290 nt_resp = data_blob_talloc(state->mem_ctx,
1292 sizeof(local_nt_response));
1295 /* what domain should we contact? */
1298 if (!(contact_domain = find_domain_from_name(name_domain))) {
1299 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1300 state->request.data.auth.user, name_domain, name_user, name_domain));
1301 result = NT_STATUS_NO_SUCH_USER;
1306 if (is_myname(name_domain)) {
1307 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1308 result = NT_STATUS_NO_SUCH_USER;
1312 contact_domain = find_our_domain();
1315 /* check authentication loop */
1318 NTSTATUS (*logon_fn)(struct rpc_pipe_client *cli,
1319 TALLOC_CTX *mem_ctx,
1320 uint32 logon_parameters,
1322 const char *username,
1324 const char *workstation,
1325 const uint8 chal[8],
1326 DATA_BLOB lm_response,
1327 DATA_BLOB nt_response,
1328 struct netr_SamInfo3 **info3);
1330 ZERO_STRUCTP(my_info3);
1333 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1335 if (!NT_STATUS_IS_OK(result)) {
1336 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1340 /* It is really important to try SamLogonEx here,
1341 * because in a clustered environment, we want to use
1342 * one machine account from multiple physical
1345 * With a normal SamLogon call, we must keep the
1346 * credentials chain updated and intact between all
1347 * users of the machine account (which would imply
1348 * cross-node communication for every NTLM logon).
1350 * (The credentials chain is not per NETLOGON pipe
1351 * connection, but globally on the server/client pair
1354 * When using SamLogonEx, the credentials are not
1355 * supplied, but the session key is implied by the
1356 * wrapping SamLogon context.
1358 * -- abartlet 21 April 2008
1361 logon_fn = contact_domain->can_do_samlogon_ex
1362 ? rpccli_netlogon_sam_network_logon_ex
1363 : rpccli_netlogon_sam_network_logon;
1365 result = logon_fn(netlogon_pipe,
1368 contact_domain->dcname, /* server name */
1369 name_user, /* user name */
1370 name_domain, /* target domain */
1371 global_myname(), /* workstation */
1378 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1379 && contact_domain->can_do_samlogon_ex) {
1380 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1381 "retrying with NetSamLogon\n"));
1382 contact_domain->can_do_samlogon_ex = False;
1387 /* We have to try a second time as cm_connect_netlogon
1388 might not yet have noticed that the DC has killed
1391 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1396 /* if we get access denied, a possible cause was that we had
1397 and open connection to the DC, but someone changed our
1398 machine account password out from underneath us using 'net
1399 rpc changetrustpw' */
1401 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1402 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1403 "ACCESS_DENIED. Maybe the trust account "
1404 "password was changed and we didn't know it. "
1405 "Killing connections to domain %s\n",
1407 invalidate_cm_connection(&contact_domain->conn);
1411 } while ( (attempts < 2) && retry );
1413 /* handle the case where a NT4 DC does not fill in the acct_flags in
1414 * the samlogon reply info3. When accurate info3 is required by the
1415 * caller, we look up the account flags ourselve - gd */
1417 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1418 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1420 struct rpc_pipe_client *samr_pipe;
1421 POLICY_HND samr_domain_handle, user_pol;
1422 union samr_UserInfo *info = NULL;
1423 NTSTATUS status_tmp;
1426 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1427 &samr_pipe, &samr_domain_handle);
1429 if (!NT_STATUS_IS_OK(status_tmp)) {
1430 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1431 nt_errstr(status_tmp)));
1435 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1436 &samr_domain_handle,
1437 MAXIMUM_ALLOWED_ACCESS,
1441 if (!NT_STATUS_IS_OK(status_tmp)) {
1442 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1443 nt_errstr(status_tmp)));
1447 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1452 if (!NT_STATUS_IS_OK(status_tmp)) {
1453 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1454 nt_errstr(status_tmp)));
1455 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1459 acct_flags = info->info16.acct_flags;
1461 if (acct_flags == 0) {
1462 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1466 my_info3->base.acct_flags = acct_flags;
1468 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1470 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1478 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1479 struct winbindd_cli_state *state)
1481 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1482 NTSTATUS krb5_result = NT_STATUS_OK;
1483 fstring name_domain, name_user;
1484 struct netr_SamInfo3 *info3 = NULL;
1486 /* Ensure null termination */
1487 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1489 /* Ensure null termination */
1490 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1492 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1493 state->request.data.auth.user));
1495 if (!check_request_flags(state->request.flags)) {
1496 result = NT_STATUS_INVALID_PARAMETER_MIX;
1500 /* Parse domain and username */
1502 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1504 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1506 if (domain->online == False) {
1507 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1508 if (domain->startup) {
1509 /* Logons are very important to users. If we're offline and
1510 we get a request within the first 30 seconds of startup,
1511 try very hard to find a DC and go online. */
1513 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1514 "request in startup mode.\n", domain->name ));
1516 winbindd_flush_negative_conn_cache(domain);
1517 result = init_dc_connection(domain);
1521 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1523 /* Check for Kerberos authentication */
1524 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1526 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1527 /* save for later */
1528 krb5_result = result;
1531 if (NT_STATUS_IS_OK(result)) {
1532 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1533 goto process_result;
1535 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1538 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1541 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1542 set_domain_offline( domain );
1546 /* there are quite some NT_STATUS errors where there is no
1547 * point in retrying with a samlogon, we explictly have to take
1548 * care not to increase the bad logon counter on the DC */
1550 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1551 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1552 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1553 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1554 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1555 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1556 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1557 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1558 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1559 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1560 goto process_result;
1563 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1564 DEBUG(3,("falling back to samlogon\n"));
1572 /* Check for Samlogon authentication */
1573 if (domain->online) {
1574 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1576 if (NT_STATUS_IS_OK(result)) {
1577 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1578 /* add the Krb5 err if we have one */
1579 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1580 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1582 goto process_result;
1585 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1586 nt_errstr(result)));
1588 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1589 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1590 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1592 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1593 set_domain_offline( domain );
1597 if (domain->online) {
1598 /* We're still online - fail. */
1604 /* Check for Cached logons */
1605 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1606 lp_winbind_offline_logon()) {
1608 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1610 if (NT_STATUS_IS_OK(result)) {
1611 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1612 goto process_result;
1614 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1621 if (NT_STATUS_IS_OK(result)) {
1625 /* In all codepaths where result == NT_STATUS_OK info3 must have
1626 been initialized. */
1628 result = NT_STATUS_INTERNAL_ERROR;
1632 netsamlogon_cache_store(name_user, info3);
1633 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1635 /* save name_to_sid info as early as possible (only if
1636 this is our primary domain so we don't invalidate
1637 the cache entry by storing the seq_num for the wrong
1639 if ( domain->primary ) {
1640 sid_compose(&user_sid, info3->base.domain_sid,
1642 cache_name2sid(domain, name_domain, name_user,
1643 SID_NAME_USER, &user_sid);
1646 /* Check if the user is in the right group */
1648 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1649 state->request.data.auth.require_membership_of_sid))) {
1650 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1651 state->request.data.auth.user,
1652 state->request.data.auth.require_membership_of_sid));
1656 result = append_data(state, info3, name_domain, name_user);
1657 if (!NT_STATUS_IS_OK(result)) {
1661 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1663 /* Store in-memory creds for single-signon using ntlm_auth. */
1664 result = winbindd_add_memory_creds(state->request.data.auth.user,
1665 get_uid_from_state(state),
1666 state->request.data.auth.pass);
1668 if (!NT_STATUS_IS_OK(result)) {
1669 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1673 if (lp_winbind_offline_logon()) {
1674 result = winbindd_store_creds(domain,
1676 state->request.data.auth.user,
1677 state->request.data.auth.pass,
1679 if (!NT_STATUS_IS_OK(result)) {
1681 /* Release refcount. */
1682 winbindd_delete_memory_creds(state->request.data.auth.user);
1684 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1691 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1692 struct winbindd_domain *our_domain = find_our_domain();
1694 /* This is not entirely correct I believe, but it is
1695 consistent. Only apply the password policy settings
1696 too warn users for our own domain. Cannot obtain these
1697 from trusted DCs all the time so don't do it at all.
1700 result = NT_STATUS_NOT_SUPPORTED;
1701 if (our_domain == domain ) {
1702 result = fillup_password_policy(our_domain, state);
1705 if (!NT_STATUS_IS_OK(result)
1706 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1708 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1709 domain->name, nt_errstr(result)));
1714 result = NT_STATUS_OK;
1718 /* give us a more useful (more correct?) error code */
1719 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1720 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1721 result = NT_STATUS_NO_LOGON_SERVERS;
1724 state->response.data.auth.nt_status = NT_STATUS_V(result);
1725 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1727 /* we might have given a more useful error above */
1728 if (!*state->response.data.auth.error_string)
1729 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1730 state->response.data.auth.pam_error = nt_status_to_pam(result);
1732 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1733 state->request.data.auth.user,
1734 state->response.data.auth.nt_status_string,
1735 state->response.data.auth.pam_error));
1737 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1740 static void ndr_child_auth_compat_auth_plain(struct winbindd_domain *domain,
1741 struct winbindd_cli_state *state,
1742 struct winbind_auth *r)
1744 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1745 NTSTATUS krb5_result = NT_STATUS_OK;
1746 fstring name_domain, name_user;
1747 fstring full_account_name;
1748 struct netr_SamInfo3 *info3 = NULL;
1749 struct winbind_auth_compat_auth_plain_req *in;
1751 in = &r->in.req.compat_auth_plain;
1753 DEBUG(3, ("comapt auth plain account[%s] domain[%s]\n",
1755 in->domain_name?in->domain_name:"(NULL)"));
1757 /* Parse domain and username */
1759 // ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1761 // parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1763 if (domain->online == False) {
1764 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1765 if (domain->startup) {
1766 /* Logons are very important to users. If we're offline and
1767 we get a request within the first 30 seconds of startup,
1768 try very hard to find a DC and go online. */
1770 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1771 "request in startup mode.\n", domain->name ));
1773 winbindd_flush_negative_conn_cache(domain);
1774 result = init_dc_connection(domain);
1778 DEBUG(10,("compat_auth_plain: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1780 /* Check for Kerberos authentication */
1781 if (domain->online && (in->flags & WBFLAG_PAM_KRB5)) {
1783 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1784 /* save for later */
1785 krb5_result = result;
1788 if (NT_STATUS_IS_OK(result)) {
1789 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1790 goto process_result;
1792 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1795 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1796 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1797 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1798 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1799 set_domain_offline( domain );
1803 /* there are quite some NT_STATUS errors where there is no
1804 * point in retrying with a samlogon, we explictly have to take
1805 * care not to increase the bad logon counter on the DC */
1807 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1808 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1809 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1810 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1811 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1812 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1813 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1814 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1815 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1816 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1817 goto process_result;
1820 if (in->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1821 DEBUG(3,("falling back to samlogon\n"));
1829 /* Check for Samlogon authentication */
1830 if (domain->online) {
1831 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1833 if (NT_STATUS_IS_OK(result)) {
1834 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1835 /* add the Krb5 err if we have one */
1836 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1837 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1839 goto process_result;
1842 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1843 nt_errstr(result)));
1845 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1846 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1847 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1849 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1850 set_domain_offline( domain );
1854 if (domain->online) {
1855 /* We're still online - fail. */
1861 /* Check for Cached logons */
1862 if (!domain->online && (in->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1863 lp_winbind_offline_logon()) {
1865 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1867 if (NT_STATUS_IS_OK(result)) {
1868 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1869 goto process_result;
1871 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1878 if (NT_STATUS_IS_OK(result)) {
1882 /* In all codepaths where result == NT_STATUS_OK info3 must have
1883 been initialized. */
1885 result = NT_STATUS_INTERNAL_ERROR;
1889 netsamlogon_cache_store(name_user, info3);
1890 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1892 /* save name_to_sid info as early as possible (only if
1893 this is our primary domain so we don't invalidate
1894 the cache entry by storing the seq_num for the wrong
1896 if ( domain->primary ) {
1897 sid_compose(&user_sid, info3->base.domain_sid,
1899 cache_name2sid(domain, name_domain, name_user,
1900 SID_NAME_USER, &user_sid);
1903 /* Check if the user is in the right group */
1905 result = check_info3_in_groups(state->mem_ctx, info3,
1906 in->num_require_membership_of_sids,
1907 in->require_membership_of_sids);
1908 if (!NT_STATUS_IS_OK(result)) {
1909 // DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1910 // state->request.data.auth.user,
1911 // state->request.data.auth.require_membership_of_sid));
1915 result = append_data(state, info3, name_domain, name_user);
1916 if (!NT_STATUS_IS_OK(result)) {
1920 if ((in->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1922 /* Store in-memory creds for single-signon using ntlm_auth. */
1923 result = winbindd_add_memory_creds(full_account_name,
1924 get_uid_from_state(state),
1927 if (!NT_STATUS_IS_OK(result)) {
1928 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1932 if (lp_winbind_offline_logon()) {
1933 result = winbindd_store_creds(domain,
1938 if (!NT_STATUS_IS_OK(result)) {
1940 /* Release refcount. */
1941 winbindd_delete_memory_creds(full_account_name);
1943 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1950 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1951 result = fillup_password_policy(domain, state);
1953 if (!NT_STATUS_IS_OK(result)
1954 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1956 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result)));
1961 result = NT_STATUS_OK;
1965 /* give us a more useful (more correct?) error code */
1966 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1967 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1968 result = NT_STATUS_NO_LOGON_SERVERS;
1971 state->response.data.auth.nt_status = NT_STATUS_V(result);
1972 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1974 /* we might have given a more useful error above */
1975 if (!*state->response.data.auth.error_string)
1976 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1977 state->response.data.auth.pam_error = nt_status_to_pam(result);
1979 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1980 state->request.data.auth.user,
1981 state->response.data.auth.nt_status_string,
1982 state->response.data.auth.pam_error));
1984 if (!NT_STATUS_IS_OK(result)) {
1985 r->out.result = WINBIND_STATUS_FOOBAR;
1989 r->out.result = WINBIND_STATUS_OK;
1993 /**********************************************************************
1994 Challenge Response Authentication Protocol
1995 **********************************************************************/
1997 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1999 struct winbindd_domain *domain = NULL;
2000 const char *domain_name = NULL;
2003 if (!check_request_flags(state->request.flags)) {
2004 result = NT_STATUS_INVALID_PARAMETER_MIX;
2008 if (!state->privileged) {
2009 char *error_string = NULL;
2010 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
2012 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
2013 "on %s are set correctly.\n",
2014 get_winbind_priv_pipe_dir()));
2015 /* send a better message than ACCESS_DENIED */
2016 error_string = talloc_asprintf(state->mem_ctx,
2017 "winbind client not authorized "
2018 "to use winbindd_pam_auth_crap."
2019 " Ensure permissions on %s "
2020 "are set correctly.",
2021 get_winbind_priv_pipe_dir());
2022 fstrcpy(state->response.data.auth.error_string, error_string);
2023 result = NT_STATUS_ACCESS_DENIED;
2027 /* Ensure null termination */
2028 state->request.data.auth_crap.user
2029 [sizeof(state->request.data.auth_crap.user)-1]=0;
2030 state->request.data.auth_crap.domain
2031 [sizeof(state->request.data.auth_crap.domain)-1]=0;
2033 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
2034 (unsigned long)state->pid,
2035 state->request.data.auth_crap.domain,
2036 state->request.data.auth_crap.user));
2038 if (*state->request.data.auth_crap.domain != '\0') {
2039 domain_name = state->request.data.auth_crap.domain;
2040 } else if (lp_winbind_use_default_domain()) {
2041 domain_name = lp_workgroup();
2044 if (domain_name != NULL)
2045 domain = find_auth_domain(state, domain_name);
2047 if (domain != NULL) {
2048 sendto_domain(state, domain);
2052 result = NT_STATUS_NO_SUCH_USER;
2055 set_auth_errors(&state->response, result);
2056 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
2057 state->request.data.auth_crap.domain,
2058 state->request.data.auth_crap.user,
2059 state->response.data.auth.nt_status_string,
2060 state->response.data.auth.pam_error));
2061 request_error(state);
2066 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
2067 struct winbindd_cli_state *state)
2070 struct netr_SamInfo3 *info3 = NULL;
2071 struct rpc_pipe_client *netlogon_pipe;
2072 const char *name_user = NULL;
2073 const char *name_domain = NULL;
2074 const char *workstation;
2075 struct winbindd_domain *contact_domain;
2079 DATA_BLOB lm_resp, nt_resp;
2081 /* This is child-only, so no check for privileged access is needed
2084 /* Ensure null termination */
2085 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
2086 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
2088 if (!check_request_flags(state->request.flags)) {
2089 result = NT_STATUS_INVALID_PARAMETER_MIX;
2093 name_user = state->request.data.auth_crap.user;
2095 if (*state->request.data.auth_crap.domain) {
2096 name_domain = state->request.data.auth_crap.domain;
2097 } else if (lp_winbind_use_default_domain()) {
2098 name_domain = lp_workgroup();
2100 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
2102 result = NT_STATUS_NO_SUCH_USER;
2106 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
2107 name_domain, name_user));
2109 if (*state->request.data.auth_crap.workstation) {
2110 workstation = state->request.data.auth_crap.workstation;
2112 workstation = global_myname();
2115 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
2116 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
2117 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2118 state->request.data.auth_crap.lm_resp_len,
2119 state->request.data.auth_crap.nt_resp_len));
2120 result = NT_STATUS_INVALID_PARAMETER;
2124 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
2125 state->request.data.auth_crap.lm_resp_len);
2126 nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
2127 state->request.data.auth_crap.nt_resp_len);
2129 /* what domain should we contact? */
2132 if (!(contact_domain = find_domain_from_name(name_domain))) {
2133 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
2134 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
2135 result = NT_STATUS_NO_SUCH_USER;
2139 if (is_myname(name_domain)) {
2140 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
2141 result = NT_STATUS_NO_SUCH_USER;
2144 contact_domain = find_our_domain();
2148 NTSTATUS (*logon_fn)(struct rpc_pipe_client *cli,
2149 TALLOC_CTX *mem_ctx,
2150 uint32 logon_parameters,
2152 const char *username,
2154 const char *workstation,
2155 const uint8 chal[8],
2156 DATA_BLOB lm_response,
2157 DATA_BLOB nt_response,
2158 struct netr_SamInfo3 **info3);
2162 netlogon_pipe = NULL;
2163 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
2165 if (!NT_STATUS_IS_OK(result)) {
2166 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
2167 nt_errstr(result)));
2171 logon_fn = contact_domain->can_do_samlogon_ex
2172 ? rpccli_netlogon_sam_network_logon_ex
2173 : rpccli_netlogon_sam_network_logon;
2175 result = logon_fn(netlogon_pipe,
2177 state->request.data.auth_crap.logon_parameters,
2178 contact_domain->dcname,
2181 /* Bug #3248 - found by Stefan Burkei. */
2182 workstation, /* We carefully set this above so use it... */
2183 state->request.data.auth_crap.chal,
2188 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
2189 && contact_domain->can_do_samlogon_ex) {
2190 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
2191 "retrying with NetSamLogon\n"));
2192 contact_domain->can_do_samlogon_ex = False;
2199 /* We have to try a second time as cm_connect_netlogon
2200 might not yet have noticed that the DC has killed
2203 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
2208 /* if we get access denied, a possible cause was that we had and open
2209 connection to the DC, but someone changed our machine account password
2210 out from underneath us using 'net rpc changetrustpw' */
2212 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
2213 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
2214 "ACCESS_DENIED. Maybe the trust account "
2215 "password was changed and we didn't know it. "
2216 "Killing connections to domain %s\n",
2218 invalidate_cm_connection(&contact_domain->conn);
2222 } while ( (attempts < 2) && retry );
2224 if (NT_STATUS_IS_OK(result)) {
2226 netsamlogon_cache_store(name_user, info3);
2227 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
2229 /* Check if the user is in the right group */
2231 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
2232 state->request.data.auth_crap.require_membership_of_sid))) {
2233 DEBUG(3, ("User %s is not in the required group (%s), so "
2234 "crap authentication is rejected\n",
2235 state->request.data.auth_crap.user,
2236 state->request.data.auth_crap.require_membership_of_sid));
2240 result = append_data(state, info3, name_domain, name_user);
2241 if (!NT_STATUS_IS_OK(result)) {
2248 /* give us a more useful (more correct?) error code */
2249 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2250 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2251 result = NT_STATUS_NO_LOGON_SERVERS;
2254 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2255 result = nt_status_squash(result);
2258 state->response.data.auth.nt_status = NT_STATUS_V(result);
2259 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2261 /* we might have given a more useful error above */
2262 if (!*state->response.data.auth.error_string) {
2263 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2265 state->response.data.auth.pam_error = nt_status_to_pam(result);
2267 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2268 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2271 state->response.data.auth.nt_status_string,
2272 state->response.data.auth.pam_error));
2274 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2277 static void ndr_child_auth_compat_auth_response(struct winbindd_domain *domain,
2278 struct winbindd_cli_state *state,
2279 struct winbind_auth *r)
2281 r->out.result = WINBIND_STATUS_NOT_IMPLEMENTED;
2284 /* Change a user password */
2286 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2288 fstring domain, user;
2289 struct winbindd_domain *contact_domain;
2291 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2292 state->request.data.chauthtok.user));
2296 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
2298 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
2299 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2300 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2302 state->request.data.auth.user,
2303 state->response.data.auth.nt_status_string,
2304 state->response.data.auth.pam_error));
2305 request_error(state);
2309 contact_domain = find_domain_from_name(domain);
2310 if (!contact_domain) {
2311 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2312 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2313 state->request.data.chauthtok.user, domain, user, domain));
2314 request_error(state);
2318 sendto_domain(state, contact_domain);
2321 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2322 struct winbindd_cli_state *state)
2325 char *newpass = NULL;
2327 struct rpc_pipe_client *cli;
2328 bool got_info = False;
2329 struct samr_DomInfo1 *info = NULL;
2330 struct samr_ChangeReject *reject = NULL;
2331 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2332 fstring domain, user;
2334 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2335 state->request.data.auth.user));
2337 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2341 /* Change password */
2343 oldpass = state->request.data.chauthtok.oldpass;
2344 newpass = state->request.data.chauthtok.newpass;
2346 /* Initialize reject reason */
2347 state->response.data.auth.reject_reason = Undefined;
2349 /* Get sam handle */
2351 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2353 if (!NT_STATUS_IS_OK(result)) {
2354 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2358 result = rpccli_samr_chgpasswd3(cli, state->mem_ctx,
2365 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2367 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2368 state->response.data.auth.policy.min_length_password =
2369 info->min_password_length;
2370 state->response.data.auth.policy.password_history =
2371 info->password_history_length;
2372 state->response.data.auth.policy.password_properties =
2373 info->password_properties;
2374 state->response.data.auth.policy.expire =
2375 nt_time_to_unix_abs((NTTIME *)&info->max_password_age);
2376 state->response.data.auth.policy.min_passwordage =
2377 nt_time_to_unix_abs((NTTIME *)&info->min_password_age);
2379 state->response.data.auth.reject_reason =
2385 /* only fallback when the chgpasswd3 call is not supported */
2386 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2387 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2388 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2390 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
2391 nt_errstr(result)));
2393 result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
2395 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2396 Map to the same status code as Windows 2003. */
2398 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2399 result = NT_STATUS_PASSWORD_RESTRICTION;
2405 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2407 /* Update the single sign-on memory creds. */
2408 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2411 if (!NT_STATUS_IS_OK(result)) {
2412 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2413 goto process_result;
2416 if (lp_winbind_offline_logon()) {
2417 result = winbindd_update_creds_by_name(contact_domain,
2418 state->mem_ctx, user,
2420 if (!NT_STATUS_IS_OK(result)) {
2421 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2422 goto process_result;
2427 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2429 NTSTATUS policy_ret;
2431 policy_ret = fillup_password_policy(contact_domain, state);
2433 /* failure of this is non critical, it will just provide no
2434 * additional information to the client why the change has
2435 * failed - Guenther */
2437 if (!NT_STATUS_IS_OK(policy_ret)) {
2438 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2439 goto process_result;
2445 state->response.data.auth.nt_status = NT_STATUS_V(result);
2446 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2447 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2448 state->response.data.auth.pam_error = nt_status_to_pam(result);
2450 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2451 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2454 state->response.data.auth.nt_status_string,
2455 state->response.data.auth.pam_error));
2457 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2460 static void ndr_child_auth_change_password_plain(struct winbindd_domain *domain,
2461 struct winbindd_cli_state *state,
2462 struct winbind_auth *r)
2464 r->out.result = WINBIND_STATUS_NOT_IMPLEMENTED;
2467 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2469 struct winbindd_domain *domain;
2470 fstring name_domain, user;
2471 uid_t caller_uid = (uid_t)-1;
2472 uid_t request_uid = state->request.data.logoff.uid;
2474 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2475 state->request.data.logoff.user));
2477 /* Ensure null termination */
2478 state->request.data.logoff.user
2479 [sizeof(state->request.data.logoff.user)-1]='\0';
2481 state->request.data.logoff.krb5ccname
2482 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2484 if (request_uid == (gid_t)-1) {
2488 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2492 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2496 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2497 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2502 switch (caller_uid) {
2506 /* root must be able to logoff any user - gd */
2507 state->request.data.logoff.uid = request_uid;
2510 if (caller_uid != request_uid) {
2511 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2514 state->request.data.logoff.uid = caller_uid;
2518 sendto_domain(state, domain);
2522 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2523 DEBUG(5, ("Pam Logoff for %s returned %s "
2525 state->request.data.logoff.user,
2526 state->response.data.auth.nt_status_string,
2527 state->response.data.auth.pam_error));
2528 request_error(state);
2532 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2533 struct winbindd_cli_state *state)
2535 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2537 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2538 state->request.data.logoff.user));
2540 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2541 result = NT_STATUS_OK;
2542 goto process_result;
2545 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2546 result = NT_STATUS_OK;
2547 goto process_result;
2552 if (state->request.data.logoff.uid < 0) {
2553 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2554 goto process_result;
2557 /* what we need here is to find the corresponding krb5 ccache name *we*
2558 * created for a given username and destroy it */
2560 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2561 result = NT_STATUS_OK;
2562 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2563 goto process_result;
2566 if (!ccache_entry_identical(state->request.data.logoff.user,
2567 state->request.data.logoff.uid,
2568 state->request.data.logoff.krb5ccname)) {
2569 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2570 goto process_result;
2573 result = remove_ccache(state->request.data.logoff.user);
2574 if (!NT_STATUS_IS_OK(result)) {
2575 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2576 nt_errstr(result)));
2577 goto process_result;
2581 result = NT_STATUS_NOT_SUPPORTED;
2586 winbindd_delete_memory_creds(state->request.data.logoff.user);
2588 state->response.data.auth.nt_status = NT_STATUS_V(result);
2589 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2590 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2591 state->response.data.auth.pam_error = nt_status_to_pam(result);
2593 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2596 static void ndr_child_auth_compat_logoff(struct winbindd_domain *domain,
2597 struct winbindd_cli_state *state,
2598 struct winbind_auth *r)
2600 r->out.result = WINBIND_STATUS_NOT_IMPLEMENTED;
2603 /* Change user password with auth crap*/
2605 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2607 struct winbindd_domain *domain = NULL;
2608 const char *domain_name = NULL;
2610 /* Ensure null termination */
2611 state->request.data.chng_pswd_auth_crap.user[
2612 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2613 state->request.data.chng_pswd_auth_crap.domain[
2614 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2616 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2617 (unsigned long)state->pid,
2618 state->request.data.chng_pswd_auth_crap.domain,
2619 state->request.data.chng_pswd_auth_crap.user));
2621 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2622 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2623 } else if (lp_winbind_use_default_domain()) {
2624 domain_name = lp_workgroup();
2627 if (domain_name != NULL)
2628 domain = find_domain_from_name(domain_name);
2630 if (domain != NULL) {
2631 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2632 "%s\n", (unsigned long)state->pid,domain->name));
2633 sendto_domain(state, domain);
2637 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2638 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2639 state->request.data.chng_pswd_auth_crap.domain,
2640 state->request.data.chng_pswd_auth_crap.user,
2641 state->response.data.auth.nt_status_string,
2642 state->response.data.auth.pam_error));
2643 request_error(state);
2647 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2650 DATA_BLOB new_nt_password;
2651 DATA_BLOB old_nt_hash_enc;
2652 DATA_BLOB new_lm_password;
2653 DATA_BLOB old_lm_hash_enc;
2654 fstring domain,user;
2656 struct winbindd_domain *contact_domain = domainSt;
2657 struct rpc_pipe_client *cli;
2659 /* Ensure null termination */
2660 state->request.data.chng_pswd_auth_crap.user[
2661 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2662 state->request.data.chng_pswd_auth_crap.domain[
2663 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2667 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2668 (unsigned long)state->pid,
2669 state->request.data.chng_pswd_auth_crap.domain,
2670 state->request.data.chng_pswd_auth_crap.user));
2672 if (lp_winbind_offline_logon()) {
2673 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2674 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2675 result = NT_STATUS_ACCESS_DENIED;
2679 if (*state->request.data.chng_pswd_auth_crap.domain) {
2680 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2682 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2686 DEBUG(3,("no domain specified with username (%s) - "
2688 state->request.data.chng_pswd_auth_crap.user));
2689 result = NT_STATUS_NO_SUCH_USER;
2694 if (!*domain && lp_winbind_use_default_domain()) {
2695 fstrcpy(domain,(char *)lp_workgroup());
2699 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2702 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2703 (unsigned long)state->pid, domain, user));
2705 /* Change password */
2706 new_nt_password = data_blob_talloc(
2708 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2709 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2711 old_nt_hash_enc = data_blob_talloc(
2713 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2714 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2716 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2717 new_lm_password = data_blob_talloc(
2719 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2720 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2722 old_lm_hash_enc = data_blob_talloc(
2724 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2725 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2727 new_lm_password.length = 0;
2728 old_lm_hash_enc.length = 0;
2731 /* Get sam handle */
2733 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2734 if (!NT_STATUS_IS_OK(result)) {
2735 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2739 result = rpccli_samr_chng_pswd_auth_crap(
2740 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2741 new_lm_password, old_lm_hash_enc);
2744 state->response.data.auth.nt_status = NT_STATUS_V(result);
2745 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2746 fstrcpy(state->response.data.auth.error_string,
2747 get_friendly_nt_error_msg(result));
2748 state->response.data.auth.pam_error = nt_status_to_pam(result);
2750 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2751 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2753 state->response.data.auth.nt_status_string,
2754 state->response.data.auth.pam_error));
2756 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2759 static void ndr_child_auth_change_password_crypt(struct winbindd_domain *domain,
2760 struct winbindd_cli_state *state,
2761 struct winbind_auth *r)
2763 r->out.result = WINBIND_STATUS_NOT_IMPLEMENTED;
2766 void winbindd_ndr_domain_child_auth(struct winbindd_domain *domain,
2767 struct winbindd_cli_state *state)
2769 struct winbind_auth *r;
2771 r = talloc_get_type_abort(state->c.ndr.r,
2772 struct winbind_auth);
2774 switch (*r->in.level) {
2775 case WINBIND_AUTH_LEVEL_COMPAT_AUTH_PLAIN:
2776 ndr_child_auth_compat_auth_plain(domain, state, r);
2779 case WINBIND_AUTH_LEVEL_COMPAT_AUTH_RESPONSE:
2780 ndr_child_auth_compat_auth_response(domain, state, r);
2783 case WINBIND_AUTH_LEVEL_COMPAT_LOGOFF:
2784 ndr_child_auth_compat_logoff(domain, state, r);
2786 case WINBIND_AUTH_LEVEL_CHANGE_PASSWORD_PLAIN:
2787 ndr_child_auth_change_password_plain(domain, state, r);
2790 case WINBIND_AUTH_LEVEL_CHANGE_PASSWORD_CRYPT:
2791 ndr_child_auth_change_password_crypt(domain, state, r);
2794 case WINBIND_AUTH_LEVEL_CCACHE_NTLM_AUTH:
2795 winbindd_ndr_child_auth_ccache_ntlm_auth(domain, state, r);
2799 r->out.result = WINBIND_STATUS_UNKNOWN_LEVEL;