r1151: fixed fill-in of force_password_change field in auth_sam
[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         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();
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                 samdb_close(sam_ctx);
234                 return NT_STATUS_NO_SUCH_USER;
235         }
236
237         if (ret > 1) {
238                 DEBUG(1,("Found %d records matching user [%s]\n", ret, username));
239                 samdb_close(sam_ctx);
240                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
241         }
242         
243         domain_sid = samdb_result_sid_prefix(mem_ctx, msgs[0], "objectSid");
244         if (!domain_sid) {
245                 samdb_close(sam_ctx);
246                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
247         }
248
249         /* find the domain's DN */
250         ret_domain = samdb_search(sam_ctx, mem_ctx, NULL, &msgs_domain, domain_attrs,
251                            "(&(objectSid=%s)(objectclass=domain))", 
252                            domain_sid);
253
254         if (ret_domain == 0) {
255                 DEBUG(3,("check_sam_security: Couldn't find domain [%s] in passdb file.\n", 
256                          domain_sid));
257                 samdb_close(sam_ctx);
258                 return NT_STATUS_NO_SUCH_USER;
259         }
260
261         if (ret_domain > 1) {
262                 DEBUG(1,("Found %d records matching domain [%s]\n", 
263                          ret_domain, domain_sid));
264                 samdb_close(sam_ctx);
265                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
266         }
267
268         domain_dn = msgs_domain[0]->dn;
269         
270         acct_flags = samdb_result_acct_flags(msgs[0], "sAMAcctFlags");
271         
272         /* Quit if the account was locked out. */
273         if (acct_flags & ACB_AUTOLOCK) {
274                 DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", 
275                          username));
276                 samdb_close(sam_ctx);
277                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
278         }
279
280         if (!NT_STATUS_IS_OK(nt_status = samdb_result_passwords(mem_ctx, msgs[0], 
281                                                                 &lm_pwd, &nt_pwd))) {
282                 return nt_status;
283         }
284
285         nt_status = sam_password_ok(auth_context, mem_ctx, 
286                                     username, acct_flags, 
287                                     lm_pwd, nt_pwd,
288                                     user_info, &user_sess_key, &lm_sess_key);
289         
290         if (!NT_STATUS_IS_OK(nt_status)) {
291                 samdb_close(sam_ctx);
292                 return nt_status;
293         }
294
295         acct_expiry = samdb_result_nttime(msgs[0], "accountExpires", 0);
296         must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx, 
297                                                               domain_dn, msgs[0], 
298                                                               "pwdLastSet");
299         last_set_time = samdb_result_nttime(msgs[0], "pwdLastSet", 0);
300
301         workstation_list = samdb_result_string(msgs[0], "userWorkstations", NULL);
302
303         nt_status = sam_account_ok(mem_ctx, username, acct_flags, 
304                                    &acct_expiry, 
305                                    &must_change_time, 
306                                    &last_set_time, 
307                                    workstation_list,
308                                    user_info);
309
310         if (!NT_STATUS_IS_OK(nt_status)) {
311                 samdb_close(sam_ctx);
312                 return nt_status;
313         }
314
315         if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info, username))) {            
316                 DEBUG(0,("check_sam_security: make_server_info_sam() failed with '%s'\n", nt_errstr(nt_status)));
317                 samdb_close(sam_ctx);
318                 return nt_status;
319         }
320
321         {
322                 struct ldb_message **group_msgs;
323                 char *dn = msgs[0]->dn;
324                 int group_ret;
325                 const char *group_attrs[3] = { "sAMAccountType", "objectSid", NULL }; 
326                 /* find list of sids */
327                 struct dom_sid **groupSIDs = NULL;
328                 struct dom_sid *user_sid;
329                 struct dom_sid *primary_group_sid;
330                 const char *sidstr;
331                 int i;
332
333                 group_ret = samdb_search(sam_ctx,
334                                          mem_ctx, NULL, &group_msgs, group_attrs,
335                                          "(&(member=%s)(sAMAccountType=*))", 
336                                          dn);
337                 
338                 if (group_ret > 0 && 
339                     !(groupSIDs = talloc_realloc_p((*server_info)->mem_ctx, groupSIDs, 
340                                                    struct dom_sid *, group_ret))) {
341                         talloc_destroy((*server_info)->mem_ctx);
342                         samdb_close(sam_ctx);
343                         return NT_STATUS_NO_MEMORY;
344                 }
345
346                 /* Need to unroll some nested groups, but not aliases */
347                 for (i = 0; i < group_ret; i++) {
348                         sidstr = ldb_msg_find_string(group_msgs[i], "objectSid", NULL);
349                         groupSIDs[i] = dom_sid_parse_talloc((*server_info)->mem_ctx, sidstr);
350                 }
351                 
352                 sidstr = ldb_msg_find_string(msgs[0], "objectSid", NULL);
353                 user_sid = dom_sid_parse_talloc((*server_info)->mem_ctx, sidstr);
354                 primary_group_sid = dom_sid_parse_talloc((*server_info)->mem_ctx, sidstr);
355                 primary_group_sid->sub_auths[primary_group_sid->num_auths-1] 
356                         = samdb_result_uint(msgs[0], "primaryGroupID", 0);
357
358                 (*server_info)->user_sid = user_sid;
359                 (*server_info)->primary_group_sid = primary_group_sid;
360                 
361                 (*server_info)->n_domain_groups = group_ret;
362                 (*server_info)->domain_groups = groupSIDs;
363         }
364
365         (*server_info)->account_name 
366                 = talloc_strdup((*server_info)->mem_ctx, 
367                                 samdb_result_string(msgs[0], "sAMAccountName", ""));
368
369         (*server_info)->domain
370                 = talloc_strdup((*server_info)->mem_ctx, 
371                                 samdb_result_string(msgs_domain[0], "name", ""));
372
373         (*server_info)->full_name 
374                 = talloc_strdup((*server_info)->mem_ctx, 
375                                 samdb_result_string(msgs[0], "displayName", ""));
376
377         (*server_info)->logon_script 
378                 = talloc_strdup((*server_info)->mem_ctx, 
379                                 samdb_result_string(msgs[0], "scriptPath", ""));
380         (*server_info)->profile_path 
381                 = talloc_strdup((*server_info)->mem_ctx, 
382                                 samdb_result_string(msgs[0], "profilePath", ""));
383         (*server_info)->home_directory 
384                 = talloc_strdup((*server_info)->mem_ctx, 
385                                 samdb_result_string(msgs[0], "homeDirectory", ""));
386
387         (*server_info)->home_drive 
388                 = talloc_strdup((*server_info)->mem_ctx, 
389                                 samdb_result_string(msgs[0], "homeDrive", ""));
390
391         (*server_info)->last_logon = samdb_result_nttime(msgs[0], "lastLogon", 0);
392         (*server_info)->last_logoff = samdb_result_nttime(msgs[0], "lastLogoff", 0);
393         (*server_info)->acct_expiry = samdb_result_nttime(msgs[0], "accountExpires", 0);
394         (*server_info)->last_password_change = samdb_result_nttime(msgs[0], "pwdLastSet", 0);
395         (*server_info)->allow_password_change
396                 = samdb_result_allow_password_change(sam_ctx, mem_ctx, 
397                                                      domain_dn, msgs[0], "pwdLastSet");
398         (*server_info)->force_password_change
399                 = samdb_result_force_password_change(sam_ctx, mem_ctx, 
400                                                      domain_dn, msgs[0], "pwdLastSet");
401
402         (*server_info)->logon_count = samdb_result_uint(msgs[0], "logonCount", 0);
403         (*server_info)->bad_password_count = samdb_result_uint(msgs[0], "badPwdCount", 0);
404
405         (*server_info)->acct_flags = samdb_result_acct_flags(msgs[0], "userAccountControl");
406
407         (*server_info)->guest = False;
408
409         (*server_info)->user_session_key = user_sess_key;
410         (*server_info)->lm_session_key = lm_sess_key;
411
412         if (!(*server_info)->account_name 
413             || !(*server_info)->full_name 
414             || !(*server_info)->logon_script
415             || !(*server_info)->profile_path
416             || !(*server_info)->home_directory
417             || !(*server_info)->home_drive) {
418                 talloc_destroy((*server_info)->mem_ctx);
419                 return NT_STATUS_NO_MEMORY;
420         }
421
422         samdb_close(sam_ctx);
423
424         return nt_status;
425 }
426
427 /* module initialisation */
428 static NTSTATUS auth_init_sam_ignoredomain(struct auth_context *auth_context, 
429                                            const char *param, 
430                                            struct auth_methods **auth_method) 
431 {
432         if (!make_auth_methods(auth_context, auth_method)) {
433                 return NT_STATUS_NO_MEMORY;
434         }
435
436         (*auth_method)->auth = check_sam_security;      
437         (*auth_method)->name = "sam_ignoredomain";
438         return NT_STATUS_OK;
439 }
440
441
442 /****************************************************************************
443 Check SAM security (above) but with a few extra checks.
444 ****************************************************************************/
445
446 static NTSTATUS check_samstrict_security(const struct auth_context *auth_context,
447                                          void *my_private_data, 
448                                          TALLOC_CTX *mem_ctx,
449                                          const struct auth_usersupplied_info *user_info, 
450                                          struct auth_serversupplied_info **server_info)
451 {
452
453         if (!user_info || !auth_context) {
454                 return NT_STATUS_LOGON_FAILURE;
455         }
456
457         /* If we are a domain member, we must not 
458            attempt to check the password locally,
459            unless it is one of our aliases. */
460         
461         if (!is_myname(user_info->domain.str)) {
462                 DEBUG(7,("The requested user domain is not the local server name. [%s]\\[%s]\n",
463                         user_info->domain.str,user_info->internal_username.str));
464                 return NT_STATUS_NO_SUCH_USER;
465         }
466         
467         return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info);
468 }
469
470 /* module initialisation */
471 static NTSTATUS auth_init_sam(struct auth_context *auth_context, 
472                               const char *param, 
473                               struct auth_methods **auth_method) 
474 {
475         if (!make_auth_methods(auth_context, auth_method)) {
476                 return NT_STATUS_NO_MEMORY;
477         }
478
479         (*auth_method)->auth = check_samstrict_security;
480         (*auth_method)->name = "sam";
481         return NT_STATUS_OK;
482 }
483
484 NTSTATUS auth_sam_init(void)
485 {
486         NTSTATUS ret;
487         struct auth_operations ops;
488
489         ops.name = "sam";
490         ops.init = auth_init_sam;
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         ops.name = "sam_ignoredomain";
499         ops.init = auth_init_sam_ignoredomain;
500         ret = register_backend("auth", &ops);
501         if (!NT_STATUS_IS_OK(ret)) {
502                 DEBUG(0,("Failed to register '%s' auth backend!\n",
503                         ops.name));
504                 return ret;
505         }
506
507         return ret;
508 }