Renamed get_nt_error_msg() to nt_errstr().
[gd/samba/.git] / source3 / auth / auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Password and authentication handling
4    Copyright (C) Andrew Bartlett         2001-2002
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 /** List of various built-in authenticaion modules */
24
25 const struct auth_init_function builtin_auth_init_functions[] = {
26         { "guest", auth_init_guest },
27         { "rhosts", auth_init_rhosts },
28         { "hostsequiv", auth_init_hostsequiv },
29         { "sam", auth_init_sam },       
30         { "samstrict", auth_init_samstrict },
31         { "unix", auth_init_unix },
32         { "smbserver", auth_init_smbserver },
33         { "ntdomain", auth_init_ntdomain },
34         { "trustdomain", auth_init_trustdomain },
35         { "winbind", auth_init_winbind },
36 #ifdef DEVELOPER
37         { "name_to_ntstatus", auth_init_name_to_ntstatus },
38 #endif
39         { NULL, NULL}
40 };
41
42 /****************************************************************************
43  Try to get a challenge out of the various authenticaion modules.
44  Returns a const char of length 8 bytes.
45 ****************************************************************************/
46
47 static const uint8 *get_ntlm_challenge(struct auth_context *auth_context) 
48 {
49         DATA_BLOB challenge = data_blob(NULL, 0);
50         char *challenge_set_by = NULL;
51         auth_methods *auth_method;
52         TALLOC_CTX *mem_ctx;
53
54         if (auth_context->challenge.length) {
55                 DEBUG(5, ("get_ntlm_challenge (auth subsystem): returning previous challenge (normal)\n"));
56                 return auth_context->challenge.data;
57         }
58
59         for (auth_method = auth_context->auth_method_list; auth_method; auth_method = auth_method->next)
60         {
61                 if (auth_method->get_chal == NULL) {
62                         DEBUG(5, ("auth_get_challenge: module %s did not want to specify a challenge\n", auth_method->name));
63                         continue;
64                 }
65
66                 DEBUG(5, ("auth_get_challenge: getting challenge from module %s\n", auth_method->name));
67                 if (challenge_set_by != NULL) {
68                         DEBUG(1, ("auth_get_challenge: CONFIGURATION ERROR: authenticaion method %s has already specified a challenge.  Challenge by %s ignored.\n", 
69                                   challenge_set_by, auth_method->name));
70                         continue;
71                 }
72
73                 mem_ctx = talloc_init_named("auth_get_challenge for module %s", auth_method->name);
74                 if (!mem_ctx) {
75                         smb_panic("talloc_init_named() failed!");
76                 }
77                 
78                 challenge = auth_method->get_chal(auth_context, &auth_method->private_data, mem_ctx);
79                 if (!challenge.length) {
80                         DEBUG(3, ("auth_get_challenge: getting challenge from authenticaion method %s FAILED.\n", 
81                                   auth_method->name));
82                 } else {
83                         DEBUG(5, ("auth_get_challenge: sucessfully got challenge from module %s\n", auth_method->name));
84                         auth_context->challenge = challenge;
85                         challenge_set_by = auth_method->name;
86                         auth_context->challenge_set_method = auth_method;
87                 }
88                 talloc_destroy(mem_ctx);
89         }
90         
91         if (!challenge_set_by) {
92                 uchar chal[8];
93                 
94                 generate_random_buffer(chal, sizeof(chal), False);
95                 auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, 
96                                                            chal, sizeof(chal));
97                 
98                 challenge_set_by = "random";
99         } 
100         
101         DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by));
102         DEBUG(5, ("challenge is: \n"));
103         dump_data(5, auth_context->challenge.data, auth_context->challenge.length);
104         
105         SMB_ASSERT(auth_context->challenge.length == 8);
106
107         auth_context->challenge_set_by=challenge_set_by;
108
109         return auth_context->challenge.data;
110 }
111
112
113 /**
114  * Check user is in correct domain (if required)
115  *
116  * @param user Only used to fill in the debug message
117  * 
118  * @param domain The domain to be verified
119  *
120  * @return True if the user can connect with that domain, 
121  *         False otherwise.
122 **/
123
124 static BOOL check_domain_match(const char *user, const char *domain) 
125 {
126         /*
127          * If we aren't serving to trusted domains, we must make sure that
128          * the validation request comes from an account in the same domain
129          * as the Samba server
130          */
131
132         if (!lp_allow_trusted_domains() &&
133             !(strequal("", domain) || 
134               strequal(lp_workgroup(), domain) || 
135               is_netbios_alias_or_name(domain))) {
136                 DEBUG(1, ("check_domain_match: Attempt to connect as user %s from domain %s denied.\n", user, domain));
137                 return False;
138         } else {
139                 return True;
140         }
141 }
142
143 /**
144  * Check a user's Plaintext, LM or NTLM password.
145  *
146  * Check a user's password, as given in the user_info struct and return various
147  * interesting details in the server_info struct.
148  *
149  * This function does NOT need to be in a become_root()/unbecome_root() pair
150  * as it makes the calls itself when needed.
151  *
152  * The return value takes precedence over the contents of the server_info 
153  * struct.  When the return is other than NT_STATUS_OK the contents 
154  * of that structure is undefined.
155  *
156  * @param user_info Contains the user supplied components, including the passwords.
157  *                  Must be created with make_user_info() or one of its wrappers.
158  *
159  * @param auth_info Supplies the challenges and some other data. 
160  *                  Must be created with make_auth_info(), and the challenges should be 
161  *                  filled in, either at creation or by calling the challenge geneation 
162  *                  function auth_get_challenge().  
163  *
164  * @param server_info If successful, contains information about the authenticaion, 
165  *                    including a SAM_ACCOUNT struct describing the user.
166  *
167  * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
168  *
169  **/
170
171 static NTSTATUS check_ntlm_password(const struct auth_context *auth_context,
172                                     const struct auth_usersupplied_info *user_info, 
173                                     struct auth_serversupplied_info **server_info)
174 {
175         
176         NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
177         const char *pdb_username;
178         auth_methods *auth_method;
179         TALLOC_CTX *mem_ctx;
180
181         if (!user_info || !auth_context || !server_info) {
182                 return NT_STATUS_LOGON_FAILURE;
183         }
184
185         DEBUG(3, ("check_password:  Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n", 
186                   user_info->client_domain.str, user_info->smb_name.str, user_info->wksta_name.str));
187
188         DEBUG(3, ("check_password:  mapped user is: [%s]\\[%s]@[%s]\n", 
189                   user_info->domain.str, user_info->internal_username.str, user_info->wksta_name.str));
190         if (auth_context->challenge_set_by) {
191                 DEBUG(10, ("auth_context challenge created by %s\n", auth_context->challenge_set_by));
192         }
193         DEBUG(10, ("challenge is: \n"));
194         dump_data(5, auth_context->challenge.data, auth_context->challenge.length);
195
196 #ifdef DEBUG_PASSWORD
197         DEBUG(100, ("user_info has passwords of length %d and %d\n", 
198                     user_info->lm_resp.length, user_info->nt_resp.length));
199         DEBUG(100, ("lm:\n"));
200         dump_data(100, user_info->lm_resp.data, user_info->lm_resp.length);
201         DEBUG(100, ("nt:\n"));
202         dump_data(100, user_info->nt_resp.data, user_info->nt_resp.length);
203 #endif
204
205         /* This needs to be sorted:  If it doesn't match, what should we do? */
206         if (!check_domain_match(user_info->smb_name.str, user_info->domain.str)) {
207                 return NT_STATUS_LOGON_FAILURE;
208         }
209
210         for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next)
211         {
212                 mem_ctx = talloc_init_named("%s authentication for user %s\\%s", auth_method->name, 
213                                             user_info->domain.str, user_info->smb_name.str);
214
215                 nt_status = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info);
216                 if (NT_STATUS_IS_OK(nt_status)) {
217                         DEBUG(3, ("check_password: %s authentication for user [%s] suceeded\n", 
218                                   auth_method->name, user_info->smb_name.str));
219                 } else {
220                         DEBUG(5, ("check_password: %s authentication for user [%s] FAILED with error %s\n", 
221                                   auth_method->name, user_info->smb_name.str, nt_errstr(nt_status)));
222                 }
223
224                 talloc_destroy(mem_ctx);
225
226                 if (NT_STATUS_IS_OK(nt_status)) {
227                         break;
228                 }
229         }
230
231         /* This is one of the few places the *relies* (rather than just sets defaults
232            on the value of lp_security().  This needs to change.  A new paramater 
233            perhaps? */
234         if (lp_security() >= SEC_SERVER) {
235                 smb_user_control(user_info, *server_info, nt_status);
236         }
237
238         if (NT_STATUS_IS_OK(nt_status)) {
239                 pdb_username = pdb_get_username((*server_info)->sam_account);
240                 if (!(*server_info)->guest) {
241                         /* We might not be root if we are an RPC call */
242                         become_root();
243                         nt_status = smb_pam_accountcheck(pdb_username);
244                         unbecome_root();
245                         
246                         if (NT_STATUS_IS_OK(nt_status)) {
247                                 DEBUG(5, ("check_password:  PAM Account for user [%s] suceeded\n", 
248                                           pdb_username));
249                         } else {
250                                 DEBUG(3, ("check_password:  PAM Account for user [%s] FAILED with error %s\n", 
251                                           pdb_username, nt_errstr(nt_status)));
252                         } 
253                 }
254                 
255                 if (NT_STATUS_IS_OK(nt_status)) {
256                         DEBUG((*server_info)->guest ? 5 : 2, 
257                               ("check_password:  %sauthenticaion for user [%s] -> [%s] -> [%s] suceeded\n", 
258                                (*server_info)->guest ? "guest " : "", 
259                                user_info->smb_name.str, 
260                                user_info->internal_username.str, 
261                                pdb_username));
262                 }
263         }
264
265         if (!NT_STATUS_IS_OK(nt_status)) {
266                 DEBUG(2, ("check_password:  Authenticaion for user [%s] -> [%s] FAILED with error %s\n", 
267                           user_info->smb_name.str, user_info->internal_username.str, 
268                           nt_errstr(nt_status)));
269                 ZERO_STRUCTP(server_info);
270         }
271         return nt_status;
272 }
273
274 /***************************************************************************
275  Clear out a auth_context, and destroy the attached TALLOC_CTX
276 ***************************************************************************/
277
278 static void free_auth_context(struct auth_context **auth_context)
279 {
280         if (*auth_context != NULL) {
281                 talloc_destroy((*auth_context)->mem_ctx);
282         }
283         *auth_context = NULL;
284 }
285
286 /***************************************************************************
287  Make a auth_info struct
288 ***************************************************************************/
289
290 static NTSTATUS make_auth_context(struct auth_context **auth_context) 
291 {
292         TALLOC_CTX *mem_ctx;
293
294         mem_ctx = talloc_init_named("authentication context");
295         
296         *auth_context = talloc(mem_ctx, sizeof(**auth_context));
297         if (!*auth_context) {
298                 DEBUG(0,("make_auth_context: talloc failed!\n"));
299                 talloc_destroy(mem_ctx);
300                 return NT_STATUS_NO_MEMORY;
301         }
302         ZERO_STRUCTP(*auth_context);
303
304         (*auth_context)->mem_ctx = mem_ctx;
305         (*auth_context)->check_ntlm_password = check_ntlm_password;
306         (*auth_context)->get_ntlm_challenge = get_ntlm_challenge;
307         (*auth_context)->free = free_auth_context;
308         
309         return NT_STATUS_OK;
310 }
311
312 /***************************************************************************
313  Make a auth_info struct for the auth subsystem
314 ***************************************************************************/
315
316 static NTSTATUS make_auth_context_text_list(struct auth_context **auth_context, char **text_list) 
317 {
318         auth_methods *list = NULL;
319         auth_methods *t = NULL;
320         auth_methods *tmp;
321         int i;
322         NTSTATUS nt_status;
323
324         if (!text_list) {
325                 DEBUG(2,("No auth method list!?\n"));
326                 return NT_STATUS_UNSUCCESSFUL;
327         }
328         
329         if (!NT_STATUS_IS_OK(nt_status = make_auth_context(auth_context))) {
330                 return nt_status;
331         }
332         
333         for (;*text_list; text_list++)
334         { 
335                 DEBUG(5,("Attempting to find an auth method to match %s\n", *text_list));
336                 for (i = 0; builtin_auth_init_functions[i].name; i++)
337                 {
338                         if (strequal(builtin_auth_init_functions[i].name, *text_list))
339                         {
340                                 DEBUG(5,("Found auth method %s (at pos %d)\n", *text_list, i));
341                                 if (builtin_auth_init_functions[i].init(*auth_context, &t)) {
342                                         DEBUG(5,("auth method %s has a valid init\n", *text_list));
343                                         t->name = builtin_auth_init_functions[i].name;
344                                         DLIST_ADD_END(list, t, tmp);
345                                 } else {
346                                         DEBUG(0,("auth method %s did not correctly init\n", *text_list));
347                                 }
348                                 break;
349                         }
350                 }
351         }
352         
353         (*auth_context)->auth_method_list = list;
354         
355         return nt_status;
356 }
357
358 /***************************************************************************
359  Make a auth_context struct for the auth subsystem
360 ***************************************************************************/
361
362 NTSTATUS make_auth_context_subsystem(struct auth_context **auth_context) 
363 {
364         char **auth_method_list = NULL; 
365         NTSTATUS nt_status;
366
367         if (lp_auth_methods() && !lp_list_copy(&auth_method_list, lp_auth_methods())) {
368                 return NT_STATUS_NO_MEMORY;
369         }
370
371         if (auth_method_list == NULL) {
372                 switch (lp_security()) 
373                 {
374                 case SEC_DOMAIN:
375                         DEBUG(5,("Making default auth method list for security=domain\n"));
376                         auth_method_list = lp_list_make("guest samstrict ntdomain");
377                         break;
378                 case SEC_SERVER:
379                         DEBUG(5,("Making default auth method list for security=server\n"));
380                         auth_method_list = lp_list_make("guest samstrict smbserver");
381                         break;
382                 case SEC_USER:
383                         if (lp_encrypted_passwords()) { 
384                                 DEBUG(5,("Making default auth method list for security=user, encrypt passwords = yes\n"));
385                                 auth_method_list = lp_list_make("guest sam");
386                         } else {
387                                 DEBUG(5,("Making default auth method list for security=user, encrypt passwords = no\n"));
388                                 auth_method_list = lp_list_make("guest unix");
389                         }
390                         break;
391                 case SEC_SHARE:
392                         if (lp_encrypted_passwords()) {
393                                 DEBUG(5,("Making default auth method list for security=share, encrypt passwords = yes\n"));
394                                 auth_method_list = lp_list_make("guest sam");
395                         } else {
396                                 DEBUG(5,("Making default auth method list for security=share, encrypt passwords = no\n"));
397                                 auth_method_list = lp_list_make("guest unix");
398                         }
399                         break;
400                 case SEC_ADS:
401                         DEBUG(5,("Making default auth method list for security=ADS\n"));
402                         auth_method_list = lp_list_make("guest samstrict ads ntdomain");
403                         break;
404                 default:
405                         DEBUG(5,("Unknown auth method!\n"));
406                         return NT_STATUS_UNSUCCESSFUL;
407                 }
408         } else {
409                 DEBUG(5,("Using specified auth order\n"));
410         }
411         
412         if (!NT_STATUS_IS_OK(nt_status = make_auth_context_text_list(auth_context, auth_method_list))) {
413                 lp_list_free(&auth_method_list);
414                 return nt_status;
415         }
416         
417         lp_list_free(&auth_method_list);
418         return nt_status;
419 }
420
421 /***************************************************************************
422  Make a auth_info struct with a random challenge
423 ***************************************************************************/
424
425 NTSTATUS make_auth_context_random(struct auth_context **auth_context) 
426 {
427         uchar chal[8];
428         NTSTATUS nt_status;
429         if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(auth_context))) {
430                 return nt_status;
431         }
432         
433         generate_random_buffer(chal, sizeof(chal), False);
434         (*auth_context)->challenge = data_blob(chal, sizeof(chal));
435
436         (*auth_context)->challenge_set_by = "random";
437
438         return nt_status;
439 }
440
441 /***************************************************************************
442  Make a auth_info struct with a fixed challenge
443 ***************************************************************************/
444
445 NTSTATUS make_auth_context_fixed(struct auth_context **auth_context, uchar chal[8]) 
446 {
447         NTSTATUS nt_status;
448         if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(auth_context))) {
449                 return nt_status;
450         }
451         
452         (*auth_context)->challenge = data_blob(chal, 8);
453         return nt_status;
454 }
455
456