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