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 r->out.result = WINBIND_STATUS_NOT_IMPLEMENTED;
1748 /**********************************************************************
1749 Challenge Response Authentication Protocol
1750 **********************************************************************/
1752 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1754 struct winbindd_domain *domain = NULL;
1755 const char *domain_name = NULL;
1758 if (!check_request_flags(state->request.flags)) {
1759 result = NT_STATUS_INVALID_PARAMETER_MIX;
1763 if (!state->privileged) {
1764 char *error_string = NULL;
1765 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1767 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1768 "on %s are set correctly.\n",
1769 get_winbind_priv_pipe_dir()));
1770 /* send a better message than ACCESS_DENIED */
1771 error_string = talloc_asprintf(state->mem_ctx,
1772 "winbind client not authorized "
1773 "to use winbindd_pam_auth_crap."
1774 " Ensure permissions on %s "
1775 "are set correctly.",
1776 get_winbind_priv_pipe_dir());
1777 fstrcpy(state->response.data.auth.error_string, error_string);
1778 result = NT_STATUS_ACCESS_DENIED;
1782 /* Ensure null termination */
1783 state->request.data.auth_crap.user
1784 [sizeof(state->request.data.auth_crap.user)-1]=0;
1785 state->request.data.auth_crap.domain
1786 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1788 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1789 (unsigned long)state->pid,
1790 state->request.data.auth_crap.domain,
1791 state->request.data.auth_crap.user));
1793 if (*state->request.data.auth_crap.domain != '\0') {
1794 domain_name = state->request.data.auth_crap.domain;
1795 } else if (lp_winbind_use_default_domain()) {
1796 domain_name = lp_workgroup();
1799 if (domain_name != NULL)
1800 domain = find_auth_domain(state, domain_name);
1802 if (domain != NULL) {
1803 sendto_domain(state, domain);
1807 result = NT_STATUS_NO_SUCH_USER;
1810 set_auth_errors(&state->response, result);
1811 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1812 state->request.data.auth_crap.domain,
1813 state->request.data.auth_crap.user,
1814 state->response.data.auth.nt_status_string,
1815 state->response.data.auth.pam_error));
1816 request_error(state);
1821 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1822 struct winbindd_cli_state *state)
1825 struct netr_SamInfo3 *info3 = NULL;
1826 struct rpc_pipe_client *netlogon_pipe;
1827 const char *name_user = NULL;
1828 const char *name_domain = NULL;
1829 const char *workstation;
1830 struct winbindd_domain *contact_domain;
1834 DATA_BLOB lm_resp, nt_resp;
1836 /* This is child-only, so no check for privileged access is needed
1839 /* Ensure null termination */
1840 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1841 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1843 if (!check_request_flags(state->request.flags)) {
1844 result = NT_STATUS_INVALID_PARAMETER_MIX;
1848 name_user = state->request.data.auth_crap.user;
1850 if (*state->request.data.auth_crap.domain) {
1851 name_domain = state->request.data.auth_crap.domain;
1852 } else if (lp_winbind_use_default_domain()) {
1853 name_domain = lp_workgroup();
1855 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1857 result = NT_STATUS_NO_SUCH_USER;
1861 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1862 name_domain, name_user));
1864 if (*state->request.data.auth_crap.workstation) {
1865 workstation = state->request.data.auth_crap.workstation;
1867 workstation = global_myname();
1870 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1871 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1872 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1873 state->request.data.auth_crap.lm_resp_len,
1874 state->request.data.auth_crap.nt_resp_len));
1875 result = NT_STATUS_INVALID_PARAMETER;
1879 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1880 state->request.data.auth_crap.lm_resp_len);
1881 nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1882 state->request.data.auth_crap.nt_resp_len);
1884 /* what domain should we contact? */
1887 if (!(contact_domain = find_domain_from_name(name_domain))) {
1888 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1889 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1890 result = NT_STATUS_NO_SUCH_USER;
1894 if (is_myname(name_domain)) {
1895 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1896 result = NT_STATUS_NO_SUCH_USER;
1899 contact_domain = find_our_domain();
1903 NTSTATUS (*logon_fn)(struct rpc_pipe_client *cli,
1904 TALLOC_CTX *mem_ctx,
1905 uint32 logon_parameters,
1907 const char *username,
1909 const char *workstation,
1910 const uint8 chal[8],
1911 DATA_BLOB lm_response,
1912 DATA_BLOB nt_response,
1913 struct netr_SamInfo3 **info3);
1917 netlogon_pipe = NULL;
1918 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1920 if (!NT_STATUS_IS_OK(result)) {
1921 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1922 nt_errstr(result)));
1926 logon_fn = contact_domain->can_do_samlogon_ex
1927 ? rpccli_netlogon_sam_network_logon_ex
1928 : rpccli_netlogon_sam_network_logon;
1930 result = logon_fn(netlogon_pipe,
1932 state->request.data.auth_crap.logon_parameters,
1933 contact_domain->dcname,
1936 /* Bug #3248 - found by Stefan Burkei. */
1937 workstation, /* We carefully set this above so use it... */
1938 state->request.data.auth_crap.chal,
1943 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1944 && contact_domain->can_do_samlogon_ex) {
1945 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1946 "retrying with NetSamLogon\n"));
1947 contact_domain->can_do_samlogon_ex = False;
1954 /* We have to try a second time as cm_connect_netlogon
1955 might not yet have noticed that the DC has killed
1958 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1963 /* if we get access denied, a possible cause was that we had and open
1964 connection to the DC, but someone changed our machine account password
1965 out from underneath us using 'net rpc changetrustpw' */
1967 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1968 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1969 "ACCESS_DENIED. Maybe the trust account "
1970 "password was changed and we didn't know it. "
1971 "Killing connections to domain %s\n",
1973 invalidate_cm_connection(&contact_domain->conn);
1977 } while ( (attempts < 2) && retry );
1979 if (NT_STATUS_IS_OK(result)) {
1981 netsamlogon_cache_store(name_user, info3);
1982 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1984 /* Check if the user is in the right group */
1986 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1987 state->request.data.auth_crap.require_membership_of_sid))) {
1988 DEBUG(3, ("User %s is not in the required group (%s), so "
1989 "crap authentication is rejected\n",
1990 state->request.data.auth_crap.user,
1991 state->request.data.auth_crap.require_membership_of_sid));
1995 result = append_data(state, info3, name_domain, name_user);
1996 if (!NT_STATUS_IS_OK(result)) {
2003 /* give us a more useful (more correct?) error code */
2004 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2005 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2006 result = NT_STATUS_NO_LOGON_SERVERS;
2009 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2010 result = nt_status_squash(result);
2013 state->response.data.auth.nt_status = NT_STATUS_V(result);
2014 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2016 /* we might have given a more useful error above */
2017 if (!*state->response.data.auth.error_string) {
2018 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2020 state->response.data.auth.pam_error = nt_status_to_pam(result);
2022 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2023 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2026 state->response.data.auth.nt_status_string,
2027 state->response.data.auth.pam_error));
2029 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2032 static void ndr_child_auth_compat_auth_response(struct winbindd_domain *domain,
2033 struct winbindd_cli_state *state,
2034 struct winbind_auth *r)
2036 r->out.result = WINBIND_STATUS_NOT_IMPLEMENTED;
2039 /* Change a user password */
2041 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2043 fstring domain, user;
2044 struct winbindd_domain *contact_domain;
2046 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2047 state->request.data.chauthtok.user));
2051 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
2053 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
2054 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2055 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2057 state->request.data.auth.user,
2058 state->response.data.auth.nt_status_string,
2059 state->response.data.auth.pam_error));
2060 request_error(state);
2064 contact_domain = find_domain_from_name(domain);
2065 if (!contact_domain) {
2066 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2067 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2068 state->request.data.chauthtok.user, domain, user, domain));
2069 request_error(state);
2073 sendto_domain(state, contact_domain);
2076 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2077 struct winbindd_cli_state *state)
2080 char *newpass = NULL;
2082 struct rpc_pipe_client *cli;
2083 bool got_info = False;
2084 struct samr_DomInfo1 *info = NULL;
2085 struct samr_ChangeReject *reject = NULL;
2086 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2087 fstring domain, user;
2089 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2090 state->request.data.auth.user));
2092 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2096 /* Change password */
2098 oldpass = state->request.data.chauthtok.oldpass;
2099 newpass = state->request.data.chauthtok.newpass;
2101 /* Initialize reject reason */
2102 state->response.data.auth.reject_reason = Undefined;
2104 /* Get sam handle */
2106 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2108 if (!NT_STATUS_IS_OK(result)) {
2109 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2113 result = rpccli_samr_chgpasswd3(cli, state->mem_ctx,
2120 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2122 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2123 state->response.data.auth.policy.min_length_password =
2124 info->min_password_length;
2125 state->response.data.auth.policy.password_history =
2126 info->password_history_length;
2127 state->response.data.auth.policy.password_properties =
2128 info->password_properties;
2129 state->response.data.auth.policy.expire =
2130 nt_time_to_unix_abs((NTTIME *)&info->max_password_age);
2131 state->response.data.auth.policy.min_passwordage =
2132 nt_time_to_unix_abs((NTTIME *)&info->min_password_age);
2134 state->response.data.auth.reject_reason =
2140 /* only fallback when the chgpasswd3 call is not supported */
2141 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2142 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2143 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2145 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
2146 nt_errstr(result)));
2148 result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
2150 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2151 Map to the same status code as Windows 2003. */
2153 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2154 result = NT_STATUS_PASSWORD_RESTRICTION;
2160 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2162 /* Update the single sign-on memory creds. */
2163 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2166 if (!NT_STATUS_IS_OK(result)) {
2167 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2168 goto process_result;
2171 if (lp_winbind_offline_logon()) {
2172 result = winbindd_update_creds_by_name(contact_domain,
2173 state->mem_ctx, user,
2175 if (!NT_STATUS_IS_OK(result)) {
2176 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2177 goto process_result;
2182 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2184 NTSTATUS policy_ret;
2186 policy_ret = fillup_password_policy(contact_domain, state);
2188 /* failure of this is non critical, it will just provide no
2189 * additional information to the client why the change has
2190 * failed - Guenther */
2192 if (!NT_STATUS_IS_OK(policy_ret)) {
2193 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2194 goto process_result;
2200 state->response.data.auth.nt_status = NT_STATUS_V(result);
2201 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2202 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2203 state->response.data.auth.pam_error = nt_status_to_pam(result);
2205 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2206 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2209 state->response.data.auth.nt_status_string,
2210 state->response.data.auth.pam_error));
2212 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2215 static void ndr_child_auth_change_password_plain(struct winbindd_domain *domain,
2216 struct winbindd_cli_state *state,
2217 struct winbind_auth *r)
2219 r->out.result = WINBIND_STATUS_NOT_IMPLEMENTED;
2222 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2224 struct winbindd_domain *domain;
2225 fstring name_domain, user;
2226 uid_t caller_uid = (uid_t)-1;
2227 uid_t request_uid = state->request.data.logoff.uid;
2229 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2230 state->request.data.logoff.user));
2232 /* Ensure null termination */
2233 state->request.data.logoff.user
2234 [sizeof(state->request.data.logoff.user)-1]='\0';
2236 state->request.data.logoff.krb5ccname
2237 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2239 if (request_uid == (gid_t)-1) {
2243 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2247 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2251 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2252 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2257 switch (caller_uid) {
2261 /* root must be able to logoff any user - gd */
2262 state->request.data.logoff.uid = request_uid;
2265 if (caller_uid != request_uid) {
2266 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2269 state->request.data.logoff.uid = caller_uid;
2273 sendto_domain(state, domain);
2277 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2278 DEBUG(5, ("Pam Logoff for %s returned %s "
2280 state->request.data.logoff.user,
2281 state->response.data.auth.nt_status_string,
2282 state->response.data.auth.pam_error));
2283 request_error(state);
2287 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2288 struct winbindd_cli_state *state)
2290 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2292 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2293 state->request.data.logoff.user));
2295 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2296 result = NT_STATUS_OK;
2297 goto process_result;
2300 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2301 result = NT_STATUS_OK;
2302 goto process_result;
2307 if (state->request.data.logoff.uid < 0) {
2308 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2309 goto process_result;
2312 /* what we need here is to find the corresponding krb5 ccache name *we*
2313 * created for a given username and destroy it */
2315 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2316 result = NT_STATUS_OK;
2317 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2318 goto process_result;
2321 if (!ccache_entry_identical(state->request.data.logoff.user,
2322 state->request.data.logoff.uid,
2323 state->request.data.logoff.krb5ccname)) {
2324 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2325 goto process_result;
2328 result = remove_ccache(state->request.data.logoff.user);
2329 if (!NT_STATUS_IS_OK(result)) {
2330 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2331 nt_errstr(result)));
2332 goto process_result;
2336 result = NT_STATUS_NOT_SUPPORTED;
2341 winbindd_delete_memory_creds(state->request.data.logoff.user);
2343 state->response.data.auth.nt_status = NT_STATUS_V(result);
2344 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2345 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2346 state->response.data.auth.pam_error = nt_status_to_pam(result);
2348 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2351 static void ndr_child_auth_compat_logoff(struct winbindd_domain *domain,
2352 struct winbindd_cli_state *state,
2353 struct winbind_auth *r)
2355 r->out.result = WINBIND_STATUS_NOT_IMPLEMENTED;
2358 /* Change user password with auth crap*/
2360 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2362 struct winbindd_domain *domain = NULL;
2363 const char *domain_name = NULL;
2365 /* Ensure null termination */
2366 state->request.data.chng_pswd_auth_crap.user[
2367 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2368 state->request.data.chng_pswd_auth_crap.domain[
2369 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2371 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2372 (unsigned long)state->pid,
2373 state->request.data.chng_pswd_auth_crap.domain,
2374 state->request.data.chng_pswd_auth_crap.user));
2376 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2377 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2378 } else if (lp_winbind_use_default_domain()) {
2379 domain_name = lp_workgroup();
2382 if (domain_name != NULL)
2383 domain = find_domain_from_name(domain_name);
2385 if (domain != NULL) {
2386 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2387 "%s\n", (unsigned long)state->pid,domain->name));
2388 sendto_domain(state, domain);
2392 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2393 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2394 state->request.data.chng_pswd_auth_crap.domain,
2395 state->request.data.chng_pswd_auth_crap.user,
2396 state->response.data.auth.nt_status_string,
2397 state->response.data.auth.pam_error));
2398 request_error(state);
2402 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2405 DATA_BLOB new_nt_password;
2406 DATA_BLOB old_nt_hash_enc;
2407 DATA_BLOB new_lm_password;
2408 DATA_BLOB old_lm_hash_enc;
2409 fstring domain,user;
2411 struct winbindd_domain *contact_domain = domainSt;
2412 struct rpc_pipe_client *cli;
2414 /* Ensure null termination */
2415 state->request.data.chng_pswd_auth_crap.user[
2416 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2417 state->request.data.chng_pswd_auth_crap.domain[
2418 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2422 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2423 (unsigned long)state->pid,
2424 state->request.data.chng_pswd_auth_crap.domain,
2425 state->request.data.chng_pswd_auth_crap.user));
2427 if (lp_winbind_offline_logon()) {
2428 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2429 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2430 result = NT_STATUS_ACCESS_DENIED;
2434 if (*state->request.data.chng_pswd_auth_crap.domain) {
2435 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2437 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2441 DEBUG(3,("no domain specified with username (%s) - "
2443 state->request.data.chng_pswd_auth_crap.user));
2444 result = NT_STATUS_NO_SUCH_USER;
2449 if (!*domain && lp_winbind_use_default_domain()) {
2450 fstrcpy(domain,(char *)lp_workgroup());
2454 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2457 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2458 (unsigned long)state->pid, domain, user));
2460 /* Change password */
2461 new_nt_password = data_blob_talloc(
2463 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2464 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2466 old_nt_hash_enc = data_blob_talloc(
2468 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2469 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2471 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2472 new_lm_password = data_blob_talloc(
2474 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2475 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2477 old_lm_hash_enc = data_blob_talloc(
2479 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2480 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2482 new_lm_password.length = 0;
2483 old_lm_hash_enc.length = 0;
2486 /* Get sam handle */
2488 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2489 if (!NT_STATUS_IS_OK(result)) {
2490 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2494 result = rpccli_samr_chng_pswd_auth_crap(
2495 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2496 new_lm_password, old_lm_hash_enc);
2499 state->response.data.auth.nt_status = NT_STATUS_V(result);
2500 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2501 fstrcpy(state->response.data.auth.error_string,
2502 get_friendly_nt_error_msg(result));
2503 state->response.data.auth.pam_error = nt_status_to_pam(result);
2505 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2506 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2508 state->response.data.auth.nt_status_string,
2509 state->response.data.auth.pam_error));
2511 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2514 void winbindd_ndr_domain_child_auth(struct winbindd_domain *domain,
2515 struct winbindd_cli_state *state)
2517 struct winbind_auth *r;
2519 r = talloc_get_type_abort(state->c.ndr.r,
2520 struct winbind_auth);
2522 switch (*r->in.level) {
2523 case WINBIND_AUTH_LEVEL_COMPAT_AUTH_PLAIN:
2524 ndr_child_auth_compat_auth_plain(domain, state, r);
2527 case WINBIND_AUTH_LEVEL_COMPAT_AUTH_RESPONSE:
2528 ndr_child_auth_compat_auth_response(domain, state, r);
2531 case WINBIND_AUTH_LEVEL_COMPAT_LOGOFF:
2532 ndr_child_auth_compat_logoff(domain, state, r);
2534 case WINBIND_AUTH_LEVEL_CHANGE_PASSWORD_PLAIN:
2535 ndr_child_auth_change_password_plain(domain, state, r);
2539 r->out.result = WINBIND_STATUS_UNKNOWN_LEVEL;