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