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 2 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, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #define DBGC_CLASS DBGC_WINBIND
31 static NTSTATUS
append_info3_as_txt(TALLOC_CTX
*mem_ctx
,
32 struct winbindd_cli_state
*state
,
33 NET_USER_INFO_3
*info3
)
37 state
->response
.data
.auth
.info3
.logon_time
=
38 nt_time_to_unix(&(info3
->logon_time
));
39 state
->response
.data
.auth
.info3
.logoff_time
=
40 nt_time_to_unix(&(info3
->logoff_time
));
41 state
->response
.data
.auth
.info3
.kickoff_time
=
42 nt_time_to_unix(&(info3
->kickoff_time
));
43 state
->response
.data
.auth
.info3
.pass_last_set_time
=
44 nt_time_to_unix(&(info3
->pass_last_set_time
));
45 state
->response
.data
.auth
.info3
.pass_can_change_time
=
46 nt_time_to_unix(&(info3
->pass_can_change_time
));
47 state
->response
.data
.auth
.info3
.pass_must_change_time
=
48 nt_time_to_unix(&(info3
->pass_must_change_time
));
50 state
->response
.data
.auth
.info3
.logon_count
= info3
->logon_count
;
51 state
->response
.data
.auth
.info3
.bad_pw_count
= info3
->bad_pw_count
;
53 state
->response
.data
.auth
.info3
.user_rid
= info3
->user_rid
;
54 state
->response
.data
.auth
.info3
.group_rid
= info3
->group_rid
;
55 sid_to_string(str_sid
, &(info3
->dom_sid
.sid
));
56 fstrcpy(state
->response
.data
.auth
.info3
.dom_sid
, str_sid
);
58 state
->response
.data
.auth
.info3
.num_groups
= info3
->num_groups
;
59 state
->response
.data
.auth
.info3
.user_flgs
= info3
->user_flgs
;
61 state
->response
.data
.auth
.info3
.acct_flags
= info3
->acct_flags
;
62 state
->response
.data
.auth
.info3
.num_other_sids
= info3
->num_other_sids
;
64 unistr2_to_ascii(state
->response
.data
.auth
.info3
.user_name
,
65 &info3
->uni_user_name
, -1);
66 unistr2_to_ascii(state
->response
.data
.auth
.info3
.full_name
,
67 &info3
->uni_full_name
, -1);
68 unistr2_to_ascii(state
->response
.data
.auth
.info3
.logon_script
,
69 &info3
->uni_logon_script
, -1);
70 unistr2_to_ascii(state
->response
.data
.auth
.info3
.profile_path
,
71 &info3
->uni_profile_path
, -1);
72 unistr2_to_ascii(state
->response
.data
.auth
.info3
.home_dir
,
73 &info3
->uni_home_dir
, -1);
74 unistr2_to_ascii(state
->response
.data
.auth
.info3
.dir_drive
,
75 &info3
->uni_dir_drive
, -1);
77 unistr2_to_ascii(state
->response
.data
.auth
.info3
.logon_srv
,
78 &info3
->uni_logon_srv
, -1);
79 unistr2_to_ascii(state
->response
.data
.auth
.info3
.logon_dom
,
80 &info3
->uni_logon_dom
, -1);
85 static NTSTATUS
append_info3_as_ndr(TALLOC_CTX
*mem_ctx
,
86 struct winbindd_cli_state
*state
,
87 NET_USER_INFO_3
*info3
)
91 if (!prs_init(&ps
, 256 /* Random, non-zero number */, mem_ctx
, MARSHALL
)) {
92 return NT_STATUS_NO_MEMORY
;
94 if (!net_io_user_info3("", info3
, &ps
, 1, 3, False
)) {
96 return NT_STATUS_UNSUCCESSFUL
;
99 size
= prs_data_size(&ps
);
100 state
->response
.extra_data
= SMB_MALLOC(size
);
101 if (!state
->response
.extra_data
) {
103 return NT_STATUS_NO_MEMORY
;
105 memset( state
->response
.extra_data
, '\0', size
);
106 prs_copy_all_data_out(state
->response
.extra_data
, &ps
);
107 state
->response
.length
+= size
;
112 static NTSTATUS
check_info3_in_group(TALLOC_CTX
*mem_ctx
,
113 NET_USER_INFO_3
*info3
,
114 const char *group_sid
)
116 DOM_SID require_membership_of_sid
;
118 size_t num_all_sids
= (2 + info3
->num_groups2
+ info3
->num_other_sids
);
121 /* Parse the 'required group' SID */
123 if (!group_sid
|| !group_sid
[0]) {
124 /* NO sid supplied, all users may access */
128 if (!string_to_sid(&require_membership_of_sid
, group_sid
)) {
129 DEBUG(0, ("check_info3_in_group: could not parse %s as a SID!",
132 return NT_STATUS_INVALID_PARAMETER
;
135 all_sids
= TALLOC_ARRAY(mem_ctx
, DOM_SID
, num_all_sids
);
137 return NT_STATUS_NO_MEMORY
;
139 /* and create (by appending rids) the 'domain' sids */
141 sid_copy(&all_sids
[0], &(info3
->dom_sid
.sid
));
143 if (!sid_append_rid(&all_sids
[0], info3
->user_rid
)) {
144 DEBUG(3,("could not append user's primary RID 0x%x\n",
147 return NT_STATUS_INVALID_PARAMETER
;
151 sid_copy(&all_sids
[1], &(info3
->dom_sid
.sid
));
153 if (!sid_append_rid(&all_sids
[1], info3
->group_rid
)) {
154 DEBUG(3,("could not append additional group rid 0x%x\n",
157 return NT_STATUS_INVALID_PARAMETER
;
161 for (i
= 0; i
< info3
->num_groups2
; i
++) {
163 sid_copy(&all_sids
[j
], &(info3
->dom_sid
.sid
));
165 if (!sid_append_rid(&all_sids
[j
], info3
->gids
[i
].g_rid
)) {
166 DEBUG(3,("could not append additional group rid 0x%x\n",
167 info3
->gids
[i
].g_rid
));
169 return NT_STATUS_INVALID_PARAMETER
;
174 /* Copy 'other' sids. We need to do sid filtering here to
175 prevent possible elevation of privileges. See:
177 http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
180 for (i
= 0; i
< info3
->num_other_sids
; i
++) {
181 sid_copy(&all_sids
[info3
->num_groups2
+ i
+ 2],
182 &info3
->other_sids
[i
].sid
);
186 for (i
= 0; i
< j
; i
++) {
188 DEBUG(10, ("User has SID: %s\n",
189 sid_to_string(sid1
, &all_sids
[i
])));
190 if (sid_equal(&require_membership_of_sid
, &all_sids
[i
])) {
191 DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n",
192 sid_to_string(sid1
, &require_membership_of_sid
), sid_to_string(sid2
, &all_sids
[i
])));
197 /* Do not distinguish this error from a wrong username/pw */
199 return NT_STATUS_LOGON_FAILURE
;
202 static struct winbindd_domain
*find_auth_domain(struct winbindd_cli_state
*state
,
203 const char *domain_name
)
205 struct winbindd_domain
*domain
;
208 domain
= find_domain_from_name_noinit(domain_name
);
209 if (domain
== NULL
) {
210 DEBUG(3, ("Authentication for domain [%s] refused"
211 "as it is not a trusted domain\n",
217 if (is_myname(domain_name
)) {
218 DEBUG(3, ("Authentication for domain %s (local domain "
219 "to this server) not supported at this "
220 "stage\n", domain_name
));
224 return find_our_domain();
227 static void set_auth_errors(struct winbindd_response
*resp
, NTSTATUS result
)
229 resp
->data
.auth
.nt_status
= NT_STATUS_V(result
);
230 fstrcpy(resp
->data
.auth
.nt_status_string
, nt_errstr(result
));
232 /* we might have given a more useful error above */
233 if (*resp
->data
.auth
.error_string
== '\0')
234 fstrcpy(resp
->data
.auth
.error_string
,
235 get_friendly_nt_error_msg(result
));
236 resp
->data
.auth
.pam_error
= nt_status_to_pam(result
);
239 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
240 struct winbindd_cli_state
*state
)
242 struct winbindd_methods
*methods
;
243 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
244 SAM_UNK_INFO_1 password_policy
;
246 methods
= domain
->methods
;
248 status
= methods
->password_policy(domain
, state
->mem_ctx
, &password_policy
);
249 if (NT_STATUS_IS_ERR(status
)) {
253 state
->response
.data
.auth
.policy
.min_length_password
=
254 password_policy
.min_length_password
;
255 state
->response
.data
.auth
.policy
.password_history
=
256 password_policy
.password_history
;
257 state
->response
.data
.auth
.policy
.password_properties
=
258 password_policy
.password_properties
;
259 state
->response
.data
.auth
.policy
.expire
=
260 nt_time_to_unix_abs(&(password_policy
.expire
));
261 state
->response
.data
.auth
.policy
.min_passwordage
=
262 nt_time_to_unix_abs(&(password_policy
.min_passwordage
));
267 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
269 uint16
*max_allowed_bad_attempts
)
271 struct winbindd_methods
*methods
;
272 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
273 SAM_UNK_INFO_12 lockout_policy
;
275 *max_allowed_bad_attempts
= 0;
277 methods
= domain
->methods
;
279 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
280 if (NT_STATUS_IS_ERR(status
)) {
284 *max_allowed_bad_attempts
= lockout_policy
.bad_attempt_lockout
;
289 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
291 uint32
*password_properties
)
293 struct winbindd_methods
*methods
;
294 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
295 SAM_UNK_INFO_1 password_policy
;
297 *password_properties
= 0;
299 methods
= domain
->methods
;
301 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
302 if (NT_STATUS_IS_ERR(status
)) {
306 *password_properties
= password_policy
.password_properties
;
311 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
314 BOOL
*internal_ccache
)
316 /* accept FILE and WRFILE as krb5_cc_type from the client and then
317 * build the full ccname string based on the user's uid here -
320 const char *gen_cc
= NULL
;
322 *internal_ccache
= True
;
328 if (!type
|| type
[0] == '\0') {
332 if (strequal(type
, "FILE")) {
333 gen_cc
= talloc_asprintf(mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
334 } else if (strequal(type
, "WRFILE")) {
335 gen_cc
= talloc_asprintf(mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
337 DEBUG(10,("we don't allow to set a %s type ccache\n", type
));
341 *internal_ccache
= False
;
345 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbind_cache");
348 if (gen_cc
== NULL
) {
349 DEBUG(0,("out of memory\n"));
353 DEBUG(10,("using ccache: %s %s\n", gen_cc
, *internal_ccache
? "(internal)":""));
358 static uid_t
get_uid_from_state(struct winbindd_cli_state
*state
)
362 uid
= state
->request
.data
.auth
.uid
;
365 DEBUG(1,("invalid uid: '%d'\n", uid
));
371 static void setup_return_cc_name(struct winbindd_cli_state
*state
, const char *cc
)
373 const char *type
= state
->request
.data
.auth
.krb5_cc_type
;
375 state
->response
.data
.auth
.krb5ccname
[0] = '\0';
377 if (type
[0] == '\0') {
381 if (!strequal(type
, "FILE") &&
382 !strequal(type
, "WRFILE")) {
383 DEBUG(10,("won't return krbccname for a %s type ccache\n",
388 fstrcpy(state
->response
.data
.auth
.krb5ccname
, cc
);
391 /**********************************************************************
392 Authenticate a user with a clear text password using Kerberos and fill up
394 **********************************************************************/
395 static NTSTATUS
winbindd_raw_kerberos_login(struct winbindd_domain
*domain
,
396 struct winbindd_cli_state
*state
,
397 NET_USER_INFO_3
**info3
)
400 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
401 krb5_error_code krb5_ret
;
402 DATA_BLOB tkt
, session_key_krb5
;
403 DATA_BLOB ap_rep
, session_key
;
404 PAC_DATA
*pac_data
= NULL
;
405 PAC_LOGON_INFO
*logon_info
= NULL
;
406 char *client_princ
= NULL
;
407 char *client_princ_out
= NULL
;
408 char *local_service
= NULL
;
409 const char *cc
= NULL
;
410 const char *principal_s
= NULL
;
411 const char *service
= NULL
;
413 fstring name_domain
, name_user
;
414 time_t ticket_lifetime
= 0;
415 time_t renewal_until
= 0;
418 time_t time_offset
= 0;
419 BOOL internal_ccache
= True
;
421 ZERO_STRUCT(session_key
);
422 ZERO_STRUCT(session_key_krb5
);
431 * prepare a krb5_cc_cache string for the user */
433 uid
= get_uid_from_state(state
);
435 DEBUG(0,("no valid uid\n"));
438 cc
= generate_krb5_ccache(state
->mem_ctx
,
439 state
->request
.data
.auth
.krb5_cc_type
,
440 state
->request
.data
.auth
.uid
,
443 return NT_STATUS_NO_MEMORY
;
448 * get kerberos properties */
450 if (domain
->private_data
) {
451 ads
= (ADS_STRUCT
*)domain
->private_data
;
452 time_offset
= ads
->auth
.time_offset
;
457 * do kerberos auth and setup ccache as the user */
459 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
461 realm
= domain
->alt_name
;
464 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
465 if (principal_s
== NULL
) {
466 return NT_STATUS_NO_MEMORY
;
469 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
470 if (service
== NULL
) {
471 return NT_STATUS_NO_MEMORY
;
474 /* if this is a user ccache, we need to act as the user to let the krb5
475 * library handle the chown, etc. */
477 /************************ NON-ROOT **********************/
479 if (!internal_ccache
) {
482 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
485 krb5_ret
= kerberos_kinit_password(principal_s
,
486 state
->request
.data
.auth
.pass
,
492 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
);
495 DEBUG(1,("winbindd_raw_kerberos_login: kinit failed for '%s' with: %s (%d)\n",
496 principal_s
, error_message(krb5_ret
), krb5_ret
));
497 result
= krb5_to_nt_status(krb5_ret
);
501 /* does http_timestring use heimdals libroken strftime?? - Guenther */
502 DEBUG(10,("got TGT for %s in %s (valid until: %s (%d), renewable till: %s (%d))\n",
504 http_timestring(ticket_lifetime
), (int)ticket_lifetime
,
505 http_timestring(renewal_until
), (int)renewal_until
));
507 client_princ
= talloc_strdup(state
->mem_ctx
, global_myname());
508 if (client_princ
== NULL
) {
509 result
= NT_STATUS_NO_MEMORY
;
512 strlower_m(client_princ
);
514 local_service
= talloc_asprintf(state
->mem_ctx
, "HOST/%s@%s", client_princ
, lp_realm());
515 if (local_service
== NULL
) {
516 DEBUG(0,("winbindd_raw_kerberos_login: out of memory\n"));
517 result
= NT_STATUS_NO_MEMORY
;
521 krb5_ret
= cli_krb5_get_ticket(local_service
,
528 DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for: %s\n",
530 result
= krb5_to_nt_status(krb5_ret
);
534 if (!internal_ccache
) {
538 /************************ NON-ROOT **********************/
540 result
= ads_verify_ticket(state
->mem_ctx
,
547 if (!NT_STATUS_IS_OK(result
)) {
548 DEBUG(0,("winbindd_raw_kerberos_login: ads_verify_ticket failed: %s\n",
553 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
557 DEBUG(3,("winbindd_raw_kerberos_login: no pac data\n"));
558 result
= NT_STATUS_INVALID_PARAMETER
;
562 logon_info
= get_logon_info_from_pac(pac_data
);
563 if (logon_info
== NULL
) {
564 DEBUG(1,("winbindd_raw_kerberos_login: no logon info\n"));
565 result
= NT_STATUS_INVALID_PARAMETER
;
571 * put results together */
573 *info3
= &logon_info
->info3
;
575 /* if we had a user's ccache then return that string for the pam
578 if (!internal_ccache
) {
580 setup_return_cc_name(state
, cc
);
582 result
= add_ccache_to_list(principal_s
,
585 state
->request
.data
.auth
.user
,
587 state
->request
.data
.auth
.pass
,
592 lp_winbind_refresh_tickets());
594 if (!NT_STATUS_IS_OK(result
)) {
595 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
600 result
= NT_STATUS_OK
;
603 data_blob_free(&session_key
);
604 data_blob_free(&session_key_krb5
);
605 data_blob_free(&ap_rep
);
606 data_blob_free(&tkt
);
608 SAFE_FREE(client_princ_out
);
610 if (!internal_ccache
) {
616 return NT_STATUS_NOT_SUPPORTED
;
617 #endif /* HAVE_KRB5 */
620 void winbindd_pam_auth(struct winbindd_cli_state
*state
)
622 struct winbindd_domain
*domain
;
623 fstring name_domain
, name_user
;
625 /* Ensure null termination */
626 state
->request
.data
.auth
.user
627 [sizeof(state
->request
.data
.auth
.user
)-1]='\0';
629 /* Ensure null termination */
630 state
->request
.data
.auth
.pass
631 [sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
633 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state
->pid
,
634 state
->request
.data
.auth
.user
));
636 /* Parse domain and username */
638 if (!parse_domain_user(state
->request
.data
.auth
.user
,
639 name_domain
, name_user
)) {
640 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
641 DEBUG(5, ("Plain text authentication for %s returned %s "
643 state
->request
.data
.auth
.user
,
644 state
->response
.data
.auth
.nt_status_string
,
645 state
->response
.data
.auth
.pam_error
));
646 request_error(state
);
650 domain
= find_auth_domain(state
, name_domain
);
652 if (domain
== NULL
) {
653 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
654 DEBUG(5, ("Plain text authentication for %s returned %s "
656 state
->request
.data
.auth
.user
,
657 state
->response
.data
.auth
.nt_status_string
,
658 state
->response
.data
.auth
.pam_error
));
659 request_error(state
);
663 sendto_domain(state
, domain
);
666 NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
667 struct winbindd_cli_state
*state
,
668 NET_USER_INFO_3
**info3
)
670 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
671 uint16 max_allowed_bad_attempts
;
672 fstring name_domain
, name_user
;
674 enum SID_NAME_USE type
;
675 uchar new_nt_pass
[NT_HASH_LEN
];
676 const uint8
*cached_nt_pass
;
677 NET_USER_INFO_3
*my_info3
;
678 time_t kickoff_time
, must_change_time
;
684 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
686 /* Parse domain and username */
688 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
691 if (!lookup_cached_name(state
->mem_ctx
,
696 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
697 return NT_STATUS_NO_SUCH_USER
;
700 if (type
!= SID_NAME_USER
) {
701 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
702 return NT_STATUS_LOGON_FAILURE
;
705 result
= winbindd_get_creds(domain
,
710 if (!NT_STATUS_IS_OK(result
)) {
711 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
717 E_md4hash(state
->request
.data
.auth
.pass
, new_nt_pass
);
719 dump_data(100, (const char *)new_nt_pass
, NT_HASH_LEN
);
720 dump_data(100, (const char *)cached_nt_pass
, NT_HASH_LEN
);
722 if (!memcmp(cached_nt_pass
, new_nt_pass
, NT_HASH_LEN
)) {
724 /* User *DOES* know the password, update logon_time and reset
727 my_info3
->user_flgs
|= LOGON_CACHED_ACCOUNT
;
729 if (my_info3
->acct_flags
& ACB_AUTOLOCK
) {
730 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
733 if (my_info3
->acct_flags
& ACB_DISABLED
) {
734 return NT_STATUS_ACCOUNT_DISABLED
;
737 if (my_info3
->acct_flags
& ACB_WSTRUST
) {
738 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
741 if (my_info3
->acct_flags
& ACB_SVRTRUST
) {
742 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
745 if (my_info3
->acct_flags
& ACB_DOMTRUST
) {
746 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
749 if (!(my_info3
->acct_flags
& ACB_NORMAL
)) {
750 DEBUG(10,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
751 my_info3
->acct_flags
));
752 return NT_STATUS_LOGON_FAILURE
;
755 kickoff_time
= nt_time_to_unix(&my_info3
->kickoff_time
);
756 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
757 return NT_STATUS_ACCOUNT_EXPIRED
;
760 must_change_time
= nt_time_to_unix(&my_info3
->pass_must_change_time
);
761 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
762 return NT_STATUS_PASSWORD_EXPIRED
;
765 /* FIXME: we possibly should handle logon hours as well (does xp when
766 * offline?) see auth/auth_sam.c:sam_account_ok for details */
768 unix_to_nt_time(&my_info3
->logon_time
, time(NULL
));
769 my_info3
->bad_pw_count
= 0;
771 result
= winbindd_update_creds_by_info3(domain
,
773 state
->request
.data
.auth
.user
,
774 state
->request
.data
.auth
.pass
,
776 if (!NT_STATUS_IS_OK(result
)) {
777 DEBUG(1,("failed to update creds: %s\n", nt_errstr(result
)));
785 /* User does *NOT* know the correct password, modify info3 accordingly */
787 /* failure of this is not critical */
788 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
789 if (!NT_STATUS_IS_OK(result
)) {
790 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
791 "Won't be able to honour account lockout policies\n"));
794 /* increase counter */
795 my_info3
->bad_pw_count
++;
797 if (max_allowed_bad_attempts
== 0) {
802 if (my_info3
->bad_pw_count
>= max_allowed_bad_attempts
) {
804 uint32 password_properties
;
806 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
807 if (!NT_STATUS_IS_OK(result
)) {
808 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
811 if ((my_info3
->user_rid
!= DOMAIN_USER_RID_ADMIN
) ||
812 (password_properties
& DOMAIN_LOCKOUT_ADMINS
)) {
813 my_info3
->acct_flags
|= ACB_AUTOLOCK
;
818 result
= winbindd_update_creds_by_info3(domain
,
820 state
->request
.data
.auth
.user
,
824 if (!NT_STATUS_IS_OK(result
)) {
825 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
829 return NT_STATUS_LOGON_FAILURE
;
832 NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
833 struct winbindd_cli_state
*state
,
834 NET_USER_INFO_3
**info3
)
836 struct winbindd_domain
*contact_domain
;
837 fstring name_domain
, name_user
;
840 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
842 /* Parse domain and username */
844 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
846 /* what domain should we contact? */
849 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
850 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
851 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
852 result
= NT_STATUS_NO_SUCH_USER
;
857 if (is_myname(name_domain
)) {
858 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
859 result
= NT_STATUS_NO_SUCH_USER
;
863 contact_domain
= find_domain_from_name(name_domain
);
864 if (contact_domain
== NULL
) {
865 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
866 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
868 contact_domain
= find_our_domain();
872 set_dc_type_and_flags(contact_domain
);
874 if (!contact_domain
->active_directory
) {
875 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
876 return NT_STATUS_INVALID_LOGON_TYPE
;
879 result
= winbindd_raw_kerberos_login(contact_domain
, state
, info3
);
884 NTSTATUS
winbindd_dual_pam_auth_samlogon(struct winbindd_domain
*domain
,
885 struct winbindd_cli_state
*state
,
886 NET_USER_INFO_3
**info3
)
889 struct rpc_pipe_client
*netlogon_pipe
;
894 unsigned char local_lm_response
[24];
895 unsigned char local_nt_response
[24];
896 struct winbindd_domain
*contact_domain
;
897 fstring name_domain
, name_user
;
900 NET_USER_INFO_3
*my_info3
;
906 my_info3
= TALLOC_ZERO_P(state
->mem_ctx
, NET_USER_INFO_3
);
907 if (my_info3
== NULL
) {
908 return NT_STATUS_NO_MEMORY
;
912 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
914 /* Parse domain and username */
916 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
918 /* do password magic */
921 generate_random_buffer(chal
, 8);
922 if (lp_client_ntlmv2_auth()) {
923 DATA_BLOB server_chal
;
924 DATA_BLOB names_blob
;
925 DATA_BLOB nt_response
;
926 DATA_BLOB lm_response
;
927 server_chal
= data_blob_talloc(state
->mem_ctx
, chal
, 8);
929 /* note that the 'workgroup' here is a best guess - we don't know
930 the server's domain at this point. The 'server name' is also
933 names_blob
= NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
935 if (!SMBNTLMv2encrypt(name_user
, name_domain
,
936 state
->request
.data
.auth
.pass
,
939 &lm_response
, &nt_response
, NULL
)) {
940 data_blob_free(&names_blob
);
941 data_blob_free(&server_chal
);
942 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
943 result
= NT_STATUS_NO_MEMORY
;
946 data_blob_free(&names_blob
);
947 data_blob_free(&server_chal
);
948 lm_resp
= data_blob_talloc(state
->mem_ctx
, lm_response
.data
,
950 nt_resp
= data_blob_talloc(state
->mem_ctx
, nt_response
.data
,
952 data_blob_free(&lm_response
);
953 data_blob_free(&nt_response
);
956 if (lp_client_lanman_auth()
957 && SMBencrypt(state
->request
.data
.auth
.pass
,
959 local_lm_response
)) {
960 lm_resp
= data_blob_talloc(state
->mem_ctx
,
962 sizeof(local_lm_response
));
964 lm_resp
= data_blob(NULL
, 0);
966 SMBNTencrypt(state
->request
.data
.auth
.pass
,
970 nt_resp
= data_blob_talloc(state
->mem_ctx
,
972 sizeof(local_nt_response
));
975 /* what domain should we contact? */
978 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
979 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
980 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
981 result
= NT_STATUS_NO_SUCH_USER
;
986 if (is_myname(name_domain
)) {
987 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
988 result
= NT_STATUS_NO_SUCH_USER
;
992 contact_domain
= find_our_domain();
995 /* check authentication loop */
999 ZERO_STRUCTP(my_info3
);
1002 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1004 if (!NT_STATUS_IS_OK(result
)) {
1005 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1009 result
= rpccli_netlogon_sam_network_logon(netlogon_pipe
,
1012 contact_domain
->dcname
, /* server name */
1013 name_user
, /* user name */
1014 name_domain
, /* target domain */
1015 global_myname(), /* workstation */
1022 /* We have to try a second time as cm_connect_netlogon
1023 might not yet have noticed that the DC has killed
1026 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1031 /* if we get access denied, a possible cause was that we had
1032 and open connection to the DC, but someone changed our
1033 machine account password out from underneath us using 'net
1034 rpc changetrustpw' */
1036 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1037 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1038 "ACCESS_DENIED. Maybe the trust account "
1039 "password was changed and we didn't know it. "
1040 "Killing connections to domain %s\n",
1042 invalidate_cm_connection(&contact_domain
->conn
);
1046 } while ( (attempts
< 2) && retry
);
1053 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1054 struct winbindd_cli_state
*state
)
1056 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1057 fstring name_domain
, name_user
;
1058 NET_USER_INFO_3
*info3
= NULL
;
1060 /* Ensure null termination */
1061 state
->request
.data
.auth
.user
[sizeof(state
->request
.data
.auth
.user
)-1]='\0';
1063 /* Ensure null termination */
1064 state
->request
.data
.auth
.pass
[sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
1066 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1067 state
->request
.data
.auth
.user
));
1069 /* Parse domain and username */
1071 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
1073 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1075 /* Check for Kerberos authentication */
1076 if (domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1078 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1080 if (NT_STATUS_IS_OK(result
)) {
1081 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1082 goto process_result
;
1084 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1087 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
)) {
1088 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1089 domain
->online
= False
;
1092 if (state
->request
.flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1093 DEBUG(3,("falling back to samlogon\n"));
1101 /* Check for Samlogon authentication */
1102 if (domain
->online
) {
1103 result
= winbindd_dual_pam_auth_samlogon(domain
, state
, &info3
);
1105 if (NT_STATUS_IS_OK(result
)) {
1106 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1107 goto process_result
;
1109 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", nt_errstr(result
)));
1110 if (domain
->online
) {
1111 /* We're still online - fail. */
1114 /* Else drop through and see if we can check offline.... */
1119 /* Check for Cached logons */
1120 if (!domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1121 lp_winbind_offline_logon()) {
1123 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1125 if (NT_STATUS_IS_OK(result
)) {
1126 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1127 goto process_result
;
1129 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1136 if (NT_STATUS_IS_OK(result
)) {
1140 /* In all codepaths were result == NT_STATUS_OK info3 must have
1141 been initialized. */
1143 result
= NT_STATUS_INTERNAL_ERROR
;
1147 netsamlogon_cache_store(name_user
, info3
);
1148 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1150 /* save name_to_sid info as early as possible */
1151 sid_compose(&user_sid
, &info3
->dom_sid
.sid
, info3
->user_rid
);
1152 cache_name2sid(domain
, name_domain
, name_user
, SID_NAME_USER
, &user_sid
);
1154 /* Check if the user is in the right group */
1156 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1157 state
->request
.data
.auth
.require_membership_of_sid
))) {
1158 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1159 state
->request
.data
.auth
.user
,
1160 state
->request
.data
.auth
.require_membership_of_sid
));
1164 if (state
->request
.flags
& WBFLAG_PAM_INFO3_NDR
) {
1165 result
= append_info3_as_ndr(state
->mem_ctx
, state
, info3
);
1166 if (!NT_STATUS_IS_OK(result
)) {
1167 DEBUG(10,("Failed to append INFO3 (NDR): %s\n", nt_errstr(result
)));
1172 if (state
->request
.flags
& WBFLAG_PAM_INFO3_TEXT
) {
1173 result
= append_info3_as_txt(state
->mem_ctx
, state
, info3
);
1174 if (!NT_STATUS_IS_OK(result
)) {
1175 DEBUG(10,("Failed to append INFO3 (TXT): %s\n", nt_errstr(result
)));
1181 if ((state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1182 lp_winbind_offline_logon()) {
1184 result
= winbindd_store_creds(domain
,
1186 state
->request
.data
.auth
.user
,
1187 state
->request
.data
.auth
.pass
,
1189 if (!NT_STATUS_IS_OK(result
)) {
1190 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
1196 result
= fillup_password_policy(domain
, state
);
1198 if (!NT_STATUS_IS_OK(result
)) {
1199 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result
)));
1206 /* give us a more useful (more correct?) error code */
1207 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1208 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1209 result
= NT_STATUS_NO_LOGON_SERVERS
;
1212 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1213 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1215 /* we might have given a more useful error above */
1216 if (!*state
->response
.data
.auth
.error_string
)
1217 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1218 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1220 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1221 state
->request
.data
.auth
.user
,
1222 state
->response
.data
.auth
.nt_status_string
,
1223 state
->response
.data
.auth
.pam_error
));
1225 if ( NT_STATUS_IS_OK(result
) &&
1226 (state
->request
.flags
& WBFLAG_PAM_AFS_TOKEN
) ) {
1228 char *afsname
= talloc_strdup(state
->mem_ctx
,
1229 lp_afs_username_map());
1232 if (afsname
== NULL
) {
1236 afsname
= talloc_string_sub(state
->mem_ctx
,
1237 lp_afs_username_map(),
1239 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1241 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1248 sid_copy(&user_sid
, &info3
->dom_sid
.sid
);
1249 sid_append_rid(&user_sid
, info3
->user_rid
);
1250 sid_to_string(sidstr
, &user_sid
);
1251 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1255 if (afsname
== NULL
) {
1259 strlower_m(afsname
);
1261 DEBUG(10, ("Generating token for user %s\n", afsname
));
1263 cell
= strchr(afsname
, '@');
1272 /* Append an AFS token string */
1273 state
->response
.extra_data
=
1274 afs_createtoken_str(afsname
, cell
);
1276 if (state
->response
.extra_data
!= NULL
)
1277 state
->response
.length
+=
1278 strlen(state
->response
.extra_data
)+1;
1281 TALLOC_FREE(afsname
);
1284 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1288 /**********************************************************************
1289 Challenge Response Authentication Protocol
1290 **********************************************************************/
1292 void winbindd_pam_auth_crap(struct winbindd_cli_state
*state
)
1294 struct winbindd_domain
*domain
= NULL
;
1295 const char *domain_name
= NULL
;
1298 if (!state
->privileged
) {
1299 char *error_string
= NULL
;
1300 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1302 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1303 "on %s are set correctly.\n",
1304 get_winbind_priv_pipe_dir()));
1305 /* send a better message than ACCESS_DENIED */
1306 error_string
= talloc_asprintf(state
->mem_ctx
,
1307 "winbind client not authorized "
1308 "to use winbindd_pam_auth_crap."
1309 " Ensure permissions on %s "
1310 "are set correctly.",
1311 get_winbind_priv_pipe_dir());
1312 fstrcpy(state
->response
.data
.auth
.error_string
, error_string
);
1313 result
= NT_STATUS_ACCESS_DENIED
;
1317 /* Ensure null termination */
1318 state
->request
.data
.auth_crap
.user
1319 [sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1320 state
->request
.data
.auth_crap
.domain
1321 [sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1323 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1324 (unsigned long)state
->pid
,
1325 state
->request
.data
.auth_crap
.domain
,
1326 state
->request
.data
.auth_crap
.user
));
1328 if (*state
->request
.data
.auth_crap
.domain
!= '\0') {
1329 domain_name
= state
->request
.data
.auth_crap
.domain
;
1330 } else if (lp_winbind_use_default_domain()) {
1331 domain_name
= lp_workgroup();
1334 if (domain_name
!= NULL
)
1335 domain
= find_auth_domain(state
, domain_name
);
1337 if (domain
!= NULL
) {
1338 sendto_domain(state
, domain
);
1342 result
= NT_STATUS_NO_SUCH_USER
;
1345 set_auth_errors(&state
->response
, result
);
1346 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1347 state
->request
.data
.auth_crap
.domain
,
1348 state
->request
.data
.auth_crap
.user
,
1349 state
->response
.data
.auth
.nt_status_string
,
1350 state
->response
.data
.auth
.pam_error
));
1351 request_error(state
);
1356 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1357 struct winbindd_cli_state
*state
)
1360 NET_USER_INFO_3 info3
;
1361 struct rpc_pipe_client
*netlogon_pipe
;
1362 const char *name_user
= NULL
;
1363 const char *name_domain
= NULL
;
1364 const char *workstation
;
1365 struct winbindd_domain
*contact_domain
;
1369 DATA_BLOB lm_resp
, nt_resp
;
1371 /* This is child-only, so no check for privileged access is needed
1374 /* Ensure null termination */
1375 state
->request
.data
.auth_crap
.user
[sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1376 state
->request
.data
.auth_crap
.domain
[sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1378 name_user
= state
->request
.data
.auth_crap
.user
;
1380 if (*state
->request
.data
.auth_crap
.domain
) {
1381 name_domain
= state
->request
.data
.auth_crap
.domain
;
1382 } else if (lp_winbind_use_default_domain()) {
1383 name_domain
= lp_workgroup();
1385 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1387 result
= NT_STATUS_NO_SUCH_USER
;
1391 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1392 name_domain
, name_user
));
1394 if (*state
->request
.data
.auth_crap
.workstation
) {
1395 workstation
= state
->request
.data
.auth_crap
.workstation
;
1397 workstation
= global_myname();
1400 if (state
->request
.data
.auth_crap
.lm_resp_len
> sizeof(state
->request
.data
.auth_crap
.lm_resp
)
1401 || state
->request
.data
.auth_crap
.nt_resp_len
> sizeof(state
->request
.data
.auth_crap
.nt_resp
)) {
1402 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1403 state
->request
.data
.auth_crap
.lm_resp_len
,
1404 state
->request
.data
.auth_crap
.nt_resp_len
));
1405 result
= NT_STATUS_INVALID_PARAMETER
;
1409 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.lm_resp
,
1410 state
->request
.data
.auth_crap
.lm_resp_len
);
1411 nt_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.nt_resp
,
1412 state
->request
.data
.auth_crap
.nt_resp_len
);
1414 /* what domain should we contact? */
1417 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1418 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1419 state
->request
.data
.auth_crap
.user
, name_domain
, name_user
, name_domain
));
1420 result
= NT_STATUS_NO_SUCH_USER
;
1424 if (is_myname(name_domain
)) {
1425 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1426 result
= NT_STATUS_NO_SUCH_USER
;
1429 contact_domain
= find_our_domain();
1436 netlogon_pipe
= NULL
;
1437 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1439 if (!NT_STATUS_IS_OK(result
)) {
1440 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1441 nt_errstr(result
)));
1445 result
= rpccli_netlogon_sam_network_logon(netlogon_pipe
,
1447 state
->request
.data
.auth_crap
.logon_parameters
,
1448 contact_domain
->dcname
,
1451 /* Bug #3248 - found by Stefan Burkei. */
1452 workstation
, /* We carefully set this above so use it... */
1453 state
->request
.data
.auth_crap
.chal
,
1460 /* We have to try a second time as cm_connect_netlogon
1461 might not yet have noticed that the DC has killed
1464 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1469 /* if we get access denied, a possible cause was that we had and open
1470 connection to the DC, but someone changed our machine account password
1471 out from underneath us using 'net rpc changetrustpw' */
1473 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1474 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1475 "ACCESS_DENIED. Maybe the trust account "
1476 "password was changed and we didn't know it. "
1477 "Killing connections to domain %s\n",
1479 invalidate_cm_connection(&contact_domain
->conn
);
1483 } while ( (attempts
< 2) && retry
);
1485 if (NT_STATUS_IS_OK(result
)) {
1487 netsamlogon_cache_store(name_user
, &info3
);
1488 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), &info3
);
1490 /* Check if the user is in the right group */
1492 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, &info3
,
1493 state
->request
.data
.auth_crap
.require_membership_of_sid
))) {
1494 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1495 state
->request
.data
.auth_crap
.user
,
1496 state
->request
.data
.auth_crap
.require_membership_of_sid
));
1500 if (state
->request
.flags
& WBFLAG_PAM_INFO3_NDR
) {
1501 result
= append_info3_as_ndr(state
->mem_ctx
, state
, &info3
);
1502 } else if (state
->request
.flags
& WBFLAG_PAM_UNIX_NAME
) {
1503 /* ntlm_auth should return the unix username, per
1504 'winbind use default domain' settings and the like */
1506 fstring username_out
;
1507 const char *nt_username
, *nt_domain
;
1508 if (!(nt_username
= unistr2_tdup(state
->mem_ctx
, &(info3
.uni_user_name
)))) {
1509 /* If the server didn't give us one, just use the one we sent them */
1510 nt_username
= name_user
;
1513 if (!(nt_domain
= unistr2_tdup(state
->mem_ctx
, &(info3
.uni_logon_dom
)))) {
1514 /* If the server didn't give us one, just use the one we sent them */
1515 nt_domain
= name_domain
;
1518 fill_domain_username(username_out
, nt_domain
, nt_username
, True
);
1520 DEBUG(5, ("Setting unix username to [%s]\n", username_out
));
1522 state
->response
.extra_data
= SMB_STRDUP(username_out
);
1523 if (!state
->response
.extra_data
) {
1524 result
= NT_STATUS_NO_MEMORY
;
1527 state
->response
.length
+= strlen(state
->response
.extra_data
)+1;
1530 if (state
->request
.flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
1531 memcpy(state
->response
.data
.auth
.user_session_key
, info3
.user_sess_key
,
1532 sizeof(state
->response
.data
.auth
.user_session_key
) /* 16 */);
1534 if (state
->request
.flags
& WBFLAG_PAM_LMKEY
) {
1535 memcpy(state
->response
.data
.auth
.first_8_lm_hash
, info3
.lm_sess_key
,
1536 sizeof(state
->response
.data
.auth
.first_8_lm_hash
) /* 8 */);
1542 /* give us a more useful (more correct?) error code */
1543 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1544 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1545 result
= NT_STATUS_NO_LOGON_SERVERS
;
1548 if (state
->request
.flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1549 result
= nt_status_squash(result
);
1552 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1553 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1555 /* we might have given a more useful error above */
1556 if (!*state
->response
.data
.auth
.error_string
) {
1557 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1559 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1561 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1562 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1565 state
->response
.data
.auth
.nt_status_string
,
1566 state
->response
.data
.auth
.pam_error
));
1568 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1571 /* Change a user password */
1573 void winbindd_pam_chauthtok(struct winbindd_cli_state
*state
)
1575 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1577 char *newpass
= NULL
;
1578 fstring domain
, user
;
1580 struct winbindd_domain
*contact_domain
;
1581 struct rpc_pipe_client
*cli
;
1582 BOOL got_info
= False
;
1583 SAM_UNK_INFO_1 info
;
1584 SAMR_CHANGE_REJECT reject
;
1586 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state
->pid
,
1587 state
->request
.data
.chauthtok
.user
));
1591 parse_domain_user(state
->request
.data
.chauthtok
.user
, domain
, user
);
1593 contact_domain
= find_domain_from_name(domain
);
1594 if (!contact_domain
) {
1595 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
1596 state
->request
.data
.chauthtok
.user
, domain
, user
, domain
));
1597 result
= NT_STATUS_NO_SUCH_USER
;
1601 /* Change password */
1603 oldpass
= state
->request
.data
.chauthtok
.oldpass
;
1604 newpass
= state
->request
.data
.chauthtok
.newpass
;
1606 /* Get sam handle */
1608 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
1610 if (!NT_STATUS_IS_OK(result
)) {
1611 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
1615 result
= rpccli_samr_chgpasswd3(cli
, state
->mem_ctx
, user
, newpass
, oldpass
, &info
, &reject
);
1617 /* FIXME: need to check for other error codes ? */
1618 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
)) {
1620 state
->response
.data
.auth
.policy
.min_length_password
=
1621 info
.min_length_password
;
1622 state
->response
.data
.auth
.policy
.password_history
=
1623 info
.password_history
;
1624 state
->response
.data
.auth
.policy
.password_properties
=
1625 info
.password_properties
;
1626 state
->response
.data
.auth
.policy
.expire
=
1627 nt_time_to_unix_abs(&info
.expire
);
1628 state
->response
.data
.auth
.policy
.min_passwordage
=
1629 nt_time_to_unix_abs(&info
.min_passwordage
);
1631 state
->response
.data
.auth
.reject_reason
=
1632 reject
.reject_reason
;
1636 } else if (!NT_STATUS_IS_OK(result
)) {
1638 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
1639 nt_errstr(result
)));
1641 state
->response
.data
.auth
.reject_reason
= 0;
1643 result
= rpccli_samr_chgpasswd_user(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
1647 if (NT_STATUS_IS_OK(result
) && (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1648 lp_winbind_offline_logon()) {
1652 cred_ret
= winbindd_update_creds_by_name(contact_domain
,
1653 state
->mem_ctx
, user
,
1655 if (!NT_STATUS_IS_OK(cred_ret
)) {
1656 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(cred_ret
)));
1657 goto process_result
; /* FIXME: hm, risking inconsistant cache ? */
1661 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
1663 NTSTATUS policy_ret
;
1665 policy_ret
= fillup_password_policy(contact_domain
, state
);
1667 /* failure of this is non critical, it will just provide no
1668 * additional information to the client why the change has
1669 * failed - Guenther */
1671 if (!NT_STATUS_IS_OK(policy_ret
)) {
1672 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
1673 goto process_result
;
1679 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1680 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1681 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1682 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1684 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1685 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1688 state
->response
.data
.auth
.nt_status_string
,
1689 state
->response
.data
.auth
.pam_error
));
1691 if (NT_STATUS_IS_OK(result
)) {
1694 request_error(state
);
1698 void winbindd_pam_logoff(struct winbindd_cli_state
*state
)
1700 struct winbindd_domain
*domain
;
1701 fstring name_domain
, user
;
1703 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state
->pid
,
1704 state
->request
.data
.logoff
.user
));
1706 /* Ensure null termination */
1707 state
->request
.data
.logoff
.user
1708 [sizeof(state
->request
.data
.logoff
.user
)-1]='\0';
1710 state
->request
.data
.logoff
.krb5ccname
1711 [sizeof(state
->request
.data
.logoff
.krb5ccname
)-1]='\0';
1713 parse_domain_user(state
->request
.data
.logoff
.user
, name_domain
, user
);
1715 domain
= find_auth_domain(state
, name_domain
);
1717 if (domain
== NULL
) {
1718 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
1719 DEBUG(5, ("Pam Logoff for %s returned %s "
1721 state
->request
.data
.auth
.user
,
1722 state
->response
.data
.auth
.nt_status_string
,
1723 state
->response
.data
.auth
.pam_error
));
1724 request_error(state
);
1728 sendto_domain(state
, domain
);
1731 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
1732 struct winbindd_cli_state
*state
)
1734 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
1735 struct WINBINDD_CCACHE_ENTRY
*entry
;
1738 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
1739 state
->request
.data
.logoff
.user
));
1741 if (!(state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1742 result
= NT_STATUS_OK
;
1743 goto process_result
;
1748 /* what we need here is to find the corresponding krb5 ccache name *we*
1749 * created for a given username and destroy it (as the user who created it) */
1751 entry
= get_ccache_by_username(state
->request
.data
.logoff
.user
);
1752 if (entry
== NULL
) {
1753 DEBUG(10,("winbindd_pam_logoff: could not get ccname for user %s\n",
1754 state
->request
.data
.logoff
.user
));
1755 goto process_result
;
1758 DEBUG(10,("winbindd_pam_logoff: found ccache [%s]\n", entry
->ccname
));
1760 if (entry
->uid
< 0 || state
->request
.data
.logoff
.uid
< 0) {
1761 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1762 goto process_result
;
1765 if (entry
->uid
!= state
->request
.data
.logoff
.uid
) {
1766 DEBUG(0,("winbindd_pam_logoff: uid's differ: %d != %d\n",
1767 entry
->uid
, state
->request
.data
.logoff
.uid
));
1768 goto process_result
;
1771 if (!strcsequal(entry
->ccname
, state
->request
.data
.logoff
.krb5ccname
)) {
1772 DEBUG(0,("winbindd_pam_logoff: krb5ccnames differ: (daemon) %s != (client) %s\n",
1773 entry
->ccname
, state
->request
.data
.logoff
.krb5ccname
));
1774 goto process_result
;
1777 seteuid(entry
->uid
);
1779 ret
= ads_kdestroy(entry
->ccname
);
1784 DEBUG(0,("winbindd_pam_logoff: failed to destroy user ccache %s with: %s\n",
1785 entry
->ccname
, error_message(ret
)));
1787 DEBUG(10,("winbindd_pam_logoff: successfully destroyed ccache %s for user %s\n",
1788 entry
->ccname
, state
->request
.data
.logoff
.user
));
1789 remove_ccache_by_ccname(entry
->ccname
);
1792 result
= krb5_to_nt_status(ret
);
1794 result
= NT_STATUS_NOT_SUPPORTED
;
1798 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1799 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1800 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1801 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1803 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;