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