(merge from 3.0)
[mat/samba.git] / source3 / 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
168                         /* reset the error code */
169                         result = NT_STATUS_UNSUCCESSFUL; 
170
171                         /* Krb5 session */
172                         
173                         if ((lp_security() == SEC_ADS) 
174                                 && (new_conn->cli->protocol >= PROTOCOL_NT1 && new_conn->cli->capabilities & CAP_EXTENDED_SECURITY)) {
175                                 new_conn->cli->use_kerberos = True;
176                                 DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n", 
177                                           new_conn->controller, global_myname(), machine_krb5_principal));
178
179                                 result = NT_STATUS_OK;
180
181                                 if (!NT_STATUS_IS_OK(result = cli_session_setup_spnego(new_conn->cli, machine_krb5_principal, 
182                                                               machine_password, 
183                                                               domain))) {
184                                         DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result)));
185                                         if (NT_STATUS_IS_OK(result)) 
186                                                 result = NT_STATUS_UNSUCCESSFUL;
187                                 }
188                         }
189                         new_conn->cli->use_kerberos = False;
190                         
191                         /* only do this is we have a username/password for thr IPC$ connection */
192                         
193                         if ( !NT_STATUS_IS_OK(result) 
194                                 && new_conn->cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE
195                                 && strlen(ipc_username) )
196                         {       
197                                 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
198                                           new_conn->controller, global_myname(), ipc_domain, ipc_username));
199
200                                 result = NT_STATUS_OK;
201
202                                 if (!cli_session_setup(new_conn->cli, ipc_username, 
203                                                        ipc_password, strlen(ipc_password)+1, 
204                                                        ipc_password, strlen(ipc_password)+1, 
205                                                        domain)) {
206                                         result = cli_nt_error(new_conn->cli);
207                                         DEBUG(4,("failed authenticated session setup with %s\n", nt_errstr(result)));
208                                         if (NT_STATUS_IS_OK(result)) 
209                                                 result = NT_STATUS_UNSUCCESSFUL;
210                                 }
211                         }
212                         
213                         /* anonymous is all that is left if we get to here */
214                         
215                         if (!NT_STATUS_IS_OK(result)) { 
216                         
217                                 DEBUG(5, ("anonymous connection attempt to %s from %s\n", 
218                                           new_conn->controller, global_myname()));
219                                           
220                                 result = NT_STATUS_OK;
221
222                                 if (!cli_session_setup(new_conn->cli, "", NULL, 0, NULL, 0, "")) 
223                                 {
224                                         result = cli_nt_error(new_conn->cli);
225                                         DEBUG(4,("failed anonymous session setup with %s\n", nt_errstr(result)));
226                                         if (NT_STATUS_IS_OK(result)) 
227                                                 result = NT_STATUS_UNSUCCESSFUL;
228                                 } 
229                                 
230                         }
231
232                         if (NT_STATUS_IS_OK(result) && !cli_send_tconX(new_conn->cli, "IPC$", "IPC",
233                                                                        "", 0)) {
234                                 result = cli_nt_error(new_conn->cli);
235                                 DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
236                                 cli_shutdown(new_conn->cli);
237                                 if (NT_STATUS_IS_OK(result)) {
238                                         result = NT_STATUS_UNSUCCESSFUL;
239                                 }
240                         }
241                 }
242
243                 if (NT_STATUS_IS_OK(result)) {
244                         struct ntuser_creds creds;
245                         init_creds(&creds, ipc_username, ipc_domain, ipc_password);
246                         cli_init_creds(new_conn->cli, &creds);
247                 }
248
249                 if (got_mutex)
250                         secrets_named_mutex_release(new_conn->controller);
251
252                 if (NT_STATUS_IS_OK(result))
253                         break;
254         }
255
256         SAFE_FREE(ipc_username);
257         SAFE_FREE(ipc_domain);
258         SAFE_FREE(ipc_password);
259         SAFE_FREE(machine_password);
260
261         if (!NT_STATUS_IS_OK(result)) {
262                 add_failed_connection_entry(domain, new_conn->controller, result);
263                 return result;
264         }
265         
266         /* set the domain if empty; needed for schannel connections */
267         if ( !*new_conn->cli->domain )
268                 fstrcpy( new_conn->cli->domain, domain );
269                 
270         
271         if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
272                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
273                 /* 
274                  * only cache a failure if we are not trying to open the 
275                  * **win2k** specific lsarpc UUID.  This could be an NT PDC 
276                  * and therefore a failure is normal.  This should probably
277                  * be abstracted to a check for 2k specific pipes and wondering
278                  * if the PDC is an NT4 box.   but since there is only one 2k 
279                  * specific UUID right now, i'm not going to bother.  --jerry
280                  */
281                 if ( !is_win2k_pipe(pipe_index) )
282                         add_failed_connection_entry(domain, new_conn->controller, result);
283                 cli_shutdown(new_conn->cli);
284                 return result;
285         }
286
287         return NT_STATUS_OK;
288 }
289
290 /************************************************************************
291  Wrapper around statuc cm_open_connection to retreive a freshly
292  setup cli_state struct
293 ************************************************************************/
294
295 NTSTATUS cm_fresh_connection(const char *domain, const int pipe_index,
296                                struct cli_state **cli)
297 {
298         NTSTATUS result;
299         struct winbindd_cm_conn conn;
300         
301         result = cm_open_connection( domain, pipe_index, &conn );
302         
303         if ( NT_STATUS_IS_OK(result) ) 
304                 *cli = conn.cli;
305
306         return result;
307 }
308
309 /* Return true if a connection is still alive */
310
311 static BOOL connection_ok(struct winbindd_cm_conn *conn)
312 {
313         if (!conn) {
314                 smb_panic("Invalid parameter passed to connection_ok():  conn was NULL!\n");
315                 return False;
316         }
317
318         if (!conn->cli) {
319                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
320                           conn->controller, conn->domain, conn->pipe_name));
321                 return False;
322         }
323
324         if (!conn->cli->initialised) {
325                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
326                           conn->controller, conn->domain, conn->pipe_name));
327                 return False;
328         }
329
330         if (conn->cli->fd == -1) {
331                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
332                           conn->controller, conn->domain, conn->pipe_name));
333                 return False;
334         }
335         
336         return True;
337 }
338
339 /* Search the cache for a connection. If there is a broken one,
340    shut it down properly and return NULL. */
341
342 static void find_cm_connection(const char *domain, const char *pipe_name,
343                                struct winbindd_cm_conn **conn_out) 
344 {
345         struct winbindd_cm_conn *conn;
346
347         for (conn = cm_conns; conn; ) {
348                 if (strequal(conn->domain, domain) && 
349                     strequal(conn->pipe_name, pipe_name)) {
350                         if (!connection_ok(conn)) {
351                                 /* Dead connection - remove it. */
352                                 struct winbindd_cm_conn *conn_temp = conn->next;
353                                 if (conn->cli)
354                                         cli_shutdown(conn->cli);
355                                 DLIST_REMOVE(cm_conns, conn);
356                                 SAFE_FREE(conn);
357                                 conn = conn_temp;  /* Keep the loop moving */
358                                 continue;
359                         } else {
360                                 break;
361                         }
362                 }
363                 conn = conn->next;
364         }
365
366         *conn_out = conn;
367 }
368
369 /* Initialize a new connection up to the RPC BIND. */
370
371 static NTSTATUS new_cm_connection(const char *domain, const char *pipe_name,
372                                   struct winbindd_cm_conn **conn_out)
373 {
374         struct winbindd_cm_conn *conn;
375         NTSTATUS result;
376
377         if (!(conn = malloc(sizeof(*conn))))
378                 return NT_STATUS_NO_MEMORY;
379                 
380         ZERO_STRUCTP(conn);
381                 
382         if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
383                 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
384                           domain, pipe_name, nt_errstr(result)));
385                 SAFE_FREE(conn);
386                 return result;
387         }
388         DLIST_ADD(cm_conns, conn);
389
390         *conn_out = conn;
391         return NT_STATUS_OK;
392 }
393
394 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
395
396 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
397                                           struct winbindd_cm_conn **conn_out)
398 {
399         find_cm_connection(domain, pipe_name, conn_out);
400
401         if (*conn_out != NULL)
402                 return NT_STATUS_OK;
403
404         return new_cm_connection(domain, pipe_name, conn_out);
405 }
406
407 /**********************************************************************************
408 **********************************************************************************/
409
410 BOOL cm_check_for_native_mode_win2k( const char *domain )
411 {
412         NTSTATUS                result;
413         struct winbindd_cm_conn conn;
414         DS_DOMINFO_CTR          ctr;
415         BOOL                    ret = False;
416         
417         ZERO_STRUCT( conn );
418         ZERO_STRUCT( ctr );
419         
420         
421         if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
422                 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n", 
423                           domain, nt_errstr(result)));
424                 return False;
425         }
426         
427         if ( conn.cli ) {
428                 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
429                                 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
430                         ret = False;
431                         goto done;
432                 }
433         }
434                                 
435         if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) 
436                         && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
437                 ret = True;
438
439 done:
440
441         /* close the connection;  no other cals use this pipe and it is called only
442            on reestablishing the domain list   --jerry */
443
444         if ( conn.cli )
445                 cli_shutdown( conn.cli );
446         
447         return ret;
448 }
449
450
451
452 /* Return a LSA policy handle on a domain */
453
454 NTSTATUS cm_get_lsa_handle(const char *domain, CLI_POLICY_HND **return_hnd)
455 {
456         struct winbindd_cm_conn *conn;
457         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
458         NTSTATUS result;
459         static CLI_POLICY_HND hnd;
460
461         /* Look for existing connections */
462
463         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
464                 return result;
465
466         /* This *shitty* code needs scrapping ! JRA */
467         
468         if (policy_handle_is_valid(&conn->pol)) {
469                 hnd.pol = conn->pol;
470                 hnd.cli = conn->cli;
471                 *return_hnd = &hnd;
472
473                 return NT_STATUS_OK;
474         }
475         
476         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
477                                      des_access, &conn->pol);
478
479         if (!NT_STATUS_IS_OK(result)) {
480                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
481                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
482                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
483                                 return result;
484
485                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
486                                                      des_access, &conn->pol);
487                 }
488
489                 if (!NT_STATUS_IS_OK(result)) {
490                         cli_shutdown(conn->cli);
491                         DLIST_REMOVE(cm_conns, conn);
492                         SAFE_FREE(conn);
493                         return result;
494                 }
495         }       
496
497         hnd.pol = conn->pol;
498         hnd.cli = conn->cli;
499
500         *return_hnd = &hnd;
501
502         return NT_STATUS_OK;
503 }
504
505 /* Return a SAM policy handle on a domain */
506
507 NTSTATUS cm_get_sam_handle(char *domain, CLI_POLICY_HND **return_hnd)
508
509         struct winbindd_cm_conn *conn;
510         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
511         NTSTATUS result;
512         static CLI_POLICY_HND hnd;
513
514         /* Look for existing connections */
515
516         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
517                 return result;
518         
519         /* This *shitty* code needs scrapping ! JRA */
520         
521         if (policy_handle_is_valid(&conn->pol)) {
522                 hnd.pol = conn->pol;
523                 hnd.cli = conn->cli;
524                 
525                 *return_hnd = &hnd;
526
527                 return NT_STATUS_OK;
528         }
529         
530         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
531                                   des_access, &conn->pol);
532
533         if (!NT_STATUS_IS_OK(result)) {
534                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
535                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
536                 
537                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
538                                 return result;
539
540                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
541                                                   des_access, &conn->pol);
542                 }
543
544                 if (!NT_STATUS_IS_OK(result)) {
545                 
546                         cli_shutdown(conn->cli);
547                         DLIST_REMOVE(cm_conns, conn);
548                         SAFE_FREE(conn);
549                         
550                         return result;
551                 }
552         }       
553
554         hnd.pol = conn->pol;
555         hnd.cli = conn->cli;
556
557         *return_hnd = &hnd;
558
559         return NT_STATUS_OK;
560 }
561
562 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
563    netlogon pipe as no handle is returned. */
564
565 NTSTATUS cm_get_netlogon_cli(const char *domain, 
566                              const unsigned char *trust_passwd, 
567                              uint32 sec_channel_type,
568                              BOOL fresh,
569                              struct cli_state **cli)
570 {
571         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
572         struct winbindd_cm_conn *conn;
573         fstring lock_name;
574         BOOL got_mutex;
575         struct winbindd_domain *wb_domain = NULL;
576
577         if (!cli)
578                 return NT_STATUS_INVALID_PARAMETER;
579
580         /* Open an initial conection - keep the mutex. */
581
582         find_cm_connection(domain, PIPE_NETLOGON, &conn);
583
584         if ( fresh && (conn != NULL) ) {
585                 cli_shutdown(conn->cli);
586                 conn->cli = NULL;
587
588                 conn = NULL;
589
590                 /* purge connection from cache */
591                 find_cm_connection(domain, PIPE_NETLOGON, &conn);
592                 if (conn != NULL) {
593                         DEBUG(0,("Could not purge connection\n"));
594                         return NT_STATUS_UNSUCCESSFUL;
595                 }
596         }
597
598         if (conn != NULL) {
599                 *cli = conn->cli;
600                 return NT_STATUS_OK;
601         }
602
603         result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
604
605         if (!NT_STATUS_IS_OK(result))
606                 return result;
607         
608         fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
609
610         if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
611                 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
612         }
613         
614         if ( sec_channel_type == SEC_CHAN_DOMAIN )
615                 fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
616                         
617         /* we need the short form of the domain name for the schanel
618            rpc bind.  What if we fail?  I don't think we should ever get 
619            a request for a domain name not in our list but I'm not bailing 
620            out if we do since I'm not 10% certain about this   --jerry */
621            
622         if ( (wb_domain = find_domain_from_name( domain )) != NULL ) {
623                 DEBUG(5,("cm_get_netlogon_cli: Using short for of domain name [%s] for netlogon rpc bind\n",
624                         wb_domain->name));
625                 fstrcpy( conn->cli->domain, wb_domain->name);
626         }
627         
628         result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
629         
630         if (got_mutex)
631                 secrets_named_mutex_release(lock_name);
632                                 
633         if (!NT_STATUS_IS_OK(result)) {
634                 cli_shutdown(conn->cli);
635                 DLIST_REMOVE(cm_conns, conn);
636                 SAFE_FREE(conn);
637                 return result;
638         }
639
640         *cli = conn->cli;
641
642         return result;
643 }
644
645 /* Dump the current connection status */
646
647 static void dump_conn_list(void)
648 {
649         struct winbindd_cm_conn *con;
650
651         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
652
653         for(con = cm_conns; con; con = con->next) {
654                 char *msg;
655
656                 /* Display pipe info */
657                 
658                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
659                         DEBUG(0, ("Error: not enough memory!\n"));
660                 } else {
661                         DEBUG(0, ("%s\n", msg));
662                         SAFE_FREE(msg);
663                 }
664         }
665 }
666
667 void winbindd_cm_status(void)
668 {
669         /* List open connections */
670
671         DEBUG(0, ("winbindd connection manager status:\n"));
672
673         if (cm_conns)
674                 dump_conn_list();
675         else
676                 DEBUG(0, ("\tNo active connections\n"));
677 }
678
679 /* Close all cached connections */
680
681 void winbindd_cm_flush(void)
682 {
683         struct winbindd_cm_conn *conn, tmp;
684
685         /* Flush connection cache */
686
687         for (conn = cm_conns; conn; conn = conn->next) {
688
689                 if (!connection_ok(conn))
690                         continue;
691
692                 DEBUG(10, ("Closing connection to %s on %s\n",
693                         conn->pipe_name, conn->controller));
694
695                 if (conn->cli)
696                         cli_shutdown(conn->cli);
697
698                 tmp.next = conn->next;
699
700                 DLIST_REMOVE(cm_conns, conn);
701                 SAFE_FREE(conn);
702                 conn = &tmp;
703         }
704
705         /* Flush failed connection cache */
706
707         flush_negative_conn_cache();
708 }