Add a pile of doxygen style comments to various parts of Samba. Many of these
[gd/samba/.git] / source3 / auth / auth.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Password and authentication handling
5    Copyright (C) Andrew Tridgell              1992-2000
6    Copyright (C) Luke Kenneth Casson Leighton 1996-2000
7    Copyright (C) Andrew Bartlett              2001
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 /**
27  * Check user is in correct domain (if required)
28  *
29  * @param user Only used to fill in the debug message
30  * 
31  * @param domain The domain to be verified
32  *
33  * @return True if the user can connect with that domain, 
34  *         False otherwise.
35 **/
36
37 static BOOL check_domain_match(const char *user, const char *domain) 
38 {
39         /*
40          * If we aren't serving to trusted domains, we must make sure that
41          * the validation request comes from an account in the same domain
42          * as the Samba server
43          */
44
45         if (!lp_allow_trusted_domains() &&
46             !(strequal("", domain) || 
47               strequal(lp_workgroup(), domain) || 
48               is_netbios_alias_or_name(domain))) {
49                 DEBUG(1, ("check_domain_match: Attempt to connect as user %s from domain %s denied.\n", user, domain));
50                 return False;
51         } else {
52                 return True;
53         }
54 }
55
56 /**
57  * Check a user's Plaintext, LM or NTLM password.
58  *
59  * Check a user's password, as given in the user_info struct and return various
60  * interesting details in the server_info struct.
61  *
62  * This function does NOT need to be in a become_root()/unbecome_root() pair
63  * as it makes the calls itself when needed.
64  *
65  * The return value takes precedence over the contents of the server_info 
66  * struct.  When the return is other than NT_STATUS_OK the contents 
67  * of that structure is undefined.
68  *
69  * @param user_info Contains the user supplied components, including the passwords.
70  *                  Must be created with make_user_info() or one of its wrappers.
71  *
72  * @param auth_info Supplies the challanges and some other data. 
73  *                  Must be created with make_auth_info(), and the challanges should be 
74  *                  filled in, either at creation or by calling the challange geneation 
75  *                  function auth_get_challange().  
76  *
77  * @param server_info If successful, contains information about the authenticaion, 
78  *                    including a SAM_ACCOUNT struct describing the user.
79  *
80  * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
81  *
82  **/
83
84 NTSTATUS check_password(const auth_usersupplied_info *user_info, 
85                              const auth_authsupplied_info *auth_info,
86                              auth_serversupplied_info **server_info)
87 {
88         
89         NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
90         const char *pdb_username;
91         auth_methods *auth_method;
92
93         if (!user_info || !auth_info || !server_info) {
94                 return NT_STATUS_LOGON_FAILURE;
95         }
96
97         DEBUG(3, ("check_password:  Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n", 
98                   user_info->client_domain.str, user_info->smb_name.str, user_info->wksta_name.str));
99
100         DEBUG(3, ("check_password:  mapped user is: [%s]\\[%s]@[%s]\n", 
101                   user_info->domain.str, user_info->internal_username.str, user_info->wksta_name.str));
102         if (auth_info->challenge_set_by) {
103                 DEBUG(10, ("auth_info challenge created by %s\n", auth_info->challenge_set_by));
104         }
105         DEBUG(10, ("challenge is: \n"));
106         dump_data(5, (auth_info)->challenge.data, (auth_info)->challenge.length);
107
108 #ifdef DEBUG_PASSWORD
109         DEBUG(100, ("user_info has passwords of length %d and %d\n", 
110                     user_info->lm_resp.length, user_info->nt_resp.length));
111         DEBUG(100, ("lm:\n"));
112         dump_data(100, user_info->lm_resp.data, user_info->lm_resp.length);
113         DEBUG(100, ("nt:\n"));
114         dump_data(100, user_info->nt_resp.data, user_info->nt_resp.length);
115 #endif
116
117         /* This needs to be sorted:  If it doesn't match, what should we do? */
118         if (!check_domain_match(user_info->smb_name.str, user_info->domain.str)) {
119                 return NT_STATUS_LOGON_FAILURE;
120         }
121
122         for (auth_method = auth_info->auth_method_list;auth_method; auth_method = auth_method->next)
123         {
124                 nt_status = auth_method->auth(auth_method->private_data, user_info, auth_info, server_info);
125                 if (NT_STATUS_IS_OK(nt_status)) {
126                         DEBUG(3, ("check_password: %s authentication for user [%s] suceeded\n", 
127                                   auth_method->name, user_info->smb_name.str));
128                 } else {
129                         DEBUG(5, ("check_password: %s authentication for user [%s] FAILED with error %s\n", 
130                                   auth_method->name, user_info->smb_name.str, get_nt_error_msg(nt_status)));
131                 }
132                 
133                 if (NT_STATUS_IS_OK(nt_status)) {
134                         break;
135                 }
136         }
137
138         /* This is one of the few places the *relies* (rather than just sets defaults
139            on the value of lp_security().  This needs to change.  A new paramater 
140            perhaps? */
141         if (lp_security() >= SEC_SERVER) {
142                 smb_user_control(user_info, *server_info, nt_status);
143         }
144
145         if (NT_STATUS_IS_OK(nt_status)) {
146                 pdb_username = pdb_get_username((*server_info)->sam_account);
147                 if (!(*server_info)->guest) {
148                         /* We might not be root if we are an RPC call */
149                         become_root();
150                         nt_status = smb_pam_accountcheck(pdb_username);
151                         unbecome_root();
152                         
153                         if (NT_STATUS_IS_OK(nt_status)) {
154                                 DEBUG(5, ("check_password:  PAM Account for user [%s] suceeded\n", 
155                                           pdb_username));
156                         } else {
157                                 DEBUG(3, ("check_password:  PAM Account for user [%s] FAILED with error %s\n", 
158                                           pdb_username, get_nt_error_msg(nt_status)));
159                         } 
160                 }
161                 
162                 if (NT_STATUS_IS_OK(nt_status)) {
163                         DEBUG((*server_info)->guest ? 5 : 2, 
164                               ("check_password:  %sauthenticaion for user [%s] -> [%s] -> [%s] suceeded\n", 
165                                (*server_info)->guest ? "guest " : "", 
166                                user_info->smb_name.str, 
167                                user_info->internal_username.str, 
168                                pdb_username));
169                 }
170         }
171
172         if (!NT_STATUS_IS_OK(nt_status)) {
173                 DEBUG(2, ("check_password:  Authenticaion for user [%s] -> [%s] FAILED with error %s\n", 
174                           user_info->smb_name.str, user_info->internal_username.str, 
175                           get_nt_error_msg(nt_status)));
176                 ZERO_STRUCTP(server_info);
177         }
178         return nt_status;
179
180 }
181
182 /**
183  * Squash an NT_STATUS in line with security requirements.
184  * In an attempt to avoid giving the whole game away when users
185  * are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and 
186  * NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations 
187  * (session setups in particular).
188  *
189  * @param nt_status NTSTATUS input for squashing.
190  * @return the 'squashed' nt_status
191  **/
192
193 NTSTATUS nt_status_squash(NTSTATUS nt_status) 
194 {
195         if NT_STATUS_IS_OK(nt_status) {
196                 return nt_status;               
197         } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
198                 /* Match WinXP and don't give the game away */
199                 return NT_STATUS_LOGON_FAILURE;
200                 
201         } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
202                 /* Match WinXP and don't give the game away */
203                 return NT_STATUS_LOGON_FAILURE;
204         } else {
205                 return nt_status;
206         }  
207 }
208
209
210
211 /****************************************************************************
212  COMPATABILITY INTERFACES:
213  ***************************************************************************/
214
215 /****************************************************************************
216 check if a username/password is OK assuming the password is a 24 byte
217 SMB hash
218 return True if the password is correct, False otherwise
219 ****************************************************************************/
220
221 static NTSTATUS pass_check_smb(char *smb_name,
222                                char *domain, 
223                                DATA_BLOB lm_pwd,
224                                DATA_BLOB nt_pwd,
225                                DATA_BLOB plaintext_password,
226                                BOOL encrypted)
227
228 {
229         NTSTATUS nt_status;
230         auth_usersupplied_info *user_info = NULL;
231         extern auth_authsupplied_info *negprot_global_auth_info;
232         auth_serversupplied_info *server_info = NULL;
233         if (encrypted) {                
234                 make_user_info_for_reply_enc(&user_info, smb_name, 
235                                              domain,
236                                              lm_pwd, 
237                                              nt_pwd, 
238                                              plaintext_password);
239                 nt_status = check_password(user_info, negprot_global_auth_info, &server_info);
240         } else {
241                 auth_authsupplied_info *plaintext_auth_info = NULL;
242                 DATA_BLOB chal;
243                 if (!make_auth_info_subsystem(&plaintext_auth_info)) {
244                         return NT_STATUS_NO_MEMORY;
245                 }
246
247                 chal = auth_get_challenge(plaintext_auth_info);
248
249                 if (!make_user_info_for_reply(&user_info, 
250                                               smb_name, domain, chal.data,
251                                               plaintext_password)) {
252                         return NT_STATUS_NO_MEMORY;
253                 }
254                 
255                 nt_status = check_password(user_info, plaintext_auth_info, &server_info); 
256                 
257                 data_blob_free(&chal);
258                 free_auth_info(&plaintext_auth_info);
259         }               
260         free_user_info(&user_info);
261         free_server_info(&server_info);
262         return nt_status;
263 }
264
265 /****************************************************************************
266 check if a username/password pair is OK either via the system password
267 database or the encrypted SMB password database
268 return True if the password is correct, False otherwise
269 ****************************************************************************/
270 BOOL password_ok(char *smb_name, DATA_BLOB password_blob)
271 {
272
273         DATA_BLOB null_password = data_blob(NULL, 0);
274         extern BOOL global_encrypted_passwords_negotiated;
275         BOOL encrypted = (global_encrypted_passwords_negotiated && password_blob.length == 24);
276         
277         if (encrypted) {
278                 /* 
279                  * The password could be either NTLM or plain LM.  Try NTLM first, 
280                  * but fall-through as required.
281                  * NTLMv2 makes no sense here.
282                  */
283                 if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, password_blob, null_password, encrypted))) {
284                         return True;
285                 }
286                 
287                 if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), password_blob, null_password, null_password, encrypted))) {
288                         return True;
289                 }
290         } else {
291                 if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, null_password, password_blob, encrypted))) {
292                         return True;
293                 }
294         }
295
296         return False;
297 }
298
299