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 static 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
,
931 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
932 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
,
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 static 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 static 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 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1735 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1736 "on %s are set correctly.\n",
1737 get_winbind_priv_pipe_dir()));
1738 /* send a better message than ACCESS_DENIED */
1739 fstr_sprintf(state
->response
->data
.auth
.error_string
,
1740 "winbind client not authorized to use "
1741 "winbindd_pam_auth_crap. Ensure permissions on "
1742 "%s are set correctly.",
1743 get_winbind_priv_pipe_dir());
1744 result
= NT_STATUS_ACCESS_DENIED
;
1748 /* Ensure null termination */
1749 state
->request
->data
.auth_crap
.user
1750 [sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1751 state
->request
->data
.auth_crap
.domain
1752 [sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1754 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1755 (unsigned long)state
->pid
,
1756 state
->request
->data
.auth_crap
.domain
,
1757 state
->request
->data
.auth_crap
.user
));
1759 if (*state
->request
->data
.auth_crap
.domain
!= '\0') {
1760 domain_name
= state
->request
->data
.auth_crap
.domain
;
1761 } else if (lp_winbind_use_default_domain()) {
1762 domain_name
= lp_workgroup();
1765 if (domain_name
!= NULL
)
1766 domain
= find_auth_domain(state
, domain_name
);
1768 if (domain
!= NULL
) {
1769 sendto_domain(state
, domain
);
1773 result
= NT_STATUS_NO_SUCH_USER
;
1776 set_auth_errors(state
->response
, result
);
1777 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1778 state
->request
->data
.auth_crap
.domain
,
1779 state
->request
->data
.auth_crap
.user
,
1780 state
->response
->data
.auth
.nt_status_string
,
1781 state
->response
->data
.auth
.pam_error
));
1782 request_error(state
);
1787 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1788 struct winbindd_cli_state
*state
)
1791 struct netr_SamInfo3
*info3
= NULL
;
1792 struct rpc_pipe_client
*netlogon_pipe
;
1793 const char *name_user
= NULL
;
1794 const char *name_domain
= NULL
;
1795 const char *workstation
;
1796 struct winbindd_domain
*contact_domain
;
1800 DATA_BLOB lm_resp
, nt_resp
;
1802 /* This is child-only, so no check for privileged access is needed
1805 /* Ensure null termination */
1806 state
->request
->data
.auth_crap
.user
[sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1807 state
->request
->data
.auth_crap
.domain
[sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1809 if (!check_request_flags(state
->request
->flags
)) {
1810 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1814 name_user
= state
->request
->data
.auth_crap
.user
;
1816 if (*state
->request
->data
.auth_crap
.domain
) {
1817 name_domain
= state
->request
->data
.auth_crap
.domain
;
1818 } else if (lp_winbind_use_default_domain()) {
1819 name_domain
= lp_workgroup();
1821 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1823 result
= NT_STATUS_NO_SUCH_USER
;
1827 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1828 name_domain
, name_user
));
1830 if (*state
->request
->data
.auth_crap
.workstation
) {
1831 workstation
= state
->request
->data
.auth_crap
.workstation
;
1833 workstation
= global_myname();
1836 if (state
->request
->data
.auth_crap
.lm_resp_len
> sizeof(state
->request
->data
.auth_crap
.lm_resp
)
1837 || state
->request
->data
.auth_crap
.nt_resp_len
> sizeof(state
->request
->data
.auth_crap
.nt_resp
)) {
1838 if (!(state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1839 state
->request
->extra_len
!= state
->request
->data
.auth_crap
.nt_resp_len
) {
1840 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1841 state
->request
->data
.auth_crap
.lm_resp_len
,
1842 state
->request
->data
.auth_crap
.nt_resp_len
));
1843 result
= NT_STATUS_INVALID_PARAMETER
;
1848 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
->data
.auth_crap
.lm_resp
,
1849 state
->request
->data
.auth_crap
.lm_resp_len
);
1851 if (state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1852 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1853 state
->request
->extra_data
.data
,
1854 state
->request
->data
.auth_crap
.nt_resp_len
);
1856 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1857 state
->request
->data
.auth_crap
.nt_resp
,
1858 state
->request
->data
.auth_crap
.nt_resp_len
);
1861 /* what domain should we contact? */
1864 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1865 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1866 state
->request
->data
.auth_crap
.user
, name_domain
, name_user
, name_domain
));
1867 result
= NT_STATUS_NO_SUCH_USER
;
1871 if (is_myname(name_domain
)) {
1872 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1873 result
= NT_STATUS_NO_SUCH_USER
;
1876 contact_domain
= find_our_domain();
1880 netlogon_fn_t logon_fn
;
1884 netlogon_pipe
= NULL
;
1885 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1887 if (!NT_STATUS_IS_OK(result
)) {
1888 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1889 nt_errstr(result
)));
1893 logon_fn
= contact_domain
->can_do_samlogon_ex
1894 ? rpccli_netlogon_sam_network_logon_ex
1895 : rpccli_netlogon_sam_network_logon
;
1897 result
= logon_fn(netlogon_pipe
,
1899 state
->request
->data
.auth_crap
.logon_parameters
,
1900 contact_domain
->dcname
,
1903 /* Bug #3248 - found by Stefan Burkei. */
1904 workstation
, /* We carefully set this above so use it... */
1905 state
->request
->data
.auth_crap
.chal
,
1910 if ((NT_STATUS_V(result
) == DCERPC_FAULT_OP_RNG_ERROR
)
1911 && contact_domain
->can_do_samlogon_ex
) {
1912 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1913 "retrying with NetSamLogon\n"));
1914 contact_domain
->can_do_samlogon_ex
= false;
1921 /* We have to try a second time as cm_connect_netlogon
1922 might not yet have noticed that the DC has killed
1925 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1930 /* if we get access denied, a possible cause was that we had and open
1931 connection to the DC, but someone changed our machine account password
1932 out from underneath us using 'net rpc changetrustpw' */
1934 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1935 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1936 "ACCESS_DENIED. Maybe the trust account "
1937 "password was changed and we didn't know it. "
1938 "Killing connections to domain %s\n",
1940 invalidate_cm_connection(&contact_domain
->conn
);
1944 } while ( (attempts
< 2) && retry
);
1946 if (NT_STATUS_IS_OK(result
)) {
1948 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1949 netsamlogon_cache_store(name_user
, info3
);
1951 /* Check if the user is in the right group */
1953 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1954 state
->request
->data
.auth_crap
.require_membership_of_sid
))) {
1955 DEBUG(3, ("User %s is not in the required group (%s), so "
1956 "crap authentication is rejected\n",
1957 state
->request
->data
.auth_crap
.user
,
1958 state
->request
->data
.auth_crap
.require_membership_of_sid
));
1962 result
= append_data(state
, info3
, name_domain
, name_user
);
1963 if (!NT_STATUS_IS_OK(result
)) {
1970 /* give us a more useful (more correct?) error code */
1971 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1972 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1973 result
= NT_STATUS_NO_LOGON_SERVERS
;
1976 if (state
->request
->flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1977 result
= nt_status_squash(result
);
1980 set_auth_errors(state
->response
, result
);
1982 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1983 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1986 state
->response
->data
.auth
.nt_status_string
,
1987 state
->response
->data
.auth
.pam_error
));
1989 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1992 /* Change a user password */
1994 void winbindd_pam_chauthtok(struct winbindd_cli_state
*state
)
1996 fstring domain
, user
;
1998 struct winbindd_domain
*contact_domain
;
1999 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
2001 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state
->pid
,
2002 state
->request
->data
.chauthtok
.user
));
2006 nt_status
= normalize_name_unmap(state
->mem_ctx
,
2007 state
->request
->data
.chauthtok
.user
,
2010 /* Update the chauthtok name if we did any mapping */
2012 if (NT_STATUS_IS_OK(nt_status
) ||
2013 NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
))
2015 fstrcpy(state
->request
->data
.chauthtok
.user
, mapped_user
);
2018 /* Must pass in state->...chauthtok.user because
2019 canonicalize_username() assumes an fstring(). Since
2020 we have already copied it (if necessary), this is ok. */
2022 if (!canonicalize_username(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
2023 set_auth_errors(state
->response
, NT_STATUS_NO_SUCH_USER
);
2024 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2026 state
->request
->data
.auth
.user
,
2027 state
->response
->data
.auth
.nt_status_string
,
2028 state
->response
->data
.auth
.pam_error
));
2029 request_error(state
);
2033 contact_domain
= find_domain_from_name(domain
);
2034 if (!contact_domain
) {
2035 set_auth_errors(state
->response
, NT_STATUS_NO_SUCH_USER
);
2036 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2037 state
->request
->data
.chauthtok
.user
, domain
, user
, domain
));
2038 request_error(state
);
2042 sendto_domain(state
, contact_domain
);
2045 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
2046 struct winbindd_cli_state
*state
)
2049 char *newpass
= NULL
;
2050 struct policy_handle dom_pol
;
2051 struct rpc_pipe_client
*cli
;
2052 bool got_info
= false;
2053 struct samr_DomInfo1
*info
= NULL
;
2054 struct samr_ChangeReject
*reject
= NULL
;
2055 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
2056 fstring domain
, user
;
2058 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
2059 state
->request
->data
.auth
.user
));
2061 if (!parse_domain_user(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
2065 /* Change password */
2067 oldpass
= state
->request
->data
.chauthtok
.oldpass
;
2068 newpass
= state
->request
->data
.chauthtok
.newpass
;
2070 /* Initialize reject reason */
2071 state
->response
->data
.auth
.reject_reason
= Undefined
;
2073 /* Get sam handle */
2075 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
2077 if (!NT_STATUS_IS_OK(result
)) {
2078 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2082 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
2089 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2091 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
2093 fill_in_password_policy(state
->response
, info
);
2095 state
->response
->data
.auth
.reject_reason
=
2101 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2102 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2103 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2104 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2106 /* only fallback when the chgpasswd_user3 call is not supported */
2107 if ((NT_STATUS_EQUAL(result
, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR
))) ||
2108 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
)) ||
2109 (NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
)) ||
2110 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
))) {
2112 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2113 nt_errstr(result
)));
2115 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2117 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2118 Map to the same status code as Windows 2003. */
2120 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2121 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2127 if (NT_STATUS_IS_OK(result
) && (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
2129 /* Update the single sign-on memory creds. */
2130 result
= winbindd_replace_memory_creds(state
->request
->data
.chauthtok
.user
,
2133 /* When we login from gdm or xdm and password expires,
2134 * we change password, but there are no memory crendentials
2135 * So, winbindd_replace_memory_creds() returns
2136 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2139 if (NT_STATUS_EQUAL(result
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
2140 result
= NT_STATUS_OK
;
2143 if (!NT_STATUS_IS_OK(result
)) {
2144 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result
)));
2145 goto process_result
;
2148 if (lp_winbind_offline_logon()) {
2149 result
= winbindd_update_creds_by_name(contact_domain
,
2150 state
->mem_ctx
, user
,
2152 /* Again, this happens when we login from gdm or xdm
2153 * and the password expires, *BUT* cached crendentials
2154 * doesn't exist. winbindd_update_creds_by_name()
2155 * returns NT_STATUS_NO_SUCH_USER.
2156 * This is not a failure.
2159 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2160 result
= NT_STATUS_OK
;
2163 if (!NT_STATUS_IS_OK(result
)) {
2164 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
2165 goto process_result
;
2170 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2172 NTSTATUS policy_ret
;
2174 policy_ret
= fillup_password_policy(contact_domain
, state
);
2176 /* failure of this is non critical, it will just provide no
2177 * additional information to the client why the change has
2178 * failed - Guenther */
2180 if (!NT_STATUS_IS_OK(policy_ret
)) {
2181 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2182 goto process_result
;
2188 set_auth_errors(state
->response
, result
);
2190 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2191 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2194 state
->response
->data
.auth
.nt_status_string
,
2195 state
->response
->data
.auth
.pam_error
));
2197 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2200 void winbindd_pam_logoff(struct winbindd_cli_state
*state
)
2202 struct winbindd_domain
*domain
;
2203 fstring name_domain
, user
;
2204 uid_t caller_uid
= (uid_t
)-1;
2205 uid_t request_uid
= state
->request
->data
.logoff
.uid
;
2207 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state
->pid
,
2208 state
->request
->data
.logoff
.user
));
2210 /* Ensure null termination */
2211 state
->request
->data
.logoff
.user
2212 [sizeof(state
->request
->data
.logoff
.user
)-1]='\0';
2214 state
->request
->data
.logoff
.krb5ccname
2215 [sizeof(state
->request
->data
.logoff
.krb5ccname
)-1]='\0';
2217 if (request_uid
== (gid_t
)-1) {
2221 if (!canonicalize_username(state
->request
->data
.logoff
.user
, name_domain
, user
)) {
2225 if ((domain
= find_auth_domain(state
, name_domain
)) == NULL
) {
2229 if ((sys_getpeereid(state
->sock
, &caller_uid
)) != 0) {
2230 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2235 switch (caller_uid
) {
2239 /* root must be able to logoff any user - gd */
2240 state
->request
->data
.logoff
.uid
= request_uid
;
2243 if (caller_uid
!= request_uid
) {
2244 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2247 state
->request
->data
.logoff
.uid
= caller_uid
;
2251 sendto_domain(state
, domain
);
2255 set_auth_errors(state
->response
, NT_STATUS_NO_SUCH_USER
);
2256 DEBUG(5, ("Pam Logoff for %s returned %s "
2258 state
->request
->data
.logoff
.user
,
2259 state
->response
->data
.auth
.nt_status_string
,
2260 state
->response
->data
.auth
.pam_error
));
2261 request_error(state
);
2265 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2266 struct winbindd_cli_state
*state
)
2268 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2270 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2271 state
->request
->data
.logoff
.user
));
2273 if (!(state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
2274 result
= NT_STATUS_OK
;
2275 goto process_result
;
2278 if (state
->request
->data
.logoff
.krb5ccname
[0] == '\0') {
2279 result
= NT_STATUS_OK
;
2280 goto process_result
;
2285 if (state
->request
->data
.logoff
.uid
< 0) {
2286 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2287 goto process_result
;
2290 /* what we need here is to find the corresponding krb5 ccache name *we*
2291 * created for a given username and destroy it */
2293 if (!ccache_entry_exists(state
->request
->data
.logoff
.user
)) {
2294 result
= NT_STATUS_OK
;
2295 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2296 goto process_result
;
2299 if (!ccache_entry_identical(state
->request
->data
.logoff
.user
,
2300 state
->request
->data
.logoff
.uid
,
2301 state
->request
->data
.logoff
.krb5ccname
)) {
2302 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2303 goto process_result
;
2306 result
= remove_ccache(state
->request
->data
.logoff
.user
);
2307 if (!NT_STATUS_IS_OK(result
)) {
2308 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2309 nt_errstr(result
)));
2310 goto process_result
;
2314 result
= NT_STATUS_NOT_SUPPORTED
;
2319 winbindd_delete_memory_creds(state
->request
->data
.logoff
.user
);
2321 set_auth_errors(state
->response
, result
);
2323 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2326 /* Change user password with auth crap*/
2328 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state
*state
)
2330 struct winbindd_domain
*domain
= NULL
;
2331 const char *domain_name
= NULL
;
2333 /* Ensure null termination */
2334 state
->request
->data
.chng_pswd_auth_crap
.user
[
2335 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2336 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2337 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2339 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2340 (unsigned long)state
->pid
,
2341 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2342 state
->request
->data
.chng_pswd_auth_crap
.user
));
2344 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
!= '\0') {
2345 domain_name
= state
->request
->data
.chng_pswd_auth_crap
.domain
;
2346 } else if (lp_winbind_use_default_domain()) {
2347 domain_name
= lp_workgroup();
2350 if (domain_name
!= NULL
)
2351 domain
= find_domain_from_name(domain_name
);
2353 if (domain
!= NULL
) {
2354 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2355 "%s\n", (unsigned long)state
->pid
,domain
->name
));
2356 sendto_domain(state
, domain
);
2360 set_auth_errors(state
->response
, NT_STATUS_NO_SUCH_USER
);
2361 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2362 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2363 state
->request
->data
.chng_pswd_auth_crap
.user
,
2364 state
->response
->data
.auth
.nt_status_string
,
2365 state
->response
->data
.auth
.pam_error
));
2366 request_error(state
);
2370 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2373 DATA_BLOB new_nt_password
;
2374 DATA_BLOB old_nt_hash_enc
;
2375 DATA_BLOB new_lm_password
;
2376 DATA_BLOB old_lm_hash_enc
;
2377 fstring domain
,user
;
2378 struct policy_handle dom_pol
;
2379 struct winbindd_domain
*contact_domain
= domainSt
;
2380 struct rpc_pipe_client
*cli
;
2382 /* Ensure null termination */
2383 state
->request
->data
.chng_pswd_auth_crap
.user
[
2384 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2385 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2386 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2390 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2391 (unsigned long)state
->pid
,
2392 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2393 state
->request
->data
.chng_pswd_auth_crap
.user
));
2395 if (lp_winbind_offline_logon()) {
2396 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2397 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2398 result
= NT_STATUS_ACCESS_DENIED
;
2402 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
) {
2403 fstrcpy(domain
,state
->request
->data
.chng_pswd_auth_crap
.domain
);
2405 parse_domain_user(state
->request
->data
.chng_pswd_auth_crap
.user
,
2409 DEBUG(3,("no domain specified with username (%s) - "
2411 state
->request
->data
.chng_pswd_auth_crap
.user
));
2412 result
= NT_STATUS_NO_SUCH_USER
;
2417 if (!*domain
&& lp_winbind_use_default_domain()) {
2418 fstrcpy(domain
,(char *)lp_workgroup());
2422 fstrcpy(user
, state
->request
->data
.chng_pswd_auth_crap
.user
);
2425 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2426 (unsigned long)state
->pid
, domain
, user
));
2428 /* Change password */
2429 new_nt_password
= data_blob_talloc(
2431 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd
,
2432 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2434 old_nt_hash_enc
= data_blob_talloc(
2436 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2437 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2439 if(state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2440 new_lm_password
= data_blob_talloc(
2442 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd
,
2443 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2445 old_lm_hash_enc
= data_blob_talloc(
2447 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2448 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2450 new_lm_password
.length
= 0;
2451 old_lm_hash_enc
.length
= 0;
2454 /* Get sam handle */
2456 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2457 if (!NT_STATUS_IS_OK(result
)) {
2458 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2462 result
= rpccli_samr_chng_pswd_auth_crap(
2463 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2464 new_lm_password
, old_lm_hash_enc
);
2468 set_auth_errors(state
->response
, result
);
2470 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2471 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2473 state
->response
->data
.auth
.nt_status_string
,
2474 state
->response
->data
.auth
.pam_error
));
2476 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;