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