Allow us to see the difference between these two errors. (We need to chase
[abartlet/samba.git/.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    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 /*
24    We need to manage connections to domain controllers without having to
25    mess up the main winbindd code with other issues.  The aim of the
26    connection manager is to:
27   
28        - make connections to domain controllers and cache them
29        - re-establish connections when networks or servers go down
30        - centralise the policy on connection timeouts, domain controller
31          selection etc
32        - manage re-entrancy for when winbindd becomes able to handle
33          multiple outstanding rpc requests
34   
35    Why not have connection management as part of the rpc layer like tng?
36    Good question.  This code may morph into libsmb/rpc_cache.c or something
37    like that but at the moment it's simply staying as part of winbind.  I
38    think the TNG architecture of forcing every user of the rpc layer to use
39    the connection caching system is a bad idea.  It should be an optional
40    method of using the routines.
41
42    The TNG design is quite good but I disagree with some aspects of the
43    implementation. -tpot
44
45  */
46
47 /*
48    TODO:
49
50      - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
51        moved down into another function.
52
53      - There needs to be a utility function in libsmb/namequery.c that does
54        cm_get_dc_name() 
55
56      - Take care when destroying cli_structs as they can be shared between
57        various sam handles.
58
59  */
60
61 #include "winbindd.h"
62
63 /* Global list of connections.  Initially a DLIST but can become a hash
64    table or whatever later. */
65
66 struct winbindd_cm_conn {
67         struct winbindd_cm_conn *prev, *next;
68         fstring domain;
69         fstring controller;
70         fstring pipe_name;
71         struct cli_state *cli;
72         POLICY_HND pol;
73 };
74
75 static struct winbindd_cm_conn *cm_conns = NULL;
76
77 /* Get a domain controller name.  Cache positive and negative lookups so we
78    don't go to the network too often when something is badly broken. */
79
80 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
81
82 struct get_dc_name_cache {
83         fstring domain_name;
84         fstring srv_name;
85         time_t lookup_time;
86         struct get_dc_name_cache *prev, *next;
87 };
88
89 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
90 {
91         static struct get_dc_name_cache *get_dc_name_cache;
92         struct get_dc_name_cache *dcc;
93         struct in_addr *ip_list, dc_ip;
94         int count, i;
95
96         /* Check the cache for previous lookups */
97
98         for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
99
100                 if (!strequal(domain, dcc->domain_name))
101                         continue; /* Not our domain */
102
103                 if ((time(NULL) - dcc->lookup_time) > 
104                     GET_DC_NAME_CACHE_TIMEOUT) {
105
106                         /* Cache entry has expired, delete it */
107
108                         DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
109
110                         DLIST_REMOVE(get_dc_name_cache, dcc);
111                         SAFE_FREE(dcc);
112
113                         break;
114                 }
115
116                 /* Return a positive or negative lookup for this domain */
117
118                 if (dcc->srv_name[0]) {
119                         DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
120                         fstrcpy(srv_name, dcc->srv_name);
121                         return True;
122                 } else {
123                         DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
124                         return False;
125                 }
126         }
127
128         /* Add cache entry for this lookup. */
129
130         DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
131
132         if (!(dcc = (struct get_dc_name_cache *) 
133               malloc(sizeof(struct get_dc_name_cache))))
134                 return False;
135
136         ZERO_STRUCTP(dcc);
137
138         fstrcpy(dcc->domain_name, domain);
139         dcc->lookup_time = time(NULL);
140
141         DLIST_ADD(get_dc_name_cache, dcc);
142
143         /* Lookup domain controller name. Try the real PDC first to avoid
144            SAM sync delays */
145         if (!get_dc_list(True, domain, &ip_list, &count)) {
146                 if (!get_dc_list(False, domain, &ip_list, &count)) {
147                         DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
148                         return False;
149                 }
150         }
151
152         /* Pick a nice close server */
153         /* Look for DC on local net */
154
155         for (i = 0; i < count; i++) {
156                 if (!is_local_net(ip_list[i]))
157                         continue;
158                 
159                 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
160                         dc_ip = ip_list[i];
161                         goto done;
162                 }
163                 zero_ip(&ip_list[i]);
164         }
165
166         /*
167          * Secondly try and contact a random PDC/BDC.
168          */
169
170         i = (sys_random() % count);
171
172         if (!is_zero_ip(ip_list[i]) &&
173             name_status_find(domain, 0x1c, 0x20,
174                              ip_list[i], srv_name)) {
175                 dc_ip = ip_list[i];
176                 goto done;
177         }
178         zero_ip(&ip_list[i]); /* Tried and failed. */
179
180         /* Finally return first DC that we can contact */
181
182         for (i = 0; i < count; i++) {
183                 if (is_zero_ip(ip_list[i]))
184                         continue;
185
186                 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
187                         dc_ip = ip_list[i];
188                         goto done;
189                 }
190         }
191
192         /* No-one to talk to )-: */
193         return False;           /* Boo-hoo */
194         
195  done:
196         /* We have the netbios name and IP address of a domain controller.
197            Ideally we should sent a SAMLOGON request to determine whether
198            the DC is alive and kicking.  If we can catch a dead DC before
199            performing a cli_connect() we can avoid a 30-second timeout. */
200
201         /* We have a name so make the cache entry positive now */
202
203         fstrcpy(dcc->srv_name, srv_name);
204
205         DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
206                   inet_ntoa(dc_ip), domain));
207
208         *ip_out = dc_ip;
209
210         SAFE_FREE(ip_list);
211
212         return True;
213 }
214
215 /* Choose between anonymous or authenticated connections.  We need to use
216    an authenticated connection if DCs have the RestrictAnonymous registry
217    entry set > 0, or the "Additional restrictions for anonymous
218    connections" set in the win2k Local Security Policy. 
219    
220    Caller to free() result in domain, username, password
221 */
222
223 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
224 {
225         *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
226         *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
227         *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
228         
229         if (*username && **username) {
230                 if (!*domain || !**domain) {
231                         *domain = smb_xstrdup(lp_workgroup());
232                 }
233                 
234                 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
235         } else {
236                 DEBUG(3, ("IPC$ connections done anonymously\n"));
237                 *username = smb_xstrdup("");
238                 *domain = smb_xstrdup("");
239                 *password = smb_xstrdup("");
240         }
241 }
242
243 /* Open a new smb pipe connection to a DC on a given domain.  Cache
244    negative creation attempts so we don't try and connect to broken
245    machines too often. */
246
247 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
248
249 struct failed_connection_cache {
250         fstring domain_name;
251         fstring controller;
252         time_t lookup_time;
253         NTSTATUS nt_status;
254         struct failed_connection_cache *prev, *next;
255 };
256
257 static struct failed_connection_cache *failed_connection_cache;
258
259 /* Add an entry to the failed conneciton cache */
260
261 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, NTSTATUS result) {
262         struct failed_connection_cache *fcc;
263
264         SMB_ASSERT(!NT_STATUS_IS_OK(result));
265
266         /* Create negative lookup cache entry for this domain and controller */
267
268         if (!(fcc = (struct failed_connection_cache *)
269               malloc(sizeof(struct failed_connection_cache)))) {
270                 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
271                 return;
272         }
273         
274         ZERO_STRUCTP(fcc);
275         
276         fstrcpy(fcc->domain_name, new_conn->domain);
277         fstrcpy(fcc->controller, new_conn->controller);
278         fcc->lookup_time = time(NULL);
279         fcc->nt_status = result;
280         
281         DLIST_ADD(failed_connection_cache, fcc);
282 }
283         
284 /* Open a connction to the remote server, cache failures for 30 seconds */
285
286 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
287                                struct winbindd_cm_conn *new_conn)
288 {
289         struct failed_connection_cache *fcc;
290         extern pstring global_myname;
291         NTSTATUS result;
292         char *ipc_username, *ipc_domain, *ipc_password;
293         struct in_addr dc_ip;
294
295         ZERO_STRUCT(dc_ip);
296
297         fstrcpy(new_conn->domain, domain);
298         fstrcpy(new_conn->pipe_name, pipe_name);
299         
300         /* Look for a domain controller for this domain.  Negative results
301            are cached so don't bother applying the caching for this
302            function just yet.  */
303
304         if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
305                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
306                 add_failed_connection_entry(new_conn, result);
307                 return result;
308         }
309                 
310         /* Return false if we have tried to look up this domain and netbios
311            name before and failed. */
312
313         for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
314                 
315                 if (!(strequal(domain, fcc->domain_name) &&
316                       strequal(new_conn->controller, fcc->controller)))
317                         continue; /* Not our domain */
318
319                 if ((time(NULL) - fcc->lookup_time) > 
320                     FAILED_CONNECTION_CACHE_TIMEOUT) {
321
322                         /* Cache entry has expired, delete it */
323
324                         DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
325
326                         DLIST_REMOVE(failed_connection_cache, fcc);
327                         free(fcc);
328
329                         break;
330                 }
331
332                 /* The timeout hasn't expired yet so return false */
333
334                 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
335
336                 result = fcc->nt_status;
337                 SMB_ASSERT(!NT_STATUS_IS_OK(result));
338                 return result;
339         }
340
341         /* Initialise SMB connection */
342
343         cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
344
345         DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
346               new_conn->controller, global_myname, ipc_domain, ipc_username));
347
348         result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller, 
349                                      &dc_ip, 0, "IPC$", 
350                                      "IPC", ipc_username, ipc_domain, 
351                                      ipc_password, strlen(ipc_password));
352
353         SAFE_FREE(ipc_username);
354         SAFE_FREE(ipc_domain);
355         SAFE_FREE(ipc_password);
356
357         if (!NT_STATUS_IS_OK(result)) {
358                 add_failed_connection_entry(new_conn, result);
359                 return result;
360         }
361         
362         if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
363                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
364                 add_failed_connection_entry(new_conn, result);
365                 cli_shutdown(new_conn->cli);
366                 return result;
367         }
368
369         return NT_STATUS_OK;
370 }
371
372 /* Return true if a connection is still alive */
373
374 static BOOL connection_ok(struct winbindd_cm_conn *conn)
375 {
376         if (!conn) {
377                 smb_panic("Invalid paramater passed to conneciton_ok():  conn was NULL!\n");
378                 return False;
379         }
380
381         if (!conn->cli) {
382                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
383                           conn->controller, conn->domain, conn->pipe_name));
384                 return False;
385         }
386
387         if (!conn->cli->initialised) {
388                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
389                           conn->controller, conn->domain, conn->pipe_name));
390                 return False;
391         }
392
393         if (conn->cli->fd == -1) {
394                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
395                           conn->controller, conn->domain, conn->pipe_name));
396                 return False;
397         }
398         
399         return True;
400 }
401
402 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
403
404 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out) 
405 {
406         struct winbindd_cm_conn *conn, conn_temp;
407         NTSTATUS result;
408
409         for (conn = cm_conns; conn; conn = conn->next) {
410                 if (strequal(conn->domain, domain) && 
411                     strequal(conn->pipe_name, pipe_name)) {
412                         if (!connection_ok(conn)) {
413                                 if (conn->cli) {
414                                         cli_shutdown(conn->cli);
415                                 }
416                                 conn_temp.next = conn->next;
417                                 DLIST_REMOVE(cm_conns, conn);
418                                 SAFE_FREE(conn);
419                                 conn = &conn_temp;  /* Just to keep the loop moving */
420                         } else {
421                                 break;
422                         }
423                 }
424         }
425         
426         if (!conn) {
427                 if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
428                         return NT_STATUS_NO_MEMORY;
429                 
430                 ZERO_STRUCTP(conn);
431                 
432                 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
433                         DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
434                                   domain, pipe_name, nt_errstr(result)));
435                         SAFE_FREE(conn);
436                         return result;
437                 }
438                 DLIST_ADD(cm_conns, conn);              
439         }
440         
441         *conn_out = conn;
442         return NT_STATUS_OK;
443 }
444
445 /* Return a LSA policy handle on a domain */
446
447 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
448 {
449         struct winbindd_cm_conn *conn;
450         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
451         NTSTATUS result;
452         static CLI_POLICY_HND hnd;
453
454         /* Look for existing connections */
455
456         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
457                 return NULL;
458         }
459         
460         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
461                                      des_access, &conn->pol);
462
463         if (!NT_STATUS_IS_OK(result)) {
464                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
465                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
466                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
467                                 return NULL;
468                         }
469
470                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
471                                                      des_access, &conn->pol);
472                 }
473
474                 if (!NT_STATUS_IS_OK(result)) {
475                         cli_shutdown(conn->cli);
476                         DLIST_REMOVE(cm_conns, conn);
477                         SAFE_FREE(conn);
478                         return NULL;
479                 }
480         }       
481
482         hnd.pol = conn->pol;
483         hnd.cli = conn->cli;
484
485         return &hnd;
486 }
487
488 /* Return a SAM policy handle on a domain */
489
490 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
491
492         struct winbindd_cm_conn *conn;
493         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
494         NTSTATUS result;
495         static CLI_POLICY_HND hnd;
496
497         /* Look for existing connections */
498
499         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
500                 return NULL;
501         }
502         
503         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
504                                   des_access, &conn->pol);
505
506         if (!NT_STATUS_IS_OK(result)) {
507                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
508                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
509                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
510                                 return NULL;
511                         }
512
513                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
514                                                   des_access, &conn->pol);
515                 }
516
517                 if (!NT_STATUS_IS_OK(result)) {
518                         cli_shutdown(conn->cli);
519                         DLIST_REMOVE(cm_conns, conn);
520                         SAFE_FREE(conn);
521                         return NULL;
522                 }
523         }       
524
525         hnd.pol = conn->pol;
526         hnd.cli = conn->cli;
527
528         return &hnd;
529 }
530
531 #if 0  /* This code now *well* out of date */
532
533 /* Return a SAM domain policy handle on a domain */
534
535 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
536 {
537         struct winbindd_cm_conn *conn, *basic_conn = NULL;
538         static CLI_POLICY_HND hnd;
539         NTSTATUS result;
540         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
541
542         /* Look for existing connections */
543
544         for (conn = cm_conns; conn; conn = conn->next) {
545                 if (strequal(conn->domain, domain) &&
546                     strequal(conn->pipe_name, PIPE_SAMR) &&
547                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
548
549                         if (!connection_ok(conn)) {
550                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
551                                 DLIST_REMOVE(cm_conns, conn);
552                                 return NULL;
553                         }
554
555                         goto ok;
556                 }
557         }
558
559         /* Create a basic handle to open a domain handle from */
560
561         if (!cm_get_sam_handle(domain))
562                 return False;
563
564         for (conn = cm_conns; conn; conn = conn->next) {
565                 if (strequal(conn->domain, domain) &&
566                     strequal(conn->pipe_name, PIPE_SAMR) &&
567                     conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
568                         basic_conn = conn;
569         }
570         
571         if (!(conn = (struct winbindd_cm_conn *)
572               malloc(sizeof(struct winbindd_cm_conn))))
573                 return NULL;
574         
575         ZERO_STRUCTP(conn);
576
577         fstrcpy(conn->domain, basic_conn->domain);
578         fstrcpy(conn->controller, basic_conn->controller);
579         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
580
581         conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
582         conn->cli = basic_conn->cli;
583
584         result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
585                                       &basic_conn->pol, des_access, 
586                                       domain_sid, &conn->pol);
587
588         if (!NT_STATUS_IS_OK(result))
589                 return NULL;
590
591         /* Add to list */
592
593         DLIST_ADD(cm_conns, conn);
594
595  ok:
596         hnd.pol = conn->pol;
597         hnd.cli = conn->cli;
598
599         return &hnd;
600 }
601
602 /* Return a SAM policy handle on a domain user */
603
604 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
605                                        uint32 user_rid)
606 {
607         struct winbindd_cm_conn *conn, *basic_conn = NULL;
608         static CLI_POLICY_HND hnd;
609         NTSTATUS result;
610         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
611
612         /* Look for existing connections */
613
614         for (conn = cm_conns; conn; conn = conn->next) {
615                 if (strequal(conn->domain, domain) &&
616                     strequal(conn->pipe_name, PIPE_SAMR) &&
617                     conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
618                     conn->pipe_data.samr.rid == user_rid) {
619
620                         if (!connection_ok(conn)) {
621                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
622                                 DLIST_REMOVE(cm_conns, conn);
623                                 return NULL;
624                         }
625                 
626                         goto ok;
627                 }
628         }
629
630         /* Create a domain handle to open a user handle from */
631
632         if (!cm_get_sam_dom_handle(domain, domain_sid))
633                 return NULL;
634
635         for (conn = cm_conns; conn; conn = conn->next) {
636                 if (strequal(conn->domain, domain) &&
637                     strequal(conn->pipe_name, PIPE_SAMR) &&
638                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
639                         basic_conn = conn;
640         }
641         
642         if (!basic_conn) {
643                 DEBUG(0, ("No domain sam handle was created!\n"));
644                 return NULL;
645         }
646
647         if (!(conn = (struct winbindd_cm_conn *)
648               malloc(sizeof(struct winbindd_cm_conn))))
649                 return NULL;
650         
651         ZERO_STRUCTP(conn);
652
653         fstrcpy(conn->domain, basic_conn->domain);
654         fstrcpy(conn->controller, basic_conn->controller);
655         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
656         
657         conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
658         conn->cli = basic_conn->cli;
659         conn->pipe_data.samr.rid = user_rid;
660
661         result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
662                                     &basic_conn->pol, des_access, user_rid,
663                                     &conn->pol);
664
665         if (!NT_STATUS_IS_OK(result))
666                 return NULL;
667
668         /* Add to list */
669
670         DLIST_ADD(cm_conns, conn);
671
672  ok:
673         hnd.pol = conn->pol;
674         hnd.cli = conn->cli;
675
676         return &hnd;
677 }
678
679 /* Return a SAM policy handle on a domain group */
680
681 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
682                                         uint32 group_rid)
683 {
684         struct winbindd_cm_conn *conn, *basic_conn = NULL;
685         static CLI_POLICY_HND hnd;
686         NTSTATUS result;
687         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
688
689         /* Look for existing connections */
690
691         for (conn = cm_conns; conn; conn = conn->next) {
692                 if (strequal(conn->domain, domain) &&
693                     strequal(conn->pipe_name, PIPE_SAMR) &&
694                     conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
695                     conn->pipe_data.samr.rid == group_rid) {
696
697                         if (!connection_ok(conn)) {
698                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
699                                 DLIST_REMOVE(cm_conns, conn);
700                                 return NULL;
701                         }
702                 
703                         goto ok;
704                 }
705         }
706
707         /* Create a domain handle to open a user handle from */
708
709         if (!cm_get_sam_dom_handle(domain, domain_sid))
710                 return NULL;
711
712         for (conn = cm_conns; conn; conn = conn->next) {
713                 if (strequal(conn->domain, domain) &&
714                     strequal(conn->pipe_name, PIPE_SAMR) &&
715                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
716                         basic_conn = conn;
717         }
718         
719         if (!basic_conn) {
720                 DEBUG(0, ("No domain sam handle was created!\n"));
721                 return NULL;
722         }
723
724         if (!(conn = (struct winbindd_cm_conn *)
725               malloc(sizeof(struct winbindd_cm_conn))))
726                 return NULL;
727         
728         ZERO_STRUCTP(conn);
729
730         fstrcpy(conn->domain, basic_conn->domain);
731         fstrcpy(conn->controller, basic_conn->controller);
732         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
733         
734         conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
735         conn->cli = basic_conn->cli;
736         conn->pipe_data.samr.rid = group_rid;
737
738         result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
739                                     &basic_conn->pol, des_access, group_rid,
740                                     &conn->pol);
741
742         if (!NT_STATUS_IS_OK(result))
743                 return NULL;
744
745         /* Add to list */
746
747         DLIST_ADD(cm_conns, conn);
748
749  ok:
750         hnd.pol = conn->pol;
751         hnd.cli = conn->cli;
752
753         return &hnd;
754 }
755
756 #endif
757
758 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
759    netlogon pipe as no handle is returned. */
760
761 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
762                              struct cli_state **cli)
763 {
764         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
765         struct winbindd_cm_conn *conn;
766
767         if (!cli) {
768                 return NT_STATUS_INVALID_PARAMETER;
769         }
770
771         /* Open an initial conection */
772
773         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
774                 return result;
775         }
776         
777         result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
778                                         SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
779
780         if (!NT_STATUS_IS_OK(result)) {
781                 DEBUG(0, ("error connecting to domain password server: %s\n",
782                           nt_errstr(result)));
783                 
784                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
785                 if (conn->cli->fd == -1) {
786                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
787                                 return result;
788                         }
789                         
790                         /* Try again */
791                         result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
792                                                         SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
793                 }
794                 
795                 if (!NT_STATUS_IS_OK(result)) {
796                         cli_shutdown(conn->cli);
797                         DLIST_REMOVE(cm_conns, conn);
798                         SAFE_FREE(conn);
799                         return result;
800                 }
801         }
802
803         *cli = conn->cli;
804
805         return result;
806 }
807
808 /* Dump the current connection status */
809
810 static void dump_conn_list(void)
811 {
812         struct winbindd_cm_conn *con;
813
814         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
815
816         for(con = cm_conns; con; con = con->next) {
817                 char *msg;
818
819                 /* Display pipe info */
820                 
821                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
822                         DEBUG(0, ("Error: not enough memory!\n"));
823                 } else {
824                         DEBUG(0, ("%s\n", msg));
825                         SAFE_FREE(msg);
826                 }
827         }
828 }
829
830 void winbindd_cm_status(void)
831 {
832         /* List open connections */
833
834         DEBUG(0, ("winbindd connection manager status:\n"));
835
836         if (cm_conns)
837                 dump_conn_list();
838         else
839                 DEBUG(0, ("\tNo active connections\n"));
840 }