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