- Make 'net' use a single funciton for setting the 'use machine account' code.
[tprouty/samba.git] / source / nsswitch / winbindd_cm.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon connection manager
5
6    Copyright (C) Tim Potter 2001
7    Copyright (C) Andrew Bartlett 2002
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 2 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, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 /*
25    We need to manage connections to domain controllers without having to
26    mess up the main winbindd code with other issues.  The aim of the
27    connection manager is to:
28   
29        - make connections to domain controllers and cache them
30        - re-establish connections when networks or servers go down
31        - centralise the policy on connection timeouts, domain controller
32          selection etc
33        - manage re-entrancy for when winbindd becomes able to handle
34          multiple outstanding rpc requests
35   
36    Why not have connection management as part of the rpc layer like tng?
37    Good question.  This code may morph into libsmb/rpc_cache.c or something
38    like that but at the moment it's simply staying as part of winbind.  I
39    think the TNG architecture of forcing every user of the rpc layer to use
40    the connection caching system is a bad idea.  It should be an optional
41    method of using the routines.
42
43    The TNG design is quite good but I disagree with some aspects of the
44    implementation. -tpot
45
46  */
47
48 /*
49    TODO:
50
51      - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
52        moved down into another function.
53
54      - Take care when destroying cli_structs as they can be shared between
55        various sam handles.
56
57  */
58
59 #include "winbindd.h"
60
61 #undef DBGC_CLASS
62 #define DBGC_CLASS DBGC_WINBIND
63
64 /* Global list of connections.  Initially a DLIST but can become a hash
65    table or whatever later. */
66
67 struct winbindd_cm_conn {
68         struct winbindd_cm_conn *prev, *next;
69         fstring domain;
70         fstring controller;
71         fstring pipe_name;
72         size_t mutex_ref_count;
73         struct cli_state *cli;
74         POLICY_HND pol;
75 };
76
77 static struct winbindd_cm_conn *cm_conns = NULL;
78
79
80 /* Choose between anonymous or authenticated connections.  We need to use
81    an authenticated connection if DCs have the RestrictAnonymous registry
82    entry set > 0, or the "Additional restrictions for anonymous
83    connections" set in the win2k Local Security Policy. 
84    
85    Caller to free() result in domain, username, password
86 */
87
88 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
89 {
90         *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
91         *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
92         *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
93         
94         if (*username && **username) {
95
96                 if (!*domain || !**domain)
97                         *domain = smb_xstrdup(lp_workgroup());
98                 
99                 if (!*password || !**password)
100                         *password = smb_xstrdup("");
101
102                 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", 
103                           *domain, *username));
104
105         } else {
106                 DEBUG(3, ("IPC$ connections done anonymously\n"));
107                 *username = smb_xstrdup("");
108                 *domain = smb_xstrdup("");
109                 *password = smb_xstrdup("");
110         }
111 }
112
113 /* Open a connction to the remote server, cache failures for 30 seconds */
114
115 static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
116                                struct winbindd_cm_conn *new_conn)
117 {
118         NTSTATUS result;
119         char *machine_password; 
120         char *machine_krb5_principal, *ipc_username, *ipc_domain, *ipc_password;
121         struct in_addr dc_ip;
122         int i;
123         BOOL retry = True;
124
125         ZERO_STRUCT(dc_ip);
126
127         fstrcpy(new_conn->domain, domain);
128         fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
129         
130         /* connection failure cache has been moved inside of get_dc_name
131            so we can deal with half dead DC's   --jerry */
132
133         if (!get_dc_name(domain, new_conn->controller, &dc_ip)) {
134                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
135                 add_failed_connection_entry(domain, "", result);
136                 return result;
137         }
138                 
139         /* Initialise SMB connection */
140
141         /* grab stored passwords */
142         machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
143         
144         if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), lp_realm()) == -1) {
145                 SAFE_FREE(machine_password);
146                 return NT_STATUS_NO_MEMORY;
147         }
148
149         cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
150
151         for (i = 0; retry && (i < 3); i++) {
152                 BOOL got_mutex;
153                 if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
154                         DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
155                         result = NT_STATUS_POSSIBLE_DEADLOCK;
156                         continue;
157                 }
158                 
159                 new_conn->cli = NULL;
160                 result = cli_start_connection(&new_conn->cli, global_myname(), 
161                                               new_conn->controller, 
162                                               &dc_ip, 0, Undefined, 
163                                               CLI_FULL_CONNECTION_USE_KERBEROS, 
164                                               &retry);
165
166                 if (NT_STATUS_IS_OK(result)) {
167                         if ((lp_security() == SEC_ADS) 
168                                 && (new_conn->cli->protocol >= PROTOCOL_NT1 && new_conn->cli->capabilities & CAP_EXTENDED_SECURITY)) {
169                                 new_conn->cli->use_kerberos = True;
170                                 DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n", 
171                                           new_conn->controller, global_myname(), machine_krb5_principal));
172
173                                 if (!cli_session_setup_spnego(new_conn->cli, machine_krb5_principal, 
174                                                               machine_password, 
175                                                               domain)) {
176                                         result = cli_nt_error(new_conn->cli);
177                                         DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result)));
178                                         if (NT_STATUS_IS_OK(result)) 
179                                                 result = NT_STATUS_UNSUCCESSFUL;
180                                 }
181                         }
182                         new_conn->cli->use_kerberos = False;
183                         if (!NT_STATUS_IS_OK(result) 
184                             && new_conn->cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {       
185                                 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
186                                           new_conn->controller, global_myname(), ipc_domain, ipc_username));
187
188                                 if (!cli_session_setup(new_conn->cli, ipc_username, 
189                                                        ipc_password, strlen(ipc_password)+1, 
190                                                        ipc_password, strlen(ipc_password)+1, 
191                                                        domain)) {
192                                         result = cli_nt_error(new_conn->cli);
193                                         DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result)));
194                                         if (NT_STATUS_IS_OK(result)) 
195                                                 result = NT_STATUS_UNSUCCESSFUL;
196                                 }
197                         }
198                         if (!NT_STATUS_IS_OK(result)) { 
199                                 if (!cli_session_setup(new_conn->cli, "", NULL, 0, 
200                                                        NULL, 0, 
201                                                        "")) {
202                                         result = cli_nt_error(new_conn->cli);
203                                         DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result)));
204                                         if (NT_STATUS_IS_OK(result)) 
205                                                 result = NT_STATUS_UNSUCCESSFUL;
206                                 } 
207                                 
208                         }
209                         if (NT_STATUS_IS_OK(result) && !cli_send_tconX(new_conn->cli, "IPC$", "IPC",
210                                                                        "", 0)) {
211                                 result = cli_nt_error(new_conn->cli);
212                                 DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
213                                 cli_shutdown(new_conn->cli);
214                                 if (NT_STATUS_IS_OK(result)) {
215                                         result = NT_STATUS_UNSUCCESSFUL;
216                                 }
217                         }
218                 }
219
220                 if (NT_STATUS_IS_OK(result)) {
221                         struct ntuser_creds creds;
222                         init_creds(&creds, ipc_username, ipc_domain, ipc_password);
223                         cli_init_creds(new_conn->cli, &creds);
224                 }
225
226                 if (got_mutex)
227                         secrets_named_mutex_release(new_conn->controller);
228
229                 if (NT_STATUS_IS_OK(result))
230                         break;
231         }
232
233         SAFE_FREE(ipc_username);
234         SAFE_FREE(ipc_domain);
235         SAFE_FREE(ipc_password);
236         SAFE_FREE(machine_password);
237
238         if (!NT_STATUS_IS_OK(result)) {
239                 add_failed_connection_entry(domain, new_conn->controller, result);
240                 return result;
241         }
242         
243         /* set the domain if empty; needed for schannel connections */
244         if ( !*new_conn->cli->domain )
245                 fstrcpy( new_conn->cli->domain, domain );
246                 
247         
248         if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
249                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
250                 /* 
251                  * only cache a failure if we are not trying to open the 
252                  * **win2k** specific lsarpc UUID.  This could be an NT PDC 
253                  * and therefore a failure is normal.  This should probably
254                  * be abstracted to a check for 2k specific pipes and wondering
255                  * if the PDC is an NT4 box.   but since there is only one 2k 
256                  * specific UUID right now, i'm not going to bother.  --jerry
257                  */
258                 if ( !is_win2k_pipe(pipe_index) )
259                         add_failed_connection_entry(domain, new_conn->controller, result);
260                 cli_shutdown(new_conn->cli);
261                 return result;
262         }
263
264         return NT_STATUS_OK;
265 }
266
267 /************************************************************************
268  Wrapper around statuc cm_open_connection to retreive a freshly
269  setup cli_state struct
270 ************************************************************************/
271
272 NTSTATUS cm_fresh_connection(const char *domain, const int pipe_index,
273                                struct cli_state **cli)
274 {
275         NTSTATUS result;
276         struct winbindd_cm_conn conn;
277         
278         result = cm_open_connection( domain, pipe_index, &conn );
279         
280         if ( NT_STATUS_IS_OK(result) ) 
281                 *cli = conn.cli;
282
283         return result;
284 }
285
286 /* Return true if a connection is still alive */
287
288 static BOOL connection_ok(struct winbindd_cm_conn *conn)
289 {
290         if (!conn) {
291                 smb_panic("Invalid parameter passed to connection_ok():  conn was NULL!\n");
292                 return False;
293         }
294
295         if (!conn->cli) {
296                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
297                           conn->controller, conn->domain, conn->pipe_name));
298                 return False;
299         }
300
301         if (!conn->cli->initialised) {
302                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
303                           conn->controller, conn->domain, conn->pipe_name));
304                 return False;
305         }
306
307         if (conn->cli->fd == -1) {
308                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
309                           conn->controller, conn->domain, conn->pipe_name));
310                 return False;
311         }
312         
313         return True;
314 }
315
316 /* Search the cache for a connection. If there is a broken one,
317    shut it down properly and return NULL. */
318
319 static void find_cm_connection(const char *domain, const char *pipe_name,
320                                struct winbindd_cm_conn **conn_out) 
321 {
322         struct winbindd_cm_conn *conn;
323
324         for (conn = cm_conns; conn; ) {
325                 if (strequal(conn->domain, domain) && 
326                     strequal(conn->pipe_name, pipe_name)) {
327                         if (!connection_ok(conn)) {
328                                 /* Dead connection - remove it. */
329                                 struct winbindd_cm_conn *conn_temp = conn->next;
330                                 if (conn->cli)
331                                         cli_shutdown(conn->cli);
332                                 DLIST_REMOVE(cm_conns, conn);
333                                 SAFE_FREE(conn);
334                                 conn = conn_temp;  /* Keep the loop moving */
335                                 continue;
336                         } else {
337                                 break;
338                         }
339                 }
340                 conn = conn->next;
341         }
342
343         *conn_out = conn;
344 }
345
346 /* Initialize a new connection up to the RPC BIND. */
347
348 static NTSTATUS new_cm_connection(const char *domain, const char *pipe_name,
349                                   struct winbindd_cm_conn **conn_out)
350 {
351         struct winbindd_cm_conn *conn;
352         NTSTATUS result;
353
354         if (!(conn = malloc(sizeof(*conn))))
355                 return NT_STATUS_NO_MEMORY;
356                 
357         ZERO_STRUCTP(conn);
358                 
359         if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
360                 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
361                           domain, pipe_name, nt_errstr(result)));
362                 SAFE_FREE(conn);
363                 return result;
364         }
365         DLIST_ADD(cm_conns, conn);
366
367         *conn_out = conn;
368         return NT_STATUS_OK;
369 }
370
371 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
372
373 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
374                                           struct winbindd_cm_conn **conn_out)
375 {
376         find_cm_connection(domain, pipe_name, conn_out);
377
378         if (*conn_out != NULL)
379                 return NT_STATUS_OK;
380
381         return new_cm_connection(domain, pipe_name, conn_out);
382 }
383
384 /**********************************************************************************
385 **********************************************************************************/
386
387 BOOL cm_check_for_native_mode_win2k( const char *domain )
388 {
389         NTSTATUS                result;
390         struct winbindd_cm_conn conn;
391         DS_DOMINFO_CTR          ctr;
392         BOOL                    ret = False;
393         
394         ZERO_STRUCT( conn );
395         ZERO_STRUCT( ctr );
396         
397         
398         if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
399                 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n", 
400                           domain, nt_errstr(result)));
401                 return False;
402         }
403         
404         if ( conn.cli ) {
405                 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
406                                 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
407                         ret = False;
408                         goto done;
409                 }
410         }
411                                 
412         if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) 
413                         && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
414                 ret = True;
415
416 done:
417
418         /* close the connection;  no other cals use this pipe and it is called only
419            on reestablishing the domain list   --jerry */
420
421         if ( conn.cli )
422                 cli_shutdown( conn.cli );
423         
424         return ret;
425 }
426
427
428
429 /* Return a LSA policy handle on a domain */
430
431 NTSTATUS cm_get_lsa_handle(const char *domain, CLI_POLICY_HND **return_hnd)
432 {
433         struct winbindd_cm_conn *conn;
434         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
435         NTSTATUS result;
436         static CLI_POLICY_HND hnd;
437
438         /* Look for existing connections */
439
440         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
441                 return result;
442
443         /* This *shitty* code needs scrapping ! JRA */
444         
445         if (policy_handle_is_valid(&conn->pol)) {
446                 hnd.pol = conn->pol;
447                 hnd.cli = conn->cli;
448                 *return_hnd = &hnd;
449
450                 return NT_STATUS_OK;
451         }
452         
453         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
454                                      des_access, &conn->pol);
455
456         if (!NT_STATUS_IS_OK(result)) {
457                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
458                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
459                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
460                                 return result;
461
462                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
463                                                      des_access, &conn->pol);
464                 }
465
466                 if (!NT_STATUS_IS_OK(result)) {
467                         cli_shutdown(conn->cli);
468                         DLIST_REMOVE(cm_conns, conn);
469                         SAFE_FREE(conn);
470                         return result;
471                 }
472         }       
473
474         hnd.pol = conn->pol;
475         hnd.cli = conn->cli;
476
477         *return_hnd = &hnd;
478
479         return NT_STATUS_OK;
480 }
481
482 /* Return a SAM policy handle on a domain */
483
484 NTSTATUS cm_get_sam_handle(char *domain, CLI_POLICY_HND **return_hnd)
485
486         struct winbindd_cm_conn *conn;
487         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
488         NTSTATUS result;
489         static CLI_POLICY_HND hnd;
490
491         /* Look for existing connections */
492
493         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
494                 return result;
495         
496         /* This *shitty* code needs scrapping ! JRA */
497         
498         if (policy_handle_is_valid(&conn->pol)) {
499                 hnd.pol = conn->pol;
500                 hnd.cli = conn->cli;
501                 
502                 *return_hnd = &hnd;
503
504                 return NT_STATUS_OK;
505         }
506         
507         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
508                                   des_access, &conn->pol);
509
510         if (!NT_STATUS_IS_OK(result)) {
511                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
512                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
513                 
514                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
515                                 return result;
516
517                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
518                                                   des_access, &conn->pol);
519                 }
520
521                 if (!NT_STATUS_IS_OK(result)) {
522                 
523                         cli_shutdown(conn->cli);
524                         DLIST_REMOVE(cm_conns, conn);
525                         SAFE_FREE(conn);
526                         
527                         return result;
528                 }
529         }       
530
531         hnd.pol = conn->pol;
532         hnd.cli = conn->cli;
533
534         *return_hnd = &hnd;
535
536         return NT_STATUS_OK;
537 }
538
539 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
540    netlogon pipe as no handle is returned. */
541
542 NTSTATUS cm_get_netlogon_cli(const char *domain, 
543                              const unsigned char *trust_passwd, 
544                              uint32 sec_channel_type,
545                              BOOL fresh,
546                              struct cli_state **cli)
547 {
548         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
549         struct winbindd_cm_conn *conn;
550         fstring lock_name;
551         BOOL got_mutex;
552
553         if (!cli)
554                 return NT_STATUS_INVALID_PARAMETER;
555
556         /* Open an initial conection - keep the mutex. */
557
558         find_cm_connection(domain, PIPE_NETLOGON, &conn);
559
560         if ( fresh && (conn != NULL) ) {
561                 cli_shutdown(conn->cli);
562                 conn->cli = NULL;
563
564                 conn = NULL;
565
566                 /* purge connection from cache */
567                 find_cm_connection(domain, PIPE_NETLOGON, &conn);
568                 if (conn != NULL) {
569                         DEBUG(0,("Could not purge connection\n"));
570                         return NT_STATUS_UNSUCCESSFUL;
571                 }
572         }
573
574         if (conn != NULL) {
575                 *cli = conn->cli;
576                 return NT_STATUS_OK;
577         }
578
579         result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
580
581         if (!NT_STATUS_IS_OK(result))
582                 return result;
583         
584         fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
585
586         if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
587                 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
588         }
589         
590         if ( sec_channel_type == SEC_CHAN_DOMAIN )
591                 fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
592                         
593         result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
594         
595         if (got_mutex)
596                 secrets_named_mutex_release(lock_name);
597                                 
598         if (!NT_STATUS_IS_OK(result)) {
599                 cli_shutdown(conn->cli);
600                 DLIST_REMOVE(cm_conns, conn);
601                 SAFE_FREE(conn);
602                 return result;
603         }
604
605         *cli = conn->cli;
606
607         return result;
608 }
609
610 /* Dump the current connection status */
611
612 static void dump_conn_list(void)
613 {
614         struct winbindd_cm_conn *con;
615
616         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
617
618         for(con = cm_conns; con; con = con->next) {
619                 char *msg;
620
621                 /* Display pipe info */
622                 
623                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
624                         DEBUG(0, ("Error: not enough memory!\n"));
625                 } else {
626                         DEBUG(0, ("%s\n", msg));
627                         SAFE_FREE(msg);
628                 }
629         }
630 }
631
632 void winbindd_cm_status(void)
633 {
634         /* List open connections */
635
636         DEBUG(0, ("winbindd connection manager status:\n"));
637
638         if (cm_conns)
639                 dump_conn_list();
640         else
641                 DEBUG(0, ("\tNo active connections\n"));
642 }
643
644 /* Close all cached connections */
645
646 void winbindd_cm_flush(void)
647 {
648         struct winbindd_cm_conn *conn, tmp;
649
650         /* Flush connection cache */
651
652         for (conn = cm_conns; conn; conn = conn->next) {
653
654                 if (!connection_ok(conn))
655                         continue;
656
657                 DEBUG(10, ("Closing connection to %s on %s\n",
658                         conn->pipe_name, conn->controller));
659
660                 if (conn->cli)
661                         cli_shutdown(conn->cli);
662
663                 tmp.next = conn->next;
664
665                 DLIST_REMOVE(cm_conns, conn);
666                 SAFE_FREE(conn);
667                 conn = &tmp;
668         }
669
670         /* Flush failed connection cache */
671
672         flush_negative_conn_cache();
673 }