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