s4:auth/gensec: let GENSEC_FEATURE_SESSION_KEY result in GSS_C_INTEG_FLAG
[metze/samba/wip.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-2010
5    Copyright (C) Gerald Carter                             2003
6    Copyright (C) Stefan Metzmacher                         2005
7    Copyright (C) Matthias Dieter Wallnöfer                 2009
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "system/time.h"
25 #include "auth/auth.h"
26 #include <ldb.h>
27 #include "dsdb/samdb/samdb.h"
28 #include "libcli/security/security.h"
29 #include "auth/auth_sam.h"
30 #include "dsdb/common/util.h"
31 #include "libcli/ldap/ldap_ndr.h"
32 #include "param/param.h"
33 #include "librpc/gen_ndr/ndr_winbind_c.h"
34
35 #define KRBTGT_ATTRS \
36         /* required for the krb5 kdc */         \
37         "objectClass",                          \
38         "sAMAccountName",                       \
39         "userPrincipalName",                    \
40         "servicePrincipalName",                 \
41         "msDS-KeyVersionNumber",                \
42         "msDS-SecondaryKrbTgtNumber",           \
43         "msDS-SupportedEncryptionTypes",        \
44         "supplementalCredentials",              \
45         "msDS-AllowedToDelegateTo",             \
46                                                 \
47         /* passwords */                         \
48         "dBCSPwd",                              \
49         "unicodePwd",                           \
50                                                 \
51         "userAccountControl",                   \
52         "msDS-User-Account-Control-Computed",   \
53         "objectSid",                            \
54                                                 \
55         "pwdLastSet",                           \
56         "msDS-UserPasswordExpiryTimeComputed",  \
57         "accountExpires"
58
59 const char *krbtgt_attrs[] = {
60         KRBTGT_ATTRS, NULL
61 };
62
63 const char *server_attrs[] = {
64         KRBTGT_ATTRS, NULL
65 };
66
67 const char *user_attrs[] = {
68         KRBTGT_ATTRS,
69
70         "logonHours",
71
72         /*
73          * To allow us to zero the badPwdCount and lockoutTime on
74          * successful logon, without database churn
75          */
76         "lockoutTime",
77
78         /*
79          * Needed for SendToSAM requests
80          */
81         "objectGUID",
82
83         /* check 'allowed workstations' */
84         "userWorkstations",
85
86         /* required for user_info_dc, not access control: */
87         "displayName",
88         "scriptPath",
89         "profilePath",
90         "homeDirectory",
91         "homeDrive",
92         "lastLogon",
93         "lastLogonTimestamp",
94         "lastLogoff",
95         "accountExpires",
96         "badPwdCount",
97         "logonCount",
98         "primaryGroupID",
99         "memberOf",
100         "badPasswordTime",
101         "lmPwdHistory",
102         "ntPwdHistory",
103         NULL,
104 };
105
106 /****************************************************************************
107  Check if a user is allowed to logon at this time. Note this is the
108  servers local time, as logon hours are just specified as a weekly
109  bitmask.
110 ****************************************************************************/
111                                                                                                               
112 static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
113 {
114         /* In logon hours first bit is Sunday from 12AM to 1AM */
115         const struct ldb_val *hours;
116         struct tm *utctime;
117         time_t lasttime;
118         const char *asct;
119         uint8_t bitmask, bitpos;
120
121         hours = ldb_msg_find_ldb_val(msg, "logonHours");
122         if (!hours) {
123                 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
124                 return true;
125         }
126
127         if (hours->length != 168/8) {
128                 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
129                 return true;            
130         }
131
132         lasttime = time(NULL);
133         utctime = gmtime(&lasttime);
134         if (!utctime) {
135                 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
136                         name_for_logs));
137                 return false;
138         }
139
140         /* find the corresponding byte and bit */
141         bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
142         bitmask = 1 << (bitpos % 8);
143
144         if (! (hours->data[bitpos/8] & bitmask)) {
145                 struct tm *t = localtime(&lasttime);
146                 if (!t) {
147                         asct = "INVALID TIME";
148                 } else {
149                         asct = asctime(t);
150                         if (!asct) {
151                                 asct = "INVALID TIME";
152                         }
153                 }
154                 
155                 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
156                           "logon at this time (%s).\n",
157                           name_for_logs, asct ));
158                 return false;
159         }
160
161         asct = asctime(utctime);
162         DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
163                 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
164
165         return true;
166 }
167
168 /****************************************************************************
169  Do a specific test for a SAM_ACCOUNT being valid for this connection
170  (ie not disabled, expired and the like).
171 ****************************************************************************/
172 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
173                                      struct ldb_context *sam_ctx,
174                                      uint32_t logon_parameters,
175                                      struct ldb_dn *domain_dn,
176                                      struct ldb_message *msg,
177                                      const char *logon_workstation,
178                                      const char *name_for_logs,
179                                      bool allow_domain_trust,
180                                      bool password_change)
181 {
182         uint16_t acct_flags;
183         const char *workstation_list;
184         NTTIME acct_expiry;
185         NTTIME must_change_time;
186         struct timeval tv_now = timeval_current();
187         NTTIME now = timeval_to_nttime(&tv_now);
188
189         DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
190
191         acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
192         
193         acct_expiry = samdb_result_account_expires(msg);
194
195         /* Check for when we must change this password, taking the
196          * userAccountControl flags into account */
197         must_change_time = samdb_result_nttime(msg,
198                         "msDS-UserPasswordExpiryTimeComputed", 0);
199
200         workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
201
202         /* Quit if the account was disabled. */
203         if (acct_flags & ACB_DISABLED) {
204                 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
205                 return NT_STATUS_ACCOUNT_DISABLED;
206         }
207
208         /* Quit if the account was locked out. */
209         if (acct_flags & ACB_AUTOLOCK) {
210                 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
211                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
212         }
213
214         /* Test account expire time */
215         if (now > acct_expiry) {
216                 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
217                 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n", 
218                          nt_time_string(mem_ctx, acct_expiry)));
219                 return NT_STATUS_ACCOUNT_EXPIRED;
220         }
221
222         /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
223         if ((must_change_time == 0) && !password_change) {
224                 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
225                          name_for_logs));
226                 return NT_STATUS_PASSWORD_MUST_CHANGE;
227         }
228
229         /* check for expired password (but not if this is a password change request) */
230         if ((must_change_time < now) && !password_change) {
231                 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
232                          name_for_logs));
233                 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
234                          nt_time_string(mem_ctx, must_change_time)));
235                 return NT_STATUS_PASSWORD_EXPIRED;
236         }
237
238         /* Test workstation. Workstation list is comma separated. */
239         if (logon_workstation && workstation_list && *workstation_list) {
240                 bool invalid_ws = true;
241                 int i;
242                 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
243
244                 for (i = 0; workstations && workstations[i]; i++) {
245                         DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
246                                   workstations[i], logon_workstation));
247
248                         if (strequal(workstations[i], logon_workstation)) {
249                                 invalid_ws = false;
250                                 break;
251                         }
252                 }
253
254                 talloc_free(workstations);
255
256                 if (invalid_ws) {
257                         return NT_STATUS_INVALID_WORKSTATION;
258                 }
259         }
260         
261         if (!logon_hours_ok(msg, name_for_logs)) {
262                 return NT_STATUS_INVALID_LOGON_HOURS;
263         }
264         
265         if (!allow_domain_trust) {
266                 if (acct_flags & ACB_DOMTRUST) {
267                         DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
268                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
269                 }
270         }
271         if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
272                 if (acct_flags & ACB_SVRTRUST) {
273                         DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
274                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
275                 }
276         }
277         if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
278                 /* TODO: this fails with current solaris client. We
279                    need to work with Gordon to work out why */
280                 if (acct_flags & ACB_WSTRUST) {
281                         DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
282                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
283                 }
284         }
285
286         return NT_STATUS_OK;
287 }
288
289 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
290                                            struct ldb_context *sam_ctx,
291                                            const char *netbios_name,
292                                            const char *domain_name,
293                                            const char *dns_domain_name,
294                                            struct ldb_dn *domain_dn, 
295                                            struct ldb_message *msg,
296                                            DATA_BLOB user_sess_key,
297                                            DATA_BLOB lm_sess_key,
298                                            struct auth_user_info_dc **_user_info_dc)
299 {
300         NTSTATUS status;
301         struct auth_user_info_dc *user_info_dc;
302         struct auth_user_info *info;
303         const char *str, *filter;
304         /* SIDs for the account and his primary group */
305         struct dom_sid *account_sid;
306         const char *primary_group_string;
307         const char *primary_group_dn;
308         DATA_BLOB primary_group_blob;
309         /* SID structures for the expanded group memberships */
310         struct dom_sid *sids = NULL;
311         unsigned int num_sids = 0, i;
312         struct dom_sid *domain_sid;
313         TALLOC_CTX *tmp_ctx;
314         struct ldb_message_element *el;
315
316         user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
317         NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
318
319         tmp_ctx = talloc_new(user_info_dc);
320         if (user_info_dc == NULL) {
321                 TALLOC_FREE(user_info_dc);
322                 return NT_STATUS_NO_MEMORY;
323         }
324
325         sids = talloc_array(user_info_dc, struct dom_sid, 2);
326         if (sids == NULL) {
327                 TALLOC_FREE(user_info_dc);
328                 return NT_STATUS_NO_MEMORY;
329         }
330
331         num_sids = 2;
332
333         account_sid = samdb_result_dom_sid(user_info_dc, msg, "objectSid");
334         if (account_sid == NULL) {
335                 TALLOC_FREE(user_info_dc);
336                 return NT_STATUS_NO_MEMORY;
337         }
338
339         status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
340         if (!NT_STATUS_IS_OK(status)) {
341                 talloc_free(user_info_dc);
342                 return status;
343         }
344
345         sids[PRIMARY_USER_SID_INDEX] = *account_sid;
346         sids[PRIMARY_GROUP_SID_INDEX] = *domain_sid;
347         sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX], ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
348
349         /* Filter out builtin groups from this token.  We will search
350          * for builtin groups later, and not include them in the PAC
351          * on SamLogon validation info */
352         filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(!(groupType:1.2.840.113556.1.4.803:=%u))(groupType:1.2.840.113556.1.4.803:=%u))", GROUP_TYPE_BUILTIN_LOCAL_GROUP, GROUP_TYPE_SECURITY_ENABLED);
353         if (filter == NULL) {
354                 TALLOC_FREE(user_info_dc);
355                 return NT_STATUS_NO_MEMORY;
356         }
357
358         primary_group_string = dom_sid_string(tmp_ctx, &sids[PRIMARY_GROUP_SID_INDEX]);
359         if (primary_group_string == NULL) {
360                 TALLOC_FREE(user_info_dc);
361                 return NT_STATUS_NO_MEMORY;
362         }
363
364         primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
365         if (primary_group_dn == NULL) {
366                 TALLOC_FREE(user_info_dc);
367                 return NT_STATUS_NO_MEMORY;
368         }
369
370         primary_group_blob = data_blob_string_const(primary_group_dn);
371
372         /* Expands the primary group - this function takes in
373          * memberOf-like values, so we fake one up with the
374          * <SID=S-...> format of DN and then let it expand
375          * them, as long as they meet the filter - so only
376          * domain groups, not builtin groups
377          *
378          * The primary group is still treated specially, so we set the
379          * 'only childs' flag to true
380          */
381         status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
382                                            user_info_dc, &sids, &num_sids);
383         if (!NT_STATUS_IS_OK(status)) {
384                 talloc_free(user_info_dc);
385                 return status;
386         }
387
388         /* Expands the additional groups */
389         el = ldb_msg_find_element(msg, "memberOf");
390         for (i = 0; el && i < el->num_values; i++) {
391                 /* This function takes in memberOf values and expands
392                  * them, as long as they meet the filter - so only
393                  * domain groups, not builtin groups */
394                 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
395                                                    user_info_dc, &sids, &num_sids);
396                 if (!NT_STATUS_IS_OK(status)) {
397                         talloc_free(user_info_dc);
398                         return status;
399                 }
400         }
401
402         user_info_dc->sids = sids;
403         user_info_dc->num_sids = num_sids;
404
405         user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
406         NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
407
408         info->account_name = talloc_steal(info,
409                 ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
410
411         info->user_principal_name = talloc_steal(info,
412                 ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL));
413         if (info->user_principal_name == NULL && dns_domain_name != NULL) {
414                 info->user_principal_name = talloc_asprintf(info, "%s@%s",
415                                         info->account_name,
416                                         dns_domain_name);
417                 if (info->user_principal_name == NULL) {
418                         TALLOC_FREE(user_info_dc);
419                         return NT_STATUS_NO_MEMORY;
420                 }
421                 info->user_principal_constructed = true;
422         }
423
424         info->domain_name = talloc_strdup(info, domain_name);
425         if (info->domain_name == NULL) {
426                 TALLOC_FREE(user_info_dc);
427                 return NT_STATUS_NO_MEMORY;
428         }
429
430         if (dns_domain_name != NULL) {
431                 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
432                 if (info->dns_domain_name == NULL) {
433                         TALLOC_FREE(user_info_dc);
434                         return NT_STATUS_NO_MEMORY;
435                 }
436         }
437
438         str = ldb_msg_find_attr_as_string(msg, "displayName", "");
439         info->full_name = talloc_strdup(info, str);
440         if (info->full_name == NULL) {
441                 TALLOC_FREE(user_info_dc);
442                 return NT_STATUS_NO_MEMORY;
443         }
444
445         str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
446         info->logon_script = talloc_strdup(info, str);
447         if (info->logon_script == NULL) {
448                 TALLOC_FREE(user_info_dc);
449                 return NT_STATUS_NO_MEMORY;
450         }
451
452         str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
453         info->profile_path = talloc_strdup(info, str);
454         if (info->profile_path == NULL) {
455                 TALLOC_FREE(user_info_dc);
456                 return NT_STATUS_NO_MEMORY;
457         }
458
459         str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
460         info->home_directory = talloc_strdup(info, str);
461         if (info->home_directory == NULL) {
462                 TALLOC_FREE(user_info_dc);
463                 return NT_STATUS_NO_MEMORY;
464         }
465
466         str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
467         info->home_drive = talloc_strdup(info, str);
468         if (info->home_drive == NULL) {
469                 TALLOC_FREE(user_info_dc);
470                 return NT_STATUS_NO_MEMORY;
471         }
472
473         info->logon_server = talloc_strdup(info, netbios_name);
474         if (info->logon_server == NULL) {
475                 TALLOC_FREE(user_info_dc);
476                 return NT_STATUS_NO_MEMORY;
477         }
478
479         info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
480         info->last_logoff = samdb_result_last_logoff(msg);
481         info->acct_expiry = samdb_result_account_expires(msg);
482         info->last_password_change = samdb_result_nttime(msg,
483                 "pwdLastSet", 0);
484         info->allow_password_change
485                 = samdb_result_allow_password_change(sam_ctx, mem_ctx, 
486                         domain_dn, msg, "pwdLastSet");
487         info->force_password_change = samdb_result_nttime(msg,
488                 "msDS-UserPasswordExpiryTimeComputed", 0);
489         info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
490         info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
491                 0);
492
493         info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
494
495         user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
496                                                          user_sess_key.data,
497                                                          user_sess_key.length);
498         if (user_sess_key.data) {
499                 if (user_info_dc->user_session_key.data == NULL) {
500                         TALLOC_FREE(user_info_dc);
501                         return NT_STATUS_NO_MEMORY;
502                 }
503         }
504         user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
505                                                        lm_sess_key.data,
506                                                        lm_sess_key.length);
507         if (lm_sess_key.data) {
508                 if (user_info_dc->lm_session_key.data == NULL) {
509                         TALLOC_FREE(user_info_dc);
510                         return NT_STATUS_NO_MEMORY;
511                 }
512         }
513
514         if (info->acct_flags & ACB_SVRTRUST) {
515                 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
516                    PAC */
517                 user_info_dc->sids = talloc_realloc(user_info_dc,
518                                                    user_info_dc->sids,
519                                                    struct dom_sid,
520                                                    user_info_dc->num_sids+1);
521                 if (user_info_dc->sids == NULL) {
522                         TALLOC_FREE(user_info_dc);
523                         return NT_STATUS_NO_MEMORY;
524                 }
525                 user_info_dc->sids[user_info_dc->num_sids] = global_sid_Enterprise_DCs;
526                 user_info_dc->num_sids++;
527         }
528
529         if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
530             (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
531                 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
532                 user_info_dc->sids = talloc_realloc(user_info_dc,
533                                                    user_info_dc->sids,
534                                                    struct dom_sid,
535                                                    user_info_dc->num_sids+1);
536                 if (user_info_dc->sids == NULL) {
537                         TALLOC_FREE(user_info_dc);
538                         return NT_STATUS_NO_MEMORY;
539                 }
540                 user_info_dc->sids[user_info_dc->num_sids] = *domain_sid;
541                 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids],
542                             DOMAIN_RID_ENTERPRISE_READONLY_DCS);
543                 user_info_dc->num_sids++;
544         }
545
546         info->authenticated = true;
547
548         talloc_free(tmp_ctx);
549         *_user_info_dc = user_info_dc;
550
551         return NT_STATUS_OK;
552 }
553
554 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
555                                    TALLOC_CTX *mem_ctx, const char *principal,
556                                    const char **attrs,
557                                    struct ldb_dn **domain_dn,
558                                    struct ldb_message **msg)
559 {                          
560         struct ldb_dn *user_dn;
561         NTSTATUS nt_status;
562         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
563         int ret;
564
565         if (!tmp_ctx) {
566                 return NT_STATUS_NO_MEMORY;
567         }
568
569         nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal, 
570                                               &user_dn, domain_dn);
571         if (!NT_STATUS_IS_OK(nt_status)) {
572                 talloc_free(tmp_ctx);
573                 return nt_status;
574         }
575         
576         /* pull the user attributes */
577         ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
578                               LDB_SCOPE_BASE, attrs,
579                               DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
580                               "(objectClass=*)");
581         if (ret != LDB_SUCCESS) {
582                 talloc_free(tmp_ctx);
583                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
584         }
585         talloc_steal(mem_ctx, *msg);
586         talloc_steal(mem_ctx, *domain_dn);
587         talloc_free(tmp_ctx);
588         
589         return NT_STATUS_OK;
590 }
591
592 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
593
594  Supply either a principal or a DN
595 */
596 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
597                                            struct loadparm_context *lp_ctx,
598                                            struct ldb_context *sam_ctx,
599                                            const char *principal,
600                                            struct ldb_dn *user_dn,
601                                            struct auth_user_info_dc **user_info_dc)
602 {
603         NTSTATUS nt_status;
604         DATA_BLOB user_sess_key = data_blob(NULL, 0);
605         DATA_BLOB lm_sess_key = data_blob(NULL, 0);
606
607         struct ldb_message *msg;
608         struct ldb_dn *domain_dn;
609
610         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
611         if (!tmp_ctx) {
612                 return NT_STATUS_NO_MEMORY;
613         }
614
615         if (principal) {
616                 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
617                                                       user_attrs, &domain_dn, &msg);
618                 if (!NT_STATUS_IS_OK(nt_status)) {
619                         talloc_free(tmp_ctx);
620                         return nt_status;
621                 }
622         } else if (user_dn) {
623                 struct dom_sid *user_sid, *domain_sid;
624                 int ret;
625                 /* pull the user attributes */
626                 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
627                                       LDB_SCOPE_BASE, user_attrs,
628                                       DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
629                                       "(objectClass=*)");
630                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
631                         talloc_free(tmp_ctx);
632                         return NT_STATUS_NO_SUCH_USER;
633                 } else if (ret != LDB_SUCCESS) {
634                         talloc_free(tmp_ctx);
635                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
636                 }
637
638                 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
639
640                 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
641                 if (!NT_STATUS_IS_OK(nt_status)) {
642                         return nt_status;
643                 }
644
645                 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
646                                           "(&(objectSid=%s)(objectClass=domain))",
647                                             ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
648                 if (!domain_dn) {
649                         DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
650                                   dom_sid_string(tmp_ctx, domain_sid)));
651                         return NT_STATUS_NO_SUCH_USER;
652                 }
653
654         } else {
655                 return NT_STATUS_INVALID_PARAMETER;
656         }
657
658         nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
659                                              lpcfg_netbios_name(lp_ctx),
660                                              lpcfg_sam_name(lp_ctx),
661                                              lpcfg_sam_dnsname(lp_ctx),
662                                              domain_dn,
663                                              msg,
664                                              user_sess_key, lm_sess_key,
665                                              user_info_dc);
666         if (!NT_STATUS_IS_OK(nt_status)) {
667                 talloc_free(tmp_ctx);
668                 return nt_status;
669         }
670
671         talloc_steal(mem_ctx, *user_info_dc);
672         talloc_free(tmp_ctx);
673
674         return NT_STATUS_OK;
675 }
676
677 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
678                                       struct ldb_message *msg,
679                                       struct ldb_dn *domain_dn)
680 {
681         const char *attrs[] = { "lockoutThreshold",
682                                 "lockOutObservationWindow",
683                                 "lockoutDuration",
684                                 "pwdProperties",
685                                 NULL };
686         int ret;
687         NTSTATUS status;
688         struct ldb_result *domain_res;
689         struct ldb_message *msg_mod = NULL;
690         TALLOC_CTX *mem_ctx;
691
692         mem_ctx = talloc_new(msg);
693         if (mem_ctx == NULL) {
694                 return NT_STATUS_NO_MEMORY;
695         }
696
697         ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
698         if (ret != LDB_SUCCESS) {
699                 TALLOC_FREE(mem_ctx);
700                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
701         }
702
703         status = dsdb_update_bad_pwd_count(mem_ctx, sam_ctx,
704                                            msg, domain_res->msgs[0], &msg_mod);
705         if (!NT_STATUS_IS_OK(status)) {
706                 TALLOC_FREE(mem_ctx);
707                 return status;
708         }
709
710         if (msg_mod != NULL) {
711                 struct ldb_request *req;
712
713                 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
714                                         msg_mod,
715                                         NULL,
716                                         NULL,
717                                         ldb_op_default_callback,
718                                         NULL);
719                 if (ret != LDB_SUCCESS) {
720                         goto done;
721                 }
722
723                 ret = ldb_request_add_control(req,
724                                               DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
725                                               false, NULL);
726                 if (ret != LDB_SUCCESS) {
727                         talloc_free(req);
728                         goto done;
729                 }
730
731                 ret = dsdb_autotransaction_request(sam_ctx, req);
732                 talloc_free(req);
733         }
734
735 done:
736         if (ret != LDB_SUCCESS) {
737                 DEBUG(0, ("Failed to update badPwdCount, badPasswordTime or set lockoutTime on %s: %s\n",
738                           ldb_dn_get_linearized(msg_mod->dn), ldb_errstring(sam_ctx)));
739                 TALLOC_FREE(mem_ctx);
740                 return NT_STATUS_INTERNAL_ERROR;
741         }
742
743         TALLOC_FREE(mem_ctx);
744         return NT_STATUS_OK;
745 }
746
747
748 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
749                                             struct ldb_message *msg_mod,
750                                             struct ldb_dn *domain_dn,
751                                             NTTIME old_timestamp,
752                                             NTTIME now)
753 {
754         /*
755          * We only set lastLogonTimestamp if the current value is older than
756          * now - msDS-LogonTimeSyncInterval days.
757          *
758          * msDS-LogonTimeSyncInterval is an int32_t number of days, while
759          * lastLogonTimestamp is in the 64 bit 100ns NTTIME format.
760          *
761          * The docs say: "the initial update, after the domain functional
762          * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
763          * 14 days minus a random percentage of 5 days", but we aren't doing
764          * that. The blogosphere seems to think that this randomised update
765          * happens everytime, but [MS-ADA1] doesn't agree.
766          *
767          * Dochelp referred us to the following blog post:
768          * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
769          *
770          * en msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
771          * not changed.
772          */
773         static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
774                                         NULL };
775         int ret;
776         struct ldb_result *domain_res = NULL;
777         TALLOC_CTX *mem_ctx = NULL;
778         int32_t sync_interval;
779         NTTIME sync_interval_nt;
780
781         mem_ctx = talloc_new(msg_mod);
782         if (mem_ctx == NULL) {
783                 return NT_STATUS_NO_MEMORY;
784         }
785
786         ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
787                              0);
788         if (ret != LDB_SUCCESS || domain_res->count != 1) {
789                 TALLOC_FREE(mem_ctx);
790                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
791         }
792
793         sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
794                                                  "msDS-LogonTimeSyncInterval",
795                                                  14);
796         DEBUG(5, ("sync interval is %d\n", sync_interval));
797         if (sync_interval == 0){
798                 /*
799                  * Setting msDS-LogonTimeSyncInterval to zero is how you ask
800                  * that nothing happens here.
801                  */
802                 TALLOC_FREE(mem_ctx);
803                 return NT_STATUS_OK;
804         }
805         else if (sync_interval >= 5){
806                 /*
807                  * Subtract "a random percentage of 5" days. Presumably this
808                  * percentage is between 0 and 100, and modulus is accurate
809                  * enough.
810                  */
811                 uint32_t r = generate_random() % 6;
812                 sync_interval -= r;
813                 DEBUG(5, ("randomised sync interval is %d (-%d)\n", sync_interval, r));
814         }
815         /* In the case where sync_interval < 5 there is no randomisation */
816
817         sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
818
819         DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
820                   (long long int)old_timestamp,
821                   (long long int)(now - sync_interval_nt),
822                   (long long int)(old_timestamp - now + sync_interval_nt)));
823
824         if (old_timestamp > now){
825                 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
826                           (long long int)old_timestamp, (long long int)now));
827                 /* then what? */
828
829         } else if (old_timestamp < now - sync_interval_nt){
830                 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
831                           (long long int)now));
832
833                 /* The time has come to update lastLogonTimestamp */
834                 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
835                                           "lastLogonTimestamp", now);
836
837                 if (ret != LDB_SUCCESS) {
838                         TALLOC_FREE(mem_ctx);
839                         return NT_STATUS_NO_MEMORY;
840                 }
841         }
842         TALLOC_FREE(mem_ctx);
843         return NT_STATUS_OK;
844 }
845
846 /****************************************************************************
847  Look for the specified user in the sam, return ldb result structures
848 ****************************************************************************/
849
850 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
851                                          const char *account_name,
852                                          struct ldb_dn *domain_dn,
853                                          struct ldb_message **ret_msg)
854 {
855         int ret;
856
857         /* pull the user attributes */
858         ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
859                               user_attrs,
860                               DSDB_SEARCH_SHOW_EXTENDED_DN,
861                               "(&(sAMAccountName=%s)(objectclass=user))",
862                               ldb_binary_encode_string(mem_ctx, account_name));
863         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
864                 DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb, under %s\n",
865                          account_name, ldb_dn_get_linearized(domain_dn)));
866                 return NT_STATUS_NO_SUCH_USER;
867         }
868         if (ret != LDB_SUCCESS) {
869                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
870         }
871
872         return NT_STATUS_OK;
873 }
874
875
876 /* Reset the badPwdCount to zero and update the lastLogon time. */
877 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
878                                           const struct ldb_message *msg,
879                                           struct ldb_dn *domain_dn,
880                                           bool interactive_or_kerberos,
881                                           struct netr_SendToSamBase **send_to_sam)
882 {
883         int ret;
884         NTSTATUS status;
885         int badPwdCount;
886         int dbBadPwdCount;
887         int64_t lockoutTime;
888         struct ldb_message *msg_mod;
889         TALLOC_CTX *mem_ctx;
890         struct timeval tv_now;
891         NTTIME now;
892         NTTIME lastLogonTimestamp;
893         bool am_rodc = false;
894
895         mem_ctx = talloc_new(msg);
896         if (mem_ctx == NULL) {
897                 return NT_STATUS_NO_MEMORY;
898         }
899
900         lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
901         dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
902         if (interactive_or_kerberos) {
903                 badPwdCount = dbBadPwdCount;
904         } else {
905                 badPwdCount = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx,
906                                                                  domain_dn, msg);
907         }
908         lastLogonTimestamp =
909                 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
910
911         DEBUG(5, ("lastLogonTimestamp is %lld\n",
912                   (long long int)lastLogonTimestamp));
913
914         msg_mod = ldb_msg_new(mem_ctx);
915         if (msg_mod == NULL) {
916                 TALLOC_FREE(mem_ctx);
917                 return NT_STATUS_NO_MEMORY;
918         }
919         msg_mod->dn = msg->dn;
920
921         if (lockoutTime != 0) {
922                 /*
923                  * This implies "badPwdCount" = 0, see samldb_lockout_time()
924                  */
925                 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
926                 if (ret != LDB_SUCCESS) {
927                         TALLOC_FREE(mem_ctx);
928                         return NT_STATUS_NO_MEMORY;
929                 }
930         } else if (badPwdCount != 0) {
931                 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
932                 if (ret != LDB_SUCCESS) {
933                         TALLOC_FREE(mem_ctx);
934                         return NT_STATUS_NO_MEMORY;
935                 }
936         }
937
938         tv_now = timeval_current();
939         now = timeval_to_nttime(&tv_now);
940
941         if (interactive_or_kerberos ||
942             (badPwdCount != 0 && lockoutTime == 0)) {
943                 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
944                                           "lastLogon", now);
945                 if (ret != LDB_SUCCESS) {
946                         TALLOC_FREE(mem_ctx);
947                         return NT_STATUS_NO_MEMORY;
948                 }
949         }
950
951         if (interactive_or_kerberos) {
952                 int logonCount;
953
954                 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
955
956                 logonCount += 1;
957
958                 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
959                                         "logonCount", logonCount);
960                 if (ret != LDB_SUCCESS) {
961                         TALLOC_FREE(mem_ctx);
962                         return NT_STATUS_NO_MEMORY;
963                 }
964         } else {
965                 /* Set an unset logonCount to 0 on first successful login */
966                 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
967                         ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
968                                                 "logonCount", 0);
969                         if (ret != LDB_SUCCESS) {
970                                 TALLOC_FREE(mem_ctx);
971                                 return NT_STATUS_NO_MEMORY;
972                         }
973                 }
974         }
975
976         ret = samdb_rodc(sam_ctx, &am_rodc);
977         if (ret != LDB_SUCCESS) {
978                 TALLOC_FREE(mem_ctx);
979                 return NT_STATUS_INTERNAL_ERROR;
980         }
981
982         if (!am_rodc) {
983                 status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn,
984                                                             lastLogonTimestamp, now);
985                 if (!NT_STATUS_IS_OK(status)) {
986                         TALLOC_FREE(mem_ctx);
987                         return NT_STATUS_NO_MEMORY;
988                 }
989         } else {
990                 /* Perform the (async) SendToSAM calls for MS-SAMS */
991                 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
992                         struct netr_SendToSamBase *base_msg;
993                         struct GUID guid = samdb_result_guid(msg, "objectGUID");
994                         base_msg = talloc_zero(msg, struct netr_SendToSamBase);
995
996                         base_msg->message_type = SendToSamResetBadPasswordCount;
997                         base_msg->message_size = 16;
998                         base_msg->message.reset_bad_password.guid = guid;
999                         *send_to_sam = base_msg;
1000                 }
1001         }
1002
1003         if (msg_mod->num_elements > 0) {
1004                 unsigned int i;
1005                 struct ldb_request *req;
1006
1007                 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1008                 for (i=0;i<msg_mod->num_elements;i++) {
1009                         msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1010                 }
1011
1012                 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1013                                         msg_mod,
1014                                         NULL,
1015                                         NULL,
1016                                         ldb_op_default_callback,
1017                                         NULL);
1018                 if (ret != LDB_SUCCESS) {
1019                         goto done;
1020                 }
1021
1022                 ret = ldb_request_add_control(req,
1023                                               DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1024                                               false, NULL);
1025                 if (ret != LDB_SUCCESS) {
1026                         talloc_free(req);
1027                         goto done;
1028                 }
1029
1030                 ret = dsdb_autotransaction_request(sam_ctx, req);
1031                 talloc_free(req);
1032         }
1033
1034 done:
1035         if (ret != LDB_SUCCESS) {
1036                 DEBUG(0, ("Failed to set badPwdCount and lockoutTime "
1037                           "to 0 and/or  lastlogon to now (%lld) "
1038                           "%s: %s\n", (long long int)now,
1039                           ldb_dn_get_linearized(msg_mod->dn),
1040                           ldb_errstring(sam_ctx)));
1041                 TALLOC_FREE(mem_ctx);
1042                 return NT_STATUS_INTERNAL_ERROR;
1043         }
1044
1045         TALLOC_FREE(mem_ctx);
1046         return NT_STATUS_OK;
1047 }