r2654: fixed some more server memory leaks. We are now down to a single leak
[kai/samba.git] / source / auth / auth_sam.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Password and authentication handling
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
5    Copyright (C) Gerald Carter                             2003
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_AUTH
26
27 /****************************************************************************
28  Do a specific test for an smb password being correct, given a smb_password and
29  the lanman and NT responses.
30 ****************************************************************************/
31
32 static NTSTATUS sam_password_ok(const struct auth_context *auth_context,
33                                 TALLOC_CTX *mem_ctx,
34                                 const char *username,
35                                 uint16_t acct_flags,
36                                 const struct samr_Password *lm_pwd, 
37                                 const struct samr_Password *nt_pwd,
38                                 const struct auth_usersupplied_info *user_info, 
39                                 DATA_BLOB *user_sess_key, 
40                                 DATA_BLOB *lm_sess_key)
41 {
42         NTSTATUS status;
43
44         if (acct_flags & ACB_PWNOTREQ) {
45                 if (lp_null_passwords()) {
46                         DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", 
47                                  username));
48                         return NT_STATUS_OK;
49                 } else {
50                         DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", 
51                                  username));
52                         return NT_STATUS_LOGON_FAILURE;
53                 }               
54         }
55
56         status = ntlm_password_check(mem_ctx, &auth_context->challenge, 
57                                    &user_info->lm_resp, &user_info->nt_resp, 
58                                    &user_info->lm_interactive_password, 
59                                    &user_info->nt_interactive_password,
60                                    username, 
61                                    user_info->smb_name.str, 
62                                    user_info->client_domain.str, 
63                                    lm_pwd->hash, nt_pwd->hash, user_sess_key, lm_sess_key);
64
65         if (NT_STATUS_IS_OK(status)) {
66                 if (user_sess_key && user_sess_key->data) {
67                         talloc_steal(auth_context, user_sess_key->data);
68                 }
69                 if (lm_sess_key && lm_sess_key->data) {
70                         talloc_steal(auth_context, lm_sess_key->data);
71                 }
72         }
73
74         return status;
75 }
76
77
78 /****************************************************************************
79  Do a specific test for a SAM_ACCOUNT being vaild for this connection 
80  (ie not disabled, expired and the like).
81 ****************************************************************************/
82
83 static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx,
84                                const char *username,
85                                uint16_t acct_flags,
86                                NTTIME *acct_expiry,
87                                NTTIME *must_change_time,
88                                NTTIME *last_set_time,
89                                const char *workstation_list,
90                                const struct auth_usersupplied_info *user_info)
91 {
92         DEBUG(4,("sam_account_ok: Checking SMB password for user %s\n", username));
93
94         /* Quit if the account was disabled. */
95         if (acct_flags & ACB_DISABLED) {
96                 DEBUG(1,("sam_account_ok: Account for user '%s' was disabled.\n", username));
97                 return NT_STATUS_ACCOUNT_DISABLED;
98         }
99
100         /* Quit if the account was locked out. */
101         if (acct_flags & ACB_AUTOLOCK) {
102                 DEBUG(1,("sam_account_ok: Account for user %s was locked out.\n", username));
103                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
104         }
105
106         /* Test account expire time */
107         if ((*acct_expiry) != -1 && time(NULL) > nt_time_to_unix(*acct_expiry)) {
108                 DEBUG(1,("sam_account_ok: Account for user '%s' has expired.\n", username));
109                 DEBUG(3,("sam_account_ok: Account expired at '%s'.\n", 
110                          nt_time_string(mem_ctx, *acct_expiry)));
111                 return NT_STATUS_ACCOUNT_EXPIRED;
112         }
113
114         if (!(acct_flags & ACB_PWNOEXP)) {
115
116                 /* check for immediate expiry "must change at next logon" */
117                 if (*must_change_time == 0 && *last_set_time != 0) {
118                         DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", 
119                                  username));
120                         return NT_STATUS_PASSWORD_MUST_CHANGE;
121                 }
122
123                 /* check for expired password */
124                 if ((*must_change_time) != 0 && nt_time_to_unix(*must_change_time) < time(NULL)) {
125                         DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n", 
126                                  username));
127                         DEBUG(1,("sam_account_ok: Password expired at '%s' unix time.\n", 
128                                  nt_time_string(mem_ctx, *must_change_time)));
129                         return NT_STATUS_PASSWORD_EXPIRED;
130                 }
131         }
132
133         /* Test workstation. Workstation list is comma separated. */
134
135         if (workstation_list && *workstation_list) {
136                 BOOL invalid_ws = True;
137                 const char *s = workstation_list;
138                         
139                 fstring tok;
140                         
141                 while (next_token(&s, tok, ",", sizeof(tok))) {
142                         DEBUG(10,("sam_account_ok: checking for workstation match %s and %s (len=%d)\n",
143                                   tok, user_info->wksta_name.str, user_info->wksta_name.len));
144                         
145                         if(strequal(tok, user_info->wksta_name.str)) {
146                                 invalid_ws = False;
147                                 break;
148                         }
149                 }
150                 
151                 if (invalid_ws) 
152                         return NT_STATUS_INVALID_WORKSTATION;
153         }
154
155         if (acct_flags & ACB_DOMTRUST) {
156                 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", username));
157                 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
158         }
159         
160         if (acct_flags & ACB_SVRTRUST) {
161                 DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", username));
162                 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
163         }
164         
165         if (acct_flags & ACB_WSTRUST) {
166                 DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", username));
167                 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
168         }
169         
170         return NT_STATUS_OK;
171 }
172
173 /****************************************************************************
174 check if a username/password is OK assuming the password is a 24 byte
175 SMB hash supplied in the user_info structure
176 return an NT_STATUS constant.
177 ****************************************************************************/
178
179 static NTSTATUS check_sam_security(const struct auth_context *auth_context,
180                                    void *my_private_data, 
181                                    TALLOC_CTX *mem_ctx,
182                                    const struct auth_usersupplied_info *user_info, 
183                                    struct auth_serversupplied_info **server_info)
184 {
185         struct ldb_message **msgs;
186         struct ldb_message **msgs_domain;
187         void *sam_ctx;
188         char *username = user_info->internal_username.str;
189         uint16_t acct_flags;
190         const char *workstation_list;
191         NTTIME acct_expiry;
192         NTTIME must_change_time;
193         NTTIME last_set_time;
194
195         uint_t ret;
196         uint_t ret_domain;
197
198         const char *domain_dn;
199         const char *domain_sid;
200
201         NTSTATUS nt_status;
202         DATA_BLOB user_sess_key = data_blob(NULL, 0);
203         DATA_BLOB lm_sess_key = data_blob(NULL, 0);
204         struct samr_Password *lm_pwd, *nt_pwd;
205
206         const char *attrs[] = {"unicodePwd", "lmPwdHash", "ntPwdHash", 
207                                "userAccountControl",
208                                "pwdLastSet",
209                                "accountExpires",
210                                "objectSid",
211                                "userWorkstations",
212                                
213                                /* required for server_info, not access control: */
214                                "sAMAccountName",
215                                "displayName",
216                                "scriptPath",
217                                "profilePath",
218                                "homeDirectory",
219                                "homeDrive",
220                                "lastLogon",
221                                "lastLogoff",
222                                "accountExpires",
223                                "badPwdCount",
224                                "logonCount",
225                                NULL,
226         };
227
228         const char *domain_attrs[] =  {"name"};
229
230         if (!user_info || !auth_context) {
231                 return NT_STATUS_UNSUCCESSFUL;
232         }
233         
234         sam_ctx = samdb_connect(mem_ctx);
235         if (sam_ctx == NULL) {
236                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
237         }
238         /* pull the user attributes */
239         ret = samdb_search(sam_ctx, mem_ctx, NULL, &msgs, attrs,
240                            "(&(sAMAccountName=%s)(objectclass=user))", 
241                            username);
242
243         if (ret == 0) {
244                 DEBUG(3,("check_sam_security: Couldn't find user [%s] in passdb file.\n", 
245                          username));
246                 return NT_STATUS_NO_SUCH_USER;
247         }
248
249         if (ret > 1) {
250                 DEBUG(0,("Found %d records matching user [%s]\n", ret, username));
251                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
252         }
253         
254         domain_sid = samdb_result_sid_prefix(mem_ctx, msgs[0], "objectSid");
255         if (!domain_sid) {
256                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
257         }
258
259         /* find the domain's DN */
260         ret_domain = samdb_search(sam_ctx, mem_ctx, NULL, &msgs_domain, domain_attrs,
261                            "(&(objectSid=%s)(objectclass=domain))", 
262                            domain_sid);
263
264         if (ret_domain == 0) {
265                 DEBUG(3,("check_sam_security: Couldn't find domain [%s] in passdb file.\n", 
266                          domain_sid));
267                 return NT_STATUS_NO_SUCH_USER;
268         }
269
270         if (ret_domain > 1) {
271                 DEBUG(0,("Found %d records matching domain [%s]\n", 
272                          ret_domain, domain_sid));
273                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
274         }
275
276         domain_dn = msgs_domain[0]->dn;
277         
278         acct_flags = samdb_result_acct_flags(msgs[0], "sAMAcctFlags");
279         
280         /* Quit if the account was locked out. */
281         if (acct_flags & ACB_AUTOLOCK) {
282                 DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", 
283                          username));
284                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
285         }
286
287         if (!NT_STATUS_IS_OK(nt_status = samdb_result_passwords(mem_ctx, msgs[0], 
288                                                                 &lm_pwd, &nt_pwd))) {
289                 return nt_status;
290         }
291
292         nt_status = sam_password_ok(auth_context, mem_ctx, 
293                                     username, acct_flags, 
294                                     lm_pwd, nt_pwd,
295                                     user_info, &user_sess_key, &lm_sess_key);
296         
297         if (!NT_STATUS_IS_OK(nt_status)) {
298                 return nt_status;
299         }
300
301         acct_expiry = samdb_result_nttime(msgs[0], "accountExpires", 0);
302         must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx, 
303                                                               domain_dn, msgs[0], 
304                                                               "pwdLastSet");
305         last_set_time = samdb_result_nttime(msgs[0], "pwdLastSet", 0);
306
307         workstation_list = samdb_result_string(msgs[0], "userWorkstations", NULL);
308
309         nt_status = sam_account_ok(mem_ctx, username, acct_flags, 
310                                    &acct_expiry, 
311                                    &must_change_time, 
312                                    &last_set_time, 
313                                    workstation_list,
314                                    user_info);
315
316         if (!NT_STATUS_IS_OK(nt_status)) {
317                 return nt_status;
318         }
319
320         if (!NT_STATUS_IS_OK(nt_status = make_server_info(auth_context, server_info, username))) {              
321                 DEBUG(0,("check_sam_security: make_server_info_sam() failed with '%s'\n", nt_errstr(nt_status)));
322                 return nt_status;
323         }
324
325         {
326                 struct ldb_message **group_msgs;
327                 char *dn = msgs[0]->dn;
328                 int group_ret;
329                 const char *group_attrs[3] = { "sAMAccountType", "objectSid", NULL }; 
330                 /* find list of sids */
331                 struct dom_sid **groupSIDs = NULL;
332                 struct dom_sid *user_sid;
333                 struct dom_sid *primary_group_sid;
334                 const char *sidstr;
335                 int i;
336
337                 group_ret = samdb_search(sam_ctx,
338                                          mem_ctx, NULL, &group_msgs, group_attrs,
339                                          "(&(member=%s)(sAMAccountType=*))", 
340                                          dn);
341                 if (group_ret == -1) {
342                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
343                 }
344                 
345                 if (group_ret > 0 && 
346                     !(groupSIDs = talloc_array_p(*server_info, struct dom_sid *, group_ret))) {
347                         talloc_free(*server_info);
348                         return NT_STATUS_NO_MEMORY;
349                 }
350
351                 /* Need to unroll some nested groups, but not aliases */
352                 for (i = 0; i < group_ret; i++) {
353                         sidstr = ldb_msg_find_string(group_msgs[i], "objectSid", NULL);
354                         groupSIDs[i] = dom_sid_parse_talloc(*server_info, sidstr);
355                 }
356                 
357                 sidstr = ldb_msg_find_string(msgs[0], "objectSid", NULL);
358                 user_sid = dom_sid_parse_talloc(*server_info, sidstr);
359                 primary_group_sid = dom_sid_parse_talloc(*server_info, sidstr);
360                 primary_group_sid->sub_auths[primary_group_sid->num_auths-1] 
361                         = samdb_result_uint(msgs[0], "primaryGroupID", 0);
362
363                 (*server_info)->user_sid = user_sid;
364                 (*server_info)->primary_group_sid = primary_group_sid;
365                 
366                 (*server_info)->n_domain_groups = group_ret;
367                 (*server_info)->domain_groups = groupSIDs;
368         }
369
370         (*server_info)->account_name 
371                 = talloc_strdup(*server_info, 
372                                 samdb_result_string(msgs[0], "sAMAccountName", ""));
373
374         (*server_info)->domain
375                 = talloc_strdup(*server_info, 
376                                 samdb_result_string(msgs_domain[0], "name", ""));
377
378         (*server_info)->full_name 
379                 = talloc_strdup(*server_info, 
380                                 samdb_result_string(msgs[0], "displayName", ""));
381
382         (*server_info)->logon_script 
383                 = talloc_strdup(*server_info, 
384                                 samdb_result_string(msgs[0], "scriptPath", ""));
385         (*server_info)->profile_path 
386                 = talloc_strdup(*server_info, 
387                                 samdb_result_string(msgs[0], "profilePath", ""));
388         (*server_info)->home_directory 
389                 = talloc_strdup(*server_info, 
390                                 samdb_result_string(msgs[0], "homeDirectory", ""));
391
392         (*server_info)->home_drive 
393                 = talloc_strdup(*server_info, 
394                                 samdb_result_string(msgs[0], "homeDrive", ""));
395
396         (*server_info)->last_logon = samdb_result_nttime(msgs[0], "lastLogon", 0);
397         (*server_info)->last_logoff = samdb_result_nttime(msgs[0], "lastLogoff", 0);
398         (*server_info)->acct_expiry = samdb_result_nttime(msgs[0], "accountExpires", 0);
399         (*server_info)->last_password_change = samdb_result_nttime(msgs[0], "pwdLastSet", 0);
400         (*server_info)->allow_password_change
401                 = samdb_result_allow_password_change(sam_ctx, mem_ctx, 
402                                                      domain_dn, msgs[0], "pwdLastSet");
403         (*server_info)->force_password_change
404                 = samdb_result_force_password_change(sam_ctx, mem_ctx, 
405                                                      domain_dn, msgs[0], "pwdLastSet");
406
407         (*server_info)->logon_count = samdb_result_uint(msgs[0], "logonCount", 0);
408         (*server_info)->bad_password_count = samdb_result_uint(msgs[0], "badPwdCount", 0);
409
410         (*server_info)->acct_flags = samdb_result_acct_flags(msgs[0], "userAccountControl");
411
412         (*server_info)->guest = False;
413
414         (*server_info)->user_session_key = user_sess_key;
415         (*server_info)->lm_session_key = lm_sess_key;
416
417         if (!(*server_info)->account_name 
418             || !(*server_info)->full_name 
419             || !(*server_info)->logon_script
420             || !(*server_info)->profile_path
421             || !(*server_info)->home_directory
422             || !(*server_info)->home_drive) {
423                 talloc_destroy(*server_info);
424                 return NT_STATUS_NO_MEMORY;
425         }
426
427         return nt_status;
428 }
429
430 /* module initialisation */
431 static NTSTATUS auth_init_sam_ignoredomain(struct auth_context *auth_context, 
432                                            const char *param, 
433                                            struct auth_methods **auth_method) 
434 {
435         if (!make_auth_methods(auth_context, auth_method)) {
436                 return NT_STATUS_NO_MEMORY;
437         }
438
439         (*auth_method)->auth = check_sam_security;      
440         (*auth_method)->name = "sam_ignoredomain";
441         return NT_STATUS_OK;
442 }
443
444
445 /****************************************************************************
446 Check SAM security (above) but with a few extra checks.
447 ****************************************************************************/
448
449 static NTSTATUS check_samstrict_security(const struct auth_context *auth_context,
450                                          void *my_private_data, 
451                                          TALLOC_CTX *mem_ctx,
452                                          const struct auth_usersupplied_info *user_info, 
453                                          struct auth_serversupplied_info **server_info)
454 {
455
456         if (!user_info || !auth_context) {
457                 return NT_STATUS_LOGON_FAILURE;
458         }
459
460         /* If we are a domain member, we must not 
461            attempt to check the password locally,
462            unless it is one of our aliases. */
463         
464         if (!is_myname(user_info->domain.str)) {
465                 DEBUG(7,("The requested user domain is not the local server name. [%s]\\[%s]\n",
466                         user_info->domain.str,user_info->internal_username.str));
467                 return NT_STATUS_NO_SUCH_USER;
468         }
469         
470         return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info);
471 }
472
473 /* module initialisation */
474 static NTSTATUS auth_init_sam(struct auth_context *auth_context, 
475                               const char *param, 
476                               struct auth_methods **auth_method) 
477 {
478         if (!make_auth_methods(auth_context, auth_method)) {
479                 return NT_STATUS_NO_MEMORY;
480         }
481
482         (*auth_method)->auth = check_samstrict_security;
483         (*auth_method)->name = "sam";
484         return NT_STATUS_OK;
485 }
486
487 NTSTATUS auth_sam_init(void)
488 {
489         NTSTATUS ret;
490         struct auth_operations ops;
491
492         ops.name = "sam";
493         ops.init = auth_init_sam;
494         ret = register_backend("auth", &ops);
495         if (!NT_STATUS_IS_OK(ret)) {
496                 DEBUG(0,("Failed to register '%s' auth backend!\n",
497                         ops.name));
498                 return ret;
499         }
500
501         ops.name = "sam_ignoredomain";
502         ops.init = auth_init_sam_ignoredomain;
503         ret = register_backend("auth", &ops);
504         if (!NT_STATUS_IS_OK(ret)) {
505                 DEBUG(0,("Failed to register '%s' auth backend!\n",
506                         ops.name));
507                 return ret;
508         }
509
510         return ret;
511 }