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