r21434: - get rid of "krb5Key"
[samba.git] / source4 / 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    Copyright (C) Stefan Metzmacher                         2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "system/time.h"
25 #include "auth/auth.h"
26 #include "db_wrap.h"
27 #include "dsdb/samdb/samdb.h"
28 #include "libcli/security/security.h"
29 #include "libcli/ldap/ldap.h"
30 #include "librpc/gen_ndr/ndr_netlogon.h"
31
32 const char *user_attrs[] = {
33         /* required for the krb5 kdc */
34         "objectClass",
35         "sAMAccountName",
36         "userPrincipalName",
37         "servicePrincipalName",
38         "msDS-KeyVersionNumber",
39         "supplementalCredentials",
40
41         /* passwords */
42         "dBCSPwd", 
43         "unicodePwd",
44
45         "userAccountControl",
46
47         "pwdLastSet",
48         "accountExpires",
49         
50         "objectSid",
51
52         /* check 'allowed workstations' */
53         "userWorkstations",
54                        
55         /* required for server_info, not access control: */
56         "displayName",
57         "scriptPath",
58         "profilePath",
59         "homeDirectory",
60         "homeDrive",
61         "lastLogon",
62         "lastLogoff",
63         "accountExpires",
64         "badPwdCount",
65         "logonCount",
66         "primaryGroupID",
67         NULL,
68 };
69
70 const char *domain_ref_attrs[] =  {"nETBIOSName", "nCName", 
71                                           "dnsRoot", "objectClass", NULL};
72
73
74 /****************************************************************************
75  Do a specific test for a SAM_ACCOUNT being vaild for this connection 
76  (ie not disabled, expired and the like).
77 ****************************************************************************/
78 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
79                             struct ldb_context *sam_ctx,
80                             uint32_t logon_parameters,
81                             struct ldb_message *msg,
82                             struct ldb_message *msg_domain_ref,
83                             const char *logon_workstation,
84                             const char *name_for_logs)
85 {
86         uint16_t acct_flags;
87         const char *workstation_list;
88         NTTIME acct_expiry;
89         NTTIME must_change_time;
90         NTTIME last_set_time;
91
92         struct ldb_dn *domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", ldb_dn_new(mem_ctx, sam_ctx, NULL));
93
94         NTTIME now;
95         DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
96
97         acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
98         
99         acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
100         must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx, 
101                                                               domain_dn, msg);
102         last_set_time = samdb_result_nttime(msg, "pwdLastSet", 0);
103
104         workstation_list = samdb_result_string(msg, "userWorkstations", NULL);
105
106         /* Quit if the account was disabled. */
107         if (acct_flags & ACB_DISABLED) {
108                 DEBUG(1,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
109                 return NT_STATUS_ACCOUNT_DISABLED;
110         }
111
112         /* Quit if the account was locked out. */
113         if (acct_flags & ACB_AUTOLOCK) {
114                 DEBUG(1,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
115                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
116         }
117
118         /* Test account expire time */
119         unix_to_nt_time(&now, time(NULL));
120         if (now > acct_expiry) {
121                 DEBUG(1,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
122                 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n", 
123                          nt_time_string(mem_ctx, acct_expiry)));
124                 return NT_STATUS_ACCOUNT_EXPIRED;
125         }
126
127         if (!(acct_flags & ACB_PWNOEXP)) {
128                 /* check for immediate expiry "must change at next logon" */
129                 if (must_change_time == 0 && last_set_time != 0) {
130                         DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", 
131                                  name_for_logs));
132                         return NT_STATUS_PASSWORD_MUST_CHANGE;
133                 }
134
135                 /* check for expired password */
136                 if ((must_change_time != 0) && (must_change_time < now)) {
137                         DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n", 
138                                  name_for_logs));
139                         DEBUG(1,("sam_account_ok: Password expired at '%s' unix time.\n", 
140                                  nt_time_string(mem_ctx, must_change_time)));
141                         return NT_STATUS_PASSWORD_EXPIRED;
142                 }
143         }
144
145         /* Test workstation. Workstation list is comma separated. */
146         if (logon_workstation && workstation_list && *workstation_list) {
147                 BOOL invalid_ws = True;
148                 int i;
149                 const char **workstations = str_list_make(mem_ctx, workstation_list, ",");
150                 
151                 for (i = 0; workstations && workstations[i]; i++) {
152                         DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
153                                   workstations[i], logon_workstation));
154
155                         if (strequal(workstations[i], logon_workstation) == 0) {
156                                 invalid_ws = False;
157                                 break;
158                         }
159                 }
160
161                 talloc_free(workstations);
162
163                 if (invalid_ws) {
164                         return NT_STATUS_INVALID_WORKSTATION;
165                 }
166         }
167         
168         if (acct_flags & ACB_DOMTRUST) {
169                 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
170                 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
171         }
172         
173         if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
174                 if (acct_flags & ACB_SVRTRUST) {
175                         DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
176                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
177                 }
178         }
179         if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
180                 if (acct_flags & ACB_WSTRUST) {
181                         DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
182                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
183                 }
184         }
185
186         return NT_STATUS_OK;
187 }
188
189 _PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
190                                   struct ldb_message *msg,
191                                   struct ldb_message *msg_domain_ref,
192                                   DATA_BLOB user_sess_key, DATA_BLOB lm_sess_key,
193                                   struct auth_serversupplied_info **_server_info)
194 {
195         struct auth_serversupplied_info *server_info;
196         struct ldb_message **group_msgs;
197         int group_ret;
198         const char *group_attrs[3] = { "sAMAccountType", "objectSid", NULL }; 
199         /* find list of sids */
200         struct dom_sid **groupSIDs = NULL;
201         struct dom_sid *account_sid;
202         struct dom_sid *primary_group_sid;
203         const char *str;
204         struct ldb_dn *ncname;
205         int i;
206         uint_t rid;
207         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
208
209         group_ret = gendb_search(sam_ctx,
210                                  tmp_ctx, NULL, &group_msgs, group_attrs,
211                                  "(&(member=%s)(sAMAccountType=*))", 
212                                  ldb_dn_get_linearized(msg->dn));
213         if (group_ret == -1) {
214                 talloc_free(tmp_ctx);
215                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
216         }
217
218         server_info = talloc(mem_ctx, struct auth_serversupplied_info);
219         NT_STATUS_HAVE_NO_MEMORY(server_info);
220         
221         if (group_ret > 0) {
222                 groupSIDs = talloc_array(server_info, struct dom_sid *, group_ret);
223                 NT_STATUS_HAVE_NO_MEMORY(groupSIDs);
224         }
225
226         /* Need to unroll some nested groups, but not aliases */
227         for (i = 0; i < group_ret; i++) {
228                 groupSIDs[i] = samdb_result_dom_sid(groupSIDs, 
229                                                     group_msgs[i], "objectSid");
230                 NT_STATUS_HAVE_NO_MEMORY(groupSIDs[i]);
231         }
232
233         talloc_free(tmp_ctx);
234
235         account_sid = samdb_result_dom_sid(server_info, msg, "objectSid");
236         NT_STATUS_HAVE_NO_MEMORY(account_sid);
237
238         primary_group_sid = dom_sid_dup(server_info, account_sid);
239         NT_STATUS_HAVE_NO_MEMORY(primary_group_sid);
240
241         rid = samdb_result_uint(msg, "primaryGroupID", ~0);
242         if (rid == ~0) {
243                 if (group_ret > 0) {
244                         primary_group_sid = groupSIDs[0];
245                 } else {
246                         primary_group_sid = NULL;
247                 }
248         } else {
249                 primary_group_sid->sub_auths[primary_group_sid->num_auths-1] = rid;
250         }
251
252         server_info->account_sid = account_sid;
253         server_info->primary_group_sid = primary_group_sid;
254         
255         server_info->n_domain_groups = group_ret;
256         server_info->domain_groups = groupSIDs;
257
258         server_info->account_name = talloc_steal(server_info, samdb_result_string(msg, "sAMAccountName", NULL));
259
260         server_info->domain_name = talloc_steal(server_info, samdb_result_string(msg_domain_ref, "nETBIOSName", NULL));
261
262         str = samdb_result_string(msg, "displayName", "");
263         server_info->full_name = talloc_strdup(server_info, str);
264         NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
265
266         str = samdb_result_string(msg, "scriptPath", "");
267         server_info->logon_script = talloc_strdup(server_info, str);
268         NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
269
270         str = samdb_result_string(msg, "profilePath", "");
271         server_info->profile_path = talloc_strdup(server_info, str);
272         NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
273
274         str = samdb_result_string(msg, "homeDirectory", "");
275         server_info->home_directory = talloc_strdup(server_info, str);
276         NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
277
278         str = samdb_result_string(msg, "homeDrive", "");
279         server_info->home_drive = talloc_strdup(server_info, str);
280         NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
281
282         server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
283         NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
284
285         server_info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
286         server_info->last_logoff = samdb_result_nttime(msg, "lastLogoff", 0);
287         server_info->acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
288         server_info->last_password_change = samdb_result_nttime(msg, "pwdLastSet", 0);
289
290         ncname = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", NULL);
291         if (!ncname) {
292                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
293         }
294         server_info->allow_password_change
295                 = samdb_result_allow_password_change(sam_ctx, mem_ctx, 
296                                                      ncname, msg, "pwdLastSet");
297         server_info->force_password_change
298                 = samdb_result_force_password_change(sam_ctx, mem_ctx, 
299                                                      ncname, msg);
300         
301         server_info->logon_count = samdb_result_uint(msg, "logonCount", 0);
302         server_info->bad_password_count = samdb_result_uint(msg, "badPwdCount", 0);
303
304         server_info->acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
305
306         server_info->user_session_key = user_sess_key;
307         server_info->lm_session_key = lm_sess_key;
308
309         server_info->authenticated = True;
310
311         *_server_info = server_info;
312
313         return NT_STATUS_OK;
314 }
315
316 _PUBLIC_ NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
317                                    TALLOC_CTX *mem_ctx, const char *principal,
318                                    struct ldb_message ***msgs,
319                                    struct ldb_message ***msgs_domain_ref)
320 {                          
321         struct ldb_dn *user_dn, *domain_dn;
322         NTSTATUS nt_status;
323         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
324         int ret;
325         struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
326
327         if (!tmp_ctx) {
328                 return NT_STATUS_NO_MEMORY;
329         }
330
331         nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal, &user_dn, &domain_dn);
332         if (!NT_STATUS_IS_OK(nt_status)) {
333                 talloc_free(tmp_ctx);
334                 return nt_status;
335         }
336         
337         /* grab domain info from the reference */
338         ret = gendb_search(sam_ctx, tmp_ctx, partitions_basedn, msgs_domain_ref, domain_ref_attrs,
339                            "(ncName=%s)", ldb_dn_get_linearized(domain_dn));
340
341         if (ret != 1) {
342                 talloc_free(tmp_ctx);
343                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
344         }
345         
346         /* pull the user attributes */
347         ret = gendb_search_dn(sam_ctx, tmp_ctx, user_dn, msgs, user_attrs);
348         if (ret != 1) {
349                 talloc_free(tmp_ctx);
350                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
351         }
352         talloc_steal(mem_ctx, *msgs);
353         talloc_steal(mem_ctx, *msgs_domain_ref);
354         talloc_free(tmp_ctx);
355         
356         return NT_STATUS_OK;
357 }
358                                    
359 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available */
360 NTSTATUS sam_get_server_info_principal(TALLOC_CTX *mem_ctx, const char *principal,
361                                        struct auth_serversupplied_info **server_info)
362 {
363         NTSTATUS nt_status;
364         DATA_BLOB user_sess_key = data_blob(NULL, 0);
365         DATA_BLOB lm_sess_key = data_blob(NULL, 0);
366
367         struct ldb_message **msgs;
368         struct ldb_message **msgs_domain_ref;
369         struct ldb_context *sam_ctx;
370
371         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
372         if (!tmp_ctx) {
373                 return NT_STATUS_NO_MEMORY;
374         }
375
376         sam_ctx = samdb_connect(tmp_ctx, system_session(tmp_ctx));
377         if (sam_ctx == NULL) {
378                 talloc_free(tmp_ctx);
379                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
380         }
381
382         nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal, 
383                                               &msgs, &msgs_domain_ref);
384         if (!NT_STATUS_IS_OK(nt_status)) {
385                 return nt_status;
386         }
387
388         nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, msgs[0], msgs_domain_ref[0],
389                                              user_sess_key, lm_sess_key,
390                                              server_info);
391         if (NT_STATUS_IS_OK(nt_status)) {
392                 talloc_steal(mem_ctx, *server_info);
393         }
394         talloc_free(tmp_ctx);
395         return nt_status;
396 }