2452f8f65d55117f194bf5c490dbeae10801f5e7
[jelmer/samba4-debian.git] / source / auth / auth_domain.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Authenticate against a remote domain
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Andrew Bartlett 2001
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_AUTH
26
27 BOOL global_machine_password_needs_changing = False;
28
29
30 /*
31   resolve the name of a DC in ways appropriate for an ADS domain mode
32   an ADS domain may not have Netbios enabled at all, so this is 
33   quite different from the RPC case
34   Note that we ignore the 'server' parameter here. That has the effect of using
35   the 'ADS server' smb.conf parameter, which is what we really want anyway
36  */
37 static NTSTATUS ads_resolve_dc(fstring remote_machine, 
38                                struct ipv4_addr *dest_ip)
39 {
40         ADS_STRUCT *ads;
41         ads = ads_init_simple();
42         if (!ads) {
43                 return NT_STATUS_NO_LOGON_SERVERS;              
44         }
45
46         DEBUG(4,("ads_resolve_dc: realm=%s\n", ads->config.realm));
47
48         ads->auth.flags |= ADS_AUTH_NO_BIND;
49
50 #ifdef HAVE_ADS
51         /* a full ads_connect() is actually overkill, as we don't srictly need
52            to do the SASL auth in order to get the info we need, but libads
53            doesn't offer a better way right now */
54         ads_connect(ads);
55 #endif
56
57         fstrcpy(remote_machine, ads->config.ldap_server_name);
58         strupper(remote_machine);
59         *dest_ip = ads->ldap_ip;
60         ads_destroy(&ads);
61         
62         if (!*remote_machine || is_zero_ip(*dest_ip)) {
63                 return NT_STATUS_NO_LOGON_SERVERS;              
64         }
65
66         DEBUG(4,("ads_resolve_dc: using server='%s' IP=%s\n",
67                  remote_machine, inet_ntoa(*dest_ip)));
68         
69         return NT_STATUS_OK;
70 }
71
72 /*
73   resolve the name of a DC in ways appropriate for RPC domain mode
74   this relies on the server supporting netbios and port 137 not being
75   firewalled
76  */
77 static NTSTATUS rpc_resolve_dc(const char *server, 
78                                fstring remote_machine, 
79                                struct ipv4_addr *dest_ip)
80 {
81         if (is_ipaddress(server)) {
82                 struct ipv4_addr to_ip = interpret_addr2(server);
83
84                 /* we need to know the machines netbios name - this is a lousy
85                    way to find it, but until we have a RPC call that does this
86                    it will have to do */
87                 if (!name_status_find("*", 0x20, 0x20, to_ip, remote_machine)) {
88                         DEBUG(2, ("rpc_resolve_dc: Can't resolve name for IP %s\n", server));
89                         return NT_STATUS_NO_LOGON_SERVERS;
90                 }
91
92                 *dest_ip = to_ip;
93                 return NT_STATUS_OK;
94         } 
95
96         fstrcpy(remote_machine, server);
97         strupper(remote_machine);
98         if (!resolve_name(remote_machine, dest_ip, 0x20)) {
99                 DEBUG(1,("rpc_resolve_dc: Can't resolve address for %s\n", 
100                          remote_machine));
101                 return NT_STATUS_NO_LOGON_SERVERS;
102         }
103
104         DEBUG(4,("rpc_resolve_dc: using server='%s' IP=%s\n",
105                  remote_machine, inet_ntoa(*dest_ip)));
106
107         return NT_STATUS_OK;
108 }
109
110 /**
111  * Connect to a remote server for domain security authenticaion.
112  *
113  * @param cli the cli to return containing the active connection
114  * @param server either a machine name or text IP address to
115  *               connect to.
116  * @param trust_passwd the trust password to establish the
117  *                       credentials with.
118  *
119  **/
120
121 static NTSTATUS connect_to_domain_password_server(struct smbcli_state **cli, 
122                                                   const char *server, 
123                                                   const char *setup_creds_as,
124                                                   uint16_t sec_chan,
125                                                   const uint8_t *trust_passwd,
126                                                   BOOL *retry)
127 {
128         struct ipv4_addr dest_ip;
129         fstring remote_machine;
130         NTSTATUS result;
131         uint32_t neg_flags = 0x000001ff;
132
133         *retry = False;
134
135         if (lp_security() == SEC_ADS)
136                 result = ads_resolve_dc(remote_machine, &dest_ip);
137         else
138                 result = rpc_resolve_dc(server, remote_machine, &dest_ip);
139
140         if (!NT_STATUS_IS_OK(result)) {
141                 DEBUG(2,("connect_to_domain_password_server: unable to resolve DC: %s\n", 
142                          nt_errstr(result)));
143                 return result;
144         }
145
146         if (ismyip(dest_ip)) {
147                 DEBUG(1,("connect_to_domain_password_server: Password server loop - not using password server %s\n",
148                          remote_machine));
149                 return NT_STATUS_NO_LOGON_SERVERS;
150         }
151   
152         /* TODO: Send a SAMLOGON request to determine whether this is a valid
153            logonserver.  We can avoid a 30-second timeout if the DC is down
154            if the SAMLOGON request fails as it is only over UDP. */
155
156         /* we use a mutex to prevent two connections at once - when a 
157            Win2k PDC get two connections where one hasn't completed a 
158            session setup yet it will send a TCP reset to the first 
159            connection (tridge) */
160
161         /*
162          * With NT4.x DC's *all* authentication must be serialized to avoid
163          * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
164          */
165
166         *retry = True;
167
168         if (!grab_server_mutex(server))
169                 return NT_STATUS_NO_LOGON_SERVERS;
170         
171         /* Attempt connection */
172         result = smbcli_full_connection(NULL, cli, lp_netbios_name(), remote_machine,
173                                      &dest_ip, 0, "IPC$", "IPC", "", "", "",0, retry);
174
175         if (!NT_STATUS_IS_OK(result)) {
176                 release_server_mutex();
177                 return result;
178         }
179
180         /*
181          * We now have an anonymous connection to IPC$ on the domain password server.
182          */
183
184         /*
185          * Even if the connect succeeds we need to setup the netlogon
186          * pipe here. We do this as we may just have changed the domain
187          * account password on the PDC and yet we may be talking to
188          * a BDC that doesn't have this replicated yet. In this case
189          * a successful connect to a DC needs to take the netlogon connect
190          * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>.
191          */
192
193         if(smbcli_nt_session_open(*cli, PI_NETLOGON) == False) {
194                 DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
195 machine %s. Error was : %s.\n", remote_machine, smbcli_errstr(*cli)));
196                 smbcli_nt_session_close(*cli);
197                 smbcli_ulogoff(*cli);
198                 smbcli_shutdown(*cli);
199                 release_server_mutex();
200                 return NT_STATUS_NO_LOGON_SERVERS;
201         }
202
203         snprintf((*cli)->mach_acct, sizeof((*cli)->mach_acct) - 1, "%s$", setup_creds_as);
204
205         if (!(*cli)->mach_acct) {
206                 release_server_mutex();
207                 return NT_STATUS_NO_MEMORY;
208         }
209
210         result = smbcli_nt_setup_creds(*cli, sec_chan, trust_passwd, &neg_flags, 2);
211
212         if (!NT_STATUS_IS_OK(result)) {
213                 DEBUG(0,("connect_to_domain_password_server: unable to setup the NETLOGON credentials to machine \
214 %s. Error was : %s.\n", remote_machine, nt_errstr(result)));
215                 smbcli_nt_session_close(*cli);
216                 smbcli_ulogoff(*cli);
217                 smbcli_shutdown(*cli);
218                 release_server_mutex();
219                 return result;
220         }
221
222         /* We exit here with the mutex *locked*. JRA */
223
224         return NT_STATUS_OK;
225 }
226
227 /***********************************************************************
228  Utility function to attempt a connection to an IP address of a DC.
229 ************************************************************************/
230
231 static NTSTATUS attempt_connect_to_dc(struct smbcli_state **cli, 
232                                       const char *domain, 
233                                       struct ipv4_addr *ip, 
234                                       const char *setup_creds_as, 
235                                       uint16_t sec_chan,
236                                       const uint8_t *trust_passwd)
237 {
238         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
239         BOOL retry = True;
240         fstring dc_name;
241         int i;
242
243         /*
244          * Ignore addresses we have already tried.
245          */
246
247         if (is_zero_ip(*ip))
248                 return NT_STATUS_NO_LOGON_SERVERS;
249
250         if (!lookup_dc_name(lp_netbios_name(), domain, ip, dc_name))
251                 return NT_STATUS_NO_LOGON_SERVERS;
252
253         for (i = 0; (!NT_STATUS_IS_OK(ret)) && retry && (i < 3); i++)
254                 ret = connect_to_domain_password_server(cli, dc_name, setup_creds_as,
255                                 sec_chan, trust_passwd, &retry);
256         return ret;
257 }
258
259 /***********************************************************************
260  We have been asked to dynamically determine the IP addresses of
261  the PDC and BDC's for DOMAIN, and query them in turn.
262 ************************************************************************/
263 static NTSTATUS find_connect_dc(struct smbcli_state **cli, 
264                                  const char *domain,
265                                  const char *setup_creds_as,
266                                  uint16_t sec_chan,
267                                  uint8_t *trust_passwd, 
268                                  time_t last_change_time)
269 {
270         struct ipv4_addr dc_ip;
271         fstring srv_name;
272
273         if ( !rpc_find_dc(lp_workgroup(), srv_name, &dc_ip) ) {
274                 DEBUG(0,("find_connect_dc: Failed to find an DCs for %s\n", lp_workgroup()));
275                 return NT_STATUS_NO_LOGON_SERVERS;
276         }
277         
278         return attempt_connect_to_dc( cli, domain, &dc_ip, setup_creds_as, 
279                         sec_chan, trust_passwd );
280 }
281
282 /***********************************************************************
283  Do the same as security=server, but using NT Domain calls and a session
284  key from the machine password.  If the server parameter is specified
285  use it, otherwise figure out a server from the 'password server' param.
286 ************************************************************************/
287
288 static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
289                                        const auth_usersupplied_info *user_info, 
290                                        const char *domain,
291                                        uint8_t chal[8],
292                                        auth_serversupplied_info **server_info, 
293                                        const char *server, const char *setup_creds_as,
294                                        uint16_t sec_chan,
295                                        uint8_t trust_passwd[16],
296                                        time_t last_change_time)
297 {
298         fstring remote_machine;
299         NET_USER_INFO_3 info3;
300         struct smbcli_state *cli = NULL;
301         NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS;
302
303         /*
304          * At this point, smb_apasswd points to the lanman response to
305          * the challenge in local_challenge, and smb_ntpasswd points to
306          * the NT response to the challenge in local_challenge. Ship
307          * these over the secure channel to a domain controller and
308          * see if they were valid.
309          */
310
311         while (!NT_STATUS_IS_OK(nt_status) &&
312                next_token(&server,remote_machine,LIST_SEP,sizeof(remote_machine))) {
313                 if(lp_security() != SEC_ADS && strequal(remote_machine, "*")) {
314                         nt_status = find_connect_dc(&cli, domain, setup_creds_as, sec_chan, trust_passwd, last_change_time);
315                 } else {
316                         int i;
317                         BOOL retry = True;
318                         for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++)
319                                 nt_status = connect_to_domain_password_server(&cli, remote_machine, setup_creds_as,
320                                                 sec_chan, trust_passwd, &retry);
321                 }
322         }
323
324         if (!NT_STATUS_IS_OK(nt_status)) {
325                 DEBUG(0,("domain_client_validate: Domain password server not available.\n"));
326                 return nt_status;
327         }
328
329         ZERO_STRUCT(info3);
330
331         /*
332          * If this call succeeds, we now have lots of info about the user
333          * in the info3 structure.  
334          */
335
336         nt_status = smbcli_netlogon_sam_network_logon(cli, mem_ctx,
337                                                    user_info->smb_name.str, user_info->domain.str, 
338                                                    user_info->wksta_name.str, chal, 
339                                                    user_info->lm_resp, user_info->nt_resp, 
340                                                    &info3);
341         
342         if (!NT_STATUS_IS_OK(nt_status)) {
343                 DEBUG(0,("domain_client_validate: unable to validate password "
344                          "for user %s in domain %s to Domain controller %s. "
345                          "Error was %s.\n", user_info->smb_name.str,
346                          user_info->domain.str, cli->srv_name_slash, 
347                          nt_errstr(nt_status)));
348         } else {
349                 nt_status = make_server_info_info3(mem_ctx, user_info->internal_username.str, 
350                                                    user_info->smb_name.str, domain, server_info, &info3);
351 #if 0 
352                 /* The stuff doesn't work right yet */
353                 SMB_ASSERT(sizeof((*server_info)->session_key) == sizeof(info3.user_sess_key)); 
354                 memcpy((*server_info)->session_key, info3.user_sess_key, sizeof((*server_info)->session_key)/* 16 */);
355                 SamOEMhash((*server_info)->session_key, trust_passwd, sizeof((*server_info)->session_key));
356 #endif          
357
358                 uni_group_cache_store_netlogon(mem_ctx, &info3);
359         }
360
361 #if 0
362         /* 
363          * We don't actually need to do this - plus it fails currently with
364          * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to
365          * send here. JRA.
366          */
367
368         if (NT_STATUS_IS_OK(status)) {
369                 if(smbcli_nt_logoff(&cli, &ctr) == False) {
370                         DEBUG(0,("domain_client_validate: unable to log off user %s in domain \
371 %s to Domain controller %s. Error was %s.\n", user, domain, remote_machine, smbcli_errstr(&cli)));        
372                         nt_status = NT_STATUS_LOGON_FAILURE;
373                 }
374         }
375 #endif /* 0 */
376
377         /* Note - once the cli stream is shutdown the mem_ctx used
378            to allocate the other_sids and gids structures has been deleted - so
379            these pointers are no longer valid..... */
380
381         smbcli_nt_session_close(cli);
382         smbcli_ulogoff(cli);
383         smbcli_shutdown(cli);
384         release_server_mutex();
385         return nt_status;
386 }
387
388 /****************************************************************************
389  Check for a valid username and password in security=domain mode.
390 ****************************************************************************/
391
392 static NTSTATUS check_ntdomain_security(const struct auth_context *auth_context,
393                                         void *my_private_data, 
394                                         TALLOC_CTX *mem_ctx,
395                                         const auth_usersupplied_info *user_info, 
396                                         auth_serversupplied_info **server_info)
397 {
398         NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
399         char *password_server;
400         uint8_t trust_passwd[16];
401         time_t last_change_time;
402         const char *domain = lp_workgroup();
403
404         if (!user_info || !server_info || !auth_context) {
405                 DEBUG(1,("check_ntdomain_security: Critical variables not present.  Failing.\n"));
406                 return NT_STATUS_INVALID_PARAMETER;
407         }
408
409         /* 
410          * Check that the requested domain is not our own machine name.
411          * If it is, we should never check the PDC here, we use our own local
412          * password file.
413          */
414
415         if(is_myname(user_info->domain.str)) {
416                 DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n"));
417                 return NT_STATUS_LOGON_FAILURE;
418         }
419
420         /*
421          * Get the machine account password for our primary domain
422          * No need to become_root() as secrets_init() is done at startup.
423          */
424
425         if (!secrets_fetch_trust_account_password(domain, trust_passwd, &last_change_time))
426         {
427                 DEBUG(0, ("check_ntdomain_security: could not fetch trust account password for domain '%s'\n", domain));
428                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
429         }
430
431         /* Test if machine password has expired and needs to be changed */
432         if (lp_machine_password_timeout()) {
433                 if (last_change_time > 0 && 
434                     time(NULL) > (last_change_time + 
435                                   lp_machine_password_timeout())) {
436                         global_machine_password_needs_changing = True;
437                 }
438         }
439
440         /*
441          * Treat each name in the 'password server =' line as a potential
442          * PDC/BDC. Contact each in turn and try and authenticate.
443          */
444
445         password_server = lp_passwordserver();
446
447         nt_status = domain_client_validate(mem_ctx, user_info, domain,
448                                            (uint8_t *)auth_context->challenge.data, 
449                                            server_info, 
450                                            password_server, lp_netbios_name(), SEC_CHAN_WKSTA, trust_passwd, last_change_time);
451         return nt_status;
452 }
453
454 /* module initialisation */
455 NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
456 {
457         if (!make_auth_methods(auth_context, auth_method)) {
458                 return NT_STATUS_NO_MEMORY;
459         }
460
461         (*auth_method)->name = "ntdomain";
462         (*auth_method)->auth = check_ntdomain_security;
463         return NT_STATUS_OK;
464 }
465
466
467 /****************************************************************************
468  Check for a valid username and password in a trusted domain
469 ****************************************************************************/
470
471 static NTSTATUS check_trustdomain_security(const struct auth_context *auth_context,
472                                            void *my_private_data, 
473                                            TALLOC_CTX *mem_ctx,
474                                            const auth_usersupplied_info *user_info, 
475                                            auth_serversupplied_info **server_info)
476 {
477         NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
478         uint8_t trust_md4_password[16];
479         char *trust_password;
480         time_t last_change_time;
481         DOM_SID sid;
482
483         if (!user_info || !server_info || !auth_context) {
484                 DEBUG(1,("check_trustdomain_security: Critical variables not present.  Failing.\n"));
485                 return NT_STATUS_INVALID_PARAMETER;
486         }
487
488         /* 
489          * Check that the requested domain is not our own machine name.
490          * If it is, we should never check the PDC here, we use our own local
491          * password file.
492          */
493
494         if(is_myname(user_info->domain.str)) {
495                 DEBUG(3,("check_trustdomain_security: Requested domain was for this machine.\n"));
496                 return NT_STATUS_LOGON_FAILURE;
497         }
498
499         /* 
500          * Check that the requested domain is not our own domain,
501          * If it is, we should use our own local password file.
502          */
503
504         if(strequal(lp_workgroup(), (user_info->domain.str))) {
505                 DEBUG(3,("check_trustdomain_security: Requested domain was for this domain.\n"));
506                 return NT_STATUS_LOGON_FAILURE;
507         }
508
509         /*
510          * Get the trusted account password for the trusted domain
511          * No need to become_root() as secrets_init() is done at startup.
512          */
513
514         if (!secrets_fetch_trusted_domain_password(user_info->domain.str, &trust_password, &sid, &last_change_time))
515         {
516                 DEBUG(0, ("check_trustdomain_security: could not fetch trust account password for domain %s\n", user_info->domain.str));
517                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
518         }
519
520 #ifdef DEBUG_PASSWORD
521         DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain.str, trust_password));
522 #endif
523         E_md4hash(trust_password, trust_md4_password);
524         SAFE_FREE(trust_password);
525
526 #if 0
527         /* Test if machine password is expired and need to be changed */
528         if (time(NULL) > last_change_time + lp_machine_password_timeout())
529         {
530                 global_machine_password_needs_changing = True;
531         }
532 #endif
533
534         nt_status = domain_client_validate(mem_ctx, user_info, user_info->domain.str,
535                                            (uint8_t *)auth_context->challenge.data, 
536                                            server_info, "*" /* Do a lookup */, 
537                                            lp_workgroup(), SEC_CHAN_DOMAIN, trust_md4_password, last_change_time);
538         
539         return nt_status;
540 }
541
542 /* module initialisation */
543 NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
544 {
545         if (!make_auth_methods(auth_context, auth_method)) {
546                 return NT_STATUS_NO_MEMORY;
547         }
548
549         (*auth_method)->name = "trustdomain";
550         (*auth_method)->auth = check_trustdomain_security;
551         return NT_STATUS_OK;
552 }