3905d00667102212bcfa15b9f92e3bf5b5a26c95
[metze/samba/wip.git] / source4 / auth / ntlm / auth_winbind.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind authentication mechnism
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Andrew Bartlett 2001 - 2002
8    Copyright (C) Stefan Metzmacher 2005
9    
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.
14    
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.
19    
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/>.
22 */
23
24 #include "includes.h"
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"
35
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)
37 {
38         size_t len = response->length - sizeof(struct winbindd_response);
39         if (len > 4) {
40                 enum ndr_err_code ndr_err;
41                 DATA_BLOB blob;
42                 blob.length = len - 4;
43                 blob.data = (uint8_t *)(((char *)response->extra_data.data) + 4);
44
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);
50                 }
51
52                 return NT_STATUS_OK;
53         } else {
54                 DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
55                 return NT_STATUS_UNSUCCESSFUL;
56         }
57 }
58
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)
63 {
64         int i, j;
65         struct samr_RidWithAttribute *rids = NULL;
66
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;
73
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),
79                                 NULL, false);
80         }
81
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),
87                                 NULL, false);
88         }
89
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),
95                                 NULL, false);
96         }
97
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),
103                                 NULL, false);
104         }
105
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),
111                                 NULL, false);
112         }
113
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),
119                                 NULL, false);
120         }
121
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),
127                                 NULL, false);
128         }
129
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),
135                                 NULL, false);
136         }
137
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));
147
148         if (info->num_sids < 2) {
149                 return NT_STATUS_INVALID_PARAMETER;
150         }
151
152         dom_sid_split_rid(mem_ctx, (struct dom_sid2 *) &info->sids[0].sid,
153                           &info3->base.domain_sid,
154                           &info3->base.rid);
155         dom_sid_split_rid(mem_ctx, (struct dom_sid2 *) &info->sids[1].sid, NULL,
156                           &info3->base.primary_gid);
157
158         /* We already handled the first two, now take care of the rest */
159         info3->base.groups.count = info->num_sids - 2;
160
161         rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
162                             info3->base.groups.count);
163         NT_STATUS_HAVE_NO_MEMORY(rids);
164
165         for (i = 2, j = 0; i < info->num_sids; ++i, ++j) {
166                 rids[j].attributes = info->sids[i].attributes;
167                 dom_sid_split_rid(mem_ctx,
168                                   (struct dom_sid2 *) &info->sids[i].sid,
169                                   NULL, &rids[j].rid);
170         }
171         info3->base.groups.rids = rids;
172
173         return NT_STATUS_OK;
174 }
175
176
177 static NTSTATUS winbind_want_check(struct auth_method_context *ctx,
178                                    TALLOC_CTX *mem_ctx,
179                                    const struct auth_usersupplied_info *user_info)
180 {
181         if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
182                 return NT_STATUS_NOT_IMPLEMENTED;
183         }
184
185         /* TODO: maybe limit the user scope to remote users only */
186         return NT_STATUS_OK;
187 }
188
189 /*
190  Authenticate a user with a challenge/response
191  using the samba3 winbind protocol
192 */
193 static NTSTATUS winbind_check_password_samba3(struct auth_method_context *ctx,
194                                               TALLOC_CTX *mem_ctx,
195                                               const struct auth_usersupplied_info *user_info, 
196                                               struct auth_serversupplied_info **server_info)
197 {
198         struct winbindd_request request;
199         struct winbindd_response response;
200         NSS_STATUS result;
201         NTSTATUS nt_status;
202         struct netr_SamInfo3 info3;             
203
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)) {
210                 return nt_status;
211         }
212         user_info = user_info_temp;
213
214         ZERO_STRUCT(request);
215         ZERO_STRUCT(response);
216         request.flags = WBFLAG_PAM_INFO3_NDR;
217
218         request.data.auth_crap.logon_parameters = user_info->logon_parameters;
219
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));
226
227         memcpy(request.data.auth_crap.chal, ctx->auth_ctx->challenge.data.data, sizeof(request.data.auth_crap.chal));
228
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));
233
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);
238
239         result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
240
241         nt_status = NT_STATUS(response.data.auth.nt_status);
242         NT_STATUS_NOT_OK_RETURN(nt_status);
243
244         if (result == NSS_STATUS_SUCCESS && response.extra_data.data) {
245                 union netr_Validation validation;
246
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); 
250
251                 validation.sam3 = &info3;
252                 nt_status = make_server_info_netlogon_validation(mem_ctx, 
253                                                                  user_info->client.account_name, 
254                                                                  3, &validation,
255                                                                  server_info);
256                 return nt_status;
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;
267         }
268
269         return nt_status;
270 }
271
272 struct winbind_check_password_state {
273         struct winbind_SamLogon req;
274 };
275
276 /*
277  Authenticate a user with a challenge/response
278  using IRPC to the winbind task
279 */
280 static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
281                                        TALLOC_CTX *mem_ctx,
282                                        const struct auth_usersupplied_info *user_info, 
283                                        struct auth_serversupplied_info **server_info)
284 {
285         NTSTATUS status;
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;
290
291         s = talloc(mem_ctx, struct winbind_check_password_state);
292         NT_STATUS_HAVE_NO_MEMORY(s);
293
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;
300         }
301
302         if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
303                 struct netr_PasswordInfo *password_info;
304
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;
309
310                 password_info = talloc(s, struct netr_PasswordInfo);
311                 NT_STATUS_HAVE_NO_MEMORY(password_info);
312
313                 password_info->lmpassword = *user_info->password.hash.lanman;
314                 password_info->ntpassword = *user_info->password.hash.nt;
315
316                 identity_info = &password_info->identity_info;
317                 s->req.in.logon_level   = 1;
318                 s->req.in.logon.password= password_info;
319         } else {
320                 struct netr_NetworkInfo *network_info;
321                 const uint8_t *challenge;
322
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;
327
328                 network_info = talloc(s, struct netr_NetworkInfo);
329                 NT_STATUS_HAVE_NO_MEMORY(network_info);
330
331                 status = auth_get_challenge(ctx->auth_ctx, &challenge);
332                 NT_STATUS_NOT_OK_RETURN(status);
333
334                 memcpy(network_info->challenge, challenge, sizeof(network_info->challenge));
335
336                 network_info->nt.length = user_info->password.response.nt.length;
337                 network_info->nt.data   = user_info->password.response.nt.data;
338
339                 network_info->lm.length = user_info->password.response.lanman.length;
340                 network_info->lm.data   = user_info->password.response.lanman.data;
341
342                 identity_info = &network_info->identity_info;
343                 s->req.in.logon_level   = 2;
344                 s->req.in.logon.network = network_info;
345         }
346
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;
353
354         s->req.in.validation_level      = 3;
355
356         status = IRPC_CALL(ctx->auth_ctx->msg_ctx, winbind_servers[0],
357                            winbind, WINBIND_SAMLOGON,
358                            &s->req, s);
359         NT_STATUS_NOT_OK_RETURN(status);
360
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,
365                                                       server_info);
366         NT_STATUS_NOT_OK_RETURN(status);
367
368         return NT_STATUS_OK;
369 }
370
371 /*
372  Authenticate a user with a challenge/response
373  using the samba3 winbind protocol via libwbclient
374 */
375 static NTSTATUS winbind_check_password_wbclient(struct auth_method_context *ctx,
376                                                 TALLOC_CTX *mem_ctx,
377                                                 const struct auth_usersupplied_info *user_info,
378                                                 struct auth_serversupplied_info **server_info)
379 {
380         struct wbcAuthUserParams params;
381         struct wbcAuthUserInfo *info = NULL;
382         struct wbcAuthErrorInfo *err = NULL;
383         wbcErr wbc_status;
384         NTSTATUS nt_status;
385         struct netr_SamInfo3 info3;
386         union netr_Validation validation;
387
388
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)) {
395                 return nt_status;
396         }
397         user_info = user_info_temp;
398
399         ZERO_STRUCT(params);
400         ZERO_STRUCT(info3);
401         /*params.flags = WBFLAG_PAM_INFO3_NDR;*/
402
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;
407
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;
411
412         d_fprintf(stderr, "looking up %s@%s logging in from %s\n",
413                   params.account_name, params.domain_name,
414                   params.workstation_name);
415
416         memcpy(params.password.response.challenge,
417                ctx->auth_ctx->challenge.data.data,
418                sizeof(params.password.response.challenge));
419
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;
424
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;
429
430         wbc_status = wbcAuthenticateUserEx(&params, &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));
434
435                 nt_status = NT_STATUS(err->nt_status);
436                 wbcFreeMemory(err);
437                 NT_STATUS_NOT_OK_RETURN(nt_status);
438         }
439         nt_status = get_info3_from_wbcAuthUserInfo(mem_ctx,
440                                 lp_iconv_convenience(ctx->auth_ctx->lp_ctx),
441                                 info, &info3);
442         wbcFreeMemory(info);
443         NT_STATUS_NOT_OK_RETURN(nt_status);
444
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);
449         return nt_status;
450
451 }
452
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
458 };
459
460 static const struct auth_operations winbind_ops = {
461         .name           = "winbind",
462         .get_challenge  = auth_get_challenge_not_implemented,
463         .want_check     = winbind_want_check,
464         .check_password = winbind_check_password
465 };
466
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
472 };
473
474 _PUBLIC_ NTSTATUS auth_winbind_init(void)
475 {
476         NTSTATUS ret;
477
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"));
481                 return ret;
482         }
483
484         ret = auth_register(&winbind_ops);
485         if (!NT_STATUS_IS_OK(ret)) {
486                 DEBUG(0,("Failed to register 'winbind' auth backend!\n"));
487                 return ret;
488         }
489
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"));
493                 return ret;
494         }
495
496         return NT_STATUS_OK;
497 }