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