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
)
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(state
->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 state
->response
->extra_data
.data
= ex
;
109 state
->response
->length
+= talloc_get_size(ex
);
114 static NTSTATUS
append_info3_as_ndr(TALLOC_CTX
*mem_ctx
,
115 struct winbindd_cli_state
*state
,
116 struct netr_SamInfo3
*info3
)
119 enum ndr_err_code ndr_err
;
121 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, NULL
, info3
,
122 (ndr_push_flags_fn_t
)ndr_push_netr_SamInfo3
);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
124 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
125 return ndr_map_error2ntstatus(ndr_err
);
128 state
->response
->extra_data
.data
= blob
.data
;
129 state
->response
->length
+= blob
.length
;
134 static NTSTATUS
append_unix_username(TALLOC_CTX
*mem_ctx
,
135 struct winbindd_cli_state
*state
,
136 const struct netr_SamInfo3
*info3
,
137 const char *name_domain
,
138 const char *name_user
)
140 /* We've been asked to return the unix username, per
141 'winbind use default domain' settings and the like */
143 const char *nt_username
, *nt_domain
;
145 nt_domain
= talloc_strdup(mem_ctx
, info3
->base
.domain
.string
);
147 /* If the server didn't give us one, just use the one
149 nt_domain
= name_domain
;
152 nt_username
= talloc_strdup(mem_ctx
, info3
->base
.account_name
.string
);
154 /* If the server didn't give us one, just use the one
156 nt_username
= name_user
;
159 fill_domain_username(state
->response
->data
.auth
.unix_username
,
160 nt_domain
, nt_username
, true);
162 DEBUG(5,("Setting unix username to [%s]\n",
163 state
->response
->data
.auth
.unix_username
));
168 static NTSTATUS
append_afs_token(TALLOC_CTX
*mem_ctx
,
169 struct winbindd_cli_state
*state
,
170 const struct netr_SamInfo3
*info3
,
171 const char *name_domain
,
172 const char *name_user
)
174 char *afsname
= NULL
;
178 afsname
= talloc_strdup(mem_ctx
, lp_afs_username_map());
179 if (afsname
== NULL
) {
180 return NT_STATUS_NO_MEMORY
;
183 afsname
= talloc_string_sub(mem_ctx
,
184 lp_afs_username_map(),
186 afsname
= talloc_string_sub(mem_ctx
, afsname
,
188 afsname
= talloc_string_sub(mem_ctx
, afsname
,
195 sid_copy(&user_sid
, info3
->base
.domain_sid
);
196 sid_append_rid(&user_sid
, info3
->base
.rid
);
197 sid_to_fstring(sidstr
, &user_sid
);
198 afsname
= talloc_string_sub(mem_ctx
, afsname
,
202 if (afsname
== NULL
) {
203 return NT_STATUS_NO_MEMORY
;
208 DEBUG(10, ("Generating token for user %s\n", afsname
));
210 cell
= strchr(afsname
, '@');
213 return NT_STATUS_NO_MEMORY
;
219 token
= afs_createtoken_str(afsname
, cell
);
223 state
->response
->extra_data
.data
= talloc_strdup(state
->mem_ctx
,
225 if (state
->response
->extra_data
.data
== NULL
) {
226 return NT_STATUS_NO_MEMORY
;
228 state
->response
->length
+=
229 strlen((const char *)state
->response
->extra_data
.data
)+1;
234 static NTSTATUS
check_info3_in_group(TALLOC_CTX
*mem_ctx
,
235 struct netr_SamInfo3
*info3
,
236 const char *group_sid
)
238 * Check whether a user belongs to a group or list of groups.
240 * @param mem_ctx talloc memory context.
241 * @param info3 user information, including group membership info.
242 * @param group_sid One or more groups , separated by commas.
244 * @return NT_STATUS_OK on success,
245 * NT_STATUS_LOGON_FAILURE if the user does not belong,
246 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
249 DOM_SID
*require_membership_of_sid
;
250 size_t num_require_membership_of_sid
;
255 struct nt_user_token
*token
;
256 TALLOC_CTX
*frame
= NULL
;
259 /* Parse the 'required group' SID */
261 if (!group_sid
|| !group_sid
[0]) {
262 /* NO sid supplied, all users may access */
266 if (!(token
= TALLOC_ZERO_P(mem_ctx
, struct nt_user_token
))) {
267 DEBUG(0, ("talloc failed\n"));
268 return NT_STATUS_NO_MEMORY
;
271 num_require_membership_of_sid
= 0;
272 require_membership_of_sid
= NULL
;
276 frame
= talloc_stackframe();
277 while (next_token_talloc(frame
, &p
, &req_sid
, ",")) {
278 if (!string_to_sid(&sid
, req_sid
)) {
279 DEBUG(0, ("check_info3_in_group: could not parse %s "
280 "as a SID!", req_sid
));
282 return NT_STATUS_INVALID_PARAMETER
;
285 status
= add_sid_to_array(mem_ctx
, &sid
,
286 &require_membership_of_sid
,
287 &num_require_membership_of_sid
);
288 if (!NT_STATUS_IS_OK(status
)) {
289 DEBUG(0, ("add_sid_to_array failed\n"));
297 status
= sid_array_from_info3(mem_ctx
, info3
,
301 if (!NT_STATUS_IS_OK(status
)) {
305 if (!NT_STATUS_IS_OK(status
= add_aliases(get_global_sam_sid(),
307 || !NT_STATUS_IS_OK(status
= add_aliases(&global_sid_Builtin
,
309 DEBUG(3, ("could not add aliases: %s\n",
314 debug_nt_user_token(DBGC_CLASS
, 10, token
);
316 for (i
=0; i
<num_require_membership_of_sid
; i
++) {
317 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
318 &require_membership_of_sid
[i
])));
319 if (nt_token_check_sid(&require_membership_of_sid
[i
],
321 DEBUG(10, ("Access ok\n"));
326 /* Do not distinguish this error from a wrong username/pw */
328 return NT_STATUS_LOGON_FAILURE
;
331 struct winbindd_domain
*find_auth_domain(struct winbindd_cli_state
*state
,
332 const char *domain_name
)
334 struct winbindd_domain
*domain
;
337 domain
= find_domain_from_name_noinit(domain_name
);
338 if (domain
== NULL
) {
339 DEBUG(3, ("Authentication for domain [%s] refused "
340 "as it is not a trusted domain\n",
346 if (is_myname(domain_name
)) {
347 DEBUG(3, ("Authentication for domain %s (local domain "
348 "to this server) not supported at this "
349 "stage\n", domain_name
));
353 /* we can auth against trusted domains */
354 if (state
->request
->flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
355 domain
= find_domain_from_name_noinit(domain_name
);
356 if (domain
== NULL
) {
357 DEBUG(3, ("Authentication for domain [%s] skipped "
358 "as it is not a trusted domain\n",
365 return find_our_domain();
368 static void fill_in_password_policy(struct winbindd_response
*r
,
369 const struct samr_DomInfo1
*p
)
371 r
->data
.auth
.policy
.min_length_password
=
372 p
->min_password_length
;
373 r
->data
.auth
.policy
.password_history
=
374 p
->password_history_length
;
375 r
->data
.auth
.policy
.password_properties
=
376 p
->password_properties
;
377 r
->data
.auth
.policy
.expire
=
378 nt_time_to_unix_abs((NTTIME
*)&(p
->max_password_age
));
379 r
->data
.auth
.policy
.min_passwordage
=
380 nt_time_to_unix_abs((NTTIME
*)&(p
->min_password_age
));
383 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
384 struct winbindd_cli_state
*state
)
386 struct winbindd_methods
*methods
;
387 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
388 struct samr_DomInfo1 password_policy
;
390 if ( !winbindd_can_contact_domain( domain
) ) {
391 DEBUG(5,("fillup_password_policy: No inbound trust to "
392 "contact domain %s\n", domain
->name
));
393 return NT_STATUS_NOT_SUPPORTED
;
396 methods
= domain
->methods
;
398 status
= methods
->password_policy(domain
, state
->mem_ctx
, &password_policy
);
399 if (NT_STATUS_IS_ERR(status
)) {
403 fill_in_password_policy(state
->response
, &password_policy
);
408 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
410 uint16
*lockout_threshold
)
412 struct winbindd_methods
*methods
;
413 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
414 struct samr_DomInfo12 lockout_policy
;
416 *lockout_threshold
= 0;
418 methods
= domain
->methods
;
420 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
421 if (NT_STATUS_IS_ERR(status
)) {
425 *lockout_threshold
= lockout_policy
.lockout_threshold
;
430 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
432 uint32
*password_properties
)
434 struct winbindd_methods
*methods
;
435 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
436 struct samr_DomInfo1 password_policy
;
438 *password_properties
= 0;
440 methods
= domain
->methods
;
442 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
443 if (NT_STATUS_IS_ERR(status
)) {
447 *password_properties
= password_policy
.password_properties
;
454 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
457 bool *internal_ccache
)
459 /* accept FILE and WRFILE as krb5_cc_type from the client and then
460 * build the full ccname string based on the user's uid here -
463 const char *gen_cc
= NULL
;
465 *internal_ccache
= true;
471 if (!type
|| type
[0] == '\0') {
475 if (strequal(type
, "FILE")) {
476 gen_cc
= talloc_asprintf(mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
477 } else if (strequal(type
, "WRFILE")) {
478 gen_cc
= talloc_asprintf(mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
480 DEBUG(10,("we don't allow to set a %s type ccache\n", type
));
484 *internal_ccache
= false;
488 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
491 if (gen_cc
== NULL
) {
492 DEBUG(0,("out of memory\n"));
496 DEBUG(10,("using ccache: %s %s\n", gen_cc
, *internal_ccache
? "(internal)":""));
501 static void setup_return_cc_name(struct winbindd_cli_state
*state
, const char *cc
)
503 const char *type
= state
->request
->data
.auth
.krb5_cc_type
;
505 state
->response
->data
.auth
.krb5ccname
[0] = '\0';
507 if (type
[0] == '\0') {
511 if (!strequal(type
, "FILE") &&
512 !strequal(type
, "WRFILE")) {
513 DEBUG(10,("won't return krbccname for a %s type ccache\n",
518 fstrcpy(state
->response
->data
.auth
.krb5ccname
, cc
);
523 static uid_t
get_uid_from_state(struct winbindd_cli_state
*state
)
527 uid
= state
->request
->data
.auth
.uid
;
530 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid
));
536 /**********************************************************************
537 Authenticate a user with a clear text password using Kerberos and fill up
539 **********************************************************************/
541 static NTSTATUS
winbindd_raw_kerberos_login(struct winbindd_domain
*domain
,
542 struct winbindd_cli_state
*state
,
543 struct netr_SamInfo3
**info3
)
546 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
547 krb5_error_code krb5_ret
;
548 const char *cc
= NULL
;
549 const char *principal_s
= NULL
;
550 const char *service
= NULL
;
552 fstring name_domain
, name_user
;
553 time_t ticket_lifetime
= 0;
554 time_t renewal_until
= 0;
557 time_t time_offset
= 0;
558 bool internal_ccache
= true;
565 * prepare a krb5_cc_cache string for the user */
567 uid
= get_uid_from_state(state
);
569 DEBUG(0,("no valid uid\n"));
572 cc
= generate_krb5_ccache(state
->mem_ctx
,
573 state
->request
->data
.auth
.krb5_cc_type
,
574 state
->request
->data
.auth
.uid
,
577 return NT_STATUS_NO_MEMORY
;
582 * get kerberos properties */
584 if (domain
->private_data
) {
585 ads
= (ADS_STRUCT
*)domain
->private_data
;
586 time_offset
= ads
->auth
.time_offset
;
591 * do kerberos auth and setup ccache as the user */
593 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
595 realm
= domain
->alt_name
;
598 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
599 if (principal_s
== NULL
) {
600 return NT_STATUS_NO_MEMORY
;
603 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
604 if (service
== NULL
) {
605 return NT_STATUS_NO_MEMORY
;
608 /* if this is a user ccache, we need to act as the user to let the krb5
609 * library handle the chown, etc. */
611 /************************ ENTERING NON-ROOT **********************/
613 if (!internal_ccache
) {
614 set_effective_uid(uid
);
615 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
618 result
= kerberos_return_info3_from_pac(state
->mem_ctx
,
620 state
->request
->data
.auth
.pass
,
627 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
629 if (!internal_ccache
) {
630 gain_root_privilege();
633 /************************ RETURNED TO ROOT **********************/
635 if (!NT_STATUS_IS_OK(result
)) {
639 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
642 /* if we had a user's ccache then return that string for the pam
645 if (!internal_ccache
) {
647 setup_return_cc_name(state
, cc
);
649 result
= add_ccache_to_list(principal_s
,
652 state
->request
->data
.auth
.user
,
660 if (!NT_STATUS_IS_OK(result
)) {
661 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
666 /* need to delete the memory cred cache, it is not used anymore */
668 krb5_ret
= ads_kdestroy(cc
);
670 DEBUG(3,("winbindd_raw_kerberos_login: "
671 "could not destroy krb5 credential cache: "
672 "%s\n", error_message(krb5_ret
)));
681 /* we could have created a new credential cache with a valid tgt in it
682 * but we werent able to get or verify the service ticket for this
683 * local host and therefor didn't get the PAC, we need to remove that
684 * cache entirely now */
686 krb5_ret
= ads_kdestroy(cc
);
688 DEBUG(3,("winbindd_raw_kerberos_login: "
689 "could not destroy krb5 credential cache: "
690 "%s\n", error_message(krb5_ret
)));
693 if (!NT_STATUS_IS_OK(remove_ccache(state
->request
->data
.auth
.user
))) {
694 DEBUG(3,("winbindd_raw_kerberos_login: "
695 "could not remove ccache for user %s\n",
696 state
->request
->data
.auth
.user
));
701 return NT_STATUS_NOT_SUPPORTED
;
702 #endif /* HAVE_KRB5 */
705 /****************************************************************
706 ****************************************************************/
708 static bool check_request_flags(uint32_t flags
)
710 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
711 WBFLAG_PAM_INFO3_TEXT
|
712 WBFLAG_PAM_INFO3_NDR
;
714 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
715 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
716 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
717 !(flags
& flags_edata
) ) {
721 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags
));
726 /****************************************************************
727 ****************************************************************/
729 static NTSTATUS
append_data(struct winbindd_cli_state
*state
,
730 struct netr_SamInfo3
*info3
,
731 const char *name_domain
,
732 const char *name_user
)
735 uint32_t flags
= state
->request
->flags
;
737 if (flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
738 memcpy(state
->response
->data
.auth
.user_session_key
,
740 sizeof(state
->response
->data
.auth
.user_session_key
)
744 if (flags
& WBFLAG_PAM_LMKEY
) {
745 memcpy(state
->response
->data
.auth
.first_8_lm_hash
,
746 info3
->base
.LMSessKey
.key
,
747 sizeof(state
->response
->data
.auth
.first_8_lm_hash
)
751 if (flags
& WBFLAG_PAM_INFO3_TEXT
) {
752 result
= append_info3_as_txt(state
->mem_ctx
, state
, info3
);
753 if (!NT_STATUS_IS_OK(result
)) {
754 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
760 /* currently, anything from here on potentially overwrites extra_data. */
762 if (flags
& WBFLAG_PAM_INFO3_NDR
) {
763 result
= append_info3_as_ndr(state
->mem_ctx
, state
, info3
);
764 if (!NT_STATUS_IS_OK(result
)) {
765 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
771 if (flags
& WBFLAG_PAM_UNIX_NAME
) {
772 result
= append_unix_username(state
->mem_ctx
, state
, info3
,
773 name_domain
, name_user
);
774 if (!NT_STATUS_IS_OK(result
)) {
775 DEBUG(10,("Failed to append Unix Username: %s\n",
781 if (flags
& WBFLAG_PAM_AFS_TOKEN
) {
782 result
= append_afs_token(state
->mem_ctx
, state
, info3
,
783 name_domain
, name_user
);
784 if (!NT_STATUS_IS_OK(result
)) {
785 DEBUG(10,("Failed to append AFS token: %s\n",
794 void winbindd_pam_auth(struct winbindd_cli_state
*state
)
796 struct winbindd_domain
*domain
;
797 fstring name_domain
, name_user
;
798 char *mapped_user
= NULL
;
800 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
802 /* Ensure null termination */
803 state
->request
->data
.auth
.user
804 [sizeof(state
->request
->data
.auth
.user
)-1]='\0';
806 /* Ensure null termination */
807 state
->request
->data
.auth
.pass
808 [sizeof(state
->request
->data
.auth
.pass
)-1]='\0';
810 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state
->pid
,
811 state
->request
->data
.auth
.user
));
813 if (!check_request_flags(state
->request
->flags
)) {
814 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
818 /* Parse domain and username */
820 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
821 state
->request
->data
.auth
.user
,
824 /* If the name normalization didnt' actually do anything,
825 just use the original name */
827 if (!NT_STATUS_IS_OK(name_map_status
) &&
828 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
830 mapped_user
= state
->request
->data
.auth
.user
;
833 if (!canonicalize_username(mapped_user
, name_domain
, name_user
)) {
834 result
= NT_STATUS_NO_SUCH_USER
;
838 domain
= find_auth_domain(state
, name_domain
);
840 if (domain
== NULL
) {
841 result
= NT_STATUS_NO_SUCH_USER
;
845 sendto_domain(state
, domain
);
848 set_auth_errors(state
->response
, result
);
849 DEBUG(5, ("Plain text authentication for %s returned %s "
851 state
->request
->data
.auth
.user
,
852 state
->response
->data
.auth
.nt_status_string
,
853 state
->response
->data
.auth
.pam_error
));
854 request_error(state
);
857 NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
858 struct winbindd_cli_state
*state
,
859 struct netr_SamInfo3
**info3
)
861 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
862 uint16 max_allowed_bad_attempts
;
863 fstring name_domain
, name_user
;
865 enum lsa_SidType type
;
866 uchar new_nt_pass
[NT_HASH_LEN
];
867 const uint8
*cached_nt_pass
;
868 const uint8
*cached_salt
;
869 struct netr_SamInfo3
*my_info3
;
870 time_t kickoff_time
, must_change_time
;
871 bool password_good
= false;
873 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
880 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
882 /* Parse domain and username */
884 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
887 if (!lookup_cached_name(state
->mem_ctx
,
892 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
893 return NT_STATUS_NO_SUCH_USER
;
896 if (type
!= SID_NAME_USER
) {
897 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
898 return NT_STATUS_LOGON_FAILURE
;
901 result
= winbindd_get_creds(domain
,
907 if (!NT_STATUS_IS_OK(result
)) {
908 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
914 E_md4hash(state
->request
->data
.auth
.pass
, new_nt_pass
);
916 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
917 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
919 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
923 /* In this case we didn't store the nt_hash itself,
924 but the MD5 combination of salt + nt_hash. */
925 uchar salted_hash
[NT_HASH_LEN
];
926 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
928 password_good
= (memcmp(cached_nt_pass
, salted_hash
, NT_HASH_LEN
) == 0) ?
931 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
932 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
, NT_HASH_LEN
) == 0) ?
938 /* User *DOES* know the password, update logon_time and reset
941 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
943 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
944 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
947 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
948 return NT_STATUS_ACCOUNT_DISABLED
;
951 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
952 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
955 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
956 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
959 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
960 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
963 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
964 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
965 my_info3
->base
.acct_flags
));
966 return NT_STATUS_LOGON_FAILURE
;
969 kickoff_time
= nt_time_to_unix(my_info3
->base
.acct_expiry
);
970 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
971 return NT_STATUS_ACCOUNT_EXPIRED
;
974 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
975 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
976 /* we allow grace logons when the password has expired */
977 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
978 /* return NT_STATUS_PASSWORD_EXPIRED; */
983 if ((state
->request
->flags
& WBFLAG_PAM_KRB5
) &&
984 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
985 (tdc_domain
->trust_type
& NETR_TRUST_TYPE_UPLEVEL
)) {
988 const char *cc
= NULL
;
990 const char *principal_s
= NULL
;
991 const char *service
= NULL
;
992 bool internal_ccache
= false;
994 uid
= get_uid_from_state(state
);
996 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
997 return NT_STATUS_INVALID_PARAMETER
;
1000 cc
= generate_krb5_ccache(state
->mem_ctx
,
1001 state
->request
->data
.auth
.krb5_cc_type
,
1002 state
->request
->data
.auth
.uid
,
1005 return NT_STATUS_NO_MEMORY
;
1008 realm
= domain
->alt_name
;
1011 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
1012 if (principal_s
== NULL
) {
1013 return NT_STATUS_NO_MEMORY
;
1016 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
1017 if (service
== NULL
) {
1018 return NT_STATUS_NO_MEMORY
;
1021 if (!internal_ccache
) {
1023 setup_return_cc_name(state
, cc
);
1025 result
= add_ccache_to_list(principal_s
,
1028 state
->request
->data
.auth
.user
,
1032 time(NULL
) + lp_winbind_cache_time(),
1033 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
1036 if (!NT_STATUS_IS_OK(result
)) {
1037 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1038 "to add ccache to list: %s\n",
1039 nt_errstr(result
)));
1043 #endif /* HAVE_KRB5 */
1045 /* FIXME: we possibly should handle logon hours as well (does xp when
1046 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1048 unix_to_nt_time(&my_info3
->base
.last_logon
, time(NULL
));
1049 my_info3
->base
.bad_password_count
= 0;
1051 result
= winbindd_update_creds_by_info3(domain
,
1053 state
->request
->data
.auth
.user
,
1054 state
->request
->data
.auth
.pass
,
1056 if (!NT_STATUS_IS_OK(result
)) {
1057 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1058 nt_errstr(result
)));
1062 return NT_STATUS_OK
;
1066 /* User does *NOT* know the correct password, modify info3 accordingly */
1068 /* failure of this is not critical */
1069 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1070 if (!NT_STATUS_IS_OK(result
)) {
1071 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1072 "Won't be able to honour account lockout policies\n"));
1075 /* increase counter */
1076 my_info3
->base
.bad_password_count
++;
1078 if (max_allowed_bad_attempts
== 0) {
1083 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1085 uint32 password_properties
;
1087 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1088 if (!NT_STATUS_IS_OK(result
)) {
1089 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1092 if ((my_info3
->base
.rid
!= DOMAIN_USER_RID_ADMIN
) ||
1093 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1094 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1099 result
= winbindd_update_creds_by_info3(domain
,
1101 state
->request
->data
.auth
.user
,
1105 if (!NT_STATUS_IS_OK(result
)) {
1106 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1107 nt_errstr(result
)));
1110 return NT_STATUS_LOGON_FAILURE
;
1113 NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1114 struct winbindd_cli_state
*state
,
1115 struct netr_SamInfo3
**info3
)
1117 struct winbindd_domain
*contact_domain
;
1118 fstring name_domain
, name_user
;
1121 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1123 /* Parse domain and username */
1125 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1127 /* what domain should we contact? */
1130 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1131 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1132 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1133 result
= NT_STATUS_NO_SUCH_USER
;
1138 if (is_myname(name_domain
)) {
1139 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1140 result
= NT_STATUS_NO_SUCH_USER
;
1144 contact_domain
= find_domain_from_name(name_domain
);
1145 if (contact_domain
== NULL
) {
1146 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1147 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1149 contact_domain
= find_our_domain();
1153 if (contact_domain
->initialized
&&
1154 contact_domain
->active_directory
) {
1158 if (!contact_domain
->initialized
) {
1159 init_dc_connection(contact_domain
);
1162 if (!contact_domain
->active_directory
) {
1163 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1164 return NT_STATUS_INVALID_LOGON_TYPE
;
1167 result
= winbindd_raw_kerberos_login(contact_domain
, state
, info3
);
1172 typedef NTSTATUS (*netlogon_fn_t
)(struct rpc_pipe_client
*cli
,
1173 TALLOC_CTX
*mem_ctx
,
1174 uint32 logon_parameters
,
1176 const char *username
,
1178 const char *workstation
,
1179 const uint8 chal
[8],
1180 DATA_BLOB lm_response
,
1181 DATA_BLOB nt_response
,
1182 struct netr_SamInfo3
**info3
);
1184 NTSTATUS
winbindd_dual_pam_auth_samlogon(struct winbindd_domain
*domain
,
1185 struct winbindd_cli_state
*state
,
1186 struct netr_SamInfo3
**info3
)
1189 struct rpc_pipe_client
*netlogon_pipe
;
1194 unsigned char local_lm_response
[24];
1195 unsigned char local_nt_response
[24];
1196 struct winbindd_domain
*contact_domain
;
1197 fstring name_domain
, name_user
;
1200 struct netr_SamInfo3
*my_info3
= NULL
;
1204 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1206 /* Parse domain and username */
1208 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1210 /* do password magic */
1213 generate_random_buffer(chal
, 8);
1214 if (lp_client_ntlmv2_auth()) {
1215 DATA_BLOB server_chal
;
1216 DATA_BLOB names_blob
;
1217 DATA_BLOB nt_response
;
1218 DATA_BLOB lm_response
;
1219 server_chal
= data_blob_talloc(state
->mem_ctx
, chal
, 8);
1221 /* note that the 'workgroup' here is a best guess - we don't know
1222 the server's domain at this point. The 'server name' is also
1225 names_blob
= NTLMv2_generate_names_blob(state
->mem_ctx
, global_myname(), lp_workgroup());
1227 if (!SMBNTLMv2encrypt(NULL
, name_user
, name_domain
,
1228 state
->request
->data
.auth
.pass
,
1231 &lm_response
, &nt_response
, NULL
, NULL
)) {
1232 data_blob_free(&names_blob
);
1233 data_blob_free(&server_chal
);
1234 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1235 result
= NT_STATUS_NO_MEMORY
;
1238 data_blob_free(&names_blob
);
1239 data_blob_free(&server_chal
);
1240 lm_resp
= data_blob_talloc(state
->mem_ctx
, lm_response
.data
,
1241 lm_response
.length
);
1242 nt_resp
= data_blob_talloc(state
->mem_ctx
, nt_response
.data
,
1243 nt_response
.length
);
1244 data_blob_free(&lm_response
);
1245 data_blob_free(&nt_response
);
1248 if (lp_client_lanman_auth()
1249 && SMBencrypt(state
->request
->data
.auth
.pass
,
1251 local_lm_response
)) {
1252 lm_resp
= data_blob_talloc(state
->mem_ctx
,
1254 sizeof(local_lm_response
));
1256 lm_resp
= data_blob_null
;
1258 SMBNTencrypt(state
->request
->data
.auth
.pass
,
1262 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1264 sizeof(local_nt_response
));
1267 /* what domain should we contact? */
1270 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1271 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1272 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1273 result
= NT_STATUS_NO_SUCH_USER
;
1278 if (is_myname(name_domain
)) {
1279 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1280 result
= NT_STATUS_NO_SUCH_USER
;
1284 contact_domain
= find_our_domain();
1287 /* check authentication loop */
1290 netlogon_fn_t logon_fn
;
1292 ZERO_STRUCTP(my_info3
);
1295 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1297 if (!NT_STATUS_IS_OK(result
)) {
1298 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1302 /* It is really important to try SamLogonEx here,
1303 * because in a clustered environment, we want to use
1304 * one machine account from multiple physical
1307 * With a normal SamLogon call, we must keep the
1308 * credentials chain updated and intact between all
1309 * users of the machine account (which would imply
1310 * cross-node communication for every NTLM logon).
1312 * (The credentials chain is not per NETLOGON pipe
1313 * connection, but globally on the server/client pair
1316 * When using SamLogonEx, the credentials are not
1317 * supplied, but the session key is implied by the
1318 * wrapping SamLogon context.
1320 * -- abartlet 21 April 2008
1323 logon_fn
= contact_domain
->can_do_samlogon_ex
1324 ? rpccli_netlogon_sam_network_logon_ex
1325 : rpccli_netlogon_sam_network_logon
;
1327 result
= logon_fn(netlogon_pipe
,
1330 contact_domain
->dcname
, /* server name */
1331 name_user
, /* user name */
1332 name_domain
, /* target domain */
1333 global_myname(), /* workstation */
1340 if ((NT_STATUS_V(result
) == DCERPC_FAULT_OP_RNG_ERROR
)
1341 && contact_domain
->can_do_samlogon_ex
) {
1342 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1343 "retrying with NetSamLogon\n"));
1344 contact_domain
->can_do_samlogon_ex
= false;
1349 /* We have to try a second time as cm_connect_netlogon
1350 might not yet have noticed that the DC has killed
1353 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1358 /* if we get access denied, a possible cause was that we had
1359 and open connection to the DC, but someone changed our
1360 machine account password out from underneath us using 'net
1361 rpc changetrustpw' */
1363 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1364 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1365 "ACCESS_DENIED. Maybe the trust account "
1366 "password was changed and we didn't know it. "
1367 "Killing connections to domain %s\n",
1369 invalidate_cm_connection(&contact_domain
->conn
);
1373 } while ( (attempts
< 2) && retry
);
1375 /* handle the case where a NT4 DC does not fill in the acct_flags in
1376 * the samlogon reply info3. When accurate info3 is required by the
1377 * caller, we look up the account flags ourselve - gd */
1379 if ((state
->request
->flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1380 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1382 struct rpc_pipe_client
*samr_pipe
;
1383 struct policy_handle samr_domain_handle
, user_pol
;
1384 union samr_UserInfo
*info
= NULL
;
1385 NTSTATUS status_tmp
;
1388 status_tmp
= cm_connect_sam(contact_domain
, state
->mem_ctx
,
1389 &samr_pipe
, &samr_domain_handle
);
1391 if (!NT_STATUS_IS_OK(status_tmp
)) {
1392 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1393 nt_errstr(status_tmp
)));
1397 status_tmp
= rpccli_samr_OpenUser(samr_pipe
, state
->mem_ctx
,
1398 &samr_domain_handle
,
1399 MAXIMUM_ALLOWED_ACCESS
,
1403 if (!NT_STATUS_IS_OK(status_tmp
)) {
1404 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1405 nt_errstr(status_tmp
)));
1409 status_tmp
= rpccli_samr_QueryUserInfo(samr_pipe
, state
->mem_ctx
,
1414 if (!NT_STATUS_IS_OK(status_tmp
)) {
1415 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1416 nt_errstr(status_tmp
)));
1417 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1421 acct_flags
= info
->info16
.acct_flags
;
1423 if (acct_flags
== 0) {
1424 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1428 my_info3
->base
.acct_flags
= acct_flags
;
1430 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1432 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1440 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1441 struct winbindd_cli_state
*state
)
1443 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1444 NTSTATUS krb5_result
= NT_STATUS_OK
;
1445 fstring name_domain
, name_user
;
1447 fstring domain_user
;
1448 struct netr_SamInfo3
*info3
= NULL
;
1449 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1451 /* Ensure null termination */
1452 state
->request
->data
.auth
.user
[sizeof(state
->request
->data
.auth
.user
)-1]='\0';
1454 /* Ensure null termination */
1455 state
->request
->data
.auth
.pass
[sizeof(state
->request
->data
.auth
.pass
)-1]='\0';
1457 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1458 state
->request
->data
.auth
.user
));
1460 if (!check_request_flags(state
->request
->flags
)) {
1461 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1465 /* Parse domain and username */
1467 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1468 state
->request
->data
.auth
.user
,
1471 /* If the name normalization didnt' actually do anything,
1472 just use the original name */
1474 if (!NT_STATUS_IS_OK(name_map_status
) &&
1475 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1477 mapped_user
= state
->request
->data
.auth
.user
;
1480 parse_domain_user(mapped_user
, name_domain
, name_user
);
1482 if ( mapped_user
!= state
->request
->data
.auth
.user
) {
1483 fstr_sprintf( domain_user
, "%s\\%s", name_domain
, name_user
);
1484 safe_strcpy( state
->request
->data
.auth
.user
, domain_user
,
1485 sizeof(state
->request
->data
.auth
.user
)-1 );
1488 if (domain
->online
== false) {
1489 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1490 if (domain
->startup
) {
1491 /* Logons are very important to users. If we're offline and
1492 we get a request within the first 30 seconds of startup,
1493 try very hard to find a DC and go online. */
1495 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1496 "request in startup mode.\n", domain
->name
));
1498 winbindd_flush_negative_conn_cache(domain
);
1499 result
= init_dc_connection(domain
);
1503 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1505 /* Check for Kerberos authentication */
1506 if (domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
1508 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1509 /* save for later */
1510 krb5_result
= result
;
1513 if (NT_STATUS_IS_OK(result
)) {
1514 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1515 goto process_result
;
1517 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1520 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1521 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1522 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1523 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1524 set_domain_offline( domain
);
1528 /* there are quite some NT_STATUS errors where there is no
1529 * point in retrying with a samlogon, we explictly have to take
1530 * care not to increase the bad logon counter on the DC */
1532 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1533 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1534 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1535 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1536 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1537 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1538 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1539 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1540 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1541 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1542 goto process_result
;
1545 if (state
->request
->flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1546 DEBUG(3,("falling back to samlogon\n"));
1554 /* Check for Samlogon authentication */
1555 if (domain
->online
) {
1556 result
= winbindd_dual_pam_auth_samlogon(domain
, state
, &info3
);
1558 if (NT_STATUS_IS_OK(result
)) {
1559 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1560 /* add the Krb5 err if we have one */
1561 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1562 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1564 goto process_result
;
1567 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1568 nt_errstr(result
)));
1570 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1571 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1572 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1574 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1575 set_domain_offline( domain
);
1579 if (domain
->online
) {
1580 /* We're still online - fail. */
1586 /* Check for Cached logons */
1587 if (!domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1588 lp_winbind_offline_logon()) {
1590 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1592 if (NT_STATUS_IS_OK(result
)) {
1593 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1594 goto process_result
;
1596 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1603 if (NT_STATUS_IS_OK(result
)) {
1607 /* In all codepaths where result == NT_STATUS_OK info3 must have
1608 been initialized. */
1610 result
= NT_STATUS_INTERNAL_ERROR
;
1614 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1615 netsamlogon_cache_store(name_user
, info3
);
1617 /* save name_to_sid info as early as possible (only if
1618 this is our primary domain so we don't invalidate
1619 the cache entry by storing the seq_num for the wrong
1621 if ( domain
->primary
) {
1622 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1624 cache_name2sid(domain
, name_domain
, name_user
,
1625 SID_NAME_USER
, &user_sid
);
1628 /* Check if the user is in the right group */
1630 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1631 state
->request
->data
.auth
.require_membership_of_sid
))) {
1632 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1633 state
->request
->data
.auth
.user
,
1634 state
->request
->data
.auth
.require_membership_of_sid
));
1638 result
= append_data(state
, info3
, name_domain
, name_user
);
1639 if (!NT_STATUS_IS_OK(result
)) {
1643 if ((state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
1645 /* Store in-memory creds for single-signon using ntlm_auth. */
1646 result
= winbindd_add_memory_creds(state
->request
->data
.auth
.user
,
1647 get_uid_from_state(state
),
1648 state
->request
->data
.auth
.pass
);
1650 if (!NT_STATUS_IS_OK(result
)) {
1651 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result
)));
1655 if (lp_winbind_offline_logon()) {
1656 result
= winbindd_store_creds(domain
,
1658 state
->request
->data
.auth
.user
,
1659 state
->request
->data
.auth
.pass
,
1661 if (!NT_STATUS_IS_OK(result
)) {
1663 /* Release refcount. */
1664 winbindd_delete_memory_creds(state
->request
->data
.auth
.user
);
1666 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
1673 if (state
->request
->flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1674 struct winbindd_domain
*our_domain
= find_our_domain();
1676 /* This is not entirely correct I believe, but it is
1677 consistent. Only apply the password policy settings
1678 too warn users for our own domain. Cannot obtain these
1679 from trusted DCs all the time so don't do it at all.
1682 result
= NT_STATUS_NOT_SUPPORTED
;
1683 if (our_domain
== domain
) {
1684 result
= fillup_password_policy(our_domain
, state
);
1687 if (!NT_STATUS_IS_OK(result
)
1688 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1690 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1691 domain
->name
, nt_errstr(result
)));
1696 result
= NT_STATUS_OK
;
1700 /* give us a more useful (more correct?) error code */
1701 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1702 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1703 result
= NT_STATUS_NO_LOGON_SERVERS
;
1706 set_auth_errors(state
->response
, result
);
1708 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1709 state
->request
->data
.auth
.user
,
1710 state
->response
->data
.auth
.nt_status_string
,
1711 state
->response
->data
.auth
.pam_error
));
1713 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1717 /**********************************************************************
1718 Challenge Response Authentication Protocol
1719 **********************************************************************/
1721 void winbindd_pam_auth_crap(struct winbindd_cli_state
*state
)
1723 struct winbindd_domain
*domain
= NULL
;
1724 const char *domain_name
= NULL
;
1727 if (!check_request_flags(state
->request
->flags
)) {
1728 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1732 if (!state
->privileged
) {
1733 char *error_string
= NULL
;
1734 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1736 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1737 "on %s are set correctly.\n",
1738 get_winbind_priv_pipe_dir()));
1739 /* send a better message than ACCESS_DENIED */
1740 error_string
= talloc_asprintf(state
->mem_ctx
,
1741 "winbind client not authorized "
1742 "to use winbindd_pam_auth_crap."
1743 " Ensure permissions on %s "
1744 "are set correctly.",
1745 get_winbind_priv_pipe_dir());
1746 fstrcpy(state
->response
->data
.auth
.error_string
, error_string
);
1747 result
= NT_STATUS_ACCESS_DENIED
;
1751 /* Ensure null termination */
1752 state
->request
->data
.auth_crap
.user
1753 [sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1754 state
->request
->data
.auth_crap
.domain
1755 [sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1757 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1758 (unsigned long)state
->pid
,
1759 state
->request
->data
.auth_crap
.domain
,
1760 state
->request
->data
.auth_crap
.user
));
1762 if (*state
->request
->data
.auth_crap
.domain
!= '\0') {
1763 domain_name
= state
->request
->data
.auth_crap
.domain
;
1764 } else if (lp_winbind_use_default_domain()) {
1765 domain_name
= lp_workgroup();
1768 if (domain_name
!= NULL
)
1769 domain
= find_auth_domain(state
, domain_name
);
1771 if (domain
!= NULL
) {
1772 sendto_domain(state
, domain
);
1776 result
= NT_STATUS_NO_SUCH_USER
;
1779 set_auth_errors(state
->response
, result
);
1780 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1781 state
->request
->data
.auth_crap
.domain
,
1782 state
->request
->data
.auth_crap
.user
,
1783 state
->response
->data
.auth
.nt_status_string
,
1784 state
->response
->data
.auth
.pam_error
));
1785 request_error(state
);
1790 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1791 struct winbindd_cli_state
*state
)
1794 struct netr_SamInfo3
*info3
= NULL
;
1795 struct rpc_pipe_client
*netlogon_pipe
;
1796 const char *name_user
= NULL
;
1797 const char *name_domain
= NULL
;
1798 const char *workstation
;
1799 struct winbindd_domain
*contact_domain
;
1803 DATA_BLOB lm_resp
, nt_resp
;
1805 /* This is child-only, so no check for privileged access is needed
1808 /* Ensure null termination */
1809 state
->request
->data
.auth_crap
.user
[sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1810 state
->request
->data
.auth_crap
.domain
[sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1812 if (!check_request_flags(state
->request
->flags
)) {
1813 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1817 name_user
= state
->request
->data
.auth_crap
.user
;
1819 if (*state
->request
->data
.auth_crap
.domain
) {
1820 name_domain
= state
->request
->data
.auth_crap
.domain
;
1821 } else if (lp_winbind_use_default_domain()) {
1822 name_domain
= lp_workgroup();
1824 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1826 result
= NT_STATUS_NO_SUCH_USER
;
1830 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1831 name_domain
, name_user
));
1833 if (*state
->request
->data
.auth_crap
.workstation
) {
1834 workstation
= state
->request
->data
.auth_crap
.workstation
;
1836 workstation
= global_myname();
1839 if (state
->request
->data
.auth_crap
.lm_resp_len
> sizeof(state
->request
->data
.auth_crap
.lm_resp
)
1840 || state
->request
->data
.auth_crap
.nt_resp_len
> sizeof(state
->request
->data
.auth_crap
.nt_resp
)) {
1841 if (!(state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1842 state
->request
->extra_len
!= state
->request
->data
.auth_crap
.nt_resp_len
) {
1843 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1844 state
->request
->data
.auth_crap
.lm_resp_len
,
1845 state
->request
->data
.auth_crap
.nt_resp_len
));
1846 result
= NT_STATUS_INVALID_PARAMETER
;
1851 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
->data
.auth_crap
.lm_resp
,
1852 state
->request
->data
.auth_crap
.lm_resp_len
);
1854 if (state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1855 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1856 state
->request
->extra_data
.data
,
1857 state
->request
->data
.auth_crap
.nt_resp_len
);
1859 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1860 state
->request
->data
.auth_crap
.nt_resp
,
1861 state
->request
->data
.auth_crap
.nt_resp_len
);
1864 /* what domain should we contact? */
1867 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1868 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1869 state
->request
->data
.auth_crap
.user
, name_domain
, name_user
, name_domain
));
1870 result
= NT_STATUS_NO_SUCH_USER
;
1874 if (is_myname(name_domain
)) {
1875 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1876 result
= NT_STATUS_NO_SUCH_USER
;
1879 contact_domain
= find_our_domain();
1883 netlogon_fn_t logon_fn
;
1887 netlogon_pipe
= NULL
;
1888 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1890 if (!NT_STATUS_IS_OK(result
)) {
1891 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1892 nt_errstr(result
)));
1896 logon_fn
= contact_domain
->can_do_samlogon_ex
1897 ? rpccli_netlogon_sam_network_logon_ex
1898 : rpccli_netlogon_sam_network_logon
;
1900 result
= logon_fn(netlogon_pipe
,
1902 state
->request
->data
.auth_crap
.logon_parameters
,
1903 contact_domain
->dcname
,
1906 /* Bug #3248 - found by Stefan Burkei. */
1907 workstation
, /* We carefully set this above so use it... */
1908 state
->request
->data
.auth_crap
.chal
,
1913 if ((NT_STATUS_V(result
) == DCERPC_FAULT_OP_RNG_ERROR
)
1914 && contact_domain
->can_do_samlogon_ex
) {
1915 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1916 "retrying with NetSamLogon\n"));
1917 contact_domain
->can_do_samlogon_ex
= false;
1924 /* We have to try a second time as cm_connect_netlogon
1925 might not yet have noticed that the DC has killed
1928 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1933 /* if we get access denied, a possible cause was that we had and open
1934 connection to the DC, but someone changed our machine account password
1935 out from underneath us using 'net rpc changetrustpw' */
1937 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1938 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1939 "ACCESS_DENIED. Maybe the trust account "
1940 "password was changed and we didn't know it. "
1941 "Killing connections to domain %s\n",
1943 invalidate_cm_connection(&contact_domain
->conn
);
1947 } while ( (attempts
< 2) && retry
);
1949 if (NT_STATUS_IS_OK(result
)) {
1951 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1952 netsamlogon_cache_store(name_user
, info3
);
1954 /* Check if the user is in the right group */
1956 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1957 state
->request
->data
.auth_crap
.require_membership_of_sid
))) {
1958 DEBUG(3, ("User %s is not in the required group (%s), so "
1959 "crap authentication is rejected\n",
1960 state
->request
->data
.auth_crap
.user
,
1961 state
->request
->data
.auth_crap
.require_membership_of_sid
));
1965 result
= append_data(state
, info3
, name_domain
, name_user
);
1966 if (!NT_STATUS_IS_OK(result
)) {
1973 /* give us a more useful (more correct?) error code */
1974 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1975 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1976 result
= NT_STATUS_NO_LOGON_SERVERS
;
1979 if (state
->request
->flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1980 result
= nt_status_squash(result
);
1983 set_auth_errors(state
->response
, result
);
1985 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1986 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1989 state
->response
->data
.auth
.nt_status_string
,
1990 state
->response
->data
.auth
.pam_error
));
1992 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1995 /* Change a user password */
1997 void winbindd_pam_chauthtok(struct winbindd_cli_state
*state
)
1999 fstring domain
, user
;
2001 struct winbindd_domain
*contact_domain
;
2002 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
2004 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state
->pid
,
2005 state
->request
->data
.chauthtok
.user
));
2009 nt_status
= normalize_name_unmap(state
->mem_ctx
,
2010 state
->request
->data
.chauthtok
.user
,
2013 /* Update the chauthtok name if we did any mapping */
2015 if (NT_STATUS_IS_OK(nt_status
) ||
2016 NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
))
2018 fstrcpy(state
->request
->data
.chauthtok
.user
, mapped_user
);
2021 /* Must pass in state->...chauthtok.user because
2022 canonicalize_username() assumes an fstring(). Since
2023 we have already copied it (if necessary), this is ok. */
2025 if (!canonicalize_username(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
2026 set_auth_errors(state
->response
, NT_STATUS_NO_SUCH_USER
);
2027 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2029 state
->request
->data
.auth
.user
,
2030 state
->response
->data
.auth
.nt_status_string
,
2031 state
->response
->data
.auth
.pam_error
));
2032 request_error(state
);
2036 contact_domain
= find_domain_from_name(domain
);
2037 if (!contact_domain
) {
2038 set_auth_errors(state
->response
, NT_STATUS_NO_SUCH_USER
);
2039 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2040 state
->request
->data
.chauthtok
.user
, domain
, user
, domain
));
2041 request_error(state
);
2045 sendto_domain(state
, contact_domain
);
2048 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
2049 struct winbindd_cli_state
*state
)
2052 char *newpass
= NULL
;
2053 struct policy_handle dom_pol
;
2054 struct rpc_pipe_client
*cli
;
2055 bool got_info
= false;
2056 struct samr_DomInfo1
*info
= NULL
;
2057 struct samr_ChangeReject
*reject
= NULL
;
2058 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
2059 fstring domain
, user
;
2061 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
2062 state
->request
->data
.auth
.user
));
2064 if (!parse_domain_user(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
2068 /* Change password */
2070 oldpass
= state
->request
->data
.chauthtok
.oldpass
;
2071 newpass
= state
->request
->data
.chauthtok
.newpass
;
2073 /* Initialize reject reason */
2074 state
->response
->data
.auth
.reject_reason
= Undefined
;
2076 /* Get sam handle */
2078 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
2080 if (!NT_STATUS_IS_OK(result
)) {
2081 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2085 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
2092 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2094 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
2096 fill_in_password_policy(state
->response
, info
);
2098 state
->response
->data
.auth
.reject_reason
=
2104 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2105 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2106 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2107 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2109 /* only fallback when the chgpasswd_user3 call is not supported */
2110 if ((NT_STATUS_EQUAL(result
, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR
))) ||
2111 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
)) ||
2112 (NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
)) ||
2113 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
))) {
2115 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2116 nt_errstr(result
)));
2118 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2120 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2121 Map to the same status code as Windows 2003. */
2123 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2124 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2130 if (NT_STATUS_IS_OK(result
) && (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
2132 /* Update the single sign-on memory creds. */
2133 result
= winbindd_replace_memory_creds(state
->request
->data
.chauthtok
.user
,
2136 /* When we login from gdm or xdm and password expires,
2137 * we change password, but there are no memory crendentials
2138 * So, winbindd_replace_memory_creds() returns
2139 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2142 if (NT_STATUS_EQUAL(result
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
2143 result
= NT_STATUS_OK
;
2146 if (!NT_STATUS_IS_OK(result
)) {
2147 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result
)));
2148 goto process_result
;
2151 if (lp_winbind_offline_logon()) {
2152 result
= winbindd_update_creds_by_name(contact_domain
,
2153 state
->mem_ctx
, user
,
2155 /* Again, this happens when we login from gdm or xdm
2156 * and the password expires, *BUT* cached crendentials
2157 * doesn't exist. winbindd_update_creds_by_name()
2158 * returns NT_STATUS_NO_SUCH_USER.
2159 * This is not a failure.
2162 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2163 result
= NT_STATUS_OK
;
2166 if (!NT_STATUS_IS_OK(result
)) {
2167 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
2168 goto process_result
;
2173 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2175 NTSTATUS policy_ret
;
2177 policy_ret
= fillup_password_policy(contact_domain
, state
);
2179 /* failure of this is non critical, it will just provide no
2180 * additional information to the client why the change has
2181 * failed - Guenther */
2183 if (!NT_STATUS_IS_OK(policy_ret
)) {
2184 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2185 goto process_result
;
2191 set_auth_errors(state
->response
, result
);
2193 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2194 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2197 state
->response
->data
.auth
.nt_status_string
,
2198 state
->response
->data
.auth
.pam_error
));
2200 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2203 void winbindd_pam_logoff(struct winbindd_cli_state
*state
)
2205 struct winbindd_domain
*domain
;
2206 fstring name_domain
, user
;
2207 uid_t caller_uid
= (uid_t
)-1;
2208 uid_t request_uid
= state
->request
->data
.logoff
.uid
;
2210 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state
->pid
,
2211 state
->request
->data
.logoff
.user
));
2213 /* Ensure null termination */
2214 state
->request
->data
.logoff
.user
2215 [sizeof(state
->request
->data
.logoff
.user
)-1]='\0';
2217 state
->request
->data
.logoff
.krb5ccname
2218 [sizeof(state
->request
->data
.logoff
.krb5ccname
)-1]='\0';
2220 if (request_uid
== (gid_t
)-1) {
2224 if (!canonicalize_username(state
->request
->data
.logoff
.user
, name_domain
, user
)) {
2228 if ((domain
= find_auth_domain(state
, name_domain
)) == NULL
) {
2232 if ((sys_getpeereid(state
->sock
, &caller_uid
)) != 0) {
2233 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2238 switch (caller_uid
) {
2242 /* root must be able to logoff any user - gd */
2243 state
->request
->data
.logoff
.uid
= request_uid
;
2246 if (caller_uid
!= request_uid
) {
2247 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2250 state
->request
->data
.logoff
.uid
= caller_uid
;
2254 sendto_domain(state
, domain
);
2258 set_auth_errors(state
->response
, NT_STATUS_NO_SUCH_USER
);
2259 DEBUG(5, ("Pam Logoff for %s returned %s "
2261 state
->request
->data
.logoff
.user
,
2262 state
->response
->data
.auth
.nt_status_string
,
2263 state
->response
->data
.auth
.pam_error
));
2264 request_error(state
);
2268 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2269 struct winbindd_cli_state
*state
)
2271 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2273 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2274 state
->request
->data
.logoff
.user
));
2276 if (!(state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
2277 result
= NT_STATUS_OK
;
2278 goto process_result
;
2281 if (state
->request
->data
.logoff
.krb5ccname
[0] == '\0') {
2282 result
= NT_STATUS_OK
;
2283 goto process_result
;
2288 if (state
->request
->data
.logoff
.uid
< 0) {
2289 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2290 goto process_result
;
2293 /* what we need here is to find the corresponding krb5 ccache name *we*
2294 * created for a given username and destroy it */
2296 if (!ccache_entry_exists(state
->request
->data
.logoff
.user
)) {
2297 result
= NT_STATUS_OK
;
2298 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2299 goto process_result
;
2302 if (!ccache_entry_identical(state
->request
->data
.logoff
.user
,
2303 state
->request
->data
.logoff
.uid
,
2304 state
->request
->data
.logoff
.krb5ccname
)) {
2305 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2306 goto process_result
;
2309 result
= remove_ccache(state
->request
->data
.logoff
.user
);
2310 if (!NT_STATUS_IS_OK(result
)) {
2311 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2312 nt_errstr(result
)));
2313 goto process_result
;
2317 result
= NT_STATUS_NOT_SUPPORTED
;
2322 winbindd_delete_memory_creds(state
->request
->data
.logoff
.user
);
2324 set_auth_errors(state
->response
, result
);
2326 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2329 /* Change user password with auth crap*/
2331 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state
*state
)
2333 struct winbindd_domain
*domain
= NULL
;
2334 const char *domain_name
= NULL
;
2336 /* Ensure null termination */
2337 state
->request
->data
.chng_pswd_auth_crap
.user
[
2338 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2339 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2340 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2342 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2343 (unsigned long)state
->pid
,
2344 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2345 state
->request
->data
.chng_pswd_auth_crap
.user
));
2347 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
!= '\0') {
2348 domain_name
= state
->request
->data
.chng_pswd_auth_crap
.domain
;
2349 } else if (lp_winbind_use_default_domain()) {
2350 domain_name
= lp_workgroup();
2353 if (domain_name
!= NULL
)
2354 domain
= find_domain_from_name(domain_name
);
2356 if (domain
!= NULL
) {
2357 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2358 "%s\n", (unsigned long)state
->pid
,domain
->name
));
2359 sendto_domain(state
, domain
);
2363 set_auth_errors(state
->response
, NT_STATUS_NO_SUCH_USER
);
2364 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2365 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2366 state
->request
->data
.chng_pswd_auth_crap
.user
,
2367 state
->response
->data
.auth
.nt_status_string
,
2368 state
->response
->data
.auth
.pam_error
));
2369 request_error(state
);
2373 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2376 DATA_BLOB new_nt_password
;
2377 DATA_BLOB old_nt_hash_enc
;
2378 DATA_BLOB new_lm_password
;
2379 DATA_BLOB old_lm_hash_enc
;
2380 fstring domain
,user
;
2381 struct policy_handle dom_pol
;
2382 struct winbindd_domain
*contact_domain
= domainSt
;
2383 struct rpc_pipe_client
*cli
;
2385 /* Ensure null termination */
2386 state
->request
->data
.chng_pswd_auth_crap
.user
[
2387 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2388 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2389 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2393 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2394 (unsigned long)state
->pid
,
2395 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2396 state
->request
->data
.chng_pswd_auth_crap
.user
));
2398 if (lp_winbind_offline_logon()) {
2399 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2400 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2401 result
= NT_STATUS_ACCESS_DENIED
;
2405 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
) {
2406 fstrcpy(domain
,state
->request
->data
.chng_pswd_auth_crap
.domain
);
2408 parse_domain_user(state
->request
->data
.chng_pswd_auth_crap
.user
,
2412 DEBUG(3,("no domain specified with username (%s) - "
2414 state
->request
->data
.chng_pswd_auth_crap
.user
));
2415 result
= NT_STATUS_NO_SUCH_USER
;
2420 if (!*domain
&& lp_winbind_use_default_domain()) {
2421 fstrcpy(domain
,(char *)lp_workgroup());
2425 fstrcpy(user
, state
->request
->data
.chng_pswd_auth_crap
.user
);
2428 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2429 (unsigned long)state
->pid
, domain
, user
));
2431 /* Change password */
2432 new_nt_password
= data_blob_talloc(
2434 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd
,
2435 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2437 old_nt_hash_enc
= data_blob_talloc(
2439 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2440 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2442 if(state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2443 new_lm_password
= data_blob_talloc(
2445 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd
,
2446 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2448 old_lm_hash_enc
= data_blob_talloc(
2450 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2451 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2453 new_lm_password
.length
= 0;
2454 old_lm_hash_enc
.length
= 0;
2457 /* Get sam handle */
2459 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2460 if (!NT_STATUS_IS_OK(result
)) {
2461 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2465 result
= rpccli_samr_chng_pswd_auth_crap(
2466 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2467 new_lm_password
, old_lm_hash_enc
);
2471 set_auth_errors(state
->response
, result
);
2473 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2474 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2476 state
->response
->data
.auth
.nt_status_string
,
2477 state
->response
->data
.auth
.pam_error
));
2479 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;