Added new error codes. Fix up connection code to retry in the same way
[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, 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 int 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         int i;
396         BOOL retry = True;
397
398         ZERO_STRUCT(dc_ip);
399
400         fstrcpy(new_conn->domain, domain);
401         fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
402         
403         /* Look for a domain controller for this domain.  Negative results
404            are cached so don't bother applying the caching for this
405            function just yet.  */
406
407         if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
408                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
409                 add_failed_connection_entry(new_conn, result);
410                 return result;
411         }
412                 
413         /* Return false if we have tried to look up this domain and netbios
414            name before and failed. */
415
416         for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
417                 
418                 if (!(strequal(domain, fcc->domain_name) &&
419                       strequal(new_conn->controller, fcc->controller)))
420                         continue; /* Not our domain */
421
422                 if ((time(NULL) - fcc->lookup_time) > 
423                     FAILED_CONNECTION_CACHE_TIMEOUT) {
424
425                         /* Cache entry has expired, delete it */
426
427                         DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
428
429                         DLIST_REMOVE(failed_connection_cache, fcc);
430                         free(fcc);
431
432                         break;
433                 }
434
435                 /* The timeout hasn't expired yet so return false */
436
437                 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
438
439                 result = fcc->nt_status;
440                 SMB_ASSERT(!NT_STATUS_IS_OK(result));
441                 return result;
442         }
443
444         /* Initialise SMB connection */
445
446         cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
447
448         DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
449               new_conn->controller, global_myname, ipc_domain, ipc_username));
450
451         for (i = 0; retry && (i < 3); i++) {
452                 
453                 if (!secrets_named_mutex(new_conn->controller, 10)) {
454                         DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
455                         continue;
456                 }
457
458                 result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller, 
459                         &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain, 
460                         ipc_password, 0, &retry);
461
462                 secrets_named_mutex_release(new_conn->controller);
463
464                 if (NT_STATUS_IS_OK(result))
465                         break;
466         }
467
468         SAFE_FREE(ipc_username);
469         SAFE_FREE(ipc_domain);
470         SAFE_FREE(ipc_password);
471
472         if (!NT_STATUS_IS_OK(result)) {
473                 add_failed_connection_entry(new_conn, result);
474                 return result;
475         }
476         
477         if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
478                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
479                 /* 
480                  * only cache a failure if we are not trying to open the 
481                  * **win2k** specific lsarpc UUID.  This could be an NT PDC 
482                  * and therefore a failure is normal.  This should probably
483                  * be abstracted to a check for 2k specific pipes and wondering
484                  * if the PDC is an NT4 box.   but since there is only one 2k 
485                  * specific UUID right now, i'm not going to bother.  --jerry
486                  */
487                 if ( !is_win2k_pipe(pipe_index) )
488                 add_failed_connection_entry(new_conn, result);
489                 cli_shutdown(new_conn->cli);
490                 return result;
491         }
492
493         return NT_STATUS_OK;
494 }
495
496 /* Return true if a connection is still alive */
497
498 static BOOL connection_ok(struct winbindd_cm_conn *conn)
499 {
500         if (!conn) {
501                 smb_panic("Invalid paramater passed to conneciton_ok():  conn was NULL!\n");
502                 return False;
503         }
504
505         if (!conn->cli) {
506                 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
507                           conn->controller, conn->domain, conn->pipe_name));
508                 smb_panic("connection_ok: conn->cli was null!");
509                 return False;
510         }
511
512         if (!conn->cli->initialised) {
513                 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
514                           conn->controller, conn->domain, conn->pipe_name));
515                 smb_panic("connection_ok: conn->cli->initialised is False!");
516                 return False;
517         }
518
519         if (conn->cli->fd == -1) {
520                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
521                           conn->controller, conn->domain, conn->pipe_name));
522                 return False;
523         }
524         
525         return True;
526 }
527
528 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
529
530 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out) 
531 {
532         struct winbindd_cm_conn *conn, conn_temp;
533         NTSTATUS result;
534
535         for (conn = cm_conns; conn; conn = conn->next) {
536                 if (strequal(conn->domain, domain) && 
537                     strequal(conn->pipe_name, pipe_name)) {
538                         if (!connection_ok(conn)) {
539                                 if (conn->cli) {
540                                         cli_shutdown(conn->cli);
541                                 }
542                                 ZERO_STRUCT(conn_temp);
543                                 conn_temp.next = conn->next;
544                                 DLIST_REMOVE(cm_conns, conn);
545                                 SAFE_FREE(conn);
546                                 conn = &conn_temp;  /* Just to keep the loop moving */
547                         } else {
548                                 break;
549                         }
550                 }
551         }
552         
553         if (!conn) {
554                 if (!(conn = malloc(sizeof(*conn))))
555                         return NT_STATUS_NO_MEMORY;
556                 
557                 ZERO_STRUCTP(conn);
558                 
559                 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
560                         DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
561                                   domain, pipe_name, nt_errstr(result)));
562                         SAFE_FREE(conn);
563                         return result;
564                 }
565                 DLIST_ADD(cm_conns, conn);              
566         }
567         
568         *conn_out = conn;
569         return NT_STATUS_OK;
570 }
571
572
573 /**********************************************************************************
574 **********************************************************************************/
575
576 BOOL cm_check_for_native_mode_win2k( const char *domain )
577 {
578         NTSTATUS                result;
579         struct winbindd_cm_conn conn;
580         DS_DOMINFO_CTR          ctr;
581         BOOL                    ret = False;
582         
583         ZERO_STRUCT( conn );
584         ZERO_STRUCT( ctr );
585         
586         
587         if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) 
588         {
589                 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n", 
590                           domain, nt_errstr(result)));
591                 return False;
592         }
593         
594         if ( conn.cli ) {
595                 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
596                         conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) 
597                 {
598                         ret = False;
599                         goto done;
600                 }
601         }
602                                 
603         if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) 
604                 && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
605         {
606                 ret = True;
607         }
608
609 done:
610         if ( conn.cli )
611                 cli_shutdown( conn.cli );
612         
613         return ret;
614 }
615
616
617
618 /* Return a LSA policy handle on a domain */
619
620 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
621 {
622         struct winbindd_cm_conn *conn;
623         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
624         NTSTATUS result;
625         static CLI_POLICY_HND hnd;
626
627         /* Look for existing connections */
628
629         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
630                 return NULL;
631         }
632
633         /* This *shitty* code needs scrapping ! JRA */
634         if (policy_handle_is_valid(&conn->pol)) {
635                 hnd.pol = conn->pol;
636                 hnd.cli = conn->cli;
637                 return &hnd;
638         }
639         
640         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
641                                      des_access, &conn->pol);
642
643         if (!NT_STATUS_IS_OK(result)) {
644                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
645                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
646                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
647                                 return NULL;
648                         }
649
650                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
651                                                      des_access, &conn->pol);
652                 }
653
654                 if (!NT_STATUS_IS_OK(result)) {
655                         cli_shutdown(conn->cli);
656                         DLIST_REMOVE(cm_conns, conn);
657                         SAFE_FREE(conn);
658                         return NULL;
659                 }
660         }       
661
662         hnd.pol = conn->pol;
663         hnd.cli = conn->cli;
664
665         return &hnd;
666 }
667
668 /* Return a SAM policy handle on a domain */
669
670 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
671
672         struct winbindd_cm_conn *conn;
673         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
674         NTSTATUS result;
675         static CLI_POLICY_HND hnd;
676
677         /* Look for existing connections */
678
679         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
680                 return NULL;
681         }
682         
683         /* This *shitty* code needs scrapping ! JRA */
684         if (policy_handle_is_valid(&conn->pol)) {
685                 hnd.pol = conn->pol;
686                 hnd.cli = conn->cli;
687                 return &hnd;
688         }
689         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
690                                   des_access, &conn->pol);
691
692         if (!NT_STATUS_IS_OK(result)) {
693                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
694                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
695                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
696                                 return NULL;
697                         }
698
699                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
700                                                   des_access, &conn->pol);
701                 }
702
703                 if (!NT_STATUS_IS_OK(result)) {
704                         cli_shutdown(conn->cli);
705                         DLIST_REMOVE(cm_conns, conn);
706                         SAFE_FREE(conn);
707                         return NULL;
708                 }
709         }       
710
711         hnd.pol = conn->pol;
712         hnd.cli = conn->cli;
713
714         return &hnd;
715 }
716
717 #if 0  /* This code now *well* out of date */
718
719 /* Return a SAM domain policy handle on a domain */
720
721 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
722 {
723         struct winbindd_cm_conn *conn, *basic_conn = NULL;
724         static CLI_POLICY_HND hnd;
725         NTSTATUS result;
726         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
727
728         /* Look for existing connections */
729
730         for (conn = cm_conns; conn; conn = conn->next) {
731                 if (strequal(conn->domain, domain) &&
732                     strequal(conn->pipe_name, PIPE_SAMR) &&
733                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
734
735                         if (!connection_ok(conn)) {
736                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
737                                 DLIST_REMOVE(cm_conns, conn);
738                                 return NULL;
739                         }
740
741                         goto ok;
742                 }
743         }
744
745         /* Create a basic handle to open a domain handle from */
746
747         if (!cm_get_sam_handle(domain))
748                 return False;
749
750         for (conn = cm_conns; conn; conn = conn->next) {
751                 if (strequal(conn->domain, domain) &&
752                     strequal(conn->pipe_name, PIPE_SAMR) &&
753                     conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
754                         basic_conn = conn;
755         }
756         
757         if (!(conn = (struct winbindd_cm_conn *)
758               malloc(sizeof(struct winbindd_cm_conn))))
759                 return NULL;
760         
761         ZERO_STRUCTP(conn);
762
763         fstrcpy(conn->domain, basic_conn->domain);
764         fstrcpy(conn->controller, basic_conn->controller);
765         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
766
767         conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
768         conn->cli = basic_conn->cli;
769
770         result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
771                                       &basic_conn->pol, des_access, 
772                                       domain_sid, &conn->pol);
773
774         if (!NT_STATUS_IS_OK(result))
775                 return NULL;
776
777         /* Add to list */
778
779         DLIST_ADD(cm_conns, conn);
780
781  ok:
782         hnd.pol = conn->pol;
783         hnd.cli = conn->cli;
784
785         return &hnd;
786 }
787
788 /* Return a SAM policy handle on a domain user */
789
790 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
791                                        uint32 user_rid)
792 {
793         struct winbindd_cm_conn *conn, *basic_conn = NULL;
794         static CLI_POLICY_HND hnd;
795         NTSTATUS result;
796         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
797
798         /* Look for existing connections */
799
800         for (conn = cm_conns; conn; conn = conn->next) {
801                 if (strequal(conn->domain, domain) &&
802                     strequal(conn->pipe_name, PIPE_SAMR) &&
803                     conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
804                     conn->pipe_data.samr.rid == user_rid) {
805
806                         if (!connection_ok(conn)) {
807                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
808                                 DLIST_REMOVE(cm_conns, conn);
809                                 return NULL;
810                         }
811                 
812                         goto ok;
813                 }
814         }
815
816         /* Create a domain handle to open a user handle from */
817
818         if (!cm_get_sam_dom_handle(domain, domain_sid))
819                 return NULL;
820
821         for (conn = cm_conns; conn; conn = conn->next) {
822                 if (strequal(conn->domain, domain) &&
823                     strequal(conn->pipe_name, PIPE_SAMR) &&
824                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
825                         basic_conn = conn;
826         }
827         
828         if (!basic_conn) {
829                 DEBUG(0, ("No domain sam handle was created!\n"));
830                 return NULL;
831         }
832
833         if (!(conn = (struct winbindd_cm_conn *)
834               malloc(sizeof(struct winbindd_cm_conn))))
835                 return NULL;
836         
837         ZERO_STRUCTP(conn);
838
839         fstrcpy(conn->domain, basic_conn->domain);
840         fstrcpy(conn->controller, basic_conn->controller);
841         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
842         
843         conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
844         conn->cli = basic_conn->cli;
845         conn->pipe_data.samr.rid = user_rid;
846
847         result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
848                                     &basic_conn->pol, des_access, user_rid,
849                                     &conn->pol);
850
851         if (!NT_STATUS_IS_OK(result))
852                 return NULL;
853
854         /* Add to list */
855
856         DLIST_ADD(cm_conns, conn);
857
858  ok:
859         hnd.pol = conn->pol;
860         hnd.cli = conn->cli;
861
862         return &hnd;
863 }
864
865 /* Return a SAM policy handle on a domain group */
866
867 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
868                                         uint32 group_rid)
869 {
870         struct winbindd_cm_conn *conn, *basic_conn = NULL;
871         static CLI_POLICY_HND hnd;
872         NTSTATUS result;
873         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
874
875         /* Look for existing connections */
876
877         for (conn = cm_conns; conn; conn = conn->next) {
878                 if (strequal(conn->domain, domain) &&
879                     strequal(conn->pipe_name, PIPE_SAMR) &&
880                     conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
881                     conn->pipe_data.samr.rid == group_rid) {
882
883                         if (!connection_ok(conn)) {
884                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
885                                 DLIST_REMOVE(cm_conns, conn);
886                                 return NULL;
887                         }
888                 
889                         goto ok;
890                 }
891         }
892
893         /* Create a domain handle to open a user handle from */
894
895         if (!cm_get_sam_dom_handle(domain, domain_sid))
896                 return NULL;
897
898         for (conn = cm_conns; conn; conn = conn->next) {
899                 if (strequal(conn->domain, domain) &&
900                     strequal(conn->pipe_name, PIPE_SAMR) &&
901                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
902                         basic_conn = conn;
903         }
904         
905         if (!basic_conn) {
906                 DEBUG(0, ("No domain sam handle was created!\n"));
907                 return NULL;
908         }
909
910         if (!(conn = (struct winbindd_cm_conn *)
911               malloc(sizeof(struct winbindd_cm_conn))))
912                 return NULL;
913         
914         ZERO_STRUCTP(conn);
915
916         fstrcpy(conn->domain, basic_conn->domain);
917         fstrcpy(conn->controller, basic_conn->controller);
918         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
919         
920         conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
921         conn->cli = basic_conn->cli;
922         conn->pipe_data.samr.rid = group_rid;
923
924         result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
925                                     &basic_conn->pol, des_access, group_rid,
926                                     &conn->pol);
927
928         if (!NT_STATUS_IS_OK(result))
929                 return NULL;
930
931         /* Add to list */
932
933         DLIST_ADD(cm_conns, conn);
934
935  ok:
936         hnd.pol = conn->pol;
937         hnd.cli = conn->cli;
938
939         return &hnd;
940 }
941
942 #endif
943
944 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
945    netlogon pipe as no handle is returned. */
946
947 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
948                              struct cli_state **cli)
949 {
950         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
951         struct winbindd_cm_conn *conn;
952         uint32 neg_flags = 0x000001ff;
953
954         if (!cli) {
955                 return NT_STATUS_INVALID_PARAMETER;
956         }
957
958         /* Open an initial conection */
959
960         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
961                 return result;
962         }
963         
964         result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
965
966         if (!NT_STATUS_IS_OK(result)) {
967                 DEBUG(0, ("error connecting to domain password server: %s\n",
968                           nt_errstr(result)));
969                 
970                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
971                 if (conn->cli->fd == -1) {
972                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
973                                 return result;
974                         }
975                         
976                         /* Try again */
977                         result = cli_nt_setup_creds( conn->cli, get_sec_chan(),trust_passwd, &neg_flags, 2);
978                 }
979                 
980                 if (!NT_STATUS_IS_OK(result)) {
981                         cli_shutdown(conn->cli);
982                         DLIST_REMOVE(cm_conns, conn);
983                         SAFE_FREE(conn);
984                         return result;
985                 }
986         }
987
988         *cli = conn->cli;
989
990         return result;
991 }
992
993 /* Dump the current connection status */
994
995 static void dump_conn_list(void)
996 {
997         struct winbindd_cm_conn *con;
998
999         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
1000
1001         for(con = cm_conns; con; con = con->next) {
1002                 char *msg;
1003
1004                 /* Display pipe info */
1005                 
1006                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
1007                         DEBUG(0, ("Error: not enough memory!\n"));
1008                 } else {
1009                         DEBUG(0, ("%s\n", msg));
1010                         SAFE_FREE(msg);
1011                 }
1012         }
1013 }
1014
1015 void winbindd_cm_status(void)
1016 {
1017         /* List open connections */
1018
1019         DEBUG(0, ("winbindd connection manager status:\n"));
1020
1021         if (cm_conns)
1022                 dump_conn_list();
1023         else
1024                 DEBUG(0, ("\tNo active connections\n"));
1025 }