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"
28 #include "../librpc/gen_ndr/cli_samr.h"
32 #define DBGC_CLASS DBGC_WINBIND
34 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
36 static NTSTATUS
append_info3_as_txt(TALLOC_CTX
*mem_ctx
,
37 struct winbindd_cli_state
*state
,
38 struct netr_SamInfo3
*info3
)
43 state
->response
->data
.auth
.info3
.logon_time
=
44 nt_time_to_unix(info3
->base
.last_logon
);
45 state
->response
->data
.auth
.info3
.logoff_time
=
46 nt_time_to_unix(info3
->base
.last_logoff
);
47 state
->response
->data
.auth
.info3
.kickoff_time
=
48 nt_time_to_unix(info3
->base
.acct_expiry
);
49 state
->response
->data
.auth
.info3
.pass_last_set_time
=
50 nt_time_to_unix(info3
->base
.last_password_change
);
51 state
->response
->data
.auth
.info3
.pass_can_change_time
=
52 nt_time_to_unix(info3
->base
.allow_password_change
);
53 state
->response
->data
.auth
.info3
.pass_must_change_time
=
54 nt_time_to_unix(info3
->base
.force_password_change
);
56 state
->response
->data
.auth
.info3
.logon_count
= info3
->base
.logon_count
;
57 state
->response
->data
.auth
.info3
.bad_pw_count
= info3
->base
.bad_password_count
;
59 state
->response
->data
.auth
.info3
.user_rid
= info3
->base
.rid
;
60 state
->response
->data
.auth
.info3
.group_rid
= info3
->base
.primary_gid
;
61 sid_to_fstring(state
->response
->data
.auth
.info3
.dom_sid
, info3
->base
.domain_sid
);
63 state
->response
->data
.auth
.info3
.num_groups
= info3
->base
.groups
.count
;
64 state
->response
->data
.auth
.info3
.user_flgs
= info3
->base
.user_flags
;
66 state
->response
->data
.auth
.info3
.acct_flags
= info3
->base
.acct_flags
;
67 state
->response
->data
.auth
.info3
.num_other_sids
= info3
->sidcount
;
69 fstrcpy(state
->response
->data
.auth
.info3
.user_name
,
70 info3
->base
.account_name
.string
);
71 fstrcpy(state
->response
->data
.auth
.info3
.full_name
,
72 info3
->base
.full_name
.string
);
73 fstrcpy(state
->response
->data
.auth
.info3
.logon_script
,
74 info3
->base
.logon_script
.string
);
75 fstrcpy(state
->response
->data
.auth
.info3
.profile_path
,
76 info3
->base
.profile_path
.string
);
77 fstrcpy(state
->response
->data
.auth
.info3
.home_dir
,
78 info3
->base
.home_directory
.string
);
79 fstrcpy(state
->response
->data
.auth
.info3
.dir_drive
,
80 info3
->base
.home_drive
.string
);
82 fstrcpy(state
->response
->data
.auth
.info3
.logon_srv
,
83 info3
->base
.logon_server
.string
);
84 fstrcpy(state
->response
->data
.auth
.info3
.logon_dom
,
85 info3
->base
.domain
.string
);
87 ex
= talloc_strdup(state
->mem_ctx
, "");
88 NT_STATUS_HAVE_NO_MEMORY(ex
);
90 for (i
=0; i
< info3
->base
.groups
.count
; i
++) {
91 ex
= talloc_asprintf_append_buffer(ex
, "0x%08X:0x%08X\n",
92 info3
->base
.groups
.rids
[i
].rid
,
93 info3
->base
.groups
.rids
[i
].attributes
);
94 NT_STATUS_HAVE_NO_MEMORY(ex
);
97 for (i
=0; i
< info3
->sidcount
; i
++) {
100 sid
= dom_sid_string(mem_ctx
, info3
->sids
[i
].sid
);
101 NT_STATUS_HAVE_NO_MEMORY(sid
);
103 ex
= talloc_asprintf_append_buffer(ex
, "%s:0x%08X\n",
105 info3
->sids
[i
].attributes
);
106 NT_STATUS_HAVE_NO_MEMORY(ex
);
111 state
->response
->extra_data
.data
= ex
;
112 state
->response
->length
+= talloc_get_size(ex
);
117 static NTSTATUS
append_info3_as_ndr(TALLOC_CTX
*mem_ctx
,
118 struct winbindd_cli_state
*state
,
119 struct netr_SamInfo3
*info3
)
122 enum ndr_err_code ndr_err
;
124 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, NULL
, info3
,
125 (ndr_push_flags_fn_t
)ndr_push_netr_SamInfo3
);
126 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
127 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
128 return ndr_map_error2ntstatus(ndr_err
);
131 state
->response
->extra_data
.data
= blob
.data
;
132 state
->response
->length
+= blob
.length
;
137 static NTSTATUS
append_unix_username(TALLOC_CTX
*mem_ctx
,
138 struct winbindd_cli_state
*state
,
139 const struct netr_SamInfo3
*info3
,
140 const char *name_domain
,
141 const char *name_user
)
143 /* We've been asked to return the unix username, per
144 'winbind use default domain' settings and the like */
146 const char *nt_username
, *nt_domain
;
148 nt_domain
= talloc_strdup(mem_ctx
, info3
->base
.domain
.string
);
150 /* If the server didn't give us one, just use the one
152 nt_domain
= name_domain
;
155 nt_username
= talloc_strdup(mem_ctx
, info3
->base
.account_name
.string
);
157 /* If the server didn't give us one, just use the one
159 nt_username
= name_user
;
162 fill_domain_username(state
->response
->data
.auth
.unix_username
,
163 nt_domain
, nt_username
, true);
165 DEBUG(5,("Setting unix username to [%s]\n",
166 state
->response
->data
.auth
.unix_username
));
171 static NTSTATUS
append_afs_token(TALLOC_CTX
*mem_ctx
,
172 struct winbindd_cli_state
*state
,
173 const struct netr_SamInfo3
*info3
,
174 const char *name_domain
,
175 const char *name_user
)
177 char *afsname
= NULL
;
181 afsname
= talloc_strdup(mem_ctx
, lp_afs_username_map());
182 if (afsname
== NULL
) {
183 return NT_STATUS_NO_MEMORY
;
186 afsname
= talloc_string_sub(mem_ctx
,
187 lp_afs_username_map(),
189 afsname
= talloc_string_sub(mem_ctx
, afsname
,
191 afsname
= talloc_string_sub(mem_ctx
, afsname
,
198 sid_compose(&user_sid
, info3
->base
.domain_sid
,
200 sid_to_fstring(sidstr
, &user_sid
);
201 afsname
= talloc_string_sub(mem_ctx
, afsname
,
205 if (afsname
== NULL
) {
206 return NT_STATUS_NO_MEMORY
;
211 DEBUG(10, ("Generating token for user %s\n", afsname
));
213 cell
= strchr(afsname
, '@');
216 return NT_STATUS_NO_MEMORY
;
222 token
= afs_createtoken_str(afsname
, cell
);
226 state
->response
->extra_data
.data
= talloc_strdup(state
->mem_ctx
,
228 if (state
->response
->extra_data
.data
== NULL
) {
229 return NT_STATUS_NO_MEMORY
;
231 state
->response
->length
+=
232 strlen((const char *)state
->response
->extra_data
.data
)+1;
237 static NTSTATUS
check_info3_in_group(struct netr_SamInfo3
*info3
,
238 const char *group_sid
)
240 * Check whether a user belongs to a group or list of groups.
242 * @param mem_ctx talloc memory context.
243 * @param info3 user information, including group membership info.
244 * @param group_sid One or more groups , separated by commas.
246 * @return NT_STATUS_OK on success,
247 * NT_STATUS_LOGON_FAILURE if the user does not belong,
248 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
251 DOM_SID
*require_membership_of_sid
;
252 size_t num_require_membership_of_sid
;
257 struct nt_user_token
*token
;
258 TALLOC_CTX
*frame
= talloc_stackframe();
261 /* Parse the 'required group' SID */
263 if (!group_sid
|| !group_sid
[0]) {
264 /* NO sid supplied, all users may access */
268 token
= talloc_zero(talloc_tos(), struct nt_user_token
);
270 DEBUG(0, ("talloc failed\n"));
272 return NT_STATUS_NO_MEMORY
;
275 num_require_membership_of_sid
= 0;
276 require_membership_of_sid
= NULL
;
280 while (next_token_talloc(talloc_tos(), &p
, &req_sid
, ",")) {
281 if (!string_to_sid(&sid
, req_sid
)) {
282 DEBUG(0, ("check_info3_in_group: could not parse %s "
283 "as a SID!", req_sid
));
285 return NT_STATUS_INVALID_PARAMETER
;
288 status
= add_sid_to_array(talloc_tos(), &sid
,
289 &require_membership_of_sid
,
290 &num_require_membership_of_sid
);
291 if (!NT_STATUS_IS_OK(status
)) {
292 DEBUG(0, ("add_sid_to_array failed\n"));
298 status
= sid_array_from_info3(talloc_tos(), info3
,
302 if (!NT_STATUS_IS_OK(status
)) {
307 if (!NT_STATUS_IS_OK(status
= add_aliases(get_global_sam_sid(),
309 || !NT_STATUS_IS_OK(status
= add_aliases(&global_sid_Builtin
,
311 DEBUG(3, ("could not add aliases: %s\n",
317 debug_nt_user_token(DBGC_CLASS
, 10, token
);
319 for (i
=0; i
<num_require_membership_of_sid
; i
++) {
320 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
321 &require_membership_of_sid
[i
])));
322 if (nt_token_check_sid(&require_membership_of_sid
[i
],
324 DEBUG(10, ("Access ok\n"));
330 /* Do not distinguish this error from a wrong username/pw */
333 return NT_STATUS_LOGON_FAILURE
;
336 struct winbindd_domain
*find_auth_domain(uint8_t flags
,
337 const char *domain_name
)
339 struct winbindd_domain
*domain
;
342 domain
= find_domain_from_name_noinit(domain_name
);
343 if (domain
== NULL
) {
344 DEBUG(3, ("Authentication for domain [%s] refused "
345 "as it is not a trusted domain\n",
351 if (strequal(domain_name
, get_global_sam_name())) {
352 return find_domain_from_name_noinit(domain_name
);
355 /* we can auth against trusted domains */
356 if (flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
357 domain
= find_domain_from_name_noinit(domain_name
);
358 if (domain
== NULL
) {
359 DEBUG(3, ("Authentication for domain [%s] skipped "
360 "as it is not a trusted domain\n",
367 return find_our_domain();
370 static void fill_in_password_policy(struct winbindd_response
*r
,
371 const struct samr_DomInfo1
*p
)
373 r
->data
.auth
.policy
.min_length_password
=
374 p
->min_password_length
;
375 r
->data
.auth
.policy
.password_history
=
376 p
->password_history_length
;
377 r
->data
.auth
.policy
.password_properties
=
378 p
->password_properties
;
379 r
->data
.auth
.policy
.expire
=
380 nt_time_to_unix_abs((NTTIME
*)&(p
->max_password_age
));
381 r
->data
.auth
.policy
.min_passwordage
=
382 nt_time_to_unix_abs((NTTIME
*)&(p
->min_password_age
));
385 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
386 struct winbindd_cli_state
*state
)
388 struct winbindd_methods
*methods
;
389 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
390 struct samr_DomInfo1 password_policy
;
392 if ( !winbindd_can_contact_domain( domain
) ) {
393 DEBUG(5,("fillup_password_policy: No inbound trust to "
394 "contact domain %s\n", domain
->name
));
395 return NT_STATUS_NOT_SUPPORTED
;
398 methods
= domain
->methods
;
400 status
= methods
->password_policy(domain
, state
->mem_ctx
, &password_policy
);
401 if (NT_STATUS_IS_ERR(status
)) {
405 fill_in_password_policy(state
->response
, &password_policy
);
410 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
412 uint16
*lockout_threshold
)
414 struct winbindd_methods
*methods
;
415 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
416 struct samr_DomInfo12 lockout_policy
;
418 *lockout_threshold
= 0;
420 methods
= domain
->methods
;
422 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
423 if (NT_STATUS_IS_ERR(status
)) {
427 *lockout_threshold
= lockout_policy
.lockout_threshold
;
432 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
434 uint32
*password_properties
)
436 struct winbindd_methods
*methods
;
437 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
438 struct samr_DomInfo1 password_policy
;
440 *password_properties
= 0;
442 methods
= domain
->methods
;
444 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
445 if (NT_STATUS_IS_ERR(status
)) {
449 *password_properties
= password_policy
.password_properties
;
456 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
459 bool *internal_ccache
)
461 /* accept FILE and WRFILE as krb5_cc_type from the client and then
462 * build the full ccname string based on the user's uid here -
465 const char *gen_cc
= NULL
;
467 *internal_ccache
= true;
473 if (!type
|| type
[0] == '\0') {
477 if (strequal(type
, "FILE")) {
478 gen_cc
= talloc_asprintf(mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
479 } else if (strequal(type
, "WRFILE")) {
480 gen_cc
= talloc_asprintf(mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
482 DEBUG(10,("we don't allow to set a %s type ccache\n", type
));
486 *internal_ccache
= false;
490 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
493 if (gen_cc
== NULL
) {
494 DEBUG(0,("out of memory\n"));
498 DEBUG(10,("using ccache: %s %s\n", gen_cc
, *internal_ccache
? "(internal)":""));
503 static void setup_return_cc_name(struct winbindd_cli_state
*state
, const char *cc
)
505 const char *type
= state
->request
->data
.auth
.krb5_cc_type
;
507 state
->response
->data
.auth
.krb5ccname
[0] = '\0';
509 if (type
[0] == '\0') {
513 if (!strequal(type
, "FILE") &&
514 !strequal(type
, "WRFILE")) {
515 DEBUG(10,("won't return krbccname for a %s type ccache\n",
520 fstrcpy(state
->response
->data
.auth
.krb5ccname
, cc
);
525 uid_t
get_uid_from_request(struct winbindd_request
*request
)
529 uid
= request
->data
.auth
.uid
;
532 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid
));
538 static uid_t
get_uid_from_state(struct winbindd_cli_state
*state
)
540 return get_uid_from_request(state
->request
);
543 /**********************************************************************
544 Authenticate a user with a clear text password using Kerberos and fill up
546 **********************************************************************/
548 static NTSTATUS
winbindd_raw_kerberos_login(struct winbindd_domain
*domain
,
549 struct winbindd_cli_state
*state
,
550 struct netr_SamInfo3
**info3
)
553 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
554 krb5_error_code krb5_ret
;
555 const char *cc
= NULL
;
556 const char *principal_s
= NULL
;
557 const char *service
= NULL
;
559 fstring name_domain
, name_user
;
560 time_t ticket_lifetime
= 0;
561 time_t renewal_until
= 0;
564 time_t time_offset
= 0;
565 bool internal_ccache
= true;
572 * prepare a krb5_cc_cache string for the user */
574 uid
= get_uid_from_state(state
);
576 DEBUG(0,("no valid uid\n"));
579 cc
= generate_krb5_ccache(state
->mem_ctx
,
580 state
->request
->data
.auth
.krb5_cc_type
,
581 state
->request
->data
.auth
.uid
,
584 return NT_STATUS_NO_MEMORY
;
589 * get kerberos properties */
591 if (domain
->private_data
) {
592 ads
= (ADS_STRUCT
*)domain
->private_data
;
593 time_offset
= ads
->auth
.time_offset
;
598 * do kerberos auth and setup ccache as the user */
600 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
602 realm
= domain
->alt_name
;
605 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
606 if (principal_s
== NULL
) {
607 return NT_STATUS_NO_MEMORY
;
610 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
611 if (service
== NULL
) {
612 return NT_STATUS_NO_MEMORY
;
615 /* if this is a user ccache, we need to act as the user to let the krb5
616 * library handle the chown, etc. */
618 /************************ ENTERING NON-ROOT **********************/
620 if (!internal_ccache
) {
621 set_effective_uid(uid
);
622 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
625 result
= kerberos_return_info3_from_pac(state
->mem_ctx
,
627 state
->request
->data
.auth
.pass
,
634 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
637 if (!internal_ccache
) {
638 gain_root_privilege();
641 /************************ RETURNED TO ROOT **********************/
643 if (!NT_STATUS_IS_OK(result
)) {
647 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
650 /* if we had a user's ccache then return that string for the pam
653 if (!internal_ccache
) {
655 setup_return_cc_name(state
, cc
);
657 result
= add_ccache_to_list(principal_s
,
660 state
->request
->data
.auth
.user
,
668 if (!NT_STATUS_IS_OK(result
)) {
669 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
674 /* need to delete the memory cred cache, it is not used anymore */
676 krb5_ret
= ads_kdestroy(cc
);
678 DEBUG(3,("winbindd_raw_kerberos_login: "
679 "could not destroy krb5 credential cache: "
680 "%s\n", error_message(krb5_ret
)));
689 /* we could have created a new credential cache with a valid tgt in it
690 * but we werent able to get or verify the service ticket for this
691 * local host and therefor didn't get the PAC, we need to remove that
692 * cache entirely now */
694 krb5_ret
= ads_kdestroy(cc
);
696 DEBUG(3,("winbindd_raw_kerberos_login: "
697 "could not destroy krb5 credential cache: "
698 "%s\n", error_message(krb5_ret
)));
701 if (!NT_STATUS_IS_OK(remove_ccache(state
->request
->data
.auth
.user
))) {
702 DEBUG(3,("winbindd_raw_kerberos_login: "
703 "could not remove ccache for user %s\n",
704 state
->request
->data
.auth
.user
));
709 return NT_STATUS_NOT_SUPPORTED
;
710 #endif /* HAVE_KRB5 */
713 /****************************************************************
714 ****************************************************************/
716 bool check_request_flags(uint32_t flags
)
718 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
719 WBFLAG_PAM_INFO3_TEXT
|
720 WBFLAG_PAM_INFO3_NDR
;
722 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
723 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
724 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
725 !(flags
& flags_edata
) ) {
729 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
735 /****************************************************************
736 ****************************************************************/
738 static NTSTATUS
append_auth_data(struct winbindd_cli_state
*state
,
739 struct netr_SamInfo3
*info3
,
740 const char *name_domain
,
741 const char *name_user
)
744 uint32_t flags
= state
->request
->flags
;
746 if (flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
747 memcpy(state
->response
->data
.auth
.user_session_key
,
749 sizeof(state
->response
->data
.auth
.user_session_key
)
753 if (flags
& WBFLAG_PAM_LMKEY
) {
754 memcpy(state
->response
->data
.auth
.first_8_lm_hash
,
755 info3
->base
.LMSessKey
.key
,
756 sizeof(state
->response
->data
.auth
.first_8_lm_hash
)
760 if (flags
& WBFLAG_PAM_INFO3_TEXT
) {
761 result
= append_info3_as_txt(state
->mem_ctx
, state
, info3
);
762 if (!NT_STATUS_IS_OK(result
)) {
763 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
769 /* currently, anything from here on potentially overwrites extra_data. */
771 if (flags
& WBFLAG_PAM_INFO3_NDR
) {
772 result
= append_info3_as_ndr(state
->mem_ctx
, state
, info3
);
773 if (!NT_STATUS_IS_OK(result
)) {
774 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
780 if (flags
& WBFLAG_PAM_UNIX_NAME
) {
781 result
= append_unix_username(state
->mem_ctx
, state
, info3
,
782 name_domain
, name_user
);
783 if (!NT_STATUS_IS_OK(result
)) {
784 DEBUG(10,("Failed to append Unix Username: %s\n",
790 if (flags
& WBFLAG_PAM_AFS_TOKEN
) {
791 result
= append_afs_token(state
->mem_ctx
, state
, info3
,
792 name_domain
, name_user
);
793 if (!NT_STATUS_IS_OK(result
)) {
794 DEBUG(10,("Failed to append AFS token: %s\n",
803 static NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
804 struct winbindd_cli_state
*state
,
805 struct netr_SamInfo3
**info3
)
807 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
808 uint16 max_allowed_bad_attempts
;
809 fstring name_domain
, name_user
;
811 enum lsa_SidType type
;
812 uchar new_nt_pass
[NT_HASH_LEN
];
813 const uint8
*cached_nt_pass
;
814 const uint8
*cached_salt
;
815 struct netr_SamInfo3
*my_info3
;
816 time_t kickoff_time
, must_change_time
;
817 bool password_good
= false;
819 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
826 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
828 /* Parse domain and username */
830 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
833 if (!lookup_cached_name(state
->mem_ctx
,
838 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
839 return NT_STATUS_NO_SUCH_USER
;
842 if (type
!= SID_NAME_USER
) {
843 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
844 return NT_STATUS_LOGON_FAILURE
;
847 result
= winbindd_get_creds(domain
,
853 if (!NT_STATUS_IS_OK(result
)) {
854 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
860 E_md4hash(state
->request
->data
.auth
.pass
, new_nt_pass
);
862 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
863 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
865 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
869 /* In this case we didn't store the nt_hash itself,
870 but the MD5 combination of salt + nt_hash. */
871 uchar salted_hash
[NT_HASH_LEN
];
872 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
874 password_good
= (memcmp(cached_nt_pass
, salted_hash
,
877 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
878 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
,
884 /* User *DOES* know the password, update logon_time and reset
887 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
889 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
890 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
893 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
894 return NT_STATUS_ACCOUNT_DISABLED
;
897 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
898 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
901 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
902 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
905 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
906 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
909 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
910 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
911 my_info3
->base
.acct_flags
));
912 return NT_STATUS_LOGON_FAILURE
;
915 kickoff_time
= nt_time_to_unix(my_info3
->base
.acct_expiry
);
916 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
917 return NT_STATUS_ACCOUNT_EXPIRED
;
920 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
921 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
922 /* we allow grace logons when the password has expired */
923 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
924 /* return NT_STATUS_PASSWORD_EXPIRED; */
929 if ((state
->request
->flags
& WBFLAG_PAM_KRB5
) &&
930 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
931 ((tdc_domain
->trust_type
& NETR_TRUST_TYPE_UPLEVEL
) ||
932 /* used to cope with the case winbindd starting without network. */
933 !strequal(tdc_domain
->domain_name
, tdc_domain
->dns_name
))) {
936 const char *cc
= NULL
;
938 const char *principal_s
= NULL
;
939 const char *service
= NULL
;
940 bool internal_ccache
= false;
942 uid
= get_uid_from_state(state
);
944 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
945 return NT_STATUS_INVALID_PARAMETER
;
948 cc
= generate_krb5_ccache(state
->mem_ctx
,
949 state
->request
->data
.auth
.krb5_cc_type
,
950 state
->request
->data
.auth
.uid
,
953 return NT_STATUS_NO_MEMORY
;
956 realm
= domain
->alt_name
;
959 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
960 if (principal_s
== NULL
) {
961 return NT_STATUS_NO_MEMORY
;
964 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
965 if (service
== NULL
) {
966 return NT_STATUS_NO_MEMORY
;
969 if (!internal_ccache
) {
971 setup_return_cc_name(state
, cc
);
973 result
= add_ccache_to_list(principal_s
,
976 state
->request
->data
.auth
.user
,
980 time(NULL
) + lp_winbind_cache_time(),
981 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
984 if (!NT_STATUS_IS_OK(result
)) {
985 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
986 "to add ccache to list: %s\n",
991 #endif /* HAVE_KRB5 */
993 /* FIXME: we possibly should handle logon hours as well (does xp when
994 * offline?) see auth/auth_sam.c:sam_account_ok for details */
996 unix_to_nt_time(&my_info3
->base
.last_logon
, time(NULL
));
997 my_info3
->base
.bad_password_count
= 0;
999 result
= winbindd_update_creds_by_info3(domain
,
1001 state
->request
->data
.auth
.user
,
1002 state
->request
->data
.auth
.pass
,
1004 if (!NT_STATUS_IS_OK(result
)) {
1005 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1006 nt_errstr(result
)));
1010 return NT_STATUS_OK
;
1014 /* User does *NOT* know the correct password, modify info3 accordingly */
1016 /* failure of this is not critical */
1017 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1018 if (!NT_STATUS_IS_OK(result
)) {
1019 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1020 "Won't be able to honour account lockout policies\n"));
1023 /* increase counter */
1024 my_info3
->base
.bad_password_count
++;
1026 if (max_allowed_bad_attempts
== 0) {
1031 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1033 uint32 password_properties
;
1035 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1036 if (!NT_STATUS_IS_OK(result
)) {
1037 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1040 if ((my_info3
->base
.rid
!= DOMAIN_USER_RID_ADMIN
) ||
1041 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1042 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1047 result
= winbindd_update_creds_by_info3(domain
,
1049 state
->request
->data
.auth
.user
,
1053 if (!NT_STATUS_IS_OK(result
)) {
1054 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1055 nt_errstr(result
)));
1058 return NT_STATUS_LOGON_FAILURE
;
1061 static NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1062 struct winbindd_cli_state
*state
,
1063 struct netr_SamInfo3
**info3
)
1065 struct winbindd_domain
*contact_domain
;
1066 fstring name_domain
, name_user
;
1069 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1071 /* Parse domain and username */
1073 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1075 /* what domain should we contact? */
1078 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1079 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1080 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1081 result
= NT_STATUS_NO_SUCH_USER
;
1086 if (is_myname(name_domain
)) {
1087 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1088 result
= NT_STATUS_NO_SUCH_USER
;
1092 contact_domain
= find_domain_from_name(name_domain
);
1093 if (contact_domain
== NULL
) {
1094 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1095 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1097 contact_domain
= find_our_domain();
1101 if (contact_domain
->initialized
&&
1102 contact_domain
->active_directory
) {
1106 if (!contact_domain
->initialized
) {
1107 init_dc_connection(contact_domain
);
1110 if (!contact_domain
->active_directory
) {
1111 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1112 return NT_STATUS_INVALID_LOGON_TYPE
;
1115 result
= winbindd_raw_kerberos_login(contact_domain
, state
, info3
);
1120 static NTSTATUS
winbindd_dual_auth_passdb(TALLOC_CTX
*mem_ctx
,
1121 const char *domain
, const char *user
,
1122 const DATA_BLOB
*challenge
,
1123 const DATA_BLOB
*lm_resp
,
1124 const DATA_BLOB
*nt_resp
,
1125 struct netr_SamInfo3
**pinfo3
)
1127 struct auth_usersupplied_info
*user_info
= NULL
;
1128 struct auth_serversupplied_info
*server_info
= NULL
;
1129 struct netr_SamInfo3
*info3
;
1132 status
= make_user_info(&user_info
, user
, user
, domain
, domain
,
1133 global_myname(), lm_resp
, nt_resp
, NULL
, NULL
,
1135 if (!NT_STATUS_IS_OK(status
)) {
1136 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status
)));
1140 status
= check_sam_security(challenge
, talloc_tos(), user_info
,
1142 free_user_info(&user_info
);
1144 if (!NT_STATUS_IS_OK(status
)) {
1145 DEBUG(10, ("check_ntlm_password failed: %s\n",
1146 nt_errstr(status
)));
1150 info3
= TALLOC_ZERO_P(mem_ctx
, struct netr_SamInfo3
);
1151 if (info3
== NULL
) {
1152 return NT_STATUS_NO_MEMORY
;
1155 status
= serverinfo_to_SamInfo3(server_info
, NULL
, 0, info3
);
1156 if (!NT_STATUS_IS_OK(status
)) {
1157 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1158 nt_errstr(status
)));
1162 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain
, user
));
1164 return NT_STATUS_OK
;
1167 typedef NTSTATUS (*netlogon_fn_t
)(struct rpc_pipe_client
*cli
,
1168 TALLOC_CTX
*mem_ctx
,
1169 uint32 logon_parameters
,
1171 const char *username
,
1173 const char *workstation
,
1174 const uint8 chal
[8],
1175 DATA_BLOB lm_response
,
1176 DATA_BLOB nt_response
,
1177 struct netr_SamInfo3
**info3
);
1179 static NTSTATUS
winbindd_dual_pam_auth_samlogon(struct winbindd_domain
*domain
,
1180 struct winbindd_cli_state
*state
,
1181 struct netr_SamInfo3
**info3
)
1184 struct rpc_pipe_client
*netlogon_pipe
;
1189 unsigned char local_lm_response
[24];
1190 unsigned char local_nt_response
[24];
1191 fstring name_domain
, name_user
;
1194 struct netr_SamInfo3
*my_info3
= NULL
;
1198 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1200 /* Parse domain and username */
1202 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1204 /* do password magic */
1206 generate_random_buffer(chal
, sizeof(chal
));
1208 if (lp_client_ntlmv2_auth()) {
1209 DATA_BLOB server_chal
;
1210 DATA_BLOB names_blob
;
1211 DATA_BLOB nt_response
;
1212 DATA_BLOB lm_response
;
1213 server_chal
= data_blob_talloc(state
->mem_ctx
, chal
, 8);
1215 /* note that the 'workgroup' here is a best guess - we don't know
1216 the server's domain at this point. The 'server name' is also
1219 names_blob
= NTLMv2_generate_names_blob(state
->mem_ctx
, global_myname(), lp_workgroup());
1221 if (!SMBNTLMv2encrypt(NULL
, name_user
, name_domain
,
1222 state
->request
->data
.auth
.pass
,
1225 &lm_response
, &nt_response
, NULL
, NULL
)) {
1226 data_blob_free(&names_blob
);
1227 data_blob_free(&server_chal
);
1228 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1229 result
= NT_STATUS_NO_MEMORY
;
1232 data_blob_free(&names_blob
);
1233 data_blob_free(&server_chal
);
1234 lm_resp
= data_blob_talloc(state
->mem_ctx
, lm_response
.data
,
1235 lm_response
.length
);
1236 nt_resp
= data_blob_talloc(state
->mem_ctx
, nt_response
.data
,
1237 nt_response
.length
);
1238 data_blob_free(&lm_response
);
1239 data_blob_free(&nt_response
);
1242 if (lp_client_lanman_auth()
1243 && SMBencrypt(state
->request
->data
.auth
.pass
,
1245 local_lm_response
)) {
1246 lm_resp
= data_blob_talloc(state
->mem_ctx
,
1248 sizeof(local_lm_response
));
1250 lm_resp
= data_blob_null
;
1252 SMBNTencrypt(state
->request
->data
.auth
.pass
,
1256 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1258 sizeof(local_nt_response
));
1261 if (strequal(name_domain
, get_global_sam_name())) {
1262 DATA_BLOB chal_blob
= data_blob_const(chal
, sizeof(chal
));
1264 result
= winbindd_dual_auth_passdb(
1265 state
->mem_ctx
, name_domain
, name_user
,
1266 &chal_blob
, &lm_resp
, &nt_resp
, info3
);
1270 /* check authentication loop */
1273 netlogon_fn_t logon_fn
;
1275 ZERO_STRUCTP(my_info3
);
1278 result
= cm_connect_netlogon(domain
, &netlogon_pipe
);
1280 if (!NT_STATUS_IS_OK(result
)) {
1281 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1285 /* It is really important to try SamLogonEx here,
1286 * because in a clustered environment, we want to use
1287 * one machine account from multiple physical
1290 * With a normal SamLogon call, we must keep the
1291 * credentials chain updated and intact between all
1292 * users of the machine account (which would imply
1293 * cross-node communication for every NTLM logon).
1295 * (The credentials chain is not per NETLOGON pipe
1296 * connection, but globally on the server/client pair
1299 * When using SamLogonEx, the credentials are not
1300 * supplied, but the session key is implied by the
1301 * wrapping SamLogon context.
1303 * -- abartlet 21 April 2008
1306 logon_fn
= domain
->can_do_samlogon_ex
1307 ? rpccli_netlogon_sam_network_logon_ex
1308 : rpccli_netlogon_sam_network_logon
;
1310 result
= logon_fn(netlogon_pipe
,
1313 domain
->dcname
, /* server name */
1314 name_user
, /* user name */
1315 name_domain
, /* target domain */
1316 global_myname(), /* workstation */
1323 if ((NT_STATUS_V(result
) == DCERPC_FAULT_OP_RNG_ERROR
)
1324 && domain
->can_do_samlogon_ex
) {
1325 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1326 "retrying with NetSamLogon\n"));
1327 domain
->can_do_samlogon_ex
= false;
1332 /* We have to try a second time as cm_connect_netlogon
1333 might not yet have noticed that the DC has killed
1336 if (!rpccli_is_connected(netlogon_pipe
)) {
1341 /* if we get access denied, a possible cause was that we had
1342 and open connection to the DC, but someone changed our
1343 machine account password out from underneath us using 'net
1344 rpc changetrustpw' */
1346 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1347 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1348 "ACCESS_DENIED. Maybe the trust account "
1349 "password was changed and we didn't know it. "
1350 "Killing connections to domain %s\n",
1352 invalidate_cm_connection(&domain
->conn
);
1356 } while ( (attempts
< 2) && retry
);
1358 /* handle the case where a NT4 DC does not fill in the acct_flags in
1359 * the samlogon reply info3. When accurate info3 is required by the
1360 * caller, we look up the account flags ourselve - gd */
1362 if ((state
->request
->flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1363 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1365 struct rpc_pipe_client
*samr_pipe
;
1366 struct policy_handle samr_domain_handle
, user_pol
;
1367 union samr_UserInfo
*info
= NULL
;
1368 NTSTATUS status_tmp
;
1371 status_tmp
= cm_connect_sam(domain
, state
->mem_ctx
,
1372 &samr_pipe
, &samr_domain_handle
);
1374 if (!NT_STATUS_IS_OK(status_tmp
)) {
1375 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1376 nt_errstr(status_tmp
)));
1380 status_tmp
= rpccli_samr_OpenUser(samr_pipe
, state
->mem_ctx
,
1381 &samr_domain_handle
,
1382 MAXIMUM_ALLOWED_ACCESS
,
1386 if (!NT_STATUS_IS_OK(status_tmp
)) {
1387 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1388 nt_errstr(status_tmp
)));
1392 status_tmp
= rpccli_samr_QueryUserInfo(samr_pipe
, state
->mem_ctx
,
1397 if (!NT_STATUS_IS_OK(status_tmp
)) {
1398 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1399 nt_errstr(status_tmp
)));
1400 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1404 acct_flags
= info
->info16
.acct_flags
;
1406 if (acct_flags
== 0) {
1407 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1411 my_info3
->base
.acct_flags
= acct_flags
;
1413 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1415 rpccli_samr_Close(samr_pipe
, state
->mem_ctx
, &user_pol
);
1423 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1424 struct winbindd_cli_state
*state
)
1426 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1427 NTSTATUS krb5_result
= NT_STATUS_OK
;
1428 fstring name_domain
, name_user
;
1430 fstring domain_user
;
1431 struct netr_SamInfo3
*info3
= NULL
;
1432 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1434 /* Ensure null termination */
1435 state
->request
->data
.auth
.user
[sizeof(state
->request
->data
.auth
.user
)-1]='\0';
1437 /* Ensure null termination */
1438 state
->request
->data
.auth
.pass
[sizeof(state
->request
->data
.auth
.pass
)-1]='\0';
1440 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1441 state
->request
->data
.auth
.user
));
1443 if (!check_request_flags(state
->request
->flags
)) {
1444 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1448 /* Parse domain and username */
1450 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1451 state
->request
->data
.auth
.user
,
1454 /* If the name normalization didnt' actually do anything,
1455 just use the original name */
1457 if (!NT_STATUS_IS_OK(name_map_status
) &&
1458 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1460 mapped_user
= state
->request
->data
.auth
.user
;
1463 parse_domain_user(mapped_user
, name_domain
, name_user
);
1465 if ( mapped_user
!= state
->request
->data
.auth
.user
) {
1466 fstr_sprintf( domain_user
, "%s\\%s", name_domain
, name_user
);
1467 safe_strcpy( state
->request
->data
.auth
.user
, domain_user
,
1468 sizeof(state
->request
->data
.auth
.user
)-1 );
1471 if (domain
->online
== false) {
1472 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1473 if (domain
->startup
) {
1474 /* Logons are very important to users. If we're offline and
1475 we get a request within the first 30 seconds of startup,
1476 try very hard to find a DC and go online. */
1478 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1479 "request in startup mode.\n", domain
->name
));
1481 winbindd_flush_negative_conn_cache(domain
);
1482 result
= init_dc_connection(domain
);
1486 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1488 /* Check for Kerberos authentication */
1489 if (domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
1491 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1492 /* save for later */
1493 krb5_result
= result
;
1496 if (NT_STATUS_IS_OK(result
)) {
1497 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1498 goto process_result
;
1500 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1503 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1504 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1505 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1506 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1507 set_domain_offline( domain
);
1511 /* there are quite some NT_STATUS errors where there is no
1512 * point in retrying with a samlogon, we explictly have to take
1513 * care not to increase the bad logon counter on the DC */
1515 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1516 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1517 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1518 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1519 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1520 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1521 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1522 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1523 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1524 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1528 if (state
->request
->flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1529 DEBUG(3,("falling back to samlogon\n"));
1537 /* Check for Samlogon authentication */
1538 if (domain
->online
) {
1539 result
= winbindd_dual_pam_auth_samlogon(domain
, state
, &info3
);
1541 if (NT_STATUS_IS_OK(result
)) {
1542 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1543 /* add the Krb5 err if we have one */
1544 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1545 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1547 goto process_result
;
1550 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1551 nt_errstr(result
)));
1553 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1554 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1555 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1557 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1558 set_domain_offline( domain
);
1562 if (domain
->online
) {
1563 /* We're still online - fail. */
1569 /* Check for Cached logons */
1570 if (!domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1571 lp_winbind_offline_logon()) {
1573 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1575 if (NT_STATUS_IS_OK(result
)) {
1576 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1577 goto process_result
;
1579 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1586 if (NT_STATUS_IS_OK(result
)) {
1590 /* In all codepaths where result == NT_STATUS_OK info3 must have
1591 been initialized. */
1593 result
= NT_STATUS_INTERNAL_ERROR
;
1597 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1598 netsamlogon_cache_store(name_user
, info3
);
1600 /* save name_to_sid info as early as possible (only if
1601 this is our primary domain so we don't invalidate
1602 the cache entry by storing the seq_num for the wrong
1604 if ( domain
->primary
) {
1605 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1607 cache_name2sid(domain
, name_domain
, name_user
,
1608 SID_NAME_USER
, &user_sid
);
1611 /* Check if the user is in the right group */
1613 result
= check_info3_in_group(
1615 state
->request
->data
.auth
.require_membership_of_sid
);
1616 if (!NT_STATUS_IS_OK(result
)) {
1617 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1618 state
->request
->data
.auth
.user
,
1619 state
->request
->data
.auth
.require_membership_of_sid
));
1623 result
= append_auth_data(state
, info3
, name_domain
,
1625 if (!NT_STATUS_IS_OK(result
)) {
1629 if ((state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
1631 if (lp_winbind_offline_logon()) {
1632 result
= winbindd_store_creds(domain
,
1634 state
->request
->data
.auth
.user
,
1635 state
->request
->data
.auth
.pass
,
1641 if (state
->request
->flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1642 struct winbindd_domain
*our_domain
= find_our_domain();
1644 /* This is not entirely correct I believe, but it is
1645 consistent. Only apply the password policy settings
1646 too warn users for our own domain. Cannot obtain these
1647 from trusted DCs all the time so don't do it at all.
1650 result
= NT_STATUS_NOT_SUPPORTED
;
1651 if (our_domain
== domain
) {
1652 result
= fillup_password_policy(our_domain
, state
);
1655 if (!NT_STATUS_IS_OK(result
)
1656 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1658 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1659 domain
->name
, nt_errstr(result
)));
1664 result
= NT_STATUS_OK
;
1668 /* give us a more useful (more correct?) error code */
1669 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1670 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1671 result
= NT_STATUS_NO_LOGON_SERVERS
;
1674 set_auth_errors(state
->response
, result
);
1676 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1677 state
->request
->data
.auth
.user
,
1678 state
->response
->data
.auth
.nt_status_string
,
1679 state
->response
->data
.auth
.pam_error
));
1681 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1684 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1685 struct winbindd_cli_state
*state
)
1688 struct netr_SamInfo3
*info3
= NULL
;
1689 struct rpc_pipe_client
*netlogon_pipe
;
1690 const char *name_user
= NULL
;
1691 const char *name_domain
= NULL
;
1692 const char *workstation
;
1696 DATA_BLOB lm_resp
, nt_resp
;
1698 /* This is child-only, so no check for privileged access is needed
1701 /* Ensure null termination */
1702 state
->request
->data
.auth_crap
.user
[sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1703 state
->request
->data
.auth_crap
.domain
[sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1705 if (!check_request_flags(state
->request
->flags
)) {
1706 result
= NT_STATUS_INVALID_PARAMETER_MIX
;
1710 name_user
= state
->request
->data
.auth_crap
.user
;
1712 if (*state
->request
->data
.auth_crap
.domain
) {
1713 name_domain
= state
->request
->data
.auth_crap
.domain
;
1714 } else if (lp_winbind_use_default_domain()) {
1715 name_domain
= lp_workgroup();
1717 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1719 result
= NT_STATUS_NO_SUCH_USER
;
1723 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1724 name_domain
, name_user
));
1726 if (*state
->request
->data
.auth_crap
.workstation
) {
1727 workstation
= state
->request
->data
.auth_crap
.workstation
;
1729 workstation
= global_myname();
1732 if (state
->request
->data
.auth_crap
.lm_resp_len
> sizeof(state
->request
->data
.auth_crap
.lm_resp
)
1733 || state
->request
->data
.auth_crap
.nt_resp_len
> sizeof(state
->request
->data
.auth_crap
.nt_resp
)) {
1734 if (!(state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1735 state
->request
->extra_len
!= state
->request
->data
.auth_crap
.nt_resp_len
) {
1736 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1737 state
->request
->data
.auth_crap
.lm_resp_len
,
1738 state
->request
->data
.auth_crap
.nt_resp_len
));
1739 result
= NT_STATUS_INVALID_PARAMETER
;
1744 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
->data
.auth_crap
.lm_resp
,
1745 state
->request
->data
.auth_crap
.lm_resp_len
);
1747 if (state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1748 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1749 state
->request
->extra_data
.data
,
1750 state
->request
->data
.auth_crap
.nt_resp_len
);
1752 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1753 state
->request
->data
.auth_crap
.nt_resp
,
1754 state
->request
->data
.auth_crap
.nt_resp_len
);
1757 if (strequal(name_domain
, get_global_sam_name())) {
1758 DATA_BLOB chal_blob
= data_blob_const(
1759 state
->request
->data
.auth_crap
.chal
,
1760 sizeof(state
->request
->data
.auth_crap
.chal
));
1762 result
= winbindd_dual_auth_passdb(
1763 state
->mem_ctx
, name_domain
, name_user
,
1764 &chal_blob
, &lm_resp
, &nt_resp
, &info3
);
1765 goto process_result
;
1769 netlogon_fn_t logon_fn
;
1773 netlogon_pipe
= NULL
;
1774 result
= cm_connect_netlogon(domain
, &netlogon_pipe
);
1776 if (!NT_STATUS_IS_OK(result
)) {
1777 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1778 nt_errstr(result
)));
1782 logon_fn
= domain
->can_do_samlogon_ex
1783 ? rpccli_netlogon_sam_network_logon_ex
1784 : rpccli_netlogon_sam_network_logon
;
1786 result
= logon_fn(netlogon_pipe
,
1788 state
->request
->data
.auth_crap
.logon_parameters
,
1792 /* Bug #3248 - found by Stefan Burkei. */
1793 workstation
, /* We carefully set this above so use it... */
1794 state
->request
->data
.auth_crap
.chal
,
1799 if ((NT_STATUS_V(result
) == DCERPC_FAULT_OP_RNG_ERROR
)
1800 && domain
->can_do_samlogon_ex
) {
1801 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1802 "retrying with NetSamLogon\n"));
1803 domain
->can_do_samlogon_ex
= false;
1810 /* We have to try a second time as cm_connect_netlogon
1811 might not yet have noticed that the DC has killed
1814 if (!rpccli_is_connected(netlogon_pipe
)) {
1819 /* if we get access denied, a possible cause was that we had and open
1820 connection to the DC, but someone changed our machine account password
1821 out from underneath us using 'net rpc changetrustpw' */
1823 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1824 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1825 "ACCESS_DENIED. Maybe the trust account "
1826 "password was changed and we didn't know it. "
1827 "Killing connections to domain %s\n",
1829 invalidate_cm_connection(&domain
->conn
);
1833 } while ( (attempts
< 2) && retry
);
1837 if (NT_STATUS_IS_OK(result
)) {
1839 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1840 netsamlogon_cache_store(name_user
, info3
);
1842 /* Check if the user is in the right group */
1844 result
= check_info3_in_group(
1846 state
->request
->data
.auth_crap
.require_membership_of_sid
);
1847 if (!NT_STATUS_IS_OK(result
)) {
1848 DEBUG(3, ("User %s is not in the required group (%s), so "
1849 "crap authentication is rejected\n",
1850 state
->request
->data
.auth_crap
.user
,
1851 state
->request
->data
.auth_crap
.require_membership_of_sid
));
1855 result
= append_auth_data(state
, info3
, name_domain
,
1857 if (!NT_STATUS_IS_OK(result
)) {
1864 /* give us a more useful (more correct?) error code */
1865 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1866 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1867 result
= NT_STATUS_NO_LOGON_SERVERS
;
1870 if (state
->request
->flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1871 result
= nt_status_squash(result
);
1874 set_auth_errors(state
->response
, result
);
1876 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1877 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1880 state
->response
->data
.auth
.nt_status_string
,
1881 state
->response
->data
.auth
.pam_error
));
1883 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1886 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
1887 struct winbindd_cli_state
*state
)
1890 char *newpass
= NULL
;
1891 struct policy_handle dom_pol
;
1892 struct rpc_pipe_client
*cli
;
1893 bool got_info
= false;
1894 struct samr_DomInfo1
*info
= NULL
;
1895 struct userPwdChangeFailureInformation
*reject
= NULL
;
1896 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1897 fstring domain
, user
;
1899 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
1900 state
->request
->data
.auth
.user
));
1902 if (!parse_domain_user(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
1906 /* Change password */
1908 oldpass
= state
->request
->data
.chauthtok
.oldpass
;
1909 newpass
= state
->request
->data
.chauthtok
.newpass
;
1911 /* Initialize reject reason */
1912 state
->response
->data
.auth
.reject_reason
= Undefined
;
1914 if (strequal(domain
, get_global_sam_name())) {
1915 struct samr_CryptPassword new_nt_password
;
1916 struct samr_CryptPassword new_lm_password
;
1917 struct samr_Password old_nt_hash_enc
;
1918 struct samr_Password old_lanman_hash_enc
;
1919 enum samPwdChangeReason rejectReason
;
1921 uchar old_nt_hash
[16];
1922 uchar old_lanman_hash
[16];
1923 uchar new_nt_hash
[16];
1924 uchar new_lanman_hash
[16];
1926 contact_domain
= NULL
;
1928 E_md4hash(oldpass
, old_nt_hash
);
1929 E_md4hash(newpass
, new_nt_hash
);
1931 if (lp_client_lanman_auth() &&
1932 E_deshash(newpass
, new_lanman_hash
) &&
1933 E_deshash(oldpass
, old_lanman_hash
)) {
1935 /* E_deshash returns false for 'long' passwords (> 14
1936 DOS chars). This allows us to match Win2k, which
1937 does not store a LM hash for these passwords (which
1938 would reduce the effective password length to 14) */
1940 encode_pw_buffer(new_lm_password
.data
, newpass
, STR_UNICODE
);
1941 arcfour_crypt(new_lm_password
.data
, old_nt_hash
, 516);
1942 E_old_pw_hash(new_nt_hash
, old_lanman_hash
, old_lanman_hash_enc
.hash
);
1944 ZERO_STRUCT(new_lm_password
);
1945 ZERO_STRUCT(old_lanman_hash_enc
);
1948 encode_pw_buffer(new_nt_password
.data
, newpass
, STR_UNICODE
);
1950 arcfour_crypt(new_nt_password
.data
, old_nt_hash
, 516);
1951 E_old_pw_hash(new_nt_hash
, old_nt_hash
, old_nt_hash_enc
.hash
);
1953 result
= pass_oem_change(
1955 new_lm_password
.data
, old_lanman_hash_enc
.hash
,
1956 new_nt_password
.data
, old_nt_hash_enc
.hash
,
1961 /* Get sam handle */
1963 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
1965 if (!NT_STATUS_IS_OK(result
)) {
1966 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
1970 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
1977 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1979 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
1981 fill_in_password_policy(state
->response
, info
);
1983 state
->response
->data
.auth
.reject_reason
=
1984 reject
->extendedFailureReason
;
1989 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1990 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1991 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1992 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1994 /* only fallback when the chgpasswd_user3 call is not supported */
1995 if ((NT_STATUS_EQUAL(result
, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR
))) ||
1996 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
)) ||
1997 (NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
)) ||
1998 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
))) {
2000 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2001 nt_errstr(result
)));
2003 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2005 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2006 Map to the same status code as Windows 2003. */
2008 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2009 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2015 if (NT_STATUS_IS_OK(result
) && (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
2016 if (lp_winbind_offline_logon()) {
2017 result
= winbindd_update_creds_by_name(contact_domain
,
2018 state
->mem_ctx
, user
,
2020 /* Again, this happens when we login from gdm or xdm
2021 * and the password expires, *BUT* cached crendentials
2022 * doesn't exist. winbindd_update_creds_by_name()
2023 * returns NT_STATUS_NO_SUCH_USER.
2024 * This is not a failure.
2027 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2028 result
= NT_STATUS_OK
;
2031 if (!NT_STATUS_IS_OK(result
)) {
2032 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
2033 goto process_result
;
2038 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2040 NTSTATUS policy_ret
;
2042 policy_ret
= fillup_password_policy(contact_domain
, state
);
2044 /* failure of this is non critical, it will just provide no
2045 * additional information to the client why the change has
2046 * failed - Guenther */
2048 if (!NT_STATUS_IS_OK(policy_ret
)) {
2049 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2050 goto process_result
;
2056 set_auth_errors(state
->response
, result
);
2058 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2059 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2062 state
->response
->data
.auth
.nt_status_string
,
2063 state
->response
->data
.auth
.pam_error
));
2065 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2068 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2069 struct winbindd_cli_state
*state
)
2071 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2073 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2074 state
->request
->data
.logoff
.user
));
2076 if (!(state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
2077 result
= NT_STATUS_OK
;
2078 goto process_result
;
2081 if (state
->request
->data
.logoff
.krb5ccname
[0] == '\0') {
2082 result
= NT_STATUS_OK
;
2083 goto process_result
;
2088 if (state
->request
->data
.logoff
.uid
< 0) {
2089 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2090 goto process_result
;
2093 /* what we need here is to find the corresponding krb5 ccache name *we*
2094 * created for a given username and destroy it */
2096 if (!ccache_entry_exists(state
->request
->data
.logoff
.user
)) {
2097 result
= NT_STATUS_OK
;
2098 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2099 goto process_result
;
2102 if (!ccache_entry_identical(state
->request
->data
.logoff
.user
,
2103 state
->request
->data
.logoff
.uid
,
2104 state
->request
->data
.logoff
.krb5ccname
)) {
2105 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2106 goto process_result
;
2109 result
= remove_ccache(state
->request
->data
.logoff
.user
);
2110 if (!NT_STATUS_IS_OK(result
)) {
2111 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2112 nt_errstr(result
)));
2113 goto process_result
;
2117 result
= NT_STATUS_NOT_SUPPORTED
;
2123 set_auth_errors(state
->response
, result
);
2125 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2128 /* Change user password with auth crap*/
2130 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state
*state
)
2132 struct winbindd_domain
*domain
= NULL
;
2133 const char *domain_name
= NULL
;
2135 /* Ensure null termination */
2136 state
->request
->data
.chng_pswd_auth_crap
.user
[
2137 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2138 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2139 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2141 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2142 (unsigned long)state
->pid
,
2143 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2144 state
->request
->data
.chng_pswd_auth_crap
.user
));
2146 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
!= '\0') {
2147 domain_name
= state
->request
->data
.chng_pswd_auth_crap
.domain
;
2148 } else if (lp_winbind_use_default_domain()) {
2149 domain_name
= lp_workgroup();
2152 if (domain_name
!= NULL
)
2153 domain
= find_domain_from_name(domain_name
);
2155 if (domain
!= NULL
) {
2156 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2157 "%s\n", (unsigned long)state
->pid
,domain
->name
));
2158 sendto_domain(state
, domain
);
2162 set_auth_errors(state
->response
, NT_STATUS_NO_SUCH_USER
);
2163 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2164 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2165 state
->request
->data
.chng_pswd_auth_crap
.user
,
2166 state
->response
->data
.auth
.nt_status_string
,
2167 state
->response
->data
.auth
.pam_error
));
2168 request_error(state
);
2172 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2175 DATA_BLOB new_nt_password
;
2176 DATA_BLOB old_nt_hash_enc
;
2177 DATA_BLOB new_lm_password
;
2178 DATA_BLOB old_lm_hash_enc
;
2179 fstring domain
,user
;
2180 struct policy_handle dom_pol
;
2181 struct winbindd_domain
*contact_domain
= domainSt
;
2182 struct rpc_pipe_client
*cli
;
2184 /* Ensure null termination */
2185 state
->request
->data
.chng_pswd_auth_crap
.user
[
2186 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2187 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2188 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2192 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2193 (unsigned long)state
->pid
,
2194 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2195 state
->request
->data
.chng_pswd_auth_crap
.user
));
2197 if (lp_winbind_offline_logon()) {
2198 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2199 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2200 result
= NT_STATUS_ACCESS_DENIED
;
2204 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
) {
2205 fstrcpy(domain
,state
->request
->data
.chng_pswd_auth_crap
.domain
);
2207 parse_domain_user(state
->request
->data
.chng_pswd_auth_crap
.user
,
2211 DEBUG(3,("no domain specified with username (%s) - "
2213 state
->request
->data
.chng_pswd_auth_crap
.user
));
2214 result
= NT_STATUS_NO_SUCH_USER
;
2219 if (!*domain
&& lp_winbind_use_default_domain()) {
2220 fstrcpy(domain
,(char *)lp_workgroup());
2224 fstrcpy(user
, state
->request
->data
.chng_pswd_auth_crap
.user
);
2227 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2228 (unsigned long)state
->pid
, domain
, user
));
2230 /* Change password */
2231 new_nt_password
= data_blob_talloc(
2233 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd
,
2234 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2236 old_nt_hash_enc
= data_blob_talloc(
2238 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2239 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2241 if(state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2242 new_lm_password
= data_blob_talloc(
2244 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd
,
2245 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2247 old_lm_hash_enc
= data_blob_talloc(
2249 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2250 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2252 new_lm_password
.length
= 0;
2253 old_lm_hash_enc
.length
= 0;
2256 /* Get sam handle */
2258 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2259 if (!NT_STATUS_IS_OK(result
)) {
2260 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2264 result
= rpccli_samr_chng_pswd_auth_crap(
2265 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2266 new_lm_password
, old_lm_hash_enc
);
2270 set_auth_errors(state
->response
, result
);
2272 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2273 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2275 state
->response
->data
.auth
.nt_status_string
,
2276 state
->response
->data
.auth
.pam_error
));
2278 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;