2 Unix SMB/CIFS implementation.
4 Winbind authentication mechnism
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Andrew Bartlett 2001 - 2002
8 Copyright (C) Stefan Metzmacher 2005
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "auth/auth.h"
26 #include "auth/ntlm/auth_proto.h"
27 #include "auth/auth_sam_reply.h"
28 #include "nsswitch/winbind_client.h"
29 #include "librpc/gen_ndr/ndr_netlogon.h"
30 #include "librpc/gen_ndr/ndr_winbind.h"
31 #include "lib/messaging/irpc.h"
32 #include "param/param.h"
33 #include "nsswitch/libwbclient/wbclient.h"
34 #include "libcli/security/dom_sid.h"
36 static NTSTATUS
get_info3_from_ndr(TALLOC_CTX
*mem_ctx
, struct smb_iconv_convenience
*iconv_convenience
, struct winbindd_response
*response
, struct netr_SamInfo3
*info3
)
38 size_t len
= response
->length
- sizeof(struct winbindd_response
);
40 enum ndr_err_code ndr_err
;
42 blob
.length
= len
- 4;
43 blob
.data
= (uint8_t *)(((char *)response
->extra_data
.data
) + 4);
45 ndr_err
= ndr_pull_struct_blob(&blob
, mem_ctx
,
46 iconv_convenience
, info3
,
47 (ndr_pull_flags_fn_t
)ndr_pull_netr_SamInfo3
);
48 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
49 return ndr_map_error2ntstatus(ndr_err
);
54 DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
55 return NT_STATUS_UNSUCCESSFUL
;
59 static NTSTATUS
get_info3_from_wbcAuthUserInfo(TALLOC_CTX
*mem_ctx
,
60 struct smb_iconv_convenience
*ic
,
61 struct wbcAuthUserInfo
*info
,
62 struct netr_SamInfo3
*info3
)
65 struct samr_RidWithAttribute
*rids
= NULL
;
67 info3
->base
.last_logon
= info
->logon_time
;
68 info3
->base
.last_logoff
= info
->logoff_time
;
69 info3
->base
.acct_expiry
= info
->kickoff_time
;
70 info3
->base
.last_password_change
= info
->pass_last_set_time
;
71 info3
->base
.allow_password_change
= info
->pass_can_change_time
;
72 info3
->base
.force_password_change
= info
->pass_must_change_time
;
74 if (info
->account_name
!= NULL
) {
75 convert_string_talloc_convenience(mem_ctx
, ic
,
76 CH_UNIX
, CH_UTF16
, info
->account_name
,
77 strlen(info
->account_name
),
78 discard_const(&info3
->base
.account_name
.string
),
82 if (info
->full_name
!= NULL
) {
83 convert_string_talloc_convenience(mem_ctx
, ic
,
84 CH_UNIX
, CH_UTF16
, info
->full_name
,
85 strlen(info
->full_name
),
86 discard_const(&info3
->base
.full_name
.string
),
90 if (info
->logon_script
!= NULL
) {
91 convert_string_talloc_convenience(mem_ctx
, ic
,
92 CH_UNIX
, CH_UTF16
, info
->logon_script
,
93 strlen(info
->logon_script
),
94 discard_const(&info3
->base
.logon_script
.string
),
98 if (info
->profile_path
!= NULL
) {
99 convert_string_talloc_convenience(mem_ctx
, ic
,
100 CH_UNIX
, CH_UTF16
, info
->profile_path
,
101 strlen(info
->profile_path
),
102 discard_const(&info3
->base
.profile_path
.string
),
106 if (info
->home_directory
!= NULL
) {
107 convert_string_talloc_convenience(mem_ctx
, ic
,
108 CH_UNIX
, CH_UTF16
, info
->home_directory
,
109 strlen(info
->home_directory
),
110 discard_const(&info3
->base
.home_directory
.string
),
114 if (info
->home_drive
!= NULL
) {
115 convert_string_talloc_convenience(mem_ctx
, ic
,
116 CH_UNIX
, CH_UTF16
, info
->home_drive
,
117 strlen(info
->home_drive
),
118 discard_const(&info3
->base
.home_drive
.string
),
122 if (info
->logon_server
!= NULL
) {
123 convert_string_talloc_convenience(mem_ctx
, ic
,
124 CH_UNIX
, CH_UTF16
, info
->logon_server
,
125 strlen(info
->logon_server
),
126 discard_const(&info3
->base
.logon_server
.string
),
130 if (info
->domain_name
!= NULL
) {
131 convert_string_talloc_convenience(mem_ctx
, ic
,
132 CH_UNIX
, CH_UTF16
, info
->domain_name
,
133 strlen(info
->domain_name
),
134 discard_const(&info3
->base
.domain
.string
),
138 info3
->base
.logon_count
= info
->logon_count
;
139 info3
->base
.bad_password_count
= info
->bad_password_count
;
140 info3
->base
.user_flags
= info
->user_flags
;
141 memcpy(info3
->base
.key
.key
, info
->user_session_key
,
142 sizeof(info3
->base
.key
.key
));
143 memcpy(info3
->base
.LMSessKey
.key
, info
->lm_session_key
,
144 sizeof(info3
->base
.LMSessKey
.key
));
145 info3
->base
.acct_flags
= info
->acct_flags
;
146 memset(info3
->base
.unknown
, 0, sizeof(info3
->base
.unknown
));
148 if (info
->num_sids
< 2) {
149 return NT_STATUS_INVALID_PARAMETER
;
152 dom_sid_split_rid(mem_ctx
, (struct dom_sid2
*) &info
->sids
[0].sid
,
153 &info3
->base
.domain_sid
,
155 dom_sid_split_rid(mem_ctx
, (struct dom_sid2
*) &info
->sids
[1].sid
, NULL
,
156 &info3
->base
.primary_gid
);
158 /* We already handled the first two, now take care of the rest */
159 info3
->base
.groups
.count
= info
->num_sids
- 2;
160 for (i
= 2, j
= 0; i
< info
->num_sids
; ++i
, ++j
) {
162 rids
= talloc_array(mem_ctx
, struct samr_RidWithAttribute
,
163 info3
->base
.groups
.count
);
164 NT_STATUS_HAVE_NO_MEMORY(rids
);
166 rids
[j
].attributes
= info
->sids
[i
].attributes
;
167 dom_sid_split_rid(mem_ctx
,
168 (struct dom_sid2
*) &info
->sids
[i
].sid
,
171 info3
->base
.groups
.rids
= rids
;
177 static NTSTATUS
winbind_want_check(struct auth_method_context
*ctx
,
179 const struct auth_usersupplied_info
*user_info
)
181 if (!user_info
->mapped
.account_name
|| !*user_info
->mapped
.account_name
) {
182 return NT_STATUS_NOT_IMPLEMENTED
;
185 /* TODO: maybe limit the user scope to remote users only */
190 Authenticate a user with a challenge/response
191 using the samba3 winbind protocol
193 static NTSTATUS
winbind_check_password_samba3(struct auth_method_context
*ctx
,
195 const struct auth_usersupplied_info
*user_info
,
196 struct auth_serversupplied_info
**server_info
)
198 struct winbindd_request request
;
199 struct winbindd_response response
;
202 struct netr_SamInfo3 info3
;
204 /* Send off request */
205 const struct auth_usersupplied_info
*user_info_temp
;
206 nt_status
= encrypt_user_info(mem_ctx
, ctx
->auth_ctx
,
207 AUTH_PASSWORD_RESPONSE
,
208 user_info
, &user_info_temp
);
209 if (!NT_STATUS_IS_OK(nt_status
)) {
212 user_info
= user_info_temp
;
214 ZERO_STRUCT(request
);
215 ZERO_STRUCT(response
);
216 request
.flags
= WBFLAG_PAM_INFO3_NDR
;
218 request
.data
.auth_crap
.logon_parameters
= user_info
->logon_parameters
;
220 safe_strcpy(request
.data
.auth_crap
.user
,
221 user_info
->client
.account_name
, sizeof(fstring
));
222 safe_strcpy(request
.data
.auth_crap
.domain
,
223 user_info
->client
.domain_name
, sizeof(fstring
));
224 safe_strcpy(request
.data
.auth_crap
.workstation
,
225 user_info
->workstation_name
, sizeof(fstring
));
227 memcpy(request
.data
.auth_crap
.chal
, ctx
->auth_ctx
->challenge
.data
.data
, sizeof(request
.data
.auth_crap
.chal
));
229 request
.data
.auth_crap
.lm_resp_len
= MIN(user_info
->password
.response
.lanman
.length
,
230 sizeof(request
.data
.auth_crap
.lm_resp
));
231 request
.data
.auth_crap
.nt_resp_len
= MIN(user_info
->password
.response
.nt
.length
,
232 sizeof(request
.data
.auth_crap
.nt_resp
));
234 memcpy(request
.data
.auth_crap
.lm_resp
, user_info
->password
.response
.lanman
.data
,
235 request
.data
.auth_crap
.lm_resp_len
);
236 memcpy(request
.data
.auth_crap
.nt_resp
, user_info
->password
.response
.nt
.data
,
237 request
.data
.auth_crap
.nt_resp_len
);
239 result
= winbindd_request_response(WINBINDD_PAM_AUTH_CRAP
, &request
, &response
);
241 nt_status
= NT_STATUS(response
.data
.auth
.nt_status
);
242 NT_STATUS_NOT_OK_RETURN(nt_status
);
244 if (result
== NSS_STATUS_SUCCESS
&& response
.extra_data
.data
) {
245 union netr_Validation validation
;
247 nt_status
= get_info3_from_ndr(mem_ctx
, lp_iconv_convenience(ctx
->auth_ctx
->lp_ctx
), &response
, &info3
);
248 SAFE_FREE(response
.extra_data
.data
);
249 NT_STATUS_NOT_OK_RETURN(nt_status
);
251 validation
.sam3
= &info3
;
252 nt_status
= make_server_info_netlogon_validation(mem_ctx
,
253 user_info
->client
.account_name
,
257 } else if (result
== NSS_STATUS_SUCCESS
&& !response
.extra_data
.data
) {
258 DEBUG(0, ("Winbindd authenticated the user [%s]\\[%s], "
259 "but did not include the required info3 reply!\n",
260 user_info
->client
.domain_name
, user_info
->client
.account_name
));
261 return NT_STATUS_INSUFFICIENT_LOGON_INFO
;
262 } else if (NT_STATUS_IS_OK(nt_status
)) {
263 DEBUG(1, ("Winbindd authentication for [%s]\\[%s] failed, "
264 "but no error code is available!\n",
265 user_info
->client
.domain_name
, user_info
->client
.account_name
));
266 return NT_STATUS_NO_LOGON_SERVERS
;
272 struct winbind_check_password_state
{
273 struct winbind_SamLogon req
;
277 Authenticate a user with a challenge/response
278 using IRPC to the winbind task
280 static NTSTATUS
winbind_check_password(struct auth_method_context
*ctx
,
282 const struct auth_usersupplied_info
*user_info
,
283 struct auth_serversupplied_info
**server_info
)
286 struct server_id
*winbind_servers
;
287 struct winbind_check_password_state
*s
;
288 const struct auth_usersupplied_info
*user_info_new
;
289 struct netr_IdentityInfo
*identity_info
;
291 s
= talloc(mem_ctx
, struct winbind_check_password_state
);
292 NT_STATUS_HAVE_NO_MEMORY(s
);
294 winbind_servers
= irpc_servers_byname(ctx
->auth_ctx
->msg_ctx
, s
, "winbind_server");
295 if ((winbind_servers
== NULL
) || (winbind_servers
[0].id
== 0)) {
296 DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, "
297 "no winbind_server running!\n",
298 user_info
->client
.domain_name
, user_info
->client
.account_name
));
299 return NT_STATUS_NO_LOGON_SERVERS
;
302 if (user_info
->flags
& USER_INFO_INTERACTIVE_LOGON
) {
303 struct netr_PasswordInfo
*password_info
;
305 status
= encrypt_user_info(s
, ctx
->auth_ctx
, AUTH_PASSWORD_HASH
,
306 user_info
, &user_info_new
);
307 NT_STATUS_NOT_OK_RETURN(status
);
308 user_info
= user_info_new
;
310 password_info
= talloc(s
, struct netr_PasswordInfo
);
311 NT_STATUS_HAVE_NO_MEMORY(password_info
);
313 password_info
->lmpassword
= *user_info
->password
.hash
.lanman
;
314 password_info
->ntpassword
= *user_info
->password
.hash
.nt
;
316 identity_info
= &password_info
->identity_info
;
317 s
->req
.in
.logon_level
= 1;
318 s
->req
.in
.logon
.password
= password_info
;
320 struct netr_NetworkInfo
*network_info
;
321 const uint8_t *challenge
;
323 status
= encrypt_user_info(s
, ctx
->auth_ctx
, AUTH_PASSWORD_RESPONSE
,
324 user_info
, &user_info_new
);
325 NT_STATUS_NOT_OK_RETURN(status
);
326 user_info
= user_info_new
;
328 network_info
= talloc(s
, struct netr_NetworkInfo
);
329 NT_STATUS_HAVE_NO_MEMORY(network_info
);
331 status
= auth_get_challenge(ctx
->auth_ctx
, &challenge
);
332 NT_STATUS_NOT_OK_RETURN(status
);
334 memcpy(network_info
->challenge
, challenge
, sizeof(network_info
->challenge
));
336 network_info
->nt
.length
= user_info
->password
.response
.nt
.length
;
337 network_info
->nt
.data
= user_info
->password
.response
.nt
.data
;
339 network_info
->lm
.length
= user_info
->password
.response
.lanman
.length
;
340 network_info
->lm
.data
= user_info
->password
.response
.lanman
.data
;
342 identity_info
= &network_info
->identity_info
;
343 s
->req
.in
.logon_level
= 2;
344 s
->req
.in
.logon
.network
= network_info
;
347 identity_info
->domain_name
.string
= user_info
->client
.domain_name
;
348 identity_info
->parameter_control
= user_info
->logon_parameters
; /* see MSV1_0_* */
349 identity_info
->logon_id_low
= 0;
350 identity_info
->logon_id_high
= 0;
351 identity_info
->account_name
.string
= user_info
->client
.account_name
;
352 identity_info
->workstation
.string
= user_info
->workstation_name
;
354 s
->req
.in
.validation_level
= 3;
356 status
= IRPC_CALL(ctx
->auth_ctx
->msg_ctx
, winbind_servers
[0],
357 winbind
, WINBIND_SAMLOGON
,
359 NT_STATUS_NOT_OK_RETURN(status
);
361 status
= make_server_info_netlogon_validation(mem_ctx
,
362 user_info
->client
.account_name
,
363 s
->req
.in
.validation_level
,
364 &s
->req
.out
.validation
,
366 NT_STATUS_NOT_OK_RETURN(status
);
372 Authenticate a user with a challenge/response
373 using the samba3 winbind protocol via libwbclient
375 static NTSTATUS
winbind_check_password_wbclient(struct auth_method_context
*ctx
,
377 const struct auth_usersupplied_info
*user_info
,
378 struct auth_serversupplied_info
**server_info
)
380 struct wbcAuthUserParams params
;
381 struct wbcAuthUserInfo
*info
= NULL
;
382 struct wbcAuthErrorInfo
*err
= NULL
;
385 struct netr_SamInfo3 info3
;
386 union netr_Validation validation
;
389 /* Send off request */
390 const struct auth_usersupplied_info
*user_info_temp
;
391 nt_status
= encrypt_user_info(mem_ctx
, ctx
->auth_ctx
,
392 AUTH_PASSWORD_RESPONSE
,
393 user_info
, &user_info_temp
);
394 if (!NT_STATUS_IS_OK(nt_status
)) {
397 user_info
= user_info_temp
;
401 /*params.flags = WBFLAG_PAM_INFO3_NDR;*/
403 params
.parameter_control
= user_info
->logon_parameters
;
404 params
.parameter_control
|= WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT
|
405 WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT
;
406 params
.level
= WBC_AUTH_USER_LEVEL_RESPONSE
;
408 params
.account_name
= user_info
->client
.account_name
;
409 params
.domain_name
= user_info
->client
.domain_name
;
410 params
.workstation_name
= user_info
->workstation_name
;
412 d_fprintf(stderr
, "looking up %s@%s logging in from %s\n",
413 params
.account_name
, params
.domain_name
,
414 params
.workstation_name
);
416 memcpy(params
.password
.response
.challenge
,
417 ctx
->auth_ctx
->challenge
.data
.data
,
418 sizeof(params
.password
.response
.challenge
));
420 params
.password
.response
.lm_length
=
421 user_info
->password
.response
.lanman
.length
;
422 params
.password
.response
.nt_length
=
423 user_info
->password
.response
.nt
.length
;
425 params
.password
.response
.lm_data
=
426 user_info
->password
.response
.lanman
.data
;
427 params
.password
.response
.nt_data
=
428 user_info
->password
.response
.nt
.data
;
430 wbc_status
= wbcAuthenticateUserEx(¶ms
, &info
, &err
);
431 if (!WBC_ERROR_IS_OK(wbc_status
)) {
432 DEBUG(1, ("error was %s (0x%08x)\nerror message was '%s'\n",
433 err
->nt_string
, err
->nt_status
, err
->display_string
));
435 nt_status
= NT_STATUS(err
->nt_status
);
437 NT_STATUS_NOT_OK_RETURN(nt_status
);
439 nt_status
= get_info3_from_wbcAuthUserInfo(mem_ctx
,
440 lp_iconv_convenience(ctx
->auth_ctx
->lp_ctx
),
443 NT_STATUS_NOT_OK_RETURN(nt_status
);
445 validation
.sam3
= &info3
;
446 nt_status
= make_server_info_netlogon_validation(mem_ctx
,
447 user_info
->client
.account_name
,
448 3, &validation
, server_info
);
453 static const struct auth_operations winbind_samba3_ops
= {
454 .name
= "winbind_samba3",
455 .get_challenge
= auth_get_challenge_not_implemented
,
456 .want_check
= winbind_want_check
,
457 .check_password
= winbind_check_password_samba3
460 static const struct auth_operations winbind_ops
= {
462 .get_challenge
= auth_get_challenge_not_implemented
,
463 .want_check
= winbind_want_check
,
464 .check_password
= winbind_check_password
467 static const struct auth_operations winbind_wbclient_ops
= {
468 .name
= "winbind_wbclient",
469 .get_challenge
= auth_get_challenge_not_implemented
,
470 .want_check
= winbind_want_check
,
471 .check_password
= winbind_check_password_wbclient
474 _PUBLIC_ NTSTATUS
auth_winbind_init(void)
478 ret
= auth_register(&winbind_samba3_ops
);
479 if (!NT_STATUS_IS_OK(ret
)) {
480 DEBUG(0,("Failed to register 'winbind_samba3' auth backend!\n"));
484 ret
= auth_register(&winbind_ops
);
485 if (!NT_STATUS_IS_OK(ret
)) {
486 DEBUG(0,("Failed to register 'winbind' auth backend!\n"));
490 ret
= auth_register(&winbind_wbclient_ops
);
491 if (!NT_STATUS_IS_OK(ret
)) {
492 DEBUG(0,("Failed to register 'winbind_wbclient' auth backend!\n"));