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