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