r115: finally checking in tridge's winbindd_schannel patch for connections
[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 "includes.h"
60 #include "winbindd.h"
61
62 #undef DBGC_CLASS
63 #define DBGC_CLASS DBGC_WINBIND
64
65 /* Global list of connections.  Initially a DLIST but can become a hash
66    table or whatever later. */
67
68 struct winbindd_cm_conn {
69         struct winbindd_cm_conn *prev, *next;
70         fstring domain;
71         fstring controller;
72         fstring pipe_name;
73         size_t mutex_ref_count;
74         struct cli_state *cli;
75         POLICY_HND pol;
76 };
77
78 static struct winbindd_cm_conn *cm_conns = NULL;
79
80
81 /* Choose between anonymous or authenticated connections.  We need to use
82    an authenticated connection if DCs have the RestrictAnonymous registry
83    entry set > 0, or the "Additional restrictions for anonymous
84    connections" set in the win2k Local Security Policy. 
85    
86    Caller to free() result in domain, username, password
87 */
88
89 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
90 {
91         *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
92         *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
93         *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
94         
95         if (*username && **username) {
96
97                 if (!*domain || !**domain)
98                         *domain = smb_xstrdup(lp_workgroup());
99                 
100                 if (!*password || !**password)
101                         *password = smb_xstrdup("");
102
103                 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", 
104                           *domain, *username));
105
106         } else {
107                 DEBUG(3, ("IPC$ connections done anonymously\n"));
108                 *username = smb_xstrdup("");
109                 *domain = smb_xstrdup("");
110                 *password = smb_xstrdup("");
111         }
112 }
113
114 /*
115   setup for schannel on any pipes opened on this connection
116 */
117 static NTSTATUS setup_schannel(struct cli_state *cli)
118 {
119         NTSTATUS ret;
120         uchar trust_password[16];
121         uint32 sec_channel_type;
122
123         if (!secrets_fetch_trust_account_password(lp_workgroup(),
124                                                   trust_password,
125                                                   NULL, &sec_channel_type)) {
126                 return NT_STATUS_UNSUCCESSFUL;
127         }
128
129         ret = cli_nt_setup_netsec(cli, sec_channel_type, 
130                                   AUTH_PIPE_NETSEC | AUTH_PIPE_SIGN, 
131                                   trust_password);
132
133         return ret;
134 }
135
136 /* Open a connction to the remote server, cache failures for 30 seconds */
137
138 static NTSTATUS cm_open_connection(const struct winbindd_domain *domain, const int pipe_index,
139                                    struct winbindd_cm_conn *new_conn)
140 {
141         NTSTATUS result;
142         char *machine_password; 
143         char *machine_krb5_principal, *ipc_username, *ipc_domain, *ipc_password;
144         struct in_addr dc_ip;
145         int i;
146         BOOL retry = True;
147
148         ZERO_STRUCT(dc_ip);
149
150         fstrcpy(new_conn->domain, domain->name);
151         
152         /* connection failure cache has been moved inside of get_dc_name
153            so we can deal with half dead DC's   --jerry */
154
155         if (!get_dc_name(domain->name, domain->alt_name[0] ? domain->alt_name : NULL, 
156                          new_conn->controller, &dc_ip)) {
157                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
158                 add_failed_connection_entry(domain->name, "", result);
159                 return result;
160         }
161                 
162         /* Initialise SMB connection */
163         fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
164
165 /* grab stored passwords */
166         machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
167         
168         if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), lp_realm()) == -1) {
169                 SAFE_FREE(machine_password);
170                 return NT_STATUS_NO_MEMORY;
171         }
172
173         cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
174
175         for (i = 0; retry && (i < 3); i++) {
176                 BOOL got_mutex;
177                 if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
178                         DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
179                         result = NT_STATUS_POSSIBLE_DEADLOCK;
180                         continue;
181                 }
182                 
183                 new_conn->cli = NULL;
184                 result = cli_start_connection(&new_conn->cli, global_myname(), 
185                                               new_conn->controller, 
186                                               &dc_ip, 0, Undefined, 
187                                               CLI_FULL_CONNECTION_USE_KERBEROS, 
188                                               &retry);
189
190                 if (NT_STATUS_IS_OK(result)) {
191
192                         /* reset the error code */
193                         result = NT_STATUS_UNSUCCESSFUL; 
194
195                         /* Krb5 session */
196                         
197                         if ((lp_security() == SEC_ADS) 
198                                 && (new_conn->cli->protocol >= PROTOCOL_NT1 && new_conn->cli->capabilities & CAP_EXTENDED_SECURITY)) {
199                                 ADS_STATUS ads_status;
200                                 new_conn->cli->use_kerberos = True;
201                                 DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n", 
202                                           new_conn->controller, global_myname(), machine_krb5_principal));
203
204                                 ads_status = cli_session_setup_spnego(new_conn->cli, machine_krb5_principal, 
205                                                                       machine_password, 
206                                                                       lp_workgroup());
207                                 if (!ADS_ERR_OK(ads_status)) {
208                                         DEBUG(4,("failed kerberos session setup with %s\n", ads_errstr(ads_status)));
209                                         result = ads_ntstatus(ads_status);
210                                 } else {
211                                         result = NT_STATUS_OK;
212                                 }
213                         }
214                         new_conn->cli->use_kerberos = False;
215                         
216                         /* only do this is we have a username/password for thr IPC$ connection */
217                         
218                         if ( !NT_STATUS_IS_OK(result) 
219                                 && new_conn->cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE
220                                 && strlen(ipc_username) )
221                         {       
222                                 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
223                                           new_conn->controller, global_myname(), ipc_domain, ipc_username));
224
225                                 result = NT_STATUS_OK;
226
227                                 if (!cli_session_setup(new_conn->cli, ipc_username, 
228                                                        ipc_password, strlen(ipc_password)+1, 
229                                                        ipc_password, strlen(ipc_password)+1, 
230                                                        ipc_domain)) {
231                                         result = cli_nt_error(new_conn->cli);
232                                         DEBUG(4,("failed authenticated session setup with %s\n", nt_errstr(result)));
233                                         if (NT_STATUS_IS_OK(result)) 
234                                                 result = NT_STATUS_UNSUCCESSFUL;
235                                 }
236                         }
237                         
238                         /* anonymous is all that is left if we get to here */
239                         
240                         if (!NT_STATUS_IS_OK(result)) { 
241                         
242                                 DEBUG(5, ("anonymous connection attempt to %s from %s\n", 
243                                           new_conn->controller, global_myname()));
244                                           
245                                 result = NT_STATUS_OK;
246
247                                 if (!cli_session_setup(new_conn->cli, "", NULL, 0, NULL, 0, "")) 
248                                 {
249                                         result = cli_nt_error(new_conn->cli);
250                                         DEBUG(4,("failed anonymous session setup with %s\n", nt_errstr(result)));
251                                         if (NT_STATUS_IS_OK(result)) 
252                                                 result = NT_STATUS_UNSUCCESSFUL;
253                                 } 
254                                 
255                         }
256
257                         if (NT_STATUS_IS_OK(result) && !cli_send_tconX(new_conn->cli, "IPC$", "IPC",
258                                                                        "", 0)) {
259                                 result = cli_nt_error(new_conn->cli);
260                                 DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
261                                 cli_shutdown(new_conn->cli);
262                                 if (NT_STATUS_IS_OK(result)) {
263                                         result = NT_STATUS_UNSUCCESSFUL;
264                                 }
265                         }
266                 }
267
268                 if (NT_STATUS_IS_OK(result)) {
269                         struct ntuser_creds creds;
270                         init_creds(&creds, ipc_username, ipc_domain, ipc_password);
271                         cli_init_creds(new_conn->cli, &creds);
272                 }
273
274                 if (got_mutex)
275                         secrets_named_mutex_release(new_conn->controller);
276
277                 if (NT_STATUS_IS_OK(result))
278                         break;
279         }
280
281         /* try and use schannel if possible, but continue anyway if it
282            failed. This allows existing setups to continue working,
283            while solving the win2003 '100 user' limit for systems that
284            are joined properly */
285         if (NT_STATUS_IS_OK(result)) {
286                 NTSTATUS status = setup_schannel(new_conn->cli);
287                 if (!NT_STATUS_IS_OK(status)) {
288                         DEBUG(3,("schannel refused - continuing without schannel (%s)\n", 
289                                  nt_errstr(status)));
290                 }
291         }
292
293         SAFE_FREE(ipc_username);
294         SAFE_FREE(ipc_domain);
295         SAFE_FREE(ipc_password);
296         SAFE_FREE(machine_password);
297
298         if (!NT_STATUS_IS_OK(result)) {
299                 add_failed_connection_entry(domain->name, new_conn->controller, result);
300                 return result;
301         }
302         
303         /* set the domain if empty; needed for schannel connections */
304         if ( !*new_conn->cli->domain )
305                 fstrcpy( new_conn->cli->domain, domain->name );
306                 
307         
308         if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
309                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
310                 /* 
311                  * only cache a failure if we are not trying to open the 
312                  * **win2k** specific lsarpc UUID.  This could be an NT PDC 
313                  * and therefore a failure is normal.  This should probably
314                  * be abstracted to a check for 2k specific pipes and wondering
315                  * if the PDC is an NT4 box.   but since there is only one 2k 
316                  * specific UUID right now, i'm not going to bother.  --jerry
317                  */
318                 if ( !is_win2k_pipe(pipe_index) )
319                         add_failed_connection_entry(domain->name, new_conn->controller, result);
320                 cli_shutdown(new_conn->cli);
321                 return result;
322         }
323
324         return NT_STATUS_OK;
325 }
326
327 /************************************************************************
328  Wrapper around statuc cm_open_connection to retreive a freshly
329  setup cli_state struct
330 ************************************************************************/
331
332 NTSTATUS cm_fresh_connection(struct winbindd_domain *domain, const int pipe_index,
333                                struct cli_state **cli)
334 {
335         NTSTATUS result;
336         struct winbindd_cm_conn conn;
337         
338         result = cm_open_connection( domain, pipe_index, &conn );
339         
340         if ( NT_STATUS_IS_OK(result) ) 
341                 *cli = conn.cli;
342
343         return result;
344 }
345
346 /* Return true if a connection is still alive */
347
348 static BOOL connection_ok(struct winbindd_cm_conn *conn)
349 {
350         if (!conn) {
351                 smb_panic("Invalid parameter passed to connection_ok():  conn was NULL!\n");
352                 return False;
353         }
354
355         if (!conn->cli) {
356                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
357                           conn->controller, conn->domain, conn->pipe_name));
358                 return False;
359         }
360
361         if (!conn->cli->initialised) {
362                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
363                           conn->controller, conn->domain, conn->pipe_name));
364                 return False;
365         }
366
367         if (conn->cli->fd == -1) {
368                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
369                           conn->controller, conn->domain, conn->pipe_name));
370                 return False;
371         }
372         
373         return True;
374 }
375
376 /* Search the cache for a connection. If there is a broken one,
377    shut it down properly and return NULL. */
378
379 static void find_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
380                                struct winbindd_cm_conn **conn_out) 
381 {
382         struct winbindd_cm_conn *conn;
383
384         for (conn = cm_conns; conn; ) {
385                 if (strequal(conn->domain, domain->name) && 
386                     strequal(conn->pipe_name, pipe_name)) {
387                         if (!connection_ok(conn)) {
388                                 /* Dead connection - remove it. */
389                                 struct winbindd_cm_conn *conn_temp = conn->next;
390                                 if (conn->cli)
391                                         cli_shutdown(conn->cli);
392                                 DLIST_REMOVE(cm_conns, conn);
393                                 SAFE_FREE(conn);
394                                 conn = conn_temp;  /* Keep the loop moving */
395                                 continue;
396                         } else {
397                                 break;
398                         }
399                 }
400                 conn = conn->next;
401         }
402
403         *conn_out = conn;
404 }
405
406 /* Initialize a new connection up to the RPC BIND. */
407
408 static NTSTATUS new_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
409                                   struct winbindd_cm_conn **conn_out)
410 {
411         struct winbindd_cm_conn *conn;
412         NTSTATUS result;
413
414         if (!(conn = malloc(sizeof(*conn))))
415                 return NT_STATUS_NO_MEMORY;
416                 
417         ZERO_STRUCTP(conn);
418                 
419         if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
420                 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
421                           domain->name, pipe_name, nt_errstr(result)));
422                 SAFE_FREE(conn);
423                 return result;
424         }
425         DLIST_ADD(cm_conns, conn);
426
427         *conn_out = conn;
428         return NT_STATUS_OK;
429 }
430
431 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
432
433 static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain, const char *pipe_name,
434                                           struct winbindd_cm_conn **conn_out)
435 {
436         find_cm_connection(domain, pipe_name, conn_out);
437
438         if (*conn_out != NULL)
439                 return NT_STATUS_OK;
440
441         return new_cm_connection(domain, pipe_name, conn_out);
442 }
443
444 /**********************************************************************************
445  We can 'sense' certain things about the DC by it's replies to certain questions.
446
447  This tells us if this particular remote server is Active Directory, and if it is
448  native mode.
449 **********************************************************************************/
450
451 void set_dc_type_and_flags( struct winbindd_domain *domain )
452 {
453         NTSTATUS                result;
454         struct winbindd_cm_conn conn;
455         DS_DOMINFO_CTR          ctr;
456         TALLOC_CTX              *mem_ctx = NULL;
457         
458         ZERO_STRUCT( conn );
459         ZERO_STRUCT( ctr );
460         
461         domain->native_mode = False;
462         domain->active_directory = False;
463         
464         if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
465                 DEBUG(5, ("set_dc_type_and_flags: Could not open a connection to %s for PIPE_LSARPC (%s)\n", 
466                           domain->name, nt_errstr(result)));
467                 return;
468         }
469         
470         if ( conn.cli ) {
471                 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
472                                 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
473                         goto done;
474                 }
475         }
476                                 
477         if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) 
478                         && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
479                 domain->native_mode = True;
480
481         /* Cheat - shut down the DS pipe, and open LSA */
482
483         cli_nt_session_close(conn.cli);
484
485         if ( cli_nt_session_open (conn.cli, PI_LSARPC) ) {
486                 char *domain_name = NULL;
487                 char *dns_name = NULL;
488                 DOM_SID *dom_sid = NULL;
489
490                 mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", domain->name);
491                 if (!mem_ctx) {
492                         DEBUG(1, ("set_dc_type_and_flags: talloc_init() failed\n"));
493                         return;
494                 }
495
496                 result = cli_lsa_open_policy2(conn.cli, mem_ctx, True, 
497                                               SEC_RIGHTS_MAXIMUM_ALLOWED,
498                                               &conn.pol);
499                 
500                 if (NT_STATUS_IS_OK(result)) {
501                         /* This particular query is exactly what Win2k clients use 
502                            to determine that the DC is active directory */
503                         result = cli_lsa_query_info_policy2(conn.cli, mem_ctx, 
504                                                             &conn.pol,
505                                                             12, &domain_name,
506                                                             &dns_name, NULL,
507                                                             NULL, &dom_sid);
508                 }
509
510                 if (NT_STATUS_IS_OK(result)) {
511                         if (domain_name)
512                                 fstrcpy(domain->name, domain_name);
513                         
514                         if (dns_name)
515                                 fstrcpy(domain->alt_name, dns_name);
516
517                         if (dom_sid) 
518                                 sid_copy(&domain->sid, dom_sid);
519
520                         domain->active_directory = True;
521                 } else {
522                         
523                         result = cli_lsa_open_policy(conn.cli, mem_ctx, True, 
524                                                      SEC_RIGHTS_MAXIMUM_ALLOWED,
525                                                      &conn.pol);
526                         
527                         if (!NT_STATUS_IS_OK(result))
528                                 goto done;
529                         
530                         result = cli_lsa_query_info_policy(conn.cli, mem_ctx, 
531                                                            &conn.pol, 5, &domain_name, 
532                                                            &dom_sid);
533                         
534                         if (NT_STATUS_IS_OK(result)) {
535                                 if (domain_name)
536                                         fstrcpy(domain->name, domain_name);
537                                 
538                                 if (dom_sid) 
539                                         sid_copy(&domain->sid, dom_sid);
540                         }
541                 }
542         }
543         
544 done:
545         
546         /* close the connection;  no other calls use this pipe and it is called only
547            on reestablishing the domain list   --jerry */
548         
549         if ( conn.cli )
550                 cli_shutdown( conn.cli );
551         
552         talloc_destroy(mem_ctx);
553         
554         return;
555 }
556
557
558
559 /* Return a LSA policy handle on a domain */
560
561 NTSTATUS cm_get_lsa_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
562 {
563         struct winbindd_cm_conn *conn;
564         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
565         NTSTATUS result;
566         static CLI_POLICY_HND hnd;
567
568         /* Look for existing connections */
569
570         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
571                 return result;
572
573         /* This *shitty* code needs scrapping ! JRA */
574         
575         if (policy_handle_is_valid(&conn->pol)) {
576                 hnd.pol = conn->pol;
577                 hnd.cli = conn->cli;
578                 *return_hnd = &hnd;
579
580                 return NT_STATUS_OK;
581         }
582         
583         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
584                                      des_access, &conn->pol);
585
586         if (!NT_STATUS_IS_OK(result)) {
587                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
588                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
589                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
590                                 return result;
591
592                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
593                                                      des_access, &conn->pol);
594                 }
595
596                 if (!NT_STATUS_IS_OK(result)) {
597                         cli_shutdown(conn->cli);
598                         DLIST_REMOVE(cm_conns, conn);
599                         SAFE_FREE(conn);
600                         return result;
601                 }
602         }       
603
604         hnd.pol = conn->pol;
605         hnd.cli = conn->cli;
606
607         *return_hnd = &hnd;
608
609         return NT_STATUS_OK;
610 }
611
612 /* Return a SAM policy handle on a domain */
613
614 NTSTATUS cm_get_sam_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
615
616         struct winbindd_cm_conn *conn;
617         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
618         NTSTATUS result;
619         static CLI_POLICY_HND hnd;
620
621         /* Look for existing connections */
622
623         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
624                 return result;
625         
626         /* This *shitty* code needs scrapping ! JRA */
627         
628         if (policy_handle_is_valid(&conn->pol)) {
629                 hnd.pol = conn->pol;
630                 hnd.cli = conn->cli;
631                 
632                 *return_hnd = &hnd;
633
634                 return NT_STATUS_OK;
635         }
636         
637         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
638                                   des_access, &conn->pol);
639
640         if (!NT_STATUS_IS_OK(result)) {
641                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
642                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
643                 
644                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
645                                 return result;
646
647                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
648                                                   des_access, &conn->pol);
649                 }
650
651                 if (!NT_STATUS_IS_OK(result)) {
652                 
653                         cli_shutdown(conn->cli);
654                         DLIST_REMOVE(cm_conns, conn);
655                         SAFE_FREE(conn);
656                         
657                         return result;
658                 }
659         }       
660
661         hnd.pol = conn->pol;
662         hnd.cli = conn->cli;
663
664         *return_hnd = &hnd;
665
666         return NT_STATUS_OK;
667 }
668
669 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
670    netlogon pipe as no handle is returned. */
671
672 NTSTATUS cm_get_netlogon_cli(struct winbindd_domain *domain, 
673                              const unsigned char *trust_passwd, 
674                              uint32 sec_channel_type,
675                              BOOL fresh,
676                              struct cli_state **cli)
677 {
678         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
679         struct winbindd_cm_conn *conn;
680         fstring lock_name;
681         BOOL got_mutex;
682
683         if (!cli)
684                 return NT_STATUS_INVALID_PARAMETER;
685
686         /* Open an initial conection - keep the mutex. */
687
688         find_cm_connection(domain, PIPE_NETLOGON, &conn);
689
690         if ( fresh && (conn != NULL) ) {
691                 cli_shutdown(conn->cli);
692                 conn->cli = NULL;
693
694                 conn = NULL;
695
696                 /* purge connection from cache */
697                 find_cm_connection(domain, PIPE_NETLOGON, &conn);
698                 if (conn != NULL) {
699                         DEBUG(0,("Could not purge connection\n"));
700                         return NT_STATUS_UNSUCCESSFUL;
701                 }
702         }
703
704         if (conn != NULL) {
705                 *cli = conn->cli;
706                 return NT_STATUS_OK;
707         }
708
709         result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
710
711         if (!NT_STATUS_IS_OK(result))
712                 return result;
713         
714         fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
715
716         if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
717                 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
718         }
719         
720         if ( sec_channel_type == SEC_CHAN_DOMAIN )
721                 fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
722                         
723         /* This must be the remote domain (not ours) for schannel */
724
725         fstrcpy( conn->cli->domain, domain->name);
726         
727         result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
728         
729         if (got_mutex)
730                 secrets_named_mutex_release(lock_name);
731                                 
732         if (!NT_STATUS_IS_OK(result)) {
733                 cli_shutdown(conn->cli);
734                 DLIST_REMOVE(cm_conns, conn);
735                 SAFE_FREE(conn);
736                 return result;
737         }
738
739         *cli = conn->cli;
740
741         return result;
742 }
743
744 /* Dump the current connection status */
745
746 static void dump_conn_list(void)
747 {
748         struct winbindd_cm_conn *con;
749
750         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
751
752         for(con = cm_conns; con; con = con->next) {
753                 char *msg;
754
755                 /* Display pipe info */
756                 
757                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
758                         DEBUG(0, ("Error: not enough memory!\n"));
759                 } else {
760                         DEBUG(0, ("%s\n", msg));
761                         SAFE_FREE(msg);
762                 }
763         }
764 }
765
766 void winbindd_cm_status(void)
767 {
768         /* List open connections */
769
770         DEBUG(0, ("winbindd connection manager status:\n"));
771
772         if (cm_conns)
773                 dump_conn_list();
774         else
775                 DEBUG(0, ("\tNo active connections\n"));
776 }
777
778 /* Close all cached connections */
779
780 void winbindd_cm_flush(void)
781 {
782         struct winbindd_cm_conn *conn, tmp;
783
784         /* Flush connection cache */
785
786         for (conn = cm_conns; conn; conn = conn->next) {
787
788                 if (!connection_ok(conn))
789                         continue;
790
791                 DEBUG(10, ("Closing connection to %s on %s\n",
792                         conn->pipe_name, conn->controller));
793
794                 if (conn->cli)
795                         cli_shutdown(conn->cli);
796
797                 tmp.next = conn->next;
798
799                 DLIST_REMOVE(cm_conns, conn);
800                 SAFE_FREE(conn);
801                 conn = &tmp;
802         }
803
804         /* Flush failed connection cache */
805
806         flush_negative_conn_cache();
807 }