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/>.
27 #include "../libcli/auth/libcli_auth.h"
29 #define DBGC_CLASS DBGC_WINBIND
31 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
33 static NTSTATUS
append_info3_as_txt(TALLOC_CTX
*mem_ctx
,
34 struct winbindd_cli_state
*state
,
35 struct netr_SamInfo3
*info3
)
41 state
->response
.data
.auth
.info3
.logon_time
=
42 nt_time_to_unix(info3
->base
.last_logon
);
43 state
->response
.data
.auth
.info3
.logoff_time
=
44 nt_time_to_unix(info3
->base
.last_logoff
);
45 state
->response
.data
.auth
.info3
.kickoff_time
=
46 nt_time_to_unix(info3
->base
.acct_expiry
);
47 state
->response
.data
.auth
.info3
.pass_last_set_time
=
48 nt_time_to_unix(info3
->base
.last_password_change
);
49 state
->response
.data
.auth
.info3
.pass_can_change_time
=
50 nt_time_to_unix(info3
->base
.allow_password_change
);
51 state
->response
.data
.auth
.info3
.pass_must_change_time
=
52 nt_time_to_unix(info3
->base
.force_password_change
);
54 state
->response
.data
.auth
.info3
.logon_count
= info3
->base
.logon_count
;
55 state
->response
.data
.auth
.info3
.bad_pw_count
= info3
->base
.bad_password_count
;
57 state
->response
.data
.auth
.info3
.user_rid
= info3
->base
.rid
;
58 state
->response
.data
.auth
.info3
.group_rid
= info3
->base
.primary_gid
;
59 sid_to_fstring(state
->response
.data
.auth
.info3
.dom_sid
, info3
->base
.domain_sid
);
61 state
->response
.data
.auth
.info3
.num_groups
= info3
->base
.groups
.count
;
62 state
->response
.data
.auth
.info3
.user_flgs
= info3
->base
.user_flags
;
64 state
->response
.data
.auth
.info3
.acct_flags
= info3
->base
.acct_flags
;
65 state
->response
.data
.auth
.info3
.num_other_sids
= info3
->sidcount
;
67 fstrcpy(state
->response
.data
.auth
.info3
.user_name
,
68 info3
->base
.account_name
.string
);
69 fstrcpy(state
->response
.data
.auth
.info3
.full_name
,
70 info3
->base
.full_name
.string
);
71 fstrcpy(state
->response
.data
.auth
.info3
.logon_script
,
72 info3
->base
.logon_script
.string
);
73 fstrcpy(state
->response
.data
.auth
.info3
.profile_path
,
74 info3
->base
.profile_path
.string
);
75 fstrcpy(state
->response
.data
.auth
.info3
.home_dir
,
76 info3
->base
.home_directory
.string
);
77 fstrcpy(state
->response
.data
.auth
.info3
.dir_drive
,
78 info3
->base
.home_drive
.string
);
80 fstrcpy(state
->response
.data
.auth
.info3
.logon_srv
,
81 info3
->base
.logon_server
.string
);
82 fstrcpy(state
->response
.data
.auth
.info3
.logon_dom
,
83 info3
->base
.domain
.string
);
85 ex
= talloc_strdup(mem_ctx
, "");
86 NT_STATUS_HAVE_NO_MEMORY(ex
);
88 for (i
=0; i
< info3
->base
.groups
.count
; i
++) {
89 ex
= talloc_asprintf_append_buffer(ex
, "0x%08X:0x%08X\n",
90 info3
->base
.groups
.rids
[i
].rid
,
91 info3
->base
.groups
.rids
[i
].attributes
);
92 NT_STATUS_HAVE_NO_MEMORY(ex
);
95 for (i
=0; i
< info3
->sidcount
; i
++) {
98 sid
= dom_sid_string(mem_ctx
, info3
->sids
[i
].sid
);
99 NT_STATUS_HAVE_NO_MEMORY(sid
);
101 ex
= talloc_asprintf_append_buffer(ex
, "%s:0x%08X\n",
103 info3
->sids
[i
].attributes
);
104 NT_STATUS_HAVE_NO_MEMORY(ex
);
109 size
= talloc_get_size(ex
);
111 SAFE_FREE(state
->response
.extra_data
.data
);
112 state
->response
.extra_data
.data
= SMB_MALLOC(size
);
113 if (!state
->response
.extra_data
.data
) {
114 return NT_STATUS_NO_MEMORY
;
116 memcpy(state
->response
.extra_data
.data
, ex
, size
);
119 state
->response
.length
+= size
;
124 static NTSTATUS
append_info3_as_ndr(TALLOC_CTX
*mem_ctx
,
125 struct winbindd_cli_state
*state
,
126 struct netr_SamInfo3
*info3
)
129 enum ndr_err_code ndr_err
;
131 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, NULL
, info3
,
132 (ndr_push_flags_fn_t
)ndr_push_netr_SamInfo3
);
133 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
134 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
135 return ndr_map_error2ntstatus(ndr_err
);
138 SAFE_FREE(state
->response
.extra_data
.data
);
139 state
->response
.extra_data
.data
= SMB_MALLOC(blob
.length
);
140 if (!state
->response
.extra_data
.data
) {
141 data_blob_free(&blob
);
142 return NT_STATUS_NO_MEMORY
;
145 memset(state
->response
.extra_data
.data
, '\0', blob
.length
);
146 memcpy(state
->response
.extra_data
.data
, blob
.data
, blob
.length
);
147 state
->response
.length
+= blob
.length
;
149 data_blob_free(&blob
);
154 static NTSTATUS
append_unix_username(TALLOC_CTX
*mem_ctx
,
155 struct winbindd_cli_state
*state
,
156 const struct netr_SamInfo3
*info3
,
157 const char *name_domain
,
158 const char *name_user
)
160 /* We've been asked to return the unix username, per
161 'winbind use default domain' settings and the like */
163 const char *nt_username
, *nt_domain
;
165 nt_domain
= talloc_strdup(mem_ctx
, info3
->base
.domain
.string
);
167 /* If the server didn't give us one, just use the one
169 nt_domain
= name_domain
;
172 nt_username
= talloc_strdup(mem_ctx
, info3
->base
.account_name
.string
);
174 /* If the server didn't give us one, just use the one
176 nt_username
= name_user
;
179 fill_domain_username(state
->response
.data
.auth
.unix_username
,
180 nt_domain
, nt_username
, true);
182 DEBUG(5,("Setting unix username to [%s]\n",
183 state
->response
.data
.auth
.unix_username
));
188 static NTSTATUS
append_afs_token(TALLOC_CTX
*mem_ctx
,
189 struct winbindd_cli_state
*state
,
190 const struct netr_SamInfo3
*info3
,
191 const char *name_domain
,
192 const char *name_user
)
194 char *afsname
= NULL
;
197 afsname
= talloc_strdup(mem_ctx
, lp_afs_username_map());
198 if (afsname
== NULL
) {
199 return NT_STATUS_NO_MEMORY
;
202 afsname
= talloc_string_sub(mem_ctx
,
203 lp_afs_username_map(),
205 afsname
= talloc_string_sub(mem_ctx
, afsname
,
207 afsname
= talloc_string_sub(mem_ctx
, afsname
,
214 sid_copy(&user_sid
, info3
->base
.domain_sid
);
215 sid_append_rid(&user_sid
, info3
->base
.rid
);
216 sid_to_fstring(sidstr
, &user_sid
);
217 afsname
= talloc_string_sub(mem_ctx
, afsname
,
221 if (afsname
== NULL
) {
222 return NT_STATUS_NO_MEMORY
;
227 DEBUG(10, ("Generating token for user %s\n", afsname
));
229 cell
= strchr(afsname
, '@');
232 return NT_STATUS_NO_MEMORY
;
238 /* Append an AFS token string */
239 SAFE_FREE(state
->response
.extra_data
.data
);
240 state
->response
.extra_data
.data
=
241 afs_createtoken_str(afsname
, cell
);
243 if (state
->response
.extra_data
.data
!= NULL
) {
244 state
->response
.length
+=
245 strlen((const char *)state
->response
.extra_data
.data
)+1;
251 static NTSTATUS
check_info3_in_group(TALLOC_CTX
*mem_ctx
,
252 struct netr_SamInfo3
*info3
,
253 const char *group_sid
)
255 * Check whether a user belongs to a group or list of groups.
257 * @param mem_ctx talloc memory context.
258 * @param info3 user information, including group membership info.
259 * @param group_sid One or more groups , separated by commas.
261 * @return NT_STATUS_OK on success,
262 * NT_STATUS_LOGON_FAILURE if the user does not belong,
263 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
266 DOM_SID
*require_membership_of_sid
;
267 size_t num_require_membership_of_sid
;
272 struct nt_user_token
*token
;
273 TALLOC_CTX
*frame
= NULL
;
276 /* Parse the 'required group' SID */
278 if (!group_sid
|| !group_sid
[0]) {
279 /* NO sid supplied, all users may access */
283 if (!(token
= TALLOC_ZERO_P(mem_ctx
, struct nt_user_token
))) {
284 DEBUG(0, ("talloc failed\n"));
285 return NT_STATUS_NO_MEMORY
;
288 num_require_membership_of_sid
= 0;
289 require_membership_of_sid
= NULL
;
293 frame
= talloc_stackframe();
294 while (next_token_talloc(frame
, &p
, &req_sid
, ",")) {
295 if (!string_to_sid(&sid
, req_sid
)) {
296 DEBUG(0, ("check_info3_in_group: could not parse %s "
297 "as a SID!", req_sid
));
299 return NT_STATUS_INVALID_PARAMETER
;
302 status
= add_sid_to_array(mem_ctx
, &sid
,
303 &require_membership_of_sid
,
304 &num_require_membership_of_sid
);
305 if (!NT_STATUS_IS_OK(status
)) {
306 DEBUG(0, ("add_sid_to_array failed\n"));
314 status
= sid_array_from_info3(mem_ctx
, info3
,
318 if (!NT_STATUS_IS_OK(status
)) {
322 if (!NT_STATUS_IS_OK(status
= add_aliases(get_global_sam_sid(),
324 || !NT_STATUS_IS_OK(status
= add_aliases(&global_sid_Builtin
,
326 DEBUG(3, ("could not add aliases: %s\n",
331 debug_nt_user_token(DBGC_CLASS
, 10, token
);
333 for (i
=0; i
<num_require_membership_of_sid
; i
++) {
334 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
335 &require_membership_of_sid
[i
])));
336 if (nt_token_check_sid(&require_membership_of_sid
[i
],
338 DEBUG(10, ("Access ok\n"));
343 /* Do not distinguish this error from a wrong username/pw */
345 return NT_STATUS_LOGON_FAILURE
;
348 struct winbindd_domain
*find_auth_domain(struct winbindd_cli_state
*state
,
349 const char *domain_name
)
351 struct winbindd_domain
*domain
;
354 domain
= find_domain_from_name_noinit(domain_name
);
355 if (domain
== NULL
) {
356 DEBUG(3, ("Authentication for domain [%s] refused "
357 "as it is not a trusted domain\n",
363 if (is_myname(domain_name
)) {
364 DEBUG(3, ("Authentication for domain %s (local domain "
365 "to this server) not supported at this "
366 "stage\n", domain_name
));
370 /* we can auth against trusted domains */
371 if (state
->request
.flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
372 domain
= find_domain_from_name_noinit(domain_name
);
373 if (domain
== NULL
) {
374 DEBUG(3, ("Authentication for domain [%s] skipped "
375 "as it is not a trusted domain\n",
382 return find_our_domain();
385 static void fill_in_password_policy(struct winbindd_response
*r
,
386 const struct samr_DomInfo1
*p
)
388 r
->data
.auth
.policy
.min_length_password
=
389 p
->min_password_length
;
390 r
->data
.auth
.policy
.password_history
=
391 p
->password_history_length
;
392 r
->data
.auth
.policy
.password_properties
=
393 p
->password_properties
;
394 r
->data
.auth
.policy
.expire
=
395 nt_time_to_unix_abs((NTTIME
*)&(p
->max_password_age
));
396 r
->data
.auth
.policy
.min_passwordage
=
397 nt_time_to_unix_abs((NTTIME
*)&(p
->min_password_age
));
400 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
401 struct winbindd_cli_state
*state
)
403 struct winbindd_methods
*methods
;
404 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
405 struct samr_DomInfo1 password_policy
;
407 if ( !winbindd_can_contact_domain( domain
) ) {
408 DEBUG(5,("fillup_password_policy: No inbound trust to "
409 "contact domain %s\n", domain
->name
));
410 return NT_STATUS_NOT_SUPPORTED
;
413 methods
= domain
->methods
;
415 status
= methods
->password_policy(domain
, state
->mem_ctx
, &password_policy
);
416 if (NT_STATUS_IS_ERR(status
)) {
420 fill_in_password_policy(&state
->response
, &password_policy
);
425 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
427 uint16
*lockout_threshold
)
429 struct winbindd_methods
*methods
;
430 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
431 struct samr_DomInfo12 lockout_policy
;
433 *lockout_threshold
= 0;
435 methods
= domain
->methods
;
437 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
438 if (NT_STATUS_IS_ERR(status
)) {
442 *lockout_threshold
= lockout_policy
.lockout_threshold
;
447 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
449 uint32
*password_properties
)
451 struct winbindd_methods
*methods
;
452 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
453 struct samr_DomInfo1 password_policy
;
455 *password_properties
= 0;
457 methods
= domain
->methods
;
459 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
460 if (NT_STATUS_IS_ERR(status
)) {
464 *password_properties
= password_policy
.password_properties
;
471 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
474 bool *internal_ccache
)
476 /* accept FILE and WRFILE as krb5_cc_type from the client and then
477 * build the full ccname string based on the user's uid here -
480 const char *gen_cc
= NULL
;
482 *internal_ccache
= true;
488 if (!type
|| type
[0] == '\0') {
492 if (strequal(type
, "FILE")) {
493 gen_cc
= talloc_asprintf(mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
494 } else if (strequal(type
, "WRFILE")) {
495 gen_cc
= talloc_asprintf(mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
497 DEBUG(10,("we don't allow to set a %s type ccache\n", type
));
501 *internal_ccache
= false;
505 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
508 if (gen_cc
== NULL
) {
509 DEBUG(0,("out of memory\n"));
513 DEBUG(10,("using ccache: %s %s\n", gen_cc
, *internal_ccache
? "(internal)":""));
518 static void setup_return_cc_name(struct winbindd_cli_state
*state
, const char *cc
)
520 const char *type
= state
->request
.data
.auth
.krb5_cc_type
;
522 state
->response
.data
.auth
.krb5ccname
[0] = '\0';
524 if (type
[0] == '\0') {
528 if (!strequal(type
, "FILE") &&
529 !strequal(type
, "WRFILE")) {
530 DEBUG(10,("won't return krbccname for a %s type ccache\n",
535 fstrcpy(state
->response
.data
.auth
.krb5ccname
, cc
);
540 static uid_t
get_uid_from_state(struct winbindd_cli_state
*state
)
544 uid
= state
->request
.data
.auth
.uid
;
547 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid
));
553 /**********************************************************************
554 Authenticate a user with a clear text password using Kerberos and fill up
556 **********************************************************************/
558 static NTSTATUS
winbindd_raw_kerberos_login(struct winbindd_domain
*domain
,
559 struct winbindd_cli_state
*state
,
560 struct netr_SamInfo3
**info3
)
563 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
564 krb5_error_code krb5_ret
;
565 const char *cc
= NULL
;
566 const char *principal_s
= NULL
;
567 const char *service
= NULL
;
569 fstring name_domain
, name_user
;
570 time_t ticket_lifetime
= 0;
571 time_t renewal_until
= 0;
574 time_t time_offset
= 0;
575 bool internal_ccache
= true;
582 * prepare a krb5_cc_cache string for the user */
584 uid
= get_uid_from_state(state
);
586 DEBUG(0,("no valid uid\n"));
589 cc
= generate_krb5_ccache(state
->mem_ctx
,
590 state
->request
.data
.auth
.krb5_cc_type
,
591 state
->request
.data
.auth
.uid
,
594 return NT_STATUS_NO_MEMORY
;
599 * get kerberos properties */
601 if (domain
->private_data
) {
602 ads
= (ADS_STRUCT
*)domain
->private_data
;
603 time_offset
= ads
->auth
.time_offset
;
608 * do kerberos auth and setup ccache as the user */
610 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
612 realm
= domain
->alt_name
;
615 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
616 if (principal_s
== NULL
) {
617 return NT_STATUS_NO_MEMORY
;
620 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
621 if (service
== NULL
) {
622 return NT_STATUS_NO_MEMORY
;
625 /* if this is a user ccache, we need to act as the user to let the krb5
626 * library handle the chown, etc. */
628 /************************ ENTERING NON-ROOT **********************/
630 if (!internal_ccache
) {
631 set_effective_uid(uid
);
632 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
635 result
= kerberos_return_info3_from_pac(state
->mem_ctx
,
637 state
->request
.data
.auth
.pass
,
644 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
646 if (!internal_ccache
) {
647 gain_root_privilege();
650 /************************ RETURNED TO ROOT **********************/
652 if (!NT_STATUS_IS_OK(result
)) {
656 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
659 /* if we had a user's ccache then return that string for the pam
662 if (!internal_ccache
) {
664 setup_return_cc_name(state
, cc
);
666 result
= add_ccache_to_list(principal_s
,
669 state
->request
.data
.auth
.user
,
677 if (!NT_STATUS_IS_OK(result
)) {
678 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
683 /* need to delete the memory cred cache, it is not used anymore */
685 krb5_ret
= ads_kdestroy(cc
);
687 DEBUG(3,("winbindd_raw_kerberos_login: "
688 "could not destroy krb5 credential cache: "
689 "%s\n", error_message(krb5_ret
)));
698 /* we could have created a new credential cache with a valid tgt in it
699 * but we werent able to get or verify the service ticket for this
700 * local host and therefor didn't get the PAC, we need to remove that
701 * cache entirely now */
703 krb5_ret
= ads_kdestroy(cc
);
705 DEBUG(3,("winbindd_raw_kerberos_login: "
706 "could not destroy krb5 credential cache: "
707 "%s\n", error_message(krb5_ret
)));
710 if (!NT_STATUS_IS_OK(remove_ccache(state
->request
.data
.auth
.user
))) {
711 DEBUG(3,("winbindd_raw_kerberos_login: "
712 "could not remove ccache for user %s\n",
713 state
->request
.data
.auth
.user
));
718 return NT_STATUS_NOT_SUPPORTED
;
719 #endif /* HAVE_KRB5 */
722 /****************************************************************
723 ****************************************************************/
725 static bool check_request_flags(uint32_t flags
)
727 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
728 WBFLAG_PAM_INFO3_TEXT
|
729 WBFLAG_PAM_INFO3_NDR
;
731 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
732 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
733 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
734 !(flags
& flags_edata
) ) {
738 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags
));
743 /****************************************************************
744 ****************************************************************/
746 static NTSTATUS
append_data(struct winbindd_cli_state
*state
,
747 struct netr_SamInfo3
*info3
,
748 const char *name_domain
,
749 const char *name_user
)
752 uint32_t flags
= state
->request
.flags
;
754 if (flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
755 memcpy(state
->response
.data
.auth
.user_session_key
,
757 sizeof(state
->response
.data
.auth
.user_session_key
)
761 if (flags
& WBFLAG_PAM_LMKEY
) {
762 memcpy(state
->response
.data
.auth
.first_8_lm_hash
,
763 info3
->base
.LMSessKey
.key
,
764 sizeof(state
->response
.data
.auth
.first_8_lm_hash
)
768 if (flags
& WBFLAG_PAM_INFO3_TEXT
) {
769 result
= append_info3_as_txt(state
->mem_ctx
, state
, info3
);
770 if (!NT_STATUS_IS_OK(result
)) {
771 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
777 /* currently, anything from here on potentially overwrites extra_data. */
779 if (flags
& WBFLAG_PAM_INFO3_NDR
) {
780 result
= append_info3_as_ndr(state
->mem_ctx
, state
, info3
);
781 if (!NT_STATUS_IS_OK(result
)) {
782 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
788 if (flags
& WBFLAG_PAM_UNIX_NAME
) {
789 result
= append_unix_username(state
->mem_ctx
, state
, info3
,
790 name_domain
, name_user
);
791 if (!NT_STATUS_IS_OK(result
)) {
792 DEBUG(10,("Failed to append Unix Username: %s\n",
798 if (flags
& WBFLAG_PAM_AFS_TOKEN
) {
799 result
= append_afs_token(state
->mem_ctx
, state
, info3
,
800 name_domain
, name_user
);
801 if (!NT_STATUS_IS_OK(result
)) {
802 DEBUG(10,("Failed to append AFS token: %s\n",
811 void winbindd_pam_auth(struct winbindd_cli_state
*state
)
813 struct winbindd_domain
*domain
;
814 fstring name_domain
, name_user
;
815 char *mapped_user
= NULL
;
817 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
819 /* Ensure null termination */
820 state
->request
.data
.auth
.user
821 [sizeof(state
->request
.data
.auth
.user
)-1]='\0';
823 /* Ensure null termination */
824 state
->request
.data
.auth
.pass
825 [sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
827 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state
->pid
,
828 state
->request
.data
.auth
.user
));
830 if (!check_request_flags(state
->request
.flags
)) {
831 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
835 /* Parse domain and username */
837 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
838 state
->request
.data
.auth
.user
,
841 /* If the name normalization didnt' actually do anything,
842 just use the original name */
844 if (!NT_STATUS_IS_OK(name_map_status
) &&
845 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
847 mapped_user
= state
->request
.data
.auth
.user
;
850 if (!canonicalize_username(mapped_user
, name_domain
, name_user
)) {
851 result
= NT_STATUS_NO_SUCH_USER
;
855 domain
= find_auth_domain(state
, name_domain
);
857 if (domain
== NULL
) {
858 result
= NT_STATUS_NO_SUCH_USER
;
862 sendto_domain(state
, domain
);
865 set_auth_errors(&state
->response
, result
);
866 DEBUG(5, ("Plain text authentication for %s returned %s "
868 state
->request
.data
.auth
.user
,
869 state
->response
.data
.auth
.nt_status_string
,
870 state
->response
.data
.auth
.pam_error
));
871 request_error(state
);
874 NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
875 struct winbindd_cli_state
*state
,
876 struct netr_SamInfo3
**info3
)
878 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
879 uint16 max_allowed_bad_attempts
;
880 fstring name_domain
, name_user
;
882 enum lsa_SidType type
;
883 uchar new_nt_pass
[NT_HASH_LEN
];
884 const uint8
*cached_nt_pass
;
885 const uint8
*cached_salt
;
886 struct netr_SamInfo3
*my_info3
;
887 time_t kickoff_time
, must_change_time
;
888 bool password_good
= false;
890 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
897 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
899 /* Parse domain and username */
901 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
904 if (!lookup_cached_name(state
->mem_ctx
,
909 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
910 return NT_STATUS_NO_SUCH_USER
;
913 if (type
!= SID_NAME_USER
) {
914 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
915 return NT_STATUS_LOGON_FAILURE
;
918 result
= winbindd_get_creds(domain
,
924 if (!NT_STATUS_IS_OK(result
)) {
925 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
931 E_md4hash(state
->request
.data
.auth
.pass
, new_nt_pass
);
933 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
934 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
936 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
940 /* In this case we didn't store the nt_hash itself,
941 but the MD5 combination of salt + nt_hash. */
942 uchar salted_hash
[NT_HASH_LEN
];
943 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
945 password_good
= (memcmp(cached_nt_pass
, salted_hash
, NT_HASH_LEN
) == 0) ?
948 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
949 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
, NT_HASH_LEN
) == 0) ?
955 /* User *DOES* know the password, update logon_time and reset
958 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
960 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
961 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
964 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
965 return NT_STATUS_ACCOUNT_DISABLED
;
968 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
969 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
972 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
973 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
976 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
977 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
980 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
981 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
982 my_info3
->base
.acct_flags
));
983 return NT_STATUS_LOGON_FAILURE
;
986 kickoff_time
= nt_time_to_unix(my_info3
->base
.acct_expiry
);
987 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
988 return NT_STATUS_ACCOUNT_EXPIRED
;
991 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
992 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
993 /* we allow grace logons when the password has expired */
994 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
995 /* return NT_STATUS_PASSWORD_EXPIRED; */
1000 if ((state
->request
.flags
& WBFLAG_PAM_KRB5
) &&
1001 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
1002 (tdc_domain
->trust_type
& NETR_TRUST_TYPE_UPLEVEL
)) {
1005 const char *cc
= NULL
;
1007 const char *principal_s
= NULL
;
1008 const char *service
= NULL
;
1009 bool internal_ccache
= false;
1011 uid
= get_uid_from_state(state
);
1013 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1014 return NT_STATUS_INVALID_PARAMETER
;
1017 cc
= generate_krb5_ccache(state
->mem_ctx
,
1018 state
->request
.data
.auth
.krb5_cc_type
,
1019 state
->request
.data
.auth
.uid
,
1022 return NT_STATUS_NO_MEMORY
;
1025 realm
= domain
->alt_name
;
1028 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
1029 if (principal_s
== NULL
) {
1030 return NT_STATUS_NO_MEMORY
;
1033 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
1034 if (service
== NULL
) {
1035 return NT_STATUS_NO_MEMORY
;
1038 if (!internal_ccache
) {
1040 setup_return_cc_name(state
, cc
);
1042 result
= add_ccache_to_list(principal_s
,
1045 state
->request
.data
.auth
.user
,
1049 time(NULL
) + lp_winbind_cache_time(),
1050 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
1053 if (!NT_STATUS_IS_OK(result
)) {
1054 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1055 "to add ccache to list: %s\n",
1056 nt_errstr(result
)));
1060 #endif /* HAVE_KRB5 */
1062 /* FIXME: we possibly should handle logon hours as well (does xp when
1063 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1065 unix_to_nt_time(&my_info3
->base
.last_logon
, time(NULL
));
1066 my_info3
->base
.bad_password_count
= 0;
1068 result
= winbindd_update_creds_by_info3(domain
,
1070 state
->request
.data
.auth
.user
,
1071 state
->request
.data
.auth
.pass
,
1073 if (!NT_STATUS_IS_OK(result
)) {
1074 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1075 nt_errstr(result
)));
1079 return NT_STATUS_OK
;
1083 /* User does *NOT* know the correct password, modify info3 accordingly */
1085 /* failure of this is not critical */
1086 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1087 if (!NT_STATUS_IS_OK(result
)) {
1088 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1089 "Won't be able to honour account lockout policies\n"));
1092 /* increase counter */
1093 my_info3
->base
.bad_password_count
++;
1095 if (max_allowed_bad_attempts
== 0) {
1100 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1102 uint32 password_properties
;
1104 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1105 if (!NT_STATUS_IS_OK(result
)) {
1106 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1109 if ((my_info3
->base
.rid
!= DOMAIN_USER_RID_ADMIN
) ||
1110 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1111 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1116 result
= winbindd_update_creds_by_info3(domain
,
1118 state
->request
.data
.auth
.user
,
1122 if (!NT_STATUS_IS_OK(result
)) {
1123 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1124 nt_errstr(result
)));
1127 return NT_STATUS_LOGON_FAILURE
;
1130 NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1131 struct winbindd_cli_state
*state
,
1132 struct netr_SamInfo3
**info3
)
1134 struct winbindd_domain
*contact_domain
;
1135 fstring name_domain
, name_user
;
1138 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1140 /* Parse domain and username */
1142 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
1144 /* what domain should we contact? */
1147 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1148 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1149 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
1150 result
= NT_STATUS_NO_SUCH_USER
;
1155 if (is_myname(name_domain
)) {
1156 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1157 result
= NT_STATUS_NO_SUCH_USER
;
1161 contact_domain
= find_domain_from_name(name_domain
);
1162 if (contact_domain
== NULL
) {
1163 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1164 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
1166 contact_domain
= find_our_domain();
1170 if (contact_domain
->initialized
&&
1171 contact_domain
->active_directory
) {
1175 if (!contact_domain
->initialized
) {
1176 init_dc_connection(contact_domain
);
1179 if (!contact_domain
->active_directory
) {
1180 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1181 return NT_STATUS_INVALID_LOGON_TYPE
;
1184 result
= winbindd_raw_kerberos_login(contact_domain
, state
, info3
);
1189 typedef NTSTATUS (*netlogon_fn_t
)(struct rpc_pipe_client
*cli
,
1190 TALLOC_CTX
*mem_ctx
,
1191 uint32 logon_parameters
,
1193 const char *username
,
1195 const char *workstation
,
1196 const uint8 chal
[8],
1197 DATA_BLOB lm_response
,
1198 DATA_BLOB nt_response
,
1199 struct netr_SamInfo3
**info3
);
1201 NTSTATUS
winbindd_dual_pam_auth_samlogon(struct winbindd_domain
*domain
,
1202 struct winbindd_cli_state
*state
,
1203 struct netr_SamInfo3
**info3
)
1206 struct rpc_pipe_client
*netlogon_pipe
;
1211 unsigned char local_lm_response
[24];
1212 unsigned char local_nt_response
[24];
1213 struct winbindd_domain
*contact_domain
;
1214 fstring name_domain
, name_user
;
1217 struct netr_SamInfo3
*my_info3
= NULL
;
1221 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1223 /* Parse domain and username */
1225 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
1227 /* do password magic */
1230 generate_random_buffer(chal
, 8);
1231 if (lp_client_ntlmv2_auth()) {
1232 DATA_BLOB server_chal
;
1233 DATA_BLOB names_blob
;
1234 DATA_BLOB nt_response
;
1235 DATA_BLOB lm_response
;
1236 server_chal
= data_blob_talloc(state
->mem_ctx
, chal
, 8);
1238 /* note that the 'workgroup' here is a best guess - we don't know
1239 the server's domain at this point. The 'server name' is also
1242 names_blob
= NTLMv2_generate_names_blob(state
->mem_ctx
, global_myname(), lp_workgroup());
1244 if (!SMBNTLMv2encrypt(NULL
, name_user
, name_domain
,
1245 state
->request
.data
.auth
.pass
,
1248 &lm_response
, &nt_response
, NULL
, NULL
)) {
1249 data_blob_free(&names_blob
);
1250 data_blob_free(&server_chal
);
1251 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1252 result
= NT_STATUS_NO_MEMORY
;
1255 data_blob_free(&names_blob
);
1256 data_blob_free(&server_chal
);
1257 lm_resp
= data_blob_talloc(state
->mem_ctx
, lm_response
.data
,
1258 lm_response
.length
);
1259 nt_resp
= data_blob_talloc(state
->mem_ctx
, nt_response
.data
,
1260 nt_response
.length
);
1261 data_blob_free(&lm_response
);
1262 data_blob_free(&nt_response
);
1265 if (lp_client_lanman_auth()
1266 && SMBencrypt(state
->request
.data
.auth
.pass
,
1268 local_lm_response
)) {
1269 lm_resp
= data_blob_talloc(state
->mem_ctx
,
1271 sizeof(local_lm_response
));
1273 lm_resp
= data_blob_null
;
1275 SMBNTencrypt(state
->request
.data
.auth
.pass
,
1279 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1281 sizeof(local_nt_response
));
1284 /* what domain should we contact? */
1287 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1288 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1289 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
1290 result
= NT_STATUS_NO_SUCH_USER
;
1295 if (is_myname(name_domain
)) {
1296 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1297 result
= NT_STATUS_NO_SUCH_USER
;
1301 contact_domain
= find_our_domain();
1304 /* check authentication loop */
1307 netlogon_fn_t logon_fn
;
1309 ZERO_STRUCTP(my_info3
);
1312 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1314 if (!NT_STATUS_IS_OK(result
)) {
1315 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1319 /* It is really important to try SamLogonEx here,
1320 * because in a clustered environment, we want to use
1321 * one machine account from multiple physical
1324 * With a normal SamLogon call, we must keep the
1325 * credentials chain updated and intact between all
1326 * users of the machine account (which would imply
1327 * cross-node communication for every NTLM logon).
1329 * (The credentials chain is not per NETLOGON pipe
1330 * connection, but globally on the server/client pair
1333 * When using SamLogonEx, the credentials are not
1334 * supplied, but the session key is implied by the
1335 * wrapping SamLogon context.
1337 * -- abartlet 21 April 2008
1340 logon_fn
= contact_domain
->can_do_samlogon_ex
1341 ? rpccli_netlogon_sam_network_logon_ex
1342 : rpccli_netlogon_sam_network_logon
;
1344 result
= logon_fn(netlogon_pipe
,
1347 contact_domain
->dcname
, /* server name */
1348 name_user
, /* user name */
1349 name_domain
, /* target domain */
1350 global_myname(), /* workstation */
1357 if ((NT_STATUS_V(result
) == DCERPC_FAULT_OP_RNG_ERROR
)
1358 && contact_domain
->can_do_samlogon_ex
) {
1359 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1360 "retrying with NetSamLogon\n"));
1361 contact_domain
->can_do_samlogon_ex
= false;
1366 /* We have to try a second time as cm_connect_netlogon
1367 might not yet have noticed that the DC has killed
1370 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1375 /* if we get access denied, a possible cause was that we had
1376 and open connection to the DC, but someone changed our
1377 machine account password out from underneath us using 'net
1378 rpc changetrustpw' */
1380 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1381 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1382 "ACCESS_DENIED. Maybe the trust account "
1383 "password was changed and we didn't know it. "
1384 "Killing connections to domain %s\n",
1386 invalidate_cm_connection(&contact_domain
->conn
);
1390 } while ( (attempts
< 2) && retry
);
1392 /* handle the case where a NT4 DC does not fill in the acct_flags in
1393 * the samlogon reply info3. When accurate info3 is required by the
1394 * caller, we look up the account flags ourselve - gd */
1396 if ((state
->request
.flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1397 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1399 struct rpc_pipe_client
*samr_pipe
;
1400 struct policy_handle samr_domain_handle
, user_pol
;
1401 union samr_UserInfo
*info
= NULL
;
1402 NTSTATUS status_tmp
;
1405 status_tmp
= cm_connect_sam(contact_domain
, state
->mem_ctx
,
1406 &samr_pipe
, &samr_domain_handle
);
1408 if (!NT_STATUS_IS_OK(status_tmp
)) {
1409 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1410 nt_errstr(status_tmp
)));
1414 status_tmp
= rpccli_samr_OpenUser(samr_pipe
, state
->mem_ctx
,
1415 &samr_domain_handle
,
1416 MAXIMUM_ALLOWED_ACCESS
,
1420 if (!NT_STATUS_IS_OK(status_tmp
)) {
1421 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1422 nt_errstr(status_tmp
)));
1426 status_tmp
= rpccli_samr_QueryUserInfo(samr_pipe
, state
->mem_ctx
,
1431 if (!NT_STATUS_IS_OK(status_tmp
)) {
1432 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1433 nt_errstr(status_tmp
)));
1434 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1438 acct_flags
= info
->info16
.acct_flags
;
1440 if (acct_flags
== 0) {
1441 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1445 my_info3
->base
.acct_flags
= acct_flags
;
1447 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1449 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1457 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1458 struct winbindd_cli_state
*state
)
1460 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1461 NTSTATUS krb5_result
= NT_STATUS_OK
;
1462 fstring name_domain
, name_user
;
1464 fstring domain_user
;
1465 struct netr_SamInfo3
*info3
= NULL
;
1466 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1468 /* Ensure null termination */
1469 state
->request
.data
.auth
.user
[sizeof(state
->request
.data
.auth
.user
)-1]='\0';
1471 /* Ensure null termination */
1472 state
->request
.data
.auth
.pass
[sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
1474 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1475 state
->request
.data
.auth
.user
));
1477 if (!check_request_flags(state
->request
.flags
)) {
1478 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1482 /* Parse domain and username */
1484 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1485 state
->request
.data
.auth
.user
,
1488 /* If the name normalization didnt' actually do anything,
1489 just use the original name */
1491 if (!NT_STATUS_IS_OK(name_map_status
) &&
1492 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1494 mapped_user
= state
->request
.data
.auth
.user
;
1497 parse_domain_user(mapped_user
, name_domain
, name_user
);
1499 if ( mapped_user
!= state
->request
.data
.auth
.user
) {
1500 fstr_sprintf( domain_user
, "%s\\%s", name_domain
, name_user
);
1501 safe_strcpy( state
->request
.data
.auth
.user
, domain_user
,
1502 sizeof(state
->request
.data
.auth
.user
)-1 );
1505 if (domain
->online
== false) {
1506 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1507 if (domain
->startup
) {
1508 /* Logons are very important to users. If we're offline and
1509 we get a request within the first 30 seconds of startup,
1510 try very hard to find a DC and go online. */
1512 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1513 "request in startup mode.\n", domain
->name
));
1515 winbindd_flush_negative_conn_cache(domain
);
1516 result
= init_dc_connection(domain
);
1520 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1522 /* Check for Kerberos authentication */
1523 if (domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1525 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1526 /* save for later */
1527 krb5_result
= result
;
1530 if (NT_STATUS_IS_OK(result
)) {
1531 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1532 goto process_result
;
1534 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1537 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1538 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1539 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1540 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1541 set_domain_offline( domain
);
1545 /* there are quite some NT_STATUS errors where there is no
1546 * point in retrying with a samlogon, we explictly have to take
1547 * care not to increase the bad logon counter on the DC */
1549 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1550 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1551 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1552 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1553 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1554 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1555 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1556 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1557 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1558 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1559 goto process_result
;
1562 if (state
->request
.flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1563 DEBUG(3,("falling back to samlogon\n"));
1571 /* Check for Samlogon authentication */
1572 if (domain
->online
) {
1573 result
= winbindd_dual_pam_auth_samlogon(domain
, state
, &info3
);
1575 if (NT_STATUS_IS_OK(result
)) {
1576 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1577 /* add the Krb5 err if we have one */
1578 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1579 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1581 goto process_result
;
1584 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1585 nt_errstr(result
)));
1587 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1588 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1589 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1591 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1592 set_domain_offline( domain
);
1596 if (domain
->online
) {
1597 /* We're still online - fail. */
1603 /* Check for Cached logons */
1604 if (!domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1605 lp_winbind_offline_logon()) {
1607 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1609 if (NT_STATUS_IS_OK(result
)) {
1610 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1611 goto process_result
;
1613 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1620 if (NT_STATUS_IS_OK(result
)) {
1624 /* In all codepaths where result == NT_STATUS_OK info3 must have
1625 been initialized. */
1627 result
= NT_STATUS_INTERNAL_ERROR
;
1631 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1632 netsamlogon_cache_store(name_user
, info3
);
1634 /* save name_to_sid info as early as possible (only if
1635 this is our primary domain so we don't invalidate
1636 the cache entry by storing the seq_num for the wrong
1638 if ( domain
->primary
) {
1639 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1641 cache_name2sid(domain
, name_domain
, name_user
,
1642 SID_NAME_USER
, &user_sid
);
1645 /* Check if the user is in the right group */
1647 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1648 state
->request
.data
.auth
.require_membership_of_sid
))) {
1649 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1650 state
->request
.data
.auth
.user
,
1651 state
->request
.data
.auth
.require_membership_of_sid
));
1655 result
= append_data(state
, info3
, name_domain
, name_user
);
1656 if (!NT_STATUS_IS_OK(result
)) {
1660 if ((state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
1662 /* Store in-memory creds for single-signon using ntlm_auth. */
1663 result
= winbindd_add_memory_creds(state
->request
.data
.auth
.user
,
1664 get_uid_from_state(state
),
1665 state
->request
.data
.auth
.pass
);
1667 if (!NT_STATUS_IS_OK(result
)) {
1668 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result
)));
1672 if (lp_winbind_offline_logon()) {
1673 result
= winbindd_store_creds(domain
,
1675 state
->request
.data
.auth
.user
,
1676 state
->request
.data
.auth
.pass
,
1678 if (!NT_STATUS_IS_OK(result
)) {
1680 /* Release refcount. */
1681 winbindd_delete_memory_creds(state
->request
.data
.auth
.user
);
1683 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
1690 if (state
->request
.flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1691 struct winbindd_domain
*our_domain
= find_our_domain();
1693 /* This is not entirely correct I believe, but it is
1694 consistent. Only apply the password policy settings
1695 too warn users for our own domain. Cannot obtain these
1696 from trusted DCs all the time so don't do it at all.
1699 result
= NT_STATUS_NOT_SUPPORTED
;
1700 if (our_domain
== domain
) {
1701 result
= fillup_password_policy(our_domain
, state
);
1704 if (!NT_STATUS_IS_OK(result
)
1705 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1707 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1708 domain
->name
, nt_errstr(result
)));
1713 result
= NT_STATUS_OK
;
1717 /* give us a more useful (more correct?) error code */
1718 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1719 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1720 result
= NT_STATUS_NO_LOGON_SERVERS
;
1723 set_auth_errors(&state
->response
, result
);
1725 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1726 state
->request
.data
.auth
.user
,
1727 state
->response
.data
.auth
.nt_status_string
,
1728 state
->response
.data
.auth
.pam_error
));
1730 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1734 /**********************************************************************
1735 Challenge Response Authentication Protocol
1736 **********************************************************************/
1738 void winbindd_pam_auth_crap(struct winbindd_cli_state
*state
)
1740 struct winbindd_domain
*domain
= NULL
;
1741 const char *domain_name
= NULL
;
1744 if (!check_request_flags(state
->request
.flags
)) {
1745 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1749 if (!state
->privileged
) {
1750 char *error_string
= NULL
;
1751 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1753 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1754 "on %s are set correctly.\n",
1755 get_winbind_priv_pipe_dir()));
1756 /* send a better message than ACCESS_DENIED */
1757 error_string
= talloc_asprintf(state
->mem_ctx
,
1758 "winbind client not authorized "
1759 "to use winbindd_pam_auth_crap."
1760 " Ensure permissions on %s "
1761 "are set correctly.",
1762 get_winbind_priv_pipe_dir());
1763 fstrcpy(state
->response
.data
.auth
.error_string
, error_string
);
1764 result
= NT_STATUS_ACCESS_DENIED
;
1768 /* Ensure null termination */
1769 state
->request
.data
.auth_crap
.user
1770 [sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1771 state
->request
.data
.auth_crap
.domain
1772 [sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1774 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1775 (unsigned long)state
->pid
,
1776 state
->request
.data
.auth_crap
.domain
,
1777 state
->request
.data
.auth_crap
.user
));
1779 if (*state
->request
.data
.auth_crap
.domain
!= '\0') {
1780 domain_name
= state
->request
.data
.auth_crap
.domain
;
1781 } else if (lp_winbind_use_default_domain()) {
1782 domain_name
= lp_workgroup();
1785 if (domain_name
!= NULL
)
1786 domain
= find_auth_domain(state
, domain_name
);
1788 if (domain
!= NULL
) {
1789 sendto_domain(state
, domain
);
1793 result
= NT_STATUS_NO_SUCH_USER
;
1796 set_auth_errors(&state
->response
, result
);
1797 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1798 state
->request
.data
.auth_crap
.domain
,
1799 state
->request
.data
.auth_crap
.user
,
1800 state
->response
.data
.auth
.nt_status_string
,
1801 state
->response
.data
.auth
.pam_error
));
1802 request_error(state
);
1807 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1808 struct winbindd_cli_state
*state
)
1811 struct netr_SamInfo3
*info3
= NULL
;
1812 struct rpc_pipe_client
*netlogon_pipe
;
1813 const char *name_user
= NULL
;
1814 const char *name_domain
= NULL
;
1815 const char *workstation
;
1816 struct winbindd_domain
*contact_domain
;
1820 DATA_BLOB lm_resp
, nt_resp
;
1822 /* This is child-only, so no check for privileged access is needed
1825 /* Ensure null termination */
1826 state
->request
.data
.auth_crap
.user
[sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1827 state
->request
.data
.auth_crap
.domain
[sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1829 if (!check_request_flags(state
->request
.flags
)) {
1830 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1834 name_user
= state
->request
.data
.auth_crap
.user
;
1836 if (*state
->request
.data
.auth_crap
.domain
) {
1837 name_domain
= state
->request
.data
.auth_crap
.domain
;
1838 } else if (lp_winbind_use_default_domain()) {
1839 name_domain
= lp_workgroup();
1841 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1843 result
= NT_STATUS_NO_SUCH_USER
;
1847 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1848 name_domain
, name_user
));
1850 if (*state
->request
.data
.auth_crap
.workstation
) {
1851 workstation
= state
->request
.data
.auth_crap
.workstation
;
1853 workstation
= global_myname();
1856 if (state
->request
.data
.auth_crap
.lm_resp_len
> sizeof(state
->request
.data
.auth_crap
.lm_resp
)
1857 || state
->request
.data
.auth_crap
.nt_resp_len
> sizeof(state
->request
.data
.auth_crap
.nt_resp
)) {
1858 if (!(state
->request
.flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1859 state
->request
.extra_len
!= state
->request
.data
.auth_crap
.nt_resp_len
) {
1860 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1861 state
->request
.data
.auth_crap
.lm_resp_len
,
1862 state
->request
.data
.auth_crap
.nt_resp_len
));
1863 result
= NT_STATUS_INVALID_PARAMETER
;
1868 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.lm_resp
,
1869 state
->request
.data
.auth_crap
.lm_resp_len
);
1871 if (state
->request
.flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1872 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1873 state
->request
.extra_data
.data
,
1874 state
->request
.data
.auth_crap
.nt_resp_len
);
1876 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1877 state
->request
.data
.auth_crap
.nt_resp
,
1878 state
->request
.data
.auth_crap
.nt_resp_len
);
1881 /* what domain should we contact? */
1884 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1885 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1886 state
->request
.data
.auth_crap
.user
, name_domain
, name_user
, name_domain
));
1887 result
= NT_STATUS_NO_SUCH_USER
;
1891 if (is_myname(name_domain
)) {
1892 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1893 result
= NT_STATUS_NO_SUCH_USER
;
1896 contact_domain
= find_our_domain();
1900 netlogon_fn_t logon_fn
;
1904 netlogon_pipe
= NULL
;
1905 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1907 if (!NT_STATUS_IS_OK(result
)) {
1908 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1909 nt_errstr(result
)));
1913 logon_fn
= contact_domain
->can_do_samlogon_ex
1914 ? rpccli_netlogon_sam_network_logon_ex
1915 : rpccli_netlogon_sam_network_logon
;
1917 result
= logon_fn(netlogon_pipe
,
1919 state
->request
.data
.auth_crap
.logon_parameters
,
1920 contact_domain
->dcname
,
1923 /* Bug #3248 - found by Stefan Burkei. */
1924 workstation
, /* We carefully set this above so use it... */
1925 state
->request
.data
.auth_crap
.chal
,
1930 if ((NT_STATUS_V(result
) == DCERPC_FAULT_OP_RNG_ERROR
)
1931 && contact_domain
->can_do_samlogon_ex
) {
1932 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1933 "retrying with NetSamLogon\n"));
1934 contact_domain
->can_do_samlogon_ex
= false;
1941 /* We have to try a second time as cm_connect_netlogon
1942 might not yet have noticed that the DC has killed
1945 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1950 /* if we get access denied, a possible cause was that we had and open
1951 connection to the DC, but someone changed our machine account password
1952 out from underneath us using 'net rpc changetrustpw' */
1954 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1955 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1956 "ACCESS_DENIED. Maybe the trust account "
1957 "password was changed and we didn't know it. "
1958 "Killing connections to domain %s\n",
1960 invalidate_cm_connection(&contact_domain
->conn
);
1964 } while ( (attempts
< 2) && retry
);
1966 if (NT_STATUS_IS_OK(result
)) {
1968 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1969 netsamlogon_cache_store(name_user
, info3
);
1971 /* Check if the user is in the right group */
1973 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1974 state
->request
.data
.auth_crap
.require_membership_of_sid
))) {
1975 DEBUG(3, ("User %s is not in the required group (%s), so "
1976 "crap authentication is rejected\n",
1977 state
->request
.data
.auth_crap
.user
,
1978 state
->request
.data
.auth_crap
.require_membership_of_sid
));
1982 result
= append_data(state
, info3
, name_domain
, name_user
);
1983 if (!NT_STATUS_IS_OK(result
)) {
1990 /* give us a more useful (more correct?) error code */
1991 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1992 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1993 result
= NT_STATUS_NO_LOGON_SERVERS
;
1996 if (state
->request
.flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1997 result
= nt_status_squash(result
);
2000 set_auth_errors(&state
->response
, result
);
2002 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2003 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2006 state
->response
.data
.auth
.nt_status_string
,
2007 state
->response
.data
.auth
.pam_error
));
2009 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2012 /* Change a user password */
2014 void winbindd_pam_chauthtok(struct winbindd_cli_state
*state
)
2016 fstring domain
, user
;
2018 struct winbindd_domain
*contact_domain
;
2019 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
2021 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state
->pid
,
2022 state
->request
.data
.chauthtok
.user
));
2026 nt_status
= normalize_name_unmap(state
->mem_ctx
,
2027 state
->request
.data
.chauthtok
.user
,
2030 /* Update the chauthtok name if we did any mapping */
2032 if (NT_STATUS_IS_OK(nt_status
) ||
2033 NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
))
2035 fstrcpy(state
->request
.data
.chauthtok
.user
, mapped_user
);
2038 /* Must pass in state->...chauthtok.user because
2039 canonicalize_username() assumes an fstring(). Since
2040 we have already copied it (if necessary), this is ok. */
2042 if (!canonicalize_username(state
->request
.data
.chauthtok
.user
, domain
, user
)) {
2043 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
2044 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2046 state
->request
.data
.auth
.user
,
2047 state
->response
.data
.auth
.nt_status_string
,
2048 state
->response
.data
.auth
.pam_error
));
2049 request_error(state
);
2053 contact_domain
= find_domain_from_name(domain
);
2054 if (!contact_domain
) {
2055 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
2056 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2057 state
->request
.data
.chauthtok
.user
, domain
, user
, domain
));
2058 request_error(state
);
2062 sendto_domain(state
, contact_domain
);
2065 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
2066 struct winbindd_cli_state
*state
)
2069 char *newpass
= NULL
;
2070 struct policy_handle dom_pol
;
2071 struct rpc_pipe_client
*cli
;
2072 bool got_info
= false;
2073 struct samr_DomInfo1
*info
= NULL
;
2074 struct samr_ChangeReject
*reject
= NULL
;
2075 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
2076 fstring domain
, user
;
2078 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
2079 state
->request
.data
.auth
.user
));
2081 if (!parse_domain_user(state
->request
.data
.chauthtok
.user
, domain
, user
)) {
2085 /* Change password */
2087 oldpass
= state
->request
.data
.chauthtok
.oldpass
;
2088 newpass
= state
->request
.data
.chauthtok
.newpass
;
2090 /* Initialize reject reason */
2091 state
->response
.data
.auth
.reject_reason
= Undefined
;
2093 /* Get sam handle */
2095 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
2097 if (!NT_STATUS_IS_OK(result
)) {
2098 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2102 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
2109 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2111 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
2113 fill_in_password_policy(&state
->response
, info
);
2115 state
->response
.data
.auth
.reject_reason
=
2121 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2122 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2123 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2124 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2126 /* only fallback when the chgpasswd_user3 call is not supported */
2127 if ((NT_STATUS_EQUAL(result
, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR
))) ||
2128 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
)) ||
2129 (NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
)) ||
2130 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
))) {
2132 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2133 nt_errstr(result
)));
2135 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2137 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2138 Map to the same status code as Windows 2003. */
2140 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2141 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2147 if (NT_STATUS_IS_OK(result
) && (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
2149 /* Update the single sign-on memory creds. */
2150 result
= winbindd_replace_memory_creds(state
->request
.data
.chauthtok
.user
,
2153 /* When we login from gdm or xdm and password expires,
2154 * we change password, but there are no memory crendentials
2155 * So, winbindd_replace_memory_creds() returns
2156 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2159 if (NT_STATUS_EQUAL(result
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
2160 result
= NT_STATUS_OK
;
2163 if (!NT_STATUS_IS_OK(result
)) {
2164 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result
)));
2165 goto process_result
;
2168 if (lp_winbind_offline_logon()) {
2169 result
= winbindd_update_creds_by_name(contact_domain
,
2170 state
->mem_ctx
, user
,
2172 /* Again, this happens when we login from gdm or xdm
2173 * and the password expires, *BUT* cached crendentials
2174 * doesn't exist. winbindd_update_creds_by_name()
2175 * returns NT_STATUS_NO_SUCH_USER.
2176 * This is not a failure.
2179 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2180 result
= NT_STATUS_OK
;
2183 if (!NT_STATUS_IS_OK(result
)) {
2184 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
2185 goto process_result
;
2190 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2192 NTSTATUS policy_ret
;
2194 policy_ret
= fillup_password_policy(contact_domain
, state
);
2196 /* failure of this is non critical, it will just provide no
2197 * additional information to the client why the change has
2198 * failed - Guenther */
2200 if (!NT_STATUS_IS_OK(policy_ret
)) {
2201 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2202 goto process_result
;
2208 set_auth_errors(&state
->response
, result
);
2210 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2211 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2214 state
->response
.data
.auth
.nt_status_string
,
2215 state
->response
.data
.auth
.pam_error
));
2217 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2220 void winbindd_pam_logoff(struct winbindd_cli_state
*state
)
2222 struct winbindd_domain
*domain
;
2223 fstring name_domain
, user
;
2224 uid_t caller_uid
= (uid_t
)-1;
2225 uid_t request_uid
= state
->request
.data
.logoff
.uid
;
2227 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state
->pid
,
2228 state
->request
.data
.logoff
.user
));
2230 /* Ensure null termination */
2231 state
->request
.data
.logoff
.user
2232 [sizeof(state
->request
.data
.logoff
.user
)-1]='\0';
2234 state
->request
.data
.logoff
.krb5ccname
2235 [sizeof(state
->request
.data
.logoff
.krb5ccname
)-1]='\0';
2237 if (request_uid
== (gid_t
)-1) {
2241 if (!canonicalize_username(state
->request
.data
.logoff
.user
, name_domain
, user
)) {
2245 if ((domain
= find_auth_domain(state
, name_domain
)) == NULL
) {
2249 if ((sys_getpeereid(state
->sock
, &caller_uid
)) != 0) {
2250 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2255 switch (caller_uid
) {
2259 /* root must be able to logoff any user - gd */
2260 state
->request
.data
.logoff
.uid
= request_uid
;
2263 if (caller_uid
!= request_uid
) {
2264 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2267 state
->request
.data
.logoff
.uid
= caller_uid
;
2271 sendto_domain(state
, domain
);
2275 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
2276 DEBUG(5, ("Pam Logoff for %s returned %s "
2278 state
->request
.data
.logoff
.user
,
2279 state
->response
.data
.auth
.nt_status_string
,
2280 state
->response
.data
.auth
.pam_error
));
2281 request_error(state
);
2285 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2286 struct winbindd_cli_state
*state
)
2288 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2290 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2291 state
->request
.data
.logoff
.user
));
2293 if (!(state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
2294 result
= NT_STATUS_OK
;
2295 goto process_result
;
2298 if (state
->request
.data
.logoff
.krb5ccname
[0] == '\0') {
2299 result
= NT_STATUS_OK
;
2300 goto process_result
;
2305 if (state
->request
.data
.logoff
.uid
< 0) {
2306 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2307 goto process_result
;
2310 /* what we need here is to find the corresponding krb5 ccache name *we*
2311 * created for a given username and destroy it */
2313 if (!ccache_entry_exists(state
->request
.data
.logoff
.user
)) {
2314 result
= NT_STATUS_OK
;
2315 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2316 goto process_result
;
2319 if (!ccache_entry_identical(state
->request
.data
.logoff
.user
,
2320 state
->request
.data
.logoff
.uid
,
2321 state
->request
.data
.logoff
.krb5ccname
)) {
2322 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2323 goto process_result
;
2326 result
= remove_ccache(state
->request
.data
.logoff
.user
);
2327 if (!NT_STATUS_IS_OK(result
)) {
2328 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2329 nt_errstr(result
)));
2330 goto process_result
;
2334 result
= NT_STATUS_NOT_SUPPORTED
;
2339 winbindd_delete_memory_creds(state
->request
.data
.logoff
.user
);
2341 set_auth_errors(&state
->response
, result
);
2343 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2346 /* Change user password with auth crap*/
2348 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state
*state
)
2350 struct winbindd_domain
*domain
= NULL
;
2351 const char *domain_name
= NULL
;
2353 /* Ensure null termination */
2354 state
->request
.data
.chng_pswd_auth_crap
.user
[
2355 sizeof(state
->request
.data
.chng_pswd_auth_crap
.user
)-1]=0;
2356 state
->request
.data
.chng_pswd_auth_crap
.domain
[
2357 sizeof(state
->request
.data
.chng_pswd_auth_crap
.domain
)-1]=0;
2359 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2360 (unsigned long)state
->pid
,
2361 state
->request
.data
.chng_pswd_auth_crap
.domain
,
2362 state
->request
.data
.chng_pswd_auth_crap
.user
));
2364 if (*state
->request
.data
.chng_pswd_auth_crap
.domain
!= '\0') {
2365 domain_name
= state
->request
.data
.chng_pswd_auth_crap
.domain
;
2366 } else if (lp_winbind_use_default_domain()) {
2367 domain_name
= lp_workgroup();
2370 if (domain_name
!= NULL
)
2371 domain
= find_domain_from_name(domain_name
);
2373 if (domain
!= NULL
) {
2374 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2375 "%s\n", (unsigned long)state
->pid
,domain
->name
));
2376 sendto_domain(state
, domain
);
2380 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
2381 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2382 state
->request
.data
.chng_pswd_auth_crap
.domain
,
2383 state
->request
.data
.chng_pswd_auth_crap
.user
,
2384 state
->response
.data
.auth
.nt_status_string
,
2385 state
->response
.data
.auth
.pam_error
));
2386 request_error(state
);
2390 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2393 DATA_BLOB new_nt_password
;
2394 DATA_BLOB old_nt_hash_enc
;
2395 DATA_BLOB new_lm_password
;
2396 DATA_BLOB old_lm_hash_enc
;
2397 fstring domain
,user
;
2398 struct policy_handle dom_pol
;
2399 struct winbindd_domain
*contact_domain
= domainSt
;
2400 struct rpc_pipe_client
*cli
;
2402 /* Ensure null termination */
2403 state
->request
.data
.chng_pswd_auth_crap
.user
[
2404 sizeof(state
->request
.data
.chng_pswd_auth_crap
.user
)-1]=0;
2405 state
->request
.data
.chng_pswd_auth_crap
.domain
[
2406 sizeof(state
->request
.data
.chng_pswd_auth_crap
.domain
)-1]=0;
2410 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2411 (unsigned long)state
->pid
,
2412 state
->request
.data
.chng_pswd_auth_crap
.domain
,
2413 state
->request
.data
.chng_pswd_auth_crap
.user
));
2415 if (lp_winbind_offline_logon()) {
2416 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2417 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2418 result
= NT_STATUS_ACCESS_DENIED
;
2422 if (*state
->request
.data
.chng_pswd_auth_crap
.domain
) {
2423 fstrcpy(domain
,state
->request
.data
.chng_pswd_auth_crap
.domain
);
2425 parse_domain_user(state
->request
.data
.chng_pswd_auth_crap
.user
,
2429 DEBUG(3,("no domain specified with username (%s) - "
2431 state
->request
.data
.chng_pswd_auth_crap
.user
));
2432 result
= NT_STATUS_NO_SUCH_USER
;
2437 if (!*domain
&& lp_winbind_use_default_domain()) {
2438 fstrcpy(domain
,(char *)lp_workgroup());
2442 fstrcpy(user
, state
->request
.data
.chng_pswd_auth_crap
.user
);
2445 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2446 (unsigned long)state
->pid
, domain
, user
));
2448 /* Change password */
2449 new_nt_password
= data_blob_talloc(
2451 state
->request
.data
.chng_pswd_auth_crap
.new_nt_pswd
,
2452 state
->request
.data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2454 old_nt_hash_enc
= data_blob_talloc(
2456 state
->request
.data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2457 state
->request
.data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2459 if(state
->request
.data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2460 new_lm_password
= data_blob_talloc(
2462 state
->request
.data
.chng_pswd_auth_crap
.new_lm_pswd
,
2463 state
->request
.data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2465 old_lm_hash_enc
= data_blob_talloc(
2467 state
->request
.data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2468 state
->request
.data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2470 new_lm_password
.length
= 0;
2471 old_lm_hash_enc
.length
= 0;
2474 /* Get sam handle */
2476 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2477 if (!NT_STATUS_IS_OK(result
)) {
2478 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2482 result
= rpccli_samr_chng_pswd_auth_crap(
2483 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2484 new_lm_password
, old_lm_hash_enc
);
2488 set_auth_errors(&state
->response
, result
);
2490 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2491 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2493 state
->response
.data
.auth
.nt_status_string
,
2494 state
->response
.data
.auth
.pam_error
));
2496 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;