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