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