auth_winbind: Allow badPwdCount to be set to 0 with this auth method
[sfrench/samba-autobuild/.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 "librpc/gen_ndr/ndr_winbind_c.h"
28 #include "lib/messaging/irpc.h"
29 #include "param/param.h"
30 #include "nsswitch/libwbclient/wbclient.h"
31 #include "auth/auth_sam_reply.h"
32 #include "libcli/security/security.h"
33 #include "dsdb/samdb/samdb.h"
34 #include "auth/auth_sam.h"
35
36 _PUBLIC_ NTSTATUS auth4_winbind_init(TALLOC_CTX *);
37
38 static NTSTATUS winbind_want_check(struct auth_method_context *ctx,
39                                    TALLOC_CTX *mem_ctx,
40                                    const struct auth_usersupplied_info *user_info)
41 {
42         if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
43                 return NT_STATUS_NOT_IMPLEMENTED;
44         }
45
46         /* TODO: maybe limit the user scope to remote users only */
47         return NT_STATUS_OK;
48 }
49
50 static NTSTATUS winbind_rodc_want_check(struct auth_method_context *ctx,
51                                         TALLOC_CTX *mem_ctx,
52                                         const struct auth_usersupplied_info *user_info)
53 {
54         int ret;
55         bool am_rodc;
56
57         if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
58                 return NT_STATUS_NOT_IMPLEMENTED;
59         }
60
61         if (ctx->auth_ctx->sam_ctx == NULL) {
62                 DBG_ERR("ctx->auth_ctx->sam_ctx == NULL, don't check.\n");
63                 return NT_STATUS_NOT_IMPLEMENTED;
64         }
65
66         ret = samdb_rodc(ctx->auth_ctx->sam_ctx, &am_rodc);
67         if (ret != LDB_SUCCESS) {
68                 DBG_ERR("samdb_rodc() failed %d %s, don't check.\n",
69                         ret, ldb_errstring(ctx->auth_ctx->sam_ctx));
70                 return NT_STATUS_NOT_IMPLEMENTED;
71         }
72
73         if (!am_rodc) {
74                 /*
75                  * We don't support trusts yet and we
76                  * don't want to add them using the
77                  * semi-async irpc call that uses
78                  * a nested event loop.
79                  */
80                 return NT_STATUS_NOT_IMPLEMENTED;
81         }
82
83         /*
84          * We're a RODC, so we forward the request to our winbind.
85          * As the RODC is not yet production ready anyway, we keep
86          * the semi-async behavior with nested event loops in order
87          * to keep autobuild happy.
88          */
89         return NT_STATUS_OK;
90 }
91
92 struct winbind_check_password_state {
93         struct winbind_SamLogon req;
94 };
95
96 /*
97  Authenticate a user with a challenge/response
98  using IRPC to the winbind task
99 */
100 static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
101                                        TALLOC_CTX *mem_ctx,
102                                        const struct auth_usersupplied_info *user_info, 
103                                        struct auth_user_info_dc **user_info_dc,
104                                        bool *authoritative)
105 {
106         NTSTATUS status;
107         struct dcerpc_binding_handle *irpc_handle;
108         struct winbind_check_password_state *s;
109         const struct auth_usersupplied_info *user_info_new;
110         struct netr_IdentityInfo *identity_info;
111         struct ldb_dn *domain_dn;
112         struct ldb_message *msg;
113
114
115         if (!ctx->auth_ctx->msg_ctx) {
116                 DEBUG(0,("winbind_check_password: auth_context_create was called with out messaging context\n"));
117                 return NT_STATUS_INTERNAL_ERROR;
118         }
119
120         s = talloc(mem_ctx, struct winbind_check_password_state);
121         NT_STATUS_HAVE_NO_MEMORY(s);
122
123         irpc_handle = irpc_binding_handle_by_name(s, ctx->auth_ctx->msg_ctx,
124                                                   "winbind_server",
125                                                   &ndr_table_winbind);
126         if (irpc_handle == NULL) {
127                 DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, " 
128                           "no winbind_server running!\n",
129                           user_info->client.domain_name, user_info->client.account_name));
130                 return NT_STATUS_NO_LOGON_SERVERS;
131         }
132
133         if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
134                 struct netr_PasswordInfo *password_info;
135
136                 status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_HASH,
137                                            user_info, &user_info_new);
138                 NT_STATUS_NOT_OK_RETURN(status);
139                 user_info = user_info_new;
140
141                 password_info = talloc(s, struct netr_PasswordInfo);
142                 NT_STATUS_HAVE_NO_MEMORY(password_info);
143
144                 password_info->lmpassword = *user_info->password.hash.lanman;
145                 password_info->ntpassword = *user_info->password.hash.nt;
146
147                 identity_info = &password_info->identity_info;
148                 s->req.in.logon_level   = 1;
149                 s->req.in.logon.password= password_info;
150         } else {
151                 struct netr_NetworkInfo *network_info;
152                 uint8_t chal[8];
153
154                 status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_RESPONSE,
155                                            user_info, &user_info_new);
156                 NT_STATUS_NOT_OK_RETURN(status);
157                 user_info = user_info_new;
158
159                 network_info = talloc(s, struct netr_NetworkInfo);
160                 NT_STATUS_HAVE_NO_MEMORY(network_info);
161
162                 status = auth_get_challenge(ctx->auth_ctx, chal);
163                 NT_STATUS_NOT_OK_RETURN(status);
164
165                 memcpy(network_info->challenge, chal, sizeof(network_info->challenge));
166
167                 network_info->nt.length = user_info->password.response.nt.length;
168                 network_info->nt.data   = user_info->password.response.nt.data;
169
170                 network_info->lm.length = user_info->password.response.lanman.length;
171                 network_info->lm.data   = user_info->password.response.lanman.data;
172
173                 identity_info = &network_info->identity_info;
174                 s->req.in.logon_level   = 2;
175                 s->req.in.logon.network = network_info;
176         }
177
178         identity_info->domain_name.string       = user_info->client.domain_name;
179         identity_info->parameter_control        = user_info->logon_parameters; /* see MSV1_0_* */
180         identity_info->logon_id_low             = 0;
181         identity_info->logon_id_high            = 0;
182         identity_info->account_name.string      = user_info->client.account_name;
183         identity_info->workstation.string       = user_info->workstation_name;
184
185         s->req.in.validation_level      = 3;
186
187         /* Note: this makes use of nested event loops... */
188         dcerpc_binding_handle_set_sync_ev(irpc_handle, ctx->auth_ctx->event_ctx);
189         status = dcerpc_winbind_SamLogon_r(irpc_handle, s, &s->req);
190         NT_STATUS_NOT_OK_RETURN(status);
191
192         if (NT_STATUS_EQUAL(s->req.out.result, NT_STATUS_NO_SUCH_USER) &&
193             !s->req.out.authoritative) {
194                 return NT_STATUS_NOT_IMPLEMENTED;
195         }
196
197         /*
198          * At best, reset the badPwdCount to 0 if the account exists.
199          * This means that lockouts happen at a badPwdCount earlier than
200          * normal, but makes it more fault tolerant.
201          */
202         if (NT_STATUS_IS_OK(s->req.out.result)) {
203                 const char *account_name = user_info->mapped.account_name;
204                 const char *p = NULL;
205                 p = strchr_m(account_name, '@');
206                 if (p != NULL) {
207                         const char *nt4_domain = NULL;
208                         const char *nt4_account = NULL;
209
210                         status = crack_name_to_nt4_name(mem_ctx,
211                                                         ctx->auth_ctx->event_ctx,
212                                                         ctx->auth_ctx->lp_ctx,
213                                                         DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
214                                                         account_name,
215                                                         &nt4_domain, &nt4_account);
216                         if (NT_STATUS_IS_OK(status) &&
217                             lpcfg_is_mydomain(ctx->auth_ctx->lp_ctx, nt4_domain)) {
218                                 account_name = nt4_account;
219                         }
220                 }
221
222                 domain_dn = ldb_get_default_basedn(ctx->auth_ctx->sam_ctx);
223                 if (domain_dn != NULL) {
224                         status = authsam_search_account(mem_ctx, ctx->auth_ctx->sam_ctx, account_name, domain_dn, &msg);
225                         if (NT_STATUS_IS_OK(status)) {
226                             authsam_logon_success_accounting(ctx->auth_ctx->sam_ctx, msg,
227                                                              domain_dn,
228                                                              user_info->flags & USER_INFO_INTERACTIVE_LOGON);
229                         }
230                 }
231         }
232
233         status = make_user_info_dc_netlogon_validation(mem_ctx,
234                                                       user_info->client.account_name,
235                                                       s->req.in.validation_level,
236                                                       &s->req.out.validation,
237                                                        true, /* This user was authenticated */
238                                                       user_info_dc);
239         NT_STATUS_NOT_OK_RETURN(status);
240
241         return NT_STATUS_OK;
242 }
243
244 /*
245  Authenticate a user with a challenge/response
246  using the samba3 winbind protocol via libwbclient
247 */
248 static NTSTATUS winbind_check_password_wbclient(struct auth_method_context *ctx,
249                                                 TALLOC_CTX *mem_ctx,
250                                                 const struct auth_usersupplied_info *user_info,
251                                                 struct auth_user_info_dc **user_info_dc,
252                                                 bool *authoritative)
253 {
254         struct wbcAuthUserParams params;
255         struct wbcAuthUserInfo *info = NULL;
256         struct wbcAuthErrorInfo *err = NULL;
257         wbcErr wbc_status;
258         NTSTATUS nt_status;
259         struct netr_SamInfo6 *info6 = NULL;
260         union netr_Validation validation;
261
262         /* Send off request */
263         const struct auth_usersupplied_info *user_info_temp;
264         nt_status = encrypt_user_info(mem_ctx, ctx->auth_ctx,
265                                       AUTH_PASSWORD_RESPONSE,
266                                       user_info, &user_info_temp);
267         if (!NT_STATUS_IS_OK(nt_status)) {
268                 return nt_status;
269         }
270         user_info = user_info_temp;
271
272         ZERO_STRUCT(params);
273         ZERO_STRUCT(validation);
274         /*params.flags = WBFLAG_PAM_INFO3_NDR;*/
275
276         params.parameter_control = user_info->logon_parameters;
277         params.parameter_control |= WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT |
278                                     WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
279         params.level = WBC_AUTH_USER_LEVEL_RESPONSE;
280
281         params.account_name     = user_info->client.account_name;
282         params.domain_name      = user_info->client.domain_name;
283         params.workstation_name = user_info->workstation_name;
284
285         DEBUG(5,("looking up %s@%s logging in from %s\n",
286                   params.account_name, params.domain_name,
287                   params.workstation_name));
288
289         memcpy(params.password.response.challenge,
290                ctx->auth_ctx->challenge.data.data,
291                sizeof(params.password.response.challenge));
292
293         params.password.response.lm_length =
294                 user_info->password.response.lanman.length;
295         params.password.response.nt_length =
296                 user_info->password.response.nt.length;
297
298         params.password.response.lm_data =
299                 user_info->password.response.lanman.data;
300         params.password.response.nt_data =
301                 user_info->password.response.nt.data;
302
303         wbc_status = wbcAuthenticateUserEx(&params, &info, &err);
304         if (wbc_status == WBC_ERR_AUTH_ERROR) {
305                 if (err) {
306                         DEBUG(1, ("error was %s (0x%08x)\nerror message was '%s'\n",
307                               err->nt_string, err->nt_status, err->display_string));
308                         nt_status = NT_STATUS(err->nt_status);
309                         wbcFreeMemory(err);
310                 } else {
311                         nt_status = NT_STATUS_LOGON_FAILURE;
312                 }
313                 NT_STATUS_NOT_OK_RETURN(nt_status);
314         } else if (!WBC_ERROR_IS_OK(wbc_status)) {
315                 DEBUG(1, ("wbcAuthenticateUserEx: failed with %u - %s\n",
316                         wbc_status, wbcErrorString(wbc_status)));
317                 if (err) {
318                         DEBUG(1, ("error was %s (0x%08x)\nerror message was '%s'\n",
319                               err->nt_string, err->nt_status, err->display_string));
320                 }
321                 return NT_STATUS_LOGON_FAILURE;
322         }
323         info6 = wbcAuthUserInfo_to_netr_SamInfo6(mem_ctx, info);
324         wbcFreeMemory(info);
325         if (!info6) {
326                 DEBUG(1, ("wbcAuthUserInfo_to_netr_SamInfo6 failed\n"));
327                 return NT_STATUS_NO_MEMORY;
328         }
329
330         validation.sam6 = info6;
331         nt_status = make_user_info_dc_netlogon_validation(mem_ctx,
332                                                           user_info->client.account_name,
333                                                           6, &validation,
334                                                           true, /* This user was authenticated */
335                                                           user_info_dc);
336         return nt_status;
337
338 }
339
340 static const struct auth_operations winbind_ops = {
341         .name           = "winbind",
342         .want_check     = winbind_want_check,
343         .check_password = winbind_check_password
344 };
345
346 static const struct auth_operations winbind_rodc_ops = {
347         .name           = "winbind_rodc",
348         .want_check     = winbind_rodc_want_check,
349         .check_password = winbind_check_password
350 };
351
352 static const struct auth_operations winbind_wbclient_ops = {
353         .name           = "winbind_wbclient",
354         .want_check     = winbind_want_check,
355         .check_password = winbind_check_password_wbclient
356 };
357
358 _PUBLIC_ NTSTATUS auth4_winbind_init(TALLOC_CTX *ctx)
359 {
360         NTSTATUS ret;
361
362         ret = auth_register(ctx, &winbind_ops);
363         if (!NT_STATUS_IS_OK(ret)) {
364                 DEBUG(0,("Failed to register 'winbind' auth backend!\n"));
365                 return ret;
366         }
367
368         ret = auth_register(ctx, &winbind_rodc_ops);
369         if (!NT_STATUS_IS_OK(ret)) {
370                 DEBUG(0,("Failed to register 'winbind_rodc' auth backend!\n"));
371                 return ret;
372         }
373
374         ret = auth_register(ctx, &winbind_wbclient_ops);
375         if (!NT_STATUS_IS_OK(ret)) {
376                 DEBUG(0,("Failed to register 'winbind_wbclient' auth backend!\n"));
377                 return ret;
378         }
379
380         return NT_STATUS_OK;
381 }