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