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