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
, token
);
224 if (state
->response
.extra_data
.data
== NULL
) {
225 return NT_STATUS_NO_MEMORY
;
227 state
->response
.length
+=
228 strlen((const char *)state
->response
.extra_data
.data
)+1;
233 static NTSTATUS
check_info3_in_group(TALLOC_CTX
*mem_ctx
,
234 struct netr_SamInfo3
*info3
,
235 const char *group_sid
)
237 * Check whether a user belongs to a group or list of groups.
239 * @param mem_ctx talloc memory context.
240 * @param info3 user information, including group membership info.
241 * @param group_sid One or more groups , separated by commas.
243 * @return NT_STATUS_OK on success,
244 * NT_STATUS_LOGON_FAILURE if the user does not belong,
245 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
248 DOM_SID
*require_membership_of_sid
;
249 size_t num_require_membership_of_sid
;
254 struct nt_user_token
*token
;
255 TALLOC_CTX
*frame
= NULL
;
258 /* Parse the 'required group' SID */
260 if (!group_sid
|| !group_sid
[0]) {
261 /* NO sid supplied, all users may access */
265 if (!(token
= TALLOC_ZERO_P(mem_ctx
, struct nt_user_token
))) {
266 DEBUG(0, ("talloc failed\n"));
267 return NT_STATUS_NO_MEMORY
;
270 num_require_membership_of_sid
= 0;
271 require_membership_of_sid
= NULL
;
275 frame
= talloc_stackframe();
276 while (next_token_talloc(frame
, &p
, &req_sid
, ",")) {
277 if (!string_to_sid(&sid
, req_sid
)) {
278 DEBUG(0, ("check_info3_in_group: could not parse %s "
279 "as a SID!", req_sid
));
281 return NT_STATUS_INVALID_PARAMETER
;
284 status
= add_sid_to_array(mem_ctx
, &sid
,
285 &require_membership_of_sid
,
286 &num_require_membership_of_sid
);
287 if (!NT_STATUS_IS_OK(status
)) {
288 DEBUG(0, ("add_sid_to_array failed\n"));
296 status
= sid_array_from_info3(mem_ctx
, info3
,
300 if (!NT_STATUS_IS_OK(status
)) {
304 if (!NT_STATUS_IS_OK(status
= add_aliases(get_global_sam_sid(),
306 || !NT_STATUS_IS_OK(status
= add_aliases(&global_sid_Builtin
,
308 DEBUG(3, ("could not add aliases: %s\n",
313 debug_nt_user_token(DBGC_CLASS
, 10, token
);
315 for (i
=0; i
<num_require_membership_of_sid
; i
++) {
316 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
317 &require_membership_of_sid
[i
])));
318 if (nt_token_check_sid(&require_membership_of_sid
[i
],
320 DEBUG(10, ("Access ok\n"));
325 /* Do not distinguish this error from a wrong username/pw */
327 return NT_STATUS_LOGON_FAILURE
;
330 struct winbindd_domain
*find_auth_domain(struct winbindd_cli_state
*state
,
331 const char *domain_name
)
333 struct winbindd_domain
*domain
;
336 domain
= find_domain_from_name_noinit(domain_name
);
337 if (domain
== NULL
) {
338 DEBUG(3, ("Authentication for domain [%s] refused "
339 "as it is not a trusted domain\n",
345 if (is_myname(domain_name
)) {
346 DEBUG(3, ("Authentication for domain %s (local domain "
347 "to this server) not supported at this "
348 "stage\n", domain_name
));
352 /* we can auth against trusted domains */
353 if (state
->request
.flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
354 domain
= find_domain_from_name_noinit(domain_name
);
355 if (domain
== NULL
) {
356 DEBUG(3, ("Authentication for domain [%s] skipped "
357 "as it is not a trusted domain\n",
364 return find_our_domain();
367 static void fill_in_password_policy(struct winbindd_response
*r
,
368 const struct samr_DomInfo1
*p
)
370 r
->data
.auth
.policy
.min_length_password
=
371 p
->min_password_length
;
372 r
->data
.auth
.policy
.password_history
=
373 p
->password_history_length
;
374 r
->data
.auth
.policy
.password_properties
=
375 p
->password_properties
;
376 r
->data
.auth
.policy
.expire
=
377 nt_time_to_unix_abs((NTTIME
*)&(p
->max_password_age
));
378 r
->data
.auth
.policy
.min_passwordage
=
379 nt_time_to_unix_abs((NTTIME
*)&(p
->min_password_age
));
382 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
383 struct winbindd_cli_state
*state
)
385 struct winbindd_methods
*methods
;
386 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
387 struct samr_DomInfo1 password_policy
;
389 if ( !winbindd_can_contact_domain( domain
) ) {
390 DEBUG(5,("fillup_password_policy: No inbound trust to "
391 "contact domain %s\n", domain
->name
));
392 return NT_STATUS_NOT_SUPPORTED
;
395 methods
= domain
->methods
;
397 status
= methods
->password_policy(domain
, state
->mem_ctx
, &password_policy
);
398 if (NT_STATUS_IS_ERR(status
)) {
402 fill_in_password_policy(&state
->response
, &password_policy
);
407 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
409 uint16
*lockout_threshold
)
411 struct winbindd_methods
*methods
;
412 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
413 struct samr_DomInfo12 lockout_policy
;
415 *lockout_threshold
= 0;
417 methods
= domain
->methods
;
419 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
420 if (NT_STATUS_IS_ERR(status
)) {
424 *lockout_threshold
= lockout_policy
.lockout_threshold
;
429 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
431 uint32
*password_properties
)
433 struct winbindd_methods
*methods
;
434 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
435 struct samr_DomInfo1 password_policy
;
437 *password_properties
= 0;
439 methods
= domain
->methods
;
441 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
442 if (NT_STATUS_IS_ERR(status
)) {
446 *password_properties
= password_policy
.password_properties
;
453 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
456 bool *internal_ccache
)
458 /* accept FILE and WRFILE as krb5_cc_type from the client and then
459 * build the full ccname string based on the user's uid here -
462 const char *gen_cc
= NULL
;
464 *internal_ccache
= true;
470 if (!type
|| type
[0] == '\0') {
474 if (strequal(type
, "FILE")) {
475 gen_cc
= talloc_asprintf(mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
476 } else if (strequal(type
, "WRFILE")) {
477 gen_cc
= talloc_asprintf(mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
479 DEBUG(10,("we don't allow to set a %s type ccache\n", type
));
483 *internal_ccache
= false;
487 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
490 if (gen_cc
== NULL
) {
491 DEBUG(0,("out of memory\n"));
495 DEBUG(10,("using ccache: %s %s\n", gen_cc
, *internal_ccache
? "(internal)":""));
500 static void setup_return_cc_name(struct winbindd_cli_state
*state
, const char *cc
)
502 const char *type
= state
->request
.data
.auth
.krb5_cc_type
;
504 state
->response
.data
.auth
.krb5ccname
[0] = '\0';
506 if (type
[0] == '\0') {
510 if (!strequal(type
, "FILE") &&
511 !strequal(type
, "WRFILE")) {
512 DEBUG(10,("won't return krbccname for a %s type ccache\n",
517 fstrcpy(state
->response
.data
.auth
.krb5ccname
, cc
);
522 static uid_t
get_uid_from_state(struct winbindd_cli_state
*state
)
526 uid
= state
->request
.data
.auth
.uid
;
529 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid
));
535 /**********************************************************************
536 Authenticate a user with a clear text password using Kerberos and fill up
538 **********************************************************************/
540 static NTSTATUS
winbindd_raw_kerberos_login(struct winbindd_domain
*domain
,
541 struct winbindd_cli_state
*state
,
542 struct netr_SamInfo3
**info3
)
545 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
546 krb5_error_code krb5_ret
;
547 const char *cc
= NULL
;
548 const char *principal_s
= NULL
;
549 const char *service
= NULL
;
551 fstring name_domain
, name_user
;
552 time_t ticket_lifetime
= 0;
553 time_t renewal_until
= 0;
556 time_t time_offset
= 0;
557 bool internal_ccache
= true;
564 * prepare a krb5_cc_cache string for the user */
566 uid
= get_uid_from_state(state
);
568 DEBUG(0,("no valid uid\n"));
571 cc
= generate_krb5_ccache(state
->mem_ctx
,
572 state
->request
.data
.auth
.krb5_cc_type
,
573 state
->request
.data
.auth
.uid
,
576 return NT_STATUS_NO_MEMORY
;
581 * get kerberos properties */
583 if (domain
->private_data
) {
584 ads
= (ADS_STRUCT
*)domain
->private_data
;
585 time_offset
= ads
->auth
.time_offset
;
590 * do kerberos auth and setup ccache as the user */
592 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
594 realm
= domain
->alt_name
;
597 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
598 if (principal_s
== NULL
) {
599 return NT_STATUS_NO_MEMORY
;
602 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
603 if (service
== NULL
) {
604 return NT_STATUS_NO_MEMORY
;
607 /* if this is a user ccache, we need to act as the user to let the krb5
608 * library handle the chown, etc. */
610 /************************ ENTERING NON-ROOT **********************/
612 if (!internal_ccache
) {
613 set_effective_uid(uid
);
614 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
617 result
= kerberos_return_info3_from_pac(state
->mem_ctx
,
619 state
->request
.data
.auth
.pass
,
626 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
628 if (!internal_ccache
) {
629 gain_root_privilege();
632 /************************ RETURNED TO ROOT **********************/
634 if (!NT_STATUS_IS_OK(result
)) {
638 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
641 /* if we had a user's ccache then return that string for the pam
644 if (!internal_ccache
) {
646 setup_return_cc_name(state
, cc
);
648 result
= add_ccache_to_list(principal_s
,
651 state
->request
.data
.auth
.user
,
659 if (!NT_STATUS_IS_OK(result
)) {
660 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
665 /* need to delete the memory cred cache, it is not used anymore */
667 krb5_ret
= ads_kdestroy(cc
);
669 DEBUG(3,("winbindd_raw_kerberos_login: "
670 "could not destroy krb5 credential cache: "
671 "%s\n", error_message(krb5_ret
)));
680 /* we could have created a new credential cache with a valid tgt in it
681 * but we werent able to get or verify the service ticket for this
682 * local host and therefor didn't get the PAC, we need to remove that
683 * cache entirely now */
685 krb5_ret
= ads_kdestroy(cc
);
687 DEBUG(3,("winbindd_raw_kerberos_login: "
688 "could not destroy krb5 credential cache: "
689 "%s\n", error_message(krb5_ret
)));
692 if (!NT_STATUS_IS_OK(remove_ccache(state
->request
.data
.auth
.user
))) {
693 DEBUG(3,("winbindd_raw_kerberos_login: "
694 "could not remove ccache for user %s\n",
695 state
->request
.data
.auth
.user
));
700 return NT_STATUS_NOT_SUPPORTED
;
701 #endif /* HAVE_KRB5 */
704 /****************************************************************
705 ****************************************************************/
707 static bool check_request_flags(uint32_t flags
)
709 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
710 WBFLAG_PAM_INFO3_TEXT
|
711 WBFLAG_PAM_INFO3_NDR
;
713 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
714 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
715 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
716 !(flags
& flags_edata
) ) {
720 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags
));
725 /****************************************************************
726 ****************************************************************/
728 static NTSTATUS
append_data(struct winbindd_cli_state
*state
,
729 struct netr_SamInfo3
*info3
,
730 const char *name_domain
,
731 const char *name_user
)
734 uint32_t flags
= state
->request
.flags
;
736 if (flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
737 memcpy(state
->response
.data
.auth
.user_session_key
,
739 sizeof(state
->response
.data
.auth
.user_session_key
)
743 if (flags
& WBFLAG_PAM_LMKEY
) {
744 memcpy(state
->response
.data
.auth
.first_8_lm_hash
,
745 info3
->base
.LMSessKey
.key
,
746 sizeof(state
->response
.data
.auth
.first_8_lm_hash
)
750 if (flags
& WBFLAG_PAM_INFO3_TEXT
) {
751 result
= append_info3_as_txt(state
->mem_ctx
, state
, info3
);
752 if (!NT_STATUS_IS_OK(result
)) {
753 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
759 /* currently, anything from here on potentially overwrites extra_data. */
761 if (flags
& WBFLAG_PAM_INFO3_NDR
) {
762 result
= append_info3_as_ndr(state
->mem_ctx
, state
, info3
);
763 if (!NT_STATUS_IS_OK(result
)) {
764 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
770 if (flags
& WBFLAG_PAM_UNIX_NAME
) {
771 result
= append_unix_username(state
->mem_ctx
, state
, info3
,
772 name_domain
, name_user
);
773 if (!NT_STATUS_IS_OK(result
)) {
774 DEBUG(10,("Failed to append Unix Username: %s\n",
780 if (flags
& WBFLAG_PAM_AFS_TOKEN
) {
781 result
= append_afs_token(state
->mem_ctx
, state
, info3
,
782 name_domain
, name_user
);
783 if (!NT_STATUS_IS_OK(result
)) {
784 DEBUG(10,("Failed to append AFS token: %s\n",
793 void winbindd_pam_auth(struct winbindd_cli_state
*state
)
795 struct winbindd_domain
*domain
;
796 fstring name_domain
, name_user
;
797 char *mapped_user
= NULL
;
799 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
801 /* Ensure null termination */
802 state
->request
.data
.auth
.user
803 [sizeof(state
->request
.data
.auth
.user
)-1]='\0';
805 /* Ensure null termination */
806 state
->request
.data
.auth
.pass
807 [sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
809 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state
->pid
,
810 state
->request
.data
.auth
.user
));
812 if (!check_request_flags(state
->request
.flags
)) {
813 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
817 /* Parse domain and username */
819 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
820 state
->request
.data
.auth
.user
,
823 /* If the name normalization didnt' actually do anything,
824 just use the original name */
826 if (!NT_STATUS_IS_OK(name_map_status
) &&
827 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
829 mapped_user
= state
->request
.data
.auth
.user
;
832 if (!canonicalize_username(mapped_user
, name_domain
, name_user
)) {
833 result
= NT_STATUS_NO_SUCH_USER
;
837 domain
= find_auth_domain(state
, name_domain
);
839 if (domain
== NULL
) {
840 result
= NT_STATUS_NO_SUCH_USER
;
844 sendto_domain(state
, domain
);
847 set_auth_errors(&state
->response
, result
);
848 DEBUG(5, ("Plain text authentication for %s returned %s "
850 state
->request
.data
.auth
.user
,
851 state
->response
.data
.auth
.nt_status_string
,
852 state
->response
.data
.auth
.pam_error
));
853 request_error(state
);
856 NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
857 struct winbindd_cli_state
*state
,
858 struct netr_SamInfo3
**info3
)
860 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
861 uint16 max_allowed_bad_attempts
;
862 fstring name_domain
, name_user
;
864 enum lsa_SidType type
;
865 uchar new_nt_pass
[NT_HASH_LEN
];
866 const uint8
*cached_nt_pass
;
867 const uint8
*cached_salt
;
868 struct netr_SamInfo3
*my_info3
;
869 time_t kickoff_time
, must_change_time
;
870 bool password_good
= false;
872 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
879 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
881 /* Parse domain and username */
883 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
886 if (!lookup_cached_name(state
->mem_ctx
,
891 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
892 return NT_STATUS_NO_SUCH_USER
;
895 if (type
!= SID_NAME_USER
) {
896 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
897 return NT_STATUS_LOGON_FAILURE
;
900 result
= winbindd_get_creds(domain
,
906 if (!NT_STATUS_IS_OK(result
)) {
907 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
913 E_md4hash(state
->request
.data
.auth
.pass
, new_nt_pass
);
915 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
916 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
918 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
922 /* In this case we didn't store the nt_hash itself,
923 but the MD5 combination of salt + nt_hash. */
924 uchar salted_hash
[NT_HASH_LEN
];
925 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
927 password_good
= (memcmp(cached_nt_pass
, salted_hash
, NT_HASH_LEN
) == 0) ?
930 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
931 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
, NT_HASH_LEN
) == 0) ?
937 /* User *DOES* know the password, update logon_time and reset
940 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
942 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
943 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
946 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
947 return NT_STATUS_ACCOUNT_DISABLED
;
950 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
951 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
954 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
955 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
958 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
959 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
962 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
963 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
964 my_info3
->base
.acct_flags
));
965 return NT_STATUS_LOGON_FAILURE
;
968 kickoff_time
= nt_time_to_unix(my_info3
->base
.acct_expiry
);
969 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
970 return NT_STATUS_ACCOUNT_EXPIRED
;
973 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
974 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
975 /* we allow grace logons when the password has expired */
976 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
977 /* return NT_STATUS_PASSWORD_EXPIRED; */
982 if ((state
->request
.flags
& WBFLAG_PAM_KRB5
) &&
983 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
984 (tdc_domain
->trust_type
& NETR_TRUST_TYPE_UPLEVEL
)) {
987 const char *cc
= NULL
;
989 const char *principal_s
= NULL
;
990 const char *service
= NULL
;
991 bool internal_ccache
= false;
993 uid
= get_uid_from_state(state
);
995 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
996 return NT_STATUS_INVALID_PARAMETER
;
999 cc
= generate_krb5_ccache(state
->mem_ctx
,
1000 state
->request
.data
.auth
.krb5_cc_type
,
1001 state
->request
.data
.auth
.uid
,
1004 return NT_STATUS_NO_MEMORY
;
1007 realm
= domain
->alt_name
;
1010 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
1011 if (principal_s
== NULL
) {
1012 return NT_STATUS_NO_MEMORY
;
1015 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
1016 if (service
== NULL
) {
1017 return NT_STATUS_NO_MEMORY
;
1020 if (!internal_ccache
) {
1022 setup_return_cc_name(state
, cc
);
1024 result
= add_ccache_to_list(principal_s
,
1027 state
->request
.data
.auth
.user
,
1031 time(NULL
) + lp_winbind_cache_time(),
1032 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
1035 if (!NT_STATUS_IS_OK(result
)) {
1036 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1037 "to add ccache to list: %s\n",
1038 nt_errstr(result
)));
1042 #endif /* HAVE_KRB5 */
1044 /* FIXME: we possibly should handle logon hours as well (does xp when
1045 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1047 unix_to_nt_time(&my_info3
->base
.last_logon
, time(NULL
));
1048 my_info3
->base
.bad_password_count
= 0;
1050 result
= winbindd_update_creds_by_info3(domain
,
1052 state
->request
.data
.auth
.user
,
1053 state
->request
.data
.auth
.pass
,
1055 if (!NT_STATUS_IS_OK(result
)) {
1056 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1057 nt_errstr(result
)));
1061 return NT_STATUS_OK
;
1065 /* User does *NOT* know the correct password, modify info3 accordingly */
1067 /* failure of this is not critical */
1068 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1069 if (!NT_STATUS_IS_OK(result
)) {
1070 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1071 "Won't be able to honour account lockout policies\n"));
1074 /* increase counter */
1075 my_info3
->base
.bad_password_count
++;
1077 if (max_allowed_bad_attempts
== 0) {
1082 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1084 uint32 password_properties
;
1086 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1087 if (!NT_STATUS_IS_OK(result
)) {
1088 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1091 if ((my_info3
->base
.rid
!= DOMAIN_USER_RID_ADMIN
) ||
1092 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1093 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1098 result
= winbindd_update_creds_by_info3(domain
,
1100 state
->request
.data
.auth
.user
,
1104 if (!NT_STATUS_IS_OK(result
)) {
1105 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1106 nt_errstr(result
)));
1109 return NT_STATUS_LOGON_FAILURE
;
1112 NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1113 struct winbindd_cli_state
*state
,
1114 struct netr_SamInfo3
**info3
)
1116 struct winbindd_domain
*contact_domain
;
1117 fstring name_domain
, name_user
;
1120 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1122 /* Parse domain and username */
1124 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
1126 /* what domain should we contact? */
1129 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1130 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1131 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
1132 result
= NT_STATUS_NO_SUCH_USER
;
1137 if (is_myname(name_domain
)) {
1138 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1139 result
= NT_STATUS_NO_SUCH_USER
;
1143 contact_domain
= find_domain_from_name(name_domain
);
1144 if (contact_domain
== NULL
) {
1145 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1146 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
1148 contact_domain
= find_our_domain();
1152 if (contact_domain
->initialized
&&
1153 contact_domain
->active_directory
) {
1157 if (!contact_domain
->initialized
) {
1158 init_dc_connection(contact_domain
);
1161 if (!contact_domain
->active_directory
) {
1162 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1163 return NT_STATUS_INVALID_LOGON_TYPE
;
1166 result
= winbindd_raw_kerberos_login(contact_domain
, state
, info3
);
1171 typedef NTSTATUS (*netlogon_fn_t
)(struct rpc_pipe_client
*cli
,
1172 TALLOC_CTX
*mem_ctx
,
1173 uint32 logon_parameters
,
1175 const char *username
,
1177 const char *workstation
,
1178 const uint8 chal
[8],
1179 DATA_BLOB lm_response
,
1180 DATA_BLOB nt_response
,
1181 struct netr_SamInfo3
**info3
);
1183 NTSTATUS
winbindd_dual_pam_auth_samlogon(struct winbindd_domain
*domain
,
1184 struct winbindd_cli_state
*state
,
1185 struct netr_SamInfo3
**info3
)
1188 struct rpc_pipe_client
*netlogon_pipe
;
1193 unsigned char local_lm_response
[24];
1194 unsigned char local_nt_response
[24];
1195 struct winbindd_domain
*contact_domain
;
1196 fstring name_domain
, name_user
;
1199 struct netr_SamInfo3
*my_info3
= NULL
;
1203 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1205 /* Parse domain and username */
1207 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
1209 /* do password magic */
1212 generate_random_buffer(chal
, 8);
1213 if (lp_client_ntlmv2_auth()) {
1214 DATA_BLOB server_chal
;
1215 DATA_BLOB names_blob
;
1216 DATA_BLOB nt_response
;
1217 DATA_BLOB lm_response
;
1218 server_chal
= data_blob_talloc(state
->mem_ctx
, chal
, 8);
1220 /* note that the 'workgroup' here is a best guess - we don't know
1221 the server's domain at this point. The 'server name' is also
1224 names_blob
= NTLMv2_generate_names_blob(state
->mem_ctx
, global_myname(), lp_workgroup());
1226 if (!SMBNTLMv2encrypt(NULL
, name_user
, name_domain
,
1227 state
->request
.data
.auth
.pass
,
1230 &lm_response
, &nt_response
, NULL
, NULL
)) {
1231 data_blob_free(&names_blob
);
1232 data_blob_free(&server_chal
);
1233 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1234 result
= NT_STATUS_NO_MEMORY
;
1237 data_blob_free(&names_blob
);
1238 data_blob_free(&server_chal
);
1239 lm_resp
= data_blob_talloc(state
->mem_ctx
, lm_response
.data
,
1240 lm_response
.length
);
1241 nt_resp
= data_blob_talloc(state
->mem_ctx
, nt_response
.data
,
1242 nt_response
.length
);
1243 data_blob_free(&lm_response
);
1244 data_blob_free(&nt_response
);
1247 if (lp_client_lanman_auth()
1248 && SMBencrypt(state
->request
.data
.auth
.pass
,
1250 local_lm_response
)) {
1251 lm_resp
= data_blob_talloc(state
->mem_ctx
,
1253 sizeof(local_lm_response
));
1255 lm_resp
= data_blob_null
;
1257 SMBNTencrypt(state
->request
.data
.auth
.pass
,
1261 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1263 sizeof(local_nt_response
));
1266 /* what domain should we contact? */
1269 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1270 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1271 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
1272 result
= NT_STATUS_NO_SUCH_USER
;
1277 if (is_myname(name_domain
)) {
1278 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1279 result
= NT_STATUS_NO_SUCH_USER
;
1283 contact_domain
= find_our_domain();
1286 /* check authentication loop */
1289 netlogon_fn_t logon_fn
;
1291 ZERO_STRUCTP(my_info3
);
1294 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1296 if (!NT_STATUS_IS_OK(result
)) {
1297 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1301 /* It is really important to try SamLogonEx here,
1302 * because in a clustered environment, we want to use
1303 * one machine account from multiple physical
1306 * With a normal SamLogon call, we must keep the
1307 * credentials chain updated and intact between all
1308 * users of the machine account (which would imply
1309 * cross-node communication for every NTLM logon).
1311 * (The credentials chain is not per NETLOGON pipe
1312 * connection, but globally on the server/client pair
1315 * When using SamLogonEx, the credentials are not
1316 * supplied, but the session key is implied by the
1317 * wrapping SamLogon context.
1319 * -- abartlet 21 April 2008
1322 logon_fn
= contact_domain
->can_do_samlogon_ex
1323 ? rpccli_netlogon_sam_network_logon_ex
1324 : rpccli_netlogon_sam_network_logon
;
1326 result
= logon_fn(netlogon_pipe
,
1329 contact_domain
->dcname
, /* server name */
1330 name_user
, /* user name */
1331 name_domain
, /* target domain */
1332 global_myname(), /* workstation */
1339 if ((NT_STATUS_V(result
) == DCERPC_FAULT_OP_RNG_ERROR
)
1340 && contact_domain
->can_do_samlogon_ex
) {
1341 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1342 "retrying with NetSamLogon\n"));
1343 contact_domain
->can_do_samlogon_ex
= false;
1348 /* We have to try a second time as cm_connect_netlogon
1349 might not yet have noticed that the DC has killed
1352 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1357 /* if we get access denied, a possible cause was that we had
1358 and open connection to the DC, but someone changed our
1359 machine account password out from underneath us using 'net
1360 rpc changetrustpw' */
1362 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1363 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1364 "ACCESS_DENIED. Maybe the trust account "
1365 "password was changed and we didn't know it. "
1366 "Killing connections to domain %s\n",
1368 invalidate_cm_connection(&contact_domain
->conn
);
1372 } while ( (attempts
< 2) && retry
);
1374 /* handle the case where a NT4 DC does not fill in the acct_flags in
1375 * the samlogon reply info3. When accurate info3 is required by the
1376 * caller, we look up the account flags ourselve - gd */
1378 if ((state
->request
.flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1379 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1381 struct rpc_pipe_client
*samr_pipe
;
1382 struct policy_handle samr_domain_handle
, user_pol
;
1383 union samr_UserInfo
*info
= NULL
;
1384 NTSTATUS status_tmp
;
1387 status_tmp
= cm_connect_sam(contact_domain
, state
->mem_ctx
,
1388 &samr_pipe
, &samr_domain_handle
);
1390 if (!NT_STATUS_IS_OK(status_tmp
)) {
1391 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1392 nt_errstr(status_tmp
)));
1396 status_tmp
= rpccli_samr_OpenUser(samr_pipe
, state
->mem_ctx
,
1397 &samr_domain_handle
,
1398 MAXIMUM_ALLOWED_ACCESS
,
1402 if (!NT_STATUS_IS_OK(status_tmp
)) {
1403 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1404 nt_errstr(status_tmp
)));
1408 status_tmp
= rpccli_samr_QueryUserInfo(samr_pipe
, state
->mem_ctx
,
1413 if (!NT_STATUS_IS_OK(status_tmp
)) {
1414 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1415 nt_errstr(status_tmp
)));
1416 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1420 acct_flags
= info
->info16
.acct_flags
;
1422 if (acct_flags
== 0) {
1423 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1427 my_info3
->base
.acct_flags
= acct_flags
;
1429 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1431 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1439 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1440 struct winbindd_cli_state
*state
)
1442 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1443 NTSTATUS krb5_result
= NT_STATUS_OK
;
1444 fstring name_domain
, name_user
;
1446 fstring domain_user
;
1447 struct netr_SamInfo3
*info3
= NULL
;
1448 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1450 /* Ensure null termination */
1451 state
->request
.data
.auth
.user
[sizeof(state
->request
.data
.auth
.user
)-1]='\0';
1453 /* Ensure null termination */
1454 state
->request
.data
.auth
.pass
[sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
1456 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1457 state
->request
.data
.auth
.user
));
1459 if (!check_request_flags(state
->request
.flags
)) {
1460 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1464 /* Parse domain and username */
1466 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1467 state
->request
.data
.auth
.user
,
1470 /* If the name normalization didnt' actually do anything,
1471 just use the original name */
1473 if (!NT_STATUS_IS_OK(name_map_status
) &&
1474 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1476 mapped_user
= state
->request
.data
.auth
.user
;
1479 parse_domain_user(mapped_user
, name_domain
, name_user
);
1481 if ( mapped_user
!= state
->request
.data
.auth
.user
) {
1482 fstr_sprintf( domain_user
, "%s\\%s", name_domain
, name_user
);
1483 safe_strcpy( state
->request
.data
.auth
.user
, domain_user
,
1484 sizeof(state
->request
.data
.auth
.user
)-1 );
1487 if (domain
->online
== false) {
1488 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1489 if (domain
->startup
) {
1490 /* Logons are very important to users. If we're offline and
1491 we get a request within the first 30 seconds of startup,
1492 try very hard to find a DC and go online. */
1494 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1495 "request in startup mode.\n", domain
->name
));
1497 winbindd_flush_negative_conn_cache(domain
);
1498 result
= init_dc_connection(domain
);
1502 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1504 /* Check for Kerberos authentication */
1505 if (domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1507 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1508 /* save for later */
1509 krb5_result
= result
;
1512 if (NT_STATUS_IS_OK(result
)) {
1513 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1514 goto process_result
;
1516 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1519 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1520 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1521 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1522 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1523 set_domain_offline( domain
);
1527 /* there are quite some NT_STATUS errors where there is no
1528 * point in retrying with a samlogon, we explictly have to take
1529 * care not to increase the bad logon counter on the DC */
1531 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1532 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1533 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1534 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1535 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1536 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1537 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1538 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1539 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1540 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1541 goto process_result
;
1544 if (state
->request
.flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1545 DEBUG(3,("falling back to samlogon\n"));
1553 /* Check for Samlogon authentication */
1554 if (domain
->online
) {
1555 result
= winbindd_dual_pam_auth_samlogon(domain
, state
, &info3
);
1557 if (NT_STATUS_IS_OK(result
)) {
1558 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1559 /* add the Krb5 err if we have one */
1560 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1561 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1563 goto process_result
;
1566 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1567 nt_errstr(result
)));
1569 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1570 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1571 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1573 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1574 set_domain_offline( domain
);
1578 if (domain
->online
) {
1579 /* We're still online - fail. */
1585 /* Check for Cached logons */
1586 if (!domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1587 lp_winbind_offline_logon()) {
1589 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1591 if (NT_STATUS_IS_OK(result
)) {
1592 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1593 goto process_result
;
1595 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1602 if (NT_STATUS_IS_OK(result
)) {
1606 /* In all codepaths where result == NT_STATUS_OK info3 must have
1607 been initialized. */
1609 result
= NT_STATUS_INTERNAL_ERROR
;
1613 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1614 netsamlogon_cache_store(name_user
, info3
);
1616 /* save name_to_sid info as early as possible (only if
1617 this is our primary domain so we don't invalidate
1618 the cache entry by storing the seq_num for the wrong
1620 if ( domain
->primary
) {
1621 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1623 cache_name2sid(domain
, name_domain
, name_user
,
1624 SID_NAME_USER
, &user_sid
);
1627 /* Check if the user is in the right group */
1629 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1630 state
->request
.data
.auth
.require_membership_of_sid
))) {
1631 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1632 state
->request
.data
.auth
.user
,
1633 state
->request
.data
.auth
.require_membership_of_sid
));
1637 result
= append_data(state
, info3
, name_domain
, name_user
);
1638 if (!NT_STATUS_IS_OK(result
)) {
1642 if ((state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
1644 /* Store in-memory creds for single-signon using ntlm_auth. */
1645 result
= winbindd_add_memory_creds(state
->request
.data
.auth
.user
,
1646 get_uid_from_state(state
),
1647 state
->request
.data
.auth
.pass
);
1649 if (!NT_STATUS_IS_OK(result
)) {
1650 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result
)));
1654 if (lp_winbind_offline_logon()) {
1655 result
= winbindd_store_creds(domain
,
1657 state
->request
.data
.auth
.user
,
1658 state
->request
.data
.auth
.pass
,
1660 if (!NT_STATUS_IS_OK(result
)) {
1662 /* Release refcount. */
1663 winbindd_delete_memory_creds(state
->request
.data
.auth
.user
);
1665 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
1672 if (state
->request
.flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1673 struct winbindd_domain
*our_domain
= find_our_domain();
1675 /* This is not entirely correct I believe, but it is
1676 consistent. Only apply the password policy settings
1677 too warn users for our own domain. Cannot obtain these
1678 from trusted DCs all the time so don't do it at all.
1681 result
= NT_STATUS_NOT_SUPPORTED
;
1682 if (our_domain
== domain
) {
1683 result
= fillup_password_policy(our_domain
, state
);
1686 if (!NT_STATUS_IS_OK(result
)
1687 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1689 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1690 domain
->name
, nt_errstr(result
)));
1695 result
= NT_STATUS_OK
;
1699 /* give us a more useful (more correct?) error code */
1700 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1701 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1702 result
= NT_STATUS_NO_LOGON_SERVERS
;
1705 set_auth_errors(&state
->response
, result
);
1707 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1708 state
->request
.data
.auth
.user
,
1709 state
->response
.data
.auth
.nt_status_string
,
1710 state
->response
.data
.auth
.pam_error
));
1712 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1716 /**********************************************************************
1717 Challenge Response Authentication Protocol
1718 **********************************************************************/
1720 void winbindd_pam_auth_crap(struct winbindd_cli_state
*state
)
1722 struct winbindd_domain
*domain
= NULL
;
1723 const char *domain_name
= NULL
;
1726 if (!check_request_flags(state
->request
.flags
)) {
1727 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1731 if (!state
->privileged
) {
1732 char *error_string
= NULL
;
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 error_string
= talloc_asprintf(state
->mem_ctx
,
1740 "winbind client not authorized "
1741 "to use winbindd_pam_auth_crap."
1742 " Ensure permissions on %s "
1743 "are set correctly.",
1744 get_winbind_priv_pipe_dir());
1745 fstrcpy(state
->response
.data
.auth
.error_string
, error_string
);
1746 result
= NT_STATUS_ACCESS_DENIED
;
1750 /* Ensure null termination */
1751 state
->request
.data
.auth_crap
.user
1752 [sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1753 state
->request
.data
.auth_crap
.domain
1754 [sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1756 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1757 (unsigned long)state
->pid
,
1758 state
->request
.data
.auth_crap
.domain
,
1759 state
->request
.data
.auth_crap
.user
));
1761 if (*state
->request
.data
.auth_crap
.domain
!= '\0') {
1762 domain_name
= state
->request
.data
.auth_crap
.domain
;
1763 } else if (lp_winbind_use_default_domain()) {
1764 domain_name
= lp_workgroup();
1767 if (domain_name
!= NULL
)
1768 domain
= find_auth_domain(state
, domain_name
);
1770 if (domain
!= NULL
) {
1771 sendto_domain(state
, domain
);
1775 result
= NT_STATUS_NO_SUCH_USER
;
1778 set_auth_errors(&state
->response
, result
);
1779 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1780 state
->request
.data
.auth_crap
.domain
,
1781 state
->request
.data
.auth_crap
.user
,
1782 state
->response
.data
.auth
.nt_status_string
,
1783 state
->response
.data
.auth
.pam_error
));
1784 request_error(state
);
1789 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1790 struct winbindd_cli_state
*state
)
1793 struct netr_SamInfo3
*info3
= NULL
;
1794 struct rpc_pipe_client
*netlogon_pipe
;
1795 const char *name_user
= NULL
;
1796 const char *name_domain
= NULL
;
1797 const char *workstation
;
1798 struct winbindd_domain
*contact_domain
;
1802 DATA_BLOB lm_resp
, nt_resp
;
1804 /* This is child-only, so no check for privileged access is needed
1807 /* Ensure null termination */
1808 state
->request
.data
.auth_crap
.user
[sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1809 state
->request
.data
.auth_crap
.domain
[sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1811 if (!check_request_flags(state
->request
.flags
)) {
1812 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1816 name_user
= state
->request
.data
.auth_crap
.user
;
1818 if (*state
->request
.data
.auth_crap
.domain
) {
1819 name_domain
= state
->request
.data
.auth_crap
.domain
;
1820 } else if (lp_winbind_use_default_domain()) {
1821 name_domain
= lp_workgroup();
1823 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1825 result
= NT_STATUS_NO_SUCH_USER
;
1829 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1830 name_domain
, name_user
));
1832 if (*state
->request
.data
.auth_crap
.workstation
) {
1833 workstation
= state
->request
.data
.auth_crap
.workstation
;
1835 workstation
= global_myname();
1838 if (state
->request
.data
.auth_crap
.lm_resp_len
> sizeof(state
->request
.data
.auth_crap
.lm_resp
)
1839 || state
->request
.data
.auth_crap
.nt_resp_len
> sizeof(state
->request
.data
.auth_crap
.nt_resp
)) {
1840 if (!(state
->request
.flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1841 state
->request
.extra_len
!= state
->request
.data
.auth_crap
.nt_resp_len
) {
1842 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1843 state
->request
.data
.auth_crap
.lm_resp_len
,
1844 state
->request
.data
.auth_crap
.nt_resp_len
));
1845 result
= NT_STATUS_INVALID_PARAMETER
;
1850 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.lm_resp
,
1851 state
->request
.data
.auth_crap
.lm_resp_len
);
1853 if (state
->request
.flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1854 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1855 state
->request
.extra_data
.data
,
1856 state
->request
.data
.auth_crap
.nt_resp_len
);
1858 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1859 state
->request
.data
.auth_crap
.nt_resp
,
1860 state
->request
.data
.auth_crap
.nt_resp_len
);
1863 /* what domain should we contact? */
1866 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1867 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1868 state
->request
.data
.auth_crap
.user
, name_domain
, name_user
, name_domain
));
1869 result
= NT_STATUS_NO_SUCH_USER
;
1873 if (is_myname(name_domain
)) {
1874 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1875 result
= NT_STATUS_NO_SUCH_USER
;
1878 contact_domain
= find_our_domain();
1882 netlogon_fn_t logon_fn
;
1886 netlogon_pipe
= NULL
;
1887 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1889 if (!NT_STATUS_IS_OK(result
)) {
1890 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1891 nt_errstr(result
)));
1895 logon_fn
= contact_domain
->can_do_samlogon_ex
1896 ? rpccli_netlogon_sam_network_logon_ex
1897 : rpccli_netlogon_sam_network_logon
;
1899 result
= logon_fn(netlogon_pipe
,
1901 state
->request
.data
.auth_crap
.logon_parameters
,
1902 contact_domain
->dcname
,
1905 /* Bug #3248 - found by Stefan Burkei. */
1906 workstation
, /* We carefully set this above so use it... */
1907 state
->request
.data
.auth_crap
.chal
,
1912 if ((NT_STATUS_V(result
) == DCERPC_FAULT_OP_RNG_ERROR
)
1913 && contact_domain
->can_do_samlogon_ex
) {
1914 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1915 "retrying with NetSamLogon\n"));
1916 contact_domain
->can_do_samlogon_ex
= false;
1923 /* We have to try a second time as cm_connect_netlogon
1924 might not yet have noticed that the DC has killed
1927 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1932 /* if we get access denied, a possible cause was that we had and open
1933 connection to the DC, but someone changed our machine account password
1934 out from underneath us using 'net rpc changetrustpw' */
1936 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1937 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1938 "ACCESS_DENIED. Maybe the trust account "
1939 "password was changed and we didn't know it. "
1940 "Killing connections to domain %s\n",
1942 invalidate_cm_connection(&contact_domain
->conn
);
1946 } while ( (attempts
< 2) && retry
);
1948 if (NT_STATUS_IS_OK(result
)) {
1950 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1951 netsamlogon_cache_store(name_user
, info3
);
1953 /* Check if the user is in the right group */
1955 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1956 state
->request
.data
.auth_crap
.require_membership_of_sid
))) {
1957 DEBUG(3, ("User %s is not in the required group (%s), so "
1958 "crap authentication is rejected\n",
1959 state
->request
.data
.auth_crap
.user
,
1960 state
->request
.data
.auth_crap
.require_membership_of_sid
));
1964 result
= append_data(state
, info3
, name_domain
, name_user
);
1965 if (!NT_STATUS_IS_OK(result
)) {
1972 /* give us a more useful (more correct?) error code */
1973 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1974 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1975 result
= NT_STATUS_NO_LOGON_SERVERS
;
1978 if (state
->request
.flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1979 result
= nt_status_squash(result
);
1982 set_auth_errors(&state
->response
, result
);
1984 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1985 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1988 state
->response
.data
.auth
.nt_status_string
,
1989 state
->response
.data
.auth
.pam_error
));
1991 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1994 /* Change a user password */
1996 void winbindd_pam_chauthtok(struct winbindd_cli_state
*state
)
1998 fstring domain
, user
;
2000 struct winbindd_domain
*contact_domain
;
2001 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
2003 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state
->pid
,
2004 state
->request
.data
.chauthtok
.user
));
2008 nt_status
= normalize_name_unmap(state
->mem_ctx
,
2009 state
->request
.data
.chauthtok
.user
,
2012 /* Update the chauthtok name if we did any mapping */
2014 if (NT_STATUS_IS_OK(nt_status
) ||
2015 NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
))
2017 fstrcpy(state
->request
.data
.chauthtok
.user
, mapped_user
);
2020 /* Must pass in state->...chauthtok.user because
2021 canonicalize_username() assumes an fstring(). Since
2022 we have already copied it (if necessary), this is ok. */
2024 if (!canonicalize_username(state
->request
.data
.chauthtok
.user
, domain
, user
)) {
2025 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
2026 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2028 state
->request
.data
.auth
.user
,
2029 state
->response
.data
.auth
.nt_status_string
,
2030 state
->response
.data
.auth
.pam_error
));
2031 request_error(state
);
2035 contact_domain
= find_domain_from_name(domain
);
2036 if (!contact_domain
) {
2037 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
2038 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2039 state
->request
.data
.chauthtok
.user
, domain
, user
, domain
));
2040 request_error(state
);
2044 sendto_domain(state
, contact_domain
);
2047 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
2048 struct winbindd_cli_state
*state
)
2051 char *newpass
= NULL
;
2052 struct policy_handle dom_pol
;
2053 struct rpc_pipe_client
*cli
;
2054 bool got_info
= false;
2055 struct samr_DomInfo1
*info
= NULL
;
2056 struct samr_ChangeReject
*reject
= NULL
;
2057 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
2058 fstring domain
, user
;
2060 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
2061 state
->request
.data
.auth
.user
));
2063 if (!parse_domain_user(state
->request
.data
.chauthtok
.user
, domain
, user
)) {
2067 /* Change password */
2069 oldpass
= state
->request
.data
.chauthtok
.oldpass
;
2070 newpass
= state
->request
.data
.chauthtok
.newpass
;
2072 /* Initialize reject reason */
2073 state
->response
.data
.auth
.reject_reason
= Undefined
;
2075 /* Get sam handle */
2077 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
2079 if (!NT_STATUS_IS_OK(result
)) {
2080 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2084 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
2091 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2093 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
2095 fill_in_password_policy(&state
->response
, info
);
2097 state
->response
.data
.auth
.reject_reason
=
2103 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2104 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2105 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2106 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2108 /* only fallback when the chgpasswd_user3 call is not supported */
2109 if ((NT_STATUS_EQUAL(result
, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR
))) ||
2110 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
)) ||
2111 (NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
)) ||
2112 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
))) {
2114 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2115 nt_errstr(result
)));
2117 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2119 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2120 Map to the same status code as Windows 2003. */
2122 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2123 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2129 if (NT_STATUS_IS_OK(result
) && (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
2131 /* Update the single sign-on memory creds. */
2132 result
= winbindd_replace_memory_creds(state
->request
.data
.chauthtok
.user
,
2135 /* When we login from gdm or xdm and password expires,
2136 * we change password, but there are no memory crendentials
2137 * So, winbindd_replace_memory_creds() returns
2138 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2141 if (NT_STATUS_EQUAL(result
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
2142 result
= NT_STATUS_OK
;
2145 if (!NT_STATUS_IS_OK(result
)) {
2146 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result
)));
2147 goto process_result
;
2150 if (lp_winbind_offline_logon()) {
2151 result
= winbindd_update_creds_by_name(contact_domain
,
2152 state
->mem_ctx
, user
,
2154 /* Again, this happens when we login from gdm or xdm
2155 * and the password expires, *BUT* cached crendentials
2156 * doesn't exist. winbindd_update_creds_by_name()
2157 * returns NT_STATUS_NO_SUCH_USER.
2158 * This is not a failure.
2161 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2162 result
= NT_STATUS_OK
;
2165 if (!NT_STATUS_IS_OK(result
)) {
2166 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
2167 goto process_result
;
2172 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2174 NTSTATUS policy_ret
;
2176 policy_ret
= fillup_password_policy(contact_domain
, state
);
2178 /* failure of this is non critical, it will just provide no
2179 * additional information to the client why the change has
2180 * failed - Guenther */
2182 if (!NT_STATUS_IS_OK(policy_ret
)) {
2183 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2184 goto process_result
;
2190 set_auth_errors(&state
->response
, result
);
2192 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2193 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2196 state
->response
.data
.auth
.nt_status_string
,
2197 state
->response
.data
.auth
.pam_error
));
2199 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2202 void winbindd_pam_logoff(struct winbindd_cli_state
*state
)
2204 struct winbindd_domain
*domain
;
2205 fstring name_domain
, user
;
2206 uid_t caller_uid
= (uid_t
)-1;
2207 uid_t request_uid
= state
->request
.data
.logoff
.uid
;
2209 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state
->pid
,
2210 state
->request
.data
.logoff
.user
));
2212 /* Ensure null termination */
2213 state
->request
.data
.logoff
.user
2214 [sizeof(state
->request
.data
.logoff
.user
)-1]='\0';
2216 state
->request
.data
.logoff
.krb5ccname
2217 [sizeof(state
->request
.data
.logoff
.krb5ccname
)-1]='\0';
2219 if (request_uid
== (gid_t
)-1) {
2223 if (!canonicalize_username(state
->request
.data
.logoff
.user
, name_domain
, user
)) {
2227 if ((domain
= find_auth_domain(state
, name_domain
)) == NULL
) {
2231 if ((sys_getpeereid(state
->sock
, &caller_uid
)) != 0) {
2232 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2237 switch (caller_uid
) {
2241 /* root must be able to logoff any user - gd */
2242 state
->request
.data
.logoff
.uid
= request_uid
;
2245 if (caller_uid
!= request_uid
) {
2246 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2249 state
->request
.data
.logoff
.uid
= caller_uid
;
2253 sendto_domain(state
, domain
);
2257 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
2258 DEBUG(5, ("Pam Logoff for %s returned %s "
2260 state
->request
.data
.logoff
.user
,
2261 state
->response
.data
.auth
.nt_status_string
,
2262 state
->response
.data
.auth
.pam_error
));
2263 request_error(state
);
2267 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2268 struct winbindd_cli_state
*state
)
2270 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2272 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2273 state
->request
.data
.logoff
.user
));
2275 if (!(state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
2276 result
= NT_STATUS_OK
;
2277 goto process_result
;
2280 if (state
->request
.data
.logoff
.krb5ccname
[0] == '\0') {
2281 result
= NT_STATUS_OK
;
2282 goto process_result
;
2287 if (state
->request
.data
.logoff
.uid
< 0) {
2288 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2289 goto process_result
;
2292 /* what we need here is to find the corresponding krb5 ccache name *we*
2293 * created for a given username and destroy it */
2295 if (!ccache_entry_exists(state
->request
.data
.logoff
.user
)) {
2296 result
= NT_STATUS_OK
;
2297 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2298 goto process_result
;
2301 if (!ccache_entry_identical(state
->request
.data
.logoff
.user
,
2302 state
->request
.data
.logoff
.uid
,
2303 state
->request
.data
.logoff
.krb5ccname
)) {
2304 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2305 goto process_result
;
2308 result
= remove_ccache(state
->request
.data
.logoff
.user
);
2309 if (!NT_STATUS_IS_OK(result
)) {
2310 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2311 nt_errstr(result
)));
2312 goto process_result
;
2316 result
= NT_STATUS_NOT_SUPPORTED
;
2321 winbindd_delete_memory_creds(state
->request
.data
.logoff
.user
);
2323 set_auth_errors(&state
->response
, result
);
2325 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2328 /* Change user password with auth crap*/
2330 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state
*state
)
2332 struct winbindd_domain
*domain
= NULL
;
2333 const char *domain_name
= NULL
;
2335 /* Ensure null termination */
2336 state
->request
.data
.chng_pswd_auth_crap
.user
[
2337 sizeof(state
->request
.data
.chng_pswd_auth_crap
.user
)-1]=0;
2338 state
->request
.data
.chng_pswd_auth_crap
.domain
[
2339 sizeof(state
->request
.data
.chng_pswd_auth_crap
.domain
)-1]=0;
2341 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2342 (unsigned long)state
->pid
,
2343 state
->request
.data
.chng_pswd_auth_crap
.domain
,
2344 state
->request
.data
.chng_pswd_auth_crap
.user
));
2346 if (*state
->request
.data
.chng_pswd_auth_crap
.domain
!= '\0') {
2347 domain_name
= state
->request
.data
.chng_pswd_auth_crap
.domain
;
2348 } else if (lp_winbind_use_default_domain()) {
2349 domain_name
= lp_workgroup();
2352 if (domain_name
!= NULL
)
2353 domain
= find_domain_from_name(domain_name
);
2355 if (domain
!= NULL
) {
2356 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2357 "%s\n", (unsigned long)state
->pid
,domain
->name
));
2358 sendto_domain(state
, domain
);
2362 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
2363 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2364 state
->request
.data
.chng_pswd_auth_crap
.domain
,
2365 state
->request
.data
.chng_pswd_auth_crap
.user
,
2366 state
->response
.data
.auth
.nt_status_string
,
2367 state
->response
.data
.auth
.pam_error
));
2368 request_error(state
);
2372 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2375 DATA_BLOB new_nt_password
;
2376 DATA_BLOB old_nt_hash_enc
;
2377 DATA_BLOB new_lm_password
;
2378 DATA_BLOB old_lm_hash_enc
;
2379 fstring domain
,user
;
2380 struct policy_handle dom_pol
;
2381 struct winbindd_domain
*contact_domain
= domainSt
;
2382 struct rpc_pipe_client
*cli
;
2384 /* Ensure null termination */
2385 state
->request
.data
.chng_pswd_auth_crap
.user
[
2386 sizeof(state
->request
.data
.chng_pswd_auth_crap
.user
)-1]=0;
2387 state
->request
.data
.chng_pswd_auth_crap
.domain
[
2388 sizeof(state
->request
.data
.chng_pswd_auth_crap
.domain
)-1]=0;
2392 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2393 (unsigned long)state
->pid
,
2394 state
->request
.data
.chng_pswd_auth_crap
.domain
,
2395 state
->request
.data
.chng_pswd_auth_crap
.user
));
2397 if (lp_winbind_offline_logon()) {
2398 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2399 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2400 result
= NT_STATUS_ACCESS_DENIED
;
2404 if (*state
->request
.data
.chng_pswd_auth_crap
.domain
) {
2405 fstrcpy(domain
,state
->request
.data
.chng_pswd_auth_crap
.domain
);
2407 parse_domain_user(state
->request
.data
.chng_pswd_auth_crap
.user
,
2411 DEBUG(3,("no domain specified with username (%s) - "
2413 state
->request
.data
.chng_pswd_auth_crap
.user
));
2414 result
= NT_STATUS_NO_SUCH_USER
;
2419 if (!*domain
&& lp_winbind_use_default_domain()) {
2420 fstrcpy(domain
,(char *)lp_workgroup());
2424 fstrcpy(user
, state
->request
.data
.chng_pswd_auth_crap
.user
);
2427 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2428 (unsigned long)state
->pid
, domain
, user
));
2430 /* Change password */
2431 new_nt_password
= data_blob_talloc(
2433 state
->request
.data
.chng_pswd_auth_crap
.new_nt_pswd
,
2434 state
->request
.data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2436 old_nt_hash_enc
= data_blob_talloc(
2438 state
->request
.data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2439 state
->request
.data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2441 if(state
->request
.data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2442 new_lm_password
= data_blob_talloc(
2444 state
->request
.data
.chng_pswd_auth_crap
.new_lm_pswd
,
2445 state
->request
.data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2447 old_lm_hash_enc
= data_blob_talloc(
2449 state
->request
.data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2450 state
->request
.data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2452 new_lm_password
.length
= 0;
2453 old_lm_hash_enc
.length
= 0;
2456 /* Get sam handle */
2458 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2459 if (!NT_STATUS_IS_OK(result
)) {
2460 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2464 result
= rpccli_samr_chng_pswd_auth_crap(
2465 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2466 new_lm_password
, old_lm_hash_enc
);
2470 set_auth_errors(&state
->response
, result
);
2472 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2473 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2475 state
->response
.data
.auth
.nt_status_string
,
2476 state
->response
.data
.auth
.pam_error
));
2478 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;