r13924: Split more prototypes out of include/proto.h + initial work on header
[kai/samba-autobuild/.git] / source4 / auth / auth_sam.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Password and authentication handling
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
5    Copyright (C) Gerald Carter                             2003
6    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 "dsdb/samdb/samdb.h"
27 #include "db_wrap.h"
28 #include "libcli/security/proto.h"
29 #include "libcli/ldap/ldap.h"
30
31 static const char *user_attrs[] = {
32         /* requried for the krb5 kdc*/
33         "objectClass",
34         "sAMAccountName",
35         "userPrincipalName",
36         "servicePrincipalName",
37         "msDS-KeyVersionNumber",
38         "krb5Key",
39
40         /* passwords */
41         "lmPwdHash", 
42         "ntPwdHash",
43
44         "userAccountControl",
45
46         "pwdLastSet",
47         "accountExpires",
48         
49         "objectSid",
50
51         /* check 'allowed workstations' */
52         "userWorkstations",
53                        
54         /* required for server_info, not access control: */
55         "displayName",
56         "scriptPath",
57         "profilePath",
58         "homeDirectory",
59         "homeDrive",
60         "lastLogon",
61         "lastLogoff",
62         "accountExpires",
63         "badPwdCount",
64         "logonCount",
65         "primaryGroupID",
66         NULL,
67 };
68
69 static const char *domain_ref_attrs[] =  {"nETBIOSName", "nCName", 
70                                           "dnsRoot", "objectClass", NULL};
71
72 /****************************************************************************
73  Do a specific test for an smb password being correct, given a smb_password and
74  the lanman and NT responses.
75 ****************************************************************************/
76 static NTSTATUS authsam_password_ok(struct auth_context *auth_context,
77                                     TALLOC_CTX *mem_ctx,
78                                     uint16_t acct_flags,
79                                     const struct samr_Password *lm_pwd, 
80                                     const struct samr_Password *nt_pwd,
81                                     const struct auth_usersupplied_info *user_info, 
82                                     DATA_BLOB *user_sess_key, 
83                                     DATA_BLOB *lm_sess_key)
84 {
85         NTSTATUS status;
86
87         if (acct_flags & ACB_PWNOTREQ) {
88                 if (lp_null_passwords()) {
89                         DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", 
90                                  user_info->mapped.account_name));
91                         return NT_STATUS_OK;
92                 } else {
93                         DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", 
94                                  user_info->mapped.account_name));
95                         return NT_STATUS_LOGON_FAILURE;
96                 }               
97         }
98
99         switch (user_info->password_state) {
100         case AUTH_PASSWORD_PLAIN: 
101         {
102                 const struct auth_usersupplied_info *user_info_temp;    
103                 status = encrypt_user_info(mem_ctx, auth_context, 
104                                            AUTH_PASSWORD_HASH, 
105                                            user_info, &user_info_temp);
106                 if (!NT_STATUS_IS_OK(status)) {
107                         DEBUG(1, ("Failed to convert plaintext password to password HASH: %s\n", nt_errstr(status)));
108                         return status;
109                 }
110                 user_info = user_info_temp;
111
112                 /* NO break */
113         }
114         case AUTH_PASSWORD_HASH:
115                 *lm_sess_key = data_blob(NULL, 0);
116                 *user_sess_key = data_blob(NULL, 0);
117                 status = hash_password_check(mem_ctx, 
118                                              user_info->password.hash.lanman,
119                                              user_info->password.hash.nt,
120                                              user_info->mapped.account_name,
121                                              lm_pwd, nt_pwd);
122                 NT_STATUS_NOT_OK_RETURN(status);
123                 break;
124                 
125         case AUTH_PASSWORD_RESPONSE:
126                 status = ntlm_password_check(mem_ctx, user_info->logon_parameters, 
127                                              &auth_context->challenge.data, 
128                                              &user_info->password.response.lanman, 
129                                              &user_info->password.response.nt,
130                                              user_info->mapped.account_name,
131                                              user_info->client.account_name, 
132                                              user_info->client.domain_name, 
133                                              lm_pwd, nt_pwd,
134                                              user_sess_key, lm_sess_key);
135                 NT_STATUS_NOT_OK_RETURN(status);
136                 break;
137         }
138
139         if (user_sess_key && user_sess_key->data) {
140                 talloc_steal(auth_context, user_sess_key->data);
141         }
142         if (lm_sess_key && lm_sess_key->data) {
143                 talloc_steal(auth_context, lm_sess_key->data);
144         }
145
146         return NT_STATUS_OK;
147 }
148
149
150 /****************************************************************************
151  Do a specific test for a SAM_ACCOUNT being vaild for this connection 
152  (ie not disabled, expired and the like).
153 ****************************************************************************/
154 NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
155                             struct ldb_context *sam_ctx,
156                             uint32_t logon_parameters,
157                             struct ldb_message *msg,
158                             struct ldb_message *msg_domain_ref,
159                             const char *logon_workstation,
160                             const char *name_for_logs)
161 {
162         uint16_t acct_flags;
163         const char *workstation_list;
164         NTTIME acct_expiry;
165         NTTIME must_change_time;
166         NTTIME last_set_time;
167
168         struct ldb_dn *domain_dn = samdb_result_dn(mem_ctx, msg_domain_ref, "nCName", ldb_dn_new(mem_ctx));
169
170         NTTIME now;
171         DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
172
173         acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
174         
175         acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
176         must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx, 
177                                                               domain_dn, msg);
178         last_set_time = samdb_result_nttime(msg, "pwdLastSet", 0);
179
180         workstation_list = samdb_result_string(msg, "userWorkstations", NULL);
181
182         /* Quit if the account was disabled. */
183         if (acct_flags & ACB_DISABLED) {
184                 DEBUG(1,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
185                 return NT_STATUS_ACCOUNT_DISABLED;
186         }
187
188         /* Quit if the account was locked out. */
189         if (acct_flags & ACB_AUTOLOCK) {
190                 DEBUG(1,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
191                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
192         }
193
194         /* Test account expire time */
195         unix_to_nt_time(&now, time(NULL));
196         if (now > acct_expiry) {
197                 DEBUG(1,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
198                 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n", 
199                          nt_time_string(mem_ctx, acct_expiry)));
200                 return NT_STATUS_ACCOUNT_EXPIRED;
201         }
202
203         if (!(acct_flags & ACB_PWNOEXP)) {
204                 /* check for immediate expiry "must change at next logon" */
205                 if (must_change_time == 0 && last_set_time != 0) {
206                         DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", 
207                                  name_for_logs));
208                         return NT_STATUS_PASSWORD_MUST_CHANGE;
209                 }
210
211                 /* check for expired password */
212                 if ((must_change_time != 0) && (must_change_time < now)) {
213                         DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n", 
214                                  name_for_logs));
215                         DEBUG(1,("sam_account_ok: Password expired at '%s' unix time.\n", 
216                                  nt_time_string(mem_ctx, must_change_time)));
217                         return NT_STATUS_PASSWORD_EXPIRED;
218                 }
219         }
220
221         /* Test workstation. Workstation list is comma separated. */
222         if (logon_workstation && workstation_list && *workstation_list) {
223                 BOOL invalid_ws = True;
224                 int i;
225                 const char **workstations = str_list_make(mem_ctx, workstation_list, ",");
226                 
227                 for (i = 0; workstations && workstations[i]; i++) {
228                         DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
229                                   workstations[i], logon_workstation));
230
231                         if (strequal(workstations[i], logon_workstation)) {
232                                 invalid_ws = False;
233                                 break;
234                         }
235                 }
236
237                 talloc_free(workstations);
238
239                 if (invalid_ws) {
240                         return NT_STATUS_INVALID_WORKSTATION;
241                 }
242         }
243         
244         if (acct_flags & ACB_DOMTRUST) {
245                 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
246                 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
247         }
248         
249         if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
250                 if (acct_flags & ACB_SVRTRUST) {
251                         DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
252                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
253                 }
254         }
255         if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
256                 if (acct_flags & ACB_WSTRUST) {
257                         DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
258                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
259                 }
260         }
261
262         return NT_STATUS_OK;
263 }
264
265 /****************************************************************************
266  Look for the specified user in the sam, return ldb result structures
267 ****************************************************************************/
268
269 static NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
270                                        const char *account_name,
271                                        const char *domain_name,
272                                        struct ldb_message ***ret_msgs,
273                                        struct ldb_message ***ret_msgs_domain_ref)
274 {
275         struct ldb_message **msgs_tmp;
276         struct ldb_message **msgs;
277         struct ldb_message **msgs_domain_ref;
278
279         int ret;
280         int ret_domain;
281
282         const struct ldb_dn *domain_dn = NULL;
283
284         if (domain_name) {
285                 char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name);
286                 /* find the domain's DN */
287                 ret_domain = gendb_search(sam_ctx, mem_ctx, NULL, &msgs_domain_ref, domain_ref_attrs,
288                                           "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", 
289                                           escaped_domain, escaped_domain);
290                 if (ret_domain == -1) {
291                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
292                 }
293
294                 if (ret_domain == 0) {
295                         DEBUG(3,("sam_search_user: Couldn't find domain [%s] in samdb.\n", 
296                                  domain_name));
297                         return NT_STATUS_NO_SUCH_USER;
298                 }
299
300                 if (ret_domain > 1) {
301                         DEBUG(0,("Found %d records matching domain [%s]\n", 
302                                  ret_domain, domain_name));
303                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
304                 }
305
306                 domain_dn = samdb_result_dn(mem_ctx, msgs_domain_ref[0], "nCName", NULL);
307         }
308
309         /* pull the user attributes */
310         ret = gendb_search(sam_ctx, mem_ctx, domain_dn, &msgs, user_attrs,
311                            "(&(sAMAccountName=%s)(objectclass=user))", 
312                            ldb_binary_encode_string(mem_ctx, account_name));
313         if (ret == -1) {
314                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
315         }
316
317         if (ret == 0) {
318                 DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb.\n", 
319                          account_name));
320                 return NT_STATUS_NO_SUCH_USER;
321         }
322
323         if (ret > 1) {
324                 DEBUG(0,("Found %d records matching user [%s]\n", ret, account_name));
325                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
326         }
327
328         if (!domain_name) {
329                 struct dom_sid *domain_sid;
330
331                 domain_sid = samdb_result_sid_prefix(mem_ctx, msgs[0], "objectSid");
332                 if (!domain_sid) {
333                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
334                 }
335
336                 /* find the domain's DN */
337                 ret = gendb_search(sam_ctx, mem_ctx, NULL, &msgs_tmp, NULL,
338                                    "(&(objectSid=%s)(objectclass=domain))", 
339                                    ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
340                 if (ret == -1) {
341                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
342                 }
343                 
344                 if (ret == 0) {
345                         DEBUG(3,("check_sam_security: Couldn't find domain_sid [%s] in passdb file.\n",
346                                  dom_sid_string(mem_ctx, domain_sid)));
347                         return NT_STATUS_NO_SUCH_USER;
348                 }
349                 
350                 if (ret > 1) {
351                         DEBUG(0,("Found %d records matching domain_sid [%s]\n", 
352                                  ret, dom_sid_string(mem_ctx, domain_sid)));
353                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
354                 }
355
356                 ret_domain = gendb_search(sam_ctx, mem_ctx, NULL, &msgs_domain_ref, domain_ref_attrs,
357                                           "(nCName=%s)", ldb_dn_linearize(msgs_tmp, msgs_tmp[0]->dn));
358
359                 if (ret_domain == -1) {
360                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
361                 }
362                 
363                 if (ret_domain == 0) {
364                         DEBUG(3,("check_sam_security: Couldn't find domain [%s] in passdb file.\n",
365                                  ldb_dn_linearize(msgs_tmp, msgs_tmp[0]->dn)));
366                         return NT_STATUS_NO_SUCH_USER;
367                 }
368                 
369                 if (ret_domain > 1) {
370                         DEBUG(0,("Found %d records matching domain [%s]\n", 
371                                  ret_domain, ldb_dn_linearize(msgs_tmp, msgs_tmp[0]->dn)));
372                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
373                 }
374
375         }
376
377         *ret_msgs = msgs;
378         *ret_msgs_domain_ref = msgs_domain_ref;
379         
380         return NT_STATUS_OK;
381 }
382
383 static NTSTATUS authsam_authenticate(struct auth_context *auth_context, 
384                                      TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx, 
385                                      struct ldb_message **msgs,
386                                      struct ldb_message **msgs_domain_ref,
387                                      const struct auth_usersupplied_info *user_info, 
388                                      DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key) 
389 {
390         struct samr_Password *lm_pwd, *nt_pwd;
391         NTSTATUS nt_status;
392         uint16_t acct_flags = samdb_result_acct_flags(msgs[0], "userAccountControl");
393         
394         /* Quit if the account was locked out. */
395         if (acct_flags & ACB_AUTOLOCK) {
396                 DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", 
397                          user_info->mapped.account_name));
398                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
399         }
400
401         /* You can only do an interactive login to normal accounts */
402         if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
403                 if (!(acct_flags & ACB_NORMAL)) {
404                         return NT_STATUS_NO_SUCH_USER;
405                 }
406         }
407
408         nt_status = samdb_result_passwords(mem_ctx, msgs[0], &lm_pwd, &nt_pwd);
409         NT_STATUS_NOT_OK_RETURN(nt_status);
410
411         nt_status = authsam_password_ok(auth_context, mem_ctx, 
412                                         acct_flags, lm_pwd, nt_pwd,
413                                         user_info, user_sess_key, lm_sess_key);
414         NT_STATUS_NOT_OK_RETURN(nt_status);
415
416         nt_status = authsam_account_ok(mem_ctx, sam_ctx, 
417                                        user_info->logon_parameters,
418                                        msgs[0],
419                                        msgs_domain_ref[0],
420                                        user_info->workstation_name,
421                                        user_info->mapped.account_name);
422
423         return nt_status;
424 }
425
426 NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
427                                   struct ldb_message *msg,
428                                   struct ldb_message *msg_domain_ref,
429                                   DATA_BLOB user_sess_key, DATA_BLOB lm_sess_key,
430                                   struct auth_serversupplied_info **_server_info)
431 {
432         struct auth_serversupplied_info *server_info;
433         struct ldb_message **group_msgs;
434         int group_ret;
435         const char *group_attrs[3] = { "sAMAccountType", "objectSid", NULL }; 
436         /* find list of sids */
437         struct dom_sid **groupSIDs = NULL;
438         struct dom_sid *account_sid;
439         struct dom_sid *primary_group_sid;
440         const char *str;
441         struct ldb_dn *ncname;
442         int i;
443         uint_t rid;
444         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
445
446         group_ret = gendb_search(sam_ctx,
447                                  tmp_ctx, NULL, &group_msgs, group_attrs,
448                                  "(&(member=%s)(sAMAccountType=*))", 
449                                  ldb_dn_linearize(tmp_ctx, msg->dn));
450         if (group_ret == -1) {
451                 talloc_free(tmp_ctx);
452                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
453         }
454
455         server_info = talloc(mem_ctx, struct auth_serversupplied_info);
456         NT_STATUS_HAVE_NO_MEMORY(server_info);
457         
458         if (group_ret > 0) {
459                 groupSIDs = talloc_array(server_info, struct dom_sid *, group_ret);
460                 NT_STATUS_HAVE_NO_MEMORY(groupSIDs);
461         }
462
463         /* Need to unroll some nested groups, but not aliases */
464         for (i = 0; i < group_ret; i++) {
465                 groupSIDs[i] = samdb_result_dom_sid(groupSIDs, 
466                                                     group_msgs[i], "objectSid");
467                 NT_STATUS_HAVE_NO_MEMORY(groupSIDs[i]);
468         }
469
470         talloc_free(tmp_ctx);
471
472         account_sid = samdb_result_dom_sid(server_info, msg, "objectSid");
473         NT_STATUS_HAVE_NO_MEMORY(account_sid);
474
475         primary_group_sid = dom_sid_dup(server_info, account_sid);
476         NT_STATUS_HAVE_NO_MEMORY(primary_group_sid);
477
478         rid = samdb_result_uint(msg, "primaryGroupID", ~0);
479         if (rid == ~0) {
480                 if (group_ret > 0) {
481                         primary_group_sid = groupSIDs[0];
482                 } else {
483                         primary_group_sid = NULL;
484                 }
485         } else {
486                 primary_group_sid->sub_auths[primary_group_sid->num_auths-1] = rid;
487         }
488
489         server_info->account_sid = account_sid;
490         server_info->primary_group_sid = primary_group_sid;
491         
492         server_info->n_domain_groups = group_ret;
493         server_info->domain_groups = groupSIDs;
494
495         server_info->account_name = talloc_steal(server_info, samdb_result_string(msg, "sAMAccountName", NULL));
496
497         server_info->domain_name = talloc_steal(server_info, samdb_result_string(msg_domain_ref, "nETBIOSName", NULL));
498
499         str = samdb_result_string(msg, "displayName", "");
500         server_info->full_name = talloc_strdup(server_info, str);
501         NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
502
503         str = samdb_result_string(msg, "scriptPath", "");
504         server_info->logon_script = talloc_strdup(server_info, str);
505         NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
506
507         str = samdb_result_string(msg, "profilePath", "");
508         server_info->profile_path = talloc_strdup(server_info, str);
509         NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
510
511         str = samdb_result_string(msg, "homeDirectory", "");
512         server_info->home_directory = talloc_strdup(server_info, str);
513         NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
514
515         str = samdb_result_string(msg, "homeDrive", "");
516         server_info->home_drive = talloc_strdup(server_info, str);
517         NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
518
519         server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
520         NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
521
522         server_info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
523         server_info->last_logoff = samdb_result_nttime(msg, "lastLogoff", 0);
524         server_info->acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
525         server_info->last_password_change = samdb_result_nttime(msg, "pwdLastSet", 0);
526
527         ncname = samdb_result_dn(mem_ctx, msg_domain_ref, "nCName", NULL);
528         if (!ncname) {
529                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
530         }
531         server_info->allow_password_change
532                 = samdb_result_allow_password_change(sam_ctx, mem_ctx, 
533                                                      ncname, msg, "pwdLastSet");
534         server_info->force_password_change
535                 = samdb_result_force_password_change(sam_ctx, mem_ctx, 
536                                                      ncname, msg);
537         
538         server_info->logon_count = samdb_result_uint(msg, "logonCount", 0);
539         server_info->bad_password_count = samdb_result_uint(msg, "badPwdCount", 0);
540
541         server_info->acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
542
543         server_info->user_session_key = user_sess_key;
544         server_info->lm_session_key = lm_sess_key;
545
546         server_info->authenticated = True;
547
548         *_server_info = server_info;
549
550         return NT_STATUS_OK;
551 }
552
553 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
554                                    TALLOC_CTX *mem_ctx, const char *principal,
555                                    struct ldb_message ***msgs,
556                                    struct ldb_message ***msgs_domain_ref)
557 {                          
558         struct ldb_dn *user_dn, *domain_dn;
559         NTSTATUS nt_status;
560         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
561         int ret;
562
563         if (!tmp_ctx) {
564                 return NT_STATUS_NO_MEMORY;
565         }
566
567         nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal, &user_dn, &domain_dn);
568         if (!NT_STATUS_IS_OK(nt_status)) {
569                 talloc_free(tmp_ctx);
570                 return nt_status;
571         }
572         
573         /* grab domain info from the reference */
574         ret = gendb_search(sam_ctx, tmp_ctx, NULL, msgs_domain_ref, domain_ref_attrs,
575                            "(ncName=%s)", ldb_dn_linearize(tmp_ctx, domain_dn));
576
577         if (ret != 1) {
578                 talloc_free(tmp_ctx);
579                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
580         }
581         
582         /* pull the user attributes */
583         ret = gendb_search_dn(sam_ctx, tmp_ctx, 
584                               user_dn, msgs, user_attrs);
585         if (ret != 1) {
586                 talloc_free(tmp_ctx);
587                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
588         }
589         talloc_steal(mem_ctx, *msgs);
590         talloc_steal(mem_ctx, *msgs_domain_ref);
591         talloc_free(tmp_ctx);
592         
593         return NT_STATUS_OK;
594 }
595                                    
596 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available */
597 NTSTATUS sam_get_server_info_principal(TALLOC_CTX *mem_ctx, const char *principal,
598                                        struct auth_serversupplied_info **server_info)
599 {
600         NTSTATUS nt_status;
601         DATA_BLOB user_sess_key = data_blob(NULL, 0);
602         DATA_BLOB lm_sess_key = data_blob(NULL, 0);
603
604         struct ldb_message **msgs;
605         struct ldb_message **msgs_domain_ref;
606         struct ldb_context *sam_ctx;
607
608         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
609         if (!tmp_ctx) {
610                 return NT_STATUS_NO_MEMORY;
611         }
612
613         sam_ctx = samdb_connect(tmp_ctx, system_session(tmp_ctx));
614         if (sam_ctx == NULL) {
615                 talloc_free(tmp_ctx);
616                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
617         }
618
619         nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal, 
620                                               &msgs, &msgs_domain_ref);
621         if (!NT_STATUS_IS_OK(nt_status)) {
622                 return nt_status;
623         }
624
625         nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, msgs[0], msgs_domain_ref[0],
626                                              user_sess_key, lm_sess_key,
627                                              server_info);
628         if (NT_STATUS_IS_OK(nt_status)) {
629                 talloc_steal(mem_ctx, *server_info);
630         }
631         talloc_free(tmp_ctx);
632         return nt_status;
633 }
634
635 static NTSTATUS authsam_check_password_internals(struct auth_method_context *ctx,
636                                                  TALLOC_CTX *mem_ctx,
637                                                  const char *domain,
638                                                  const struct auth_usersupplied_info *user_info, 
639                                                  struct auth_serversupplied_info **server_info)
640 {
641         NTSTATUS nt_status;
642         const char *account_name = user_info->mapped.account_name;
643         struct ldb_message **msgs;
644         struct ldb_message **domain_ref_msgs;
645         struct ldb_context *sam_ctx;
646         DATA_BLOB user_sess_key, lm_sess_key;
647         TALLOC_CTX *tmp_ctx;
648
649         if (!account_name || !*account_name) {
650                 /* 'not for me' */
651                 return NT_STATUS_NOT_IMPLEMENTED;
652         }
653
654         tmp_ctx = talloc_new(mem_ctx);
655         if (!tmp_ctx) {
656                 return NT_STATUS_NO_MEMORY;
657         }
658
659         sam_ctx = samdb_connect(tmp_ctx, system_session(mem_ctx));
660         if (sam_ctx == NULL) {
661                 talloc_free(tmp_ctx);
662                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
663         }
664
665         nt_status = authsam_search_account(tmp_ctx, sam_ctx, account_name, domain, &msgs, &domain_ref_msgs);
666         if (!NT_STATUS_IS_OK(nt_status)) {
667                 talloc_free(tmp_ctx);
668                 return nt_status;
669         }
670
671         nt_status = authsam_authenticate(ctx->auth_ctx, tmp_ctx, sam_ctx, msgs, domain_ref_msgs, user_info,
672                                          &user_sess_key, &lm_sess_key);
673         if (!NT_STATUS_IS_OK(nt_status)) {
674                 talloc_free(tmp_ctx);
675                 return nt_status;
676         }
677
678         nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, msgs[0], domain_ref_msgs[0],
679                                              user_sess_key, lm_sess_key,
680                                              server_info);
681         if (!NT_STATUS_IS_OK(nt_status)) {
682                 talloc_free(tmp_ctx);
683                 return nt_status;
684         }
685
686         talloc_steal(mem_ctx, *server_info);
687         talloc_free(tmp_ctx);
688
689         return NT_STATUS_OK;
690 }
691
692 static NTSTATUS authsam_ignoredomain_check_password(struct auth_method_context *ctx,
693                                                     TALLOC_CTX *mem_ctx,
694                                                     const struct auth_usersupplied_info *user_info, 
695                                                     struct auth_serversupplied_info **server_info)
696 {
697         return authsam_check_password_internals(ctx, mem_ctx, NULL, user_info, server_info);
698 }
699
700 /****************************************************************************
701 Check SAM security (above) but with a few extra checks.
702 ****************************************************************************/
703 static NTSTATUS authsam_check_password(struct auth_method_context *ctx,
704                                        TALLOC_CTX *mem_ctx,
705                                        const struct auth_usersupplied_info *user_info, 
706                                        struct auth_serversupplied_info **server_info)
707 {
708         const char *domain;
709         BOOL is_local_name, is_my_domain;
710
711         is_local_name = is_myname(user_info->mapped.domain_name);
712         is_my_domain  = strequal(user_info->mapped.domain_name, lp_workgroup());
713
714         /* check whether or not we service this domain/workgroup name */
715         switch (lp_server_role()) {
716                 case ROLE_STANDALONE:
717                         domain = lp_netbios_name();
718                         break;
719                 case ROLE_DOMAIN_MEMBER:
720                         if (!is_local_name) {
721                                 DEBUG(6,("authsam_check_password: %s is not one of my local names (%s)\n",
722                                         user_info->mapped.domain_name, (lp_server_role() == ROLE_DOMAIN_MEMBER 
723                                         ? "ROLE_DOMAIN_MEMBER" : "ROLE_STANDALONE") ));
724                                 return NT_STATUS_NOT_IMPLEMENTED;
725                         }
726                         domain = lp_netbios_name();
727                         break;
728                 case ROLE_DOMAIN_PDC:
729                 case ROLE_DOMAIN_BDC:
730                         if (!is_local_name && !is_my_domain) {
731                                 DEBUG(6,("authsam_check_password: %s is not one of my local names or domain name (DC)\n",
732                                         user_info->mapped.domain_name));
733                                 return NT_STATUS_NOT_IMPLEMENTED;
734                         }
735                         domain = lp_workgroup();
736                         break;
737                 default:
738                         DEBUG(6,("authsam_check_password: lp_server_role() has an undefined value\n"));
739                         return NT_STATUS_NOT_IMPLEMENTED;
740         }
741
742         return authsam_check_password_internals(ctx, mem_ctx, domain, user_info, server_info);
743 }
744
745 static const struct auth_operations sam_ignoredomain_ops = {
746         .name           = "sam_ignoredomain",
747         .get_challenge  = auth_get_challenge_not_implemented,
748         .check_password = authsam_ignoredomain_check_password
749 };
750
751 static const struct auth_operations sam_ops = {
752         .name           = "sam",
753         .get_challenge  = auth_get_challenge_not_implemented,
754         .check_password = authsam_check_password
755 };
756
757 NTSTATUS auth_sam_init(void)
758 {
759         NTSTATUS ret;
760
761         ret = auth_register(&sam_ops);
762         if (!NT_STATUS_IS_OK(ret)) {
763                 DEBUG(0,("Failed to register 'sam' auth backend!\n"));
764                 return ret;
765         }
766
767         ret = auth_register(&sam_ignoredomain_ops);
768         if (!NT_STATUS_IS_OK(ret)) {
769                 DEBUG(0,("Failed to register 'sam_ignoredomain' auth backend!\n"));
770                 return ret;
771         }
772
773         return ret;
774 }