Merge from HEAD - save the type of channel used to contact the DC.
[tprouty/samba.git] / source / nsswitch / winbindd_cm.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon connection manager
5
6    Copyright (C) Tim Potter 2001
7    Copyright (C) Andrew Bartlett 2002
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 /*
25    We need to manage connections to domain controllers without having to
26    mess up the main winbindd code with other issues.  The aim of the
27    connection manager is to:
28   
29        - make connections to domain controllers and cache them
30        - re-establish connections when networks or servers go down
31        - centralise the policy on connection timeouts, domain controller
32          selection etc
33        - manage re-entrancy for when winbindd becomes able to handle
34          multiple outstanding rpc requests
35   
36    Why not have connection management as part of the rpc layer like tng?
37    Good question.  This code may morph into libsmb/rpc_cache.c or something
38    like that but at the moment it's simply staying as part of winbind.  I
39    think the TNG architecture of forcing every user of the rpc layer to use
40    the connection caching system is a bad idea.  It should be an optional
41    method of using the routines.
42
43    The TNG design is quite good but I disagree with some aspects of the
44    implementation. -tpot
45
46  */
47
48 /*
49    TODO:
50
51      - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
52        moved down into another function.
53
54      - There needs to be a utility function in libsmb/namequery.c that does
55        cm_get_dc_name() 
56
57      - Take care when destroying cli_structs as they can be shared between
58        various sam handles.
59
60  */
61
62 #include "winbindd.h"
63
64 #undef DBGC_CLASS
65 #define DBGC_CLASS DBGC_WINBIND
66
67 /* Global list of connections.  Initially a DLIST but can become a hash
68    table or whatever later. */
69
70 struct winbindd_cm_conn {
71         struct winbindd_cm_conn *prev, *next;
72         fstring domain;
73         fstring controller;
74         fstring pipe_name;
75         size_t mutex_ref_count;
76         struct cli_state *cli;
77         POLICY_HND pol;
78 };
79
80 static struct winbindd_cm_conn *cm_conns = NULL;
81
82 /* Get a domain controller name.  Cache positive and negative lookups so we
83    don't go to the network too often when something is badly broken. */
84
85 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
86
87 struct get_dc_name_cache {
88         fstring domain_name;
89         fstring srv_name;
90         time_t lookup_time;
91         struct get_dc_name_cache *prev, *next;
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         ads = ads_init(realm, domain, NULL);
106         if (!ads)
107                 return False;
108
109         /* we don't need to bind, just connect */
110         ads->auth.flags |= ADS_AUTH_NO_BIND;
111
112         DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
113
114 #ifdef HAVE_ADS
115         /* a full ads_connect() is actually overkill, as we don't srictly need
116            to do the SASL auth in order to get the info we need, but libads
117            doesn't offer a better way right now */
118         ads_connect(ads);
119 #endif
120
121         if (!ads->config.realm)
122                 return False;
123
124         fstrcpy(srv_name, ads->config.ldap_server_name);
125         strupper(srv_name);
126         *dc_ip = ads->ldap_ip;
127         ads_destroy(&ads);
128         
129         DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
130                  srv_name, inet_ntoa(*dc_ip)));
131         
132         return True;
133 }
134
135
136
137 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
138 {
139         static struct get_dc_name_cache *get_dc_name_cache;
140         struct get_dc_name_cache *dcc;
141         struct in_addr dc_ip;
142         BOOL ret;
143
144         /* Check the cache for previous lookups */
145
146         for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
147
148                 if (!strequal(domain, dcc->domain_name))
149                         continue; /* Not our domain */
150
151                 if ((time(NULL) - dcc->lookup_time) > 
152                     GET_DC_NAME_CACHE_TIMEOUT) {
153
154                         /* Cache entry has expired, delete it */
155
156                         DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
157
158                         DLIST_REMOVE(get_dc_name_cache, dcc);
159                         SAFE_FREE(dcc);
160
161                         break;
162                 }
163
164                 /* Return a positive or negative lookup for this domain */
165
166                 if (dcc->srv_name[0]) {
167                         DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
168                         fstrcpy(srv_name, dcc->srv_name);
169                         return True;
170                 } else {
171                         DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
172                         return False;
173                 }
174         }
175
176         /* Add cache entry for this lookup. */
177
178         DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
179
180         if (!(dcc = (struct get_dc_name_cache *) 
181               malloc(sizeof(struct get_dc_name_cache))))
182                 return False;
183
184         ZERO_STRUCTP(dcc);
185
186         fstrcpy(dcc->domain_name, domain);
187         dcc->lookup_time = time(NULL);
188
189         DLIST_ADD(get_dc_name_cache, dcc);
190
191         zero_ip(&dc_ip);
192
193         ret = False;
194         if (lp_security() == SEC_ADS)
195                 ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
196
197         if (!ret) {
198                 /* fall back on rpc methods if the ADS methods fail */
199                 ret = rpc_find_dc(domain, srv_name, &dc_ip);
200         }
201
202         if (!ret)
203                 return False;
204
205         /* We have a name so make the cache entry positive now */
206         fstrcpy(dcc->srv_name, srv_name);
207
208         DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
209                   inet_ntoa(dc_ip), domain));
210
211         *ip_out = dc_ip;
212
213         return True;
214 }
215
216 /* Choose between anonymous or authenticated connections.  We need to use
217    an authenticated connection if DCs have the RestrictAnonymous registry
218    entry set > 0, or the "Additional restrictions for anonymous
219    connections" set in the win2k Local Security Policy. 
220    
221    Caller to free() result in domain, username, password
222 */
223
224 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
225 {
226         *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
227         *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
228         *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
229         
230         if (*username && **username) {
231
232                 if (!*domain || !**domain)
233                         *domain = smb_xstrdup(lp_workgroup());
234                 
235                 if (!*password || !**password)
236                         *password = smb_xstrdup("");
237
238                 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", 
239                           *domain, *username));
240
241         } else {
242                 DEBUG(3, ("IPC$ connections done anonymously\n"));
243                 *username = smb_xstrdup("");
244                 *domain = smb_xstrdup("");
245                 *password = smb_xstrdup("");
246         }
247 }
248
249 /* Open a new smb pipe connection to a DC on a given domain.  Cache
250    negative creation attempts so we don't try and connect to broken
251    machines too often. */
252
253 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
254
255 struct failed_connection_cache {
256         fstring domain_name;
257         fstring controller;
258         time_t lookup_time;
259         NTSTATUS nt_status;
260         struct failed_connection_cache *prev, *next;
261 };
262
263 static struct failed_connection_cache *failed_connection_cache;
264
265 /* Add an entry to the failed conneciton cache */
266
267 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, 
268                                         NTSTATUS result) 
269 {
270         struct failed_connection_cache *fcc;
271
272         SMB_ASSERT(!NT_STATUS_IS_OK(result));
273
274         /* Check we already aren't in the cache */
275
276         for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
277                 if (strequal(fcc->domain_name, new_conn->domain)) {
278                         DEBUG(10, ("domain %s already tried and failed\n",
279                                    fcc->domain_name));
280                         return;
281                 }
282         }
283
284         /* Create negative lookup cache entry for this domain and controller */
285
286         if (!(fcc = (struct failed_connection_cache *)
287               malloc(sizeof(struct failed_connection_cache)))) {
288                 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
289                 return;
290         }
291         
292         ZERO_STRUCTP(fcc);
293         
294         fstrcpy(fcc->domain_name, new_conn->domain);
295         fstrcpy(fcc->controller, new_conn->controller);
296         fcc->lookup_time = time(NULL);
297         fcc->nt_status = result;
298         
299         DLIST_ADD(failed_connection_cache, fcc);
300 }
301         
302 /* Open a connction to the remote server, cache failures for 30 seconds */
303
304 static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
305                                struct winbindd_cm_conn *new_conn)
306 {
307         struct failed_connection_cache *fcc;
308         NTSTATUS result;
309         char *ipc_username, *ipc_domain, *ipc_password;
310         struct in_addr dc_ip;
311         int i;
312         BOOL retry = True;
313
314         ZERO_STRUCT(dc_ip);
315
316         fstrcpy(new_conn->domain, domain);
317         fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
318         
319         /* Look for a domain controller for this domain.  Negative results
320            are cached so don't bother applying the caching for this
321            function just yet.  */
322
323         if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
324                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
325                 add_failed_connection_entry(new_conn, result);
326                 return result;
327         }
328                 
329         /* Return false if we have tried to look up this domain and netbios
330            name before and failed. */
331
332         for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
333                 
334                 if (!(strequal(domain, fcc->domain_name) &&
335                       strequal(new_conn->controller, fcc->controller)))
336                         continue; /* Not our domain */
337
338                 if ((time(NULL) - fcc->lookup_time) > 
339                     FAILED_CONNECTION_CACHE_TIMEOUT) {
340
341                         /* Cache entry has expired, delete it */
342
343                         DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
344
345                         DLIST_REMOVE(failed_connection_cache, fcc);
346                         free(fcc);
347
348                         break;
349                 }
350
351                 /* The timeout hasn't expired yet so return false */
352
353                 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
354
355                 result = fcc->nt_status;
356                 SMB_ASSERT(!NT_STATUS_IS_OK(result));
357                 return result;
358         }
359
360         /* Initialise SMB connection */
361
362         cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
363
364         DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
365               new_conn->controller, global_myname(), ipc_domain, ipc_username));
366
367         for (i = 0; retry && (i < 3); i++) {
368                 BOOL got_mutex;
369                 if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
370                         DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
371                         result = NT_STATUS_POSSIBLE_DEADLOCK;
372                         continue;
373                 }
374                 
375                 result = cli_full_connection(&new_conn->cli, global_myname(), new_conn->controller, 
376                         &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain, 
377                                              ipc_password, CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, &retry);
378                 
379                 secrets_named_mutex_release(new_conn->controller);
380
381                 if (NT_STATUS_IS_OK(result))
382                         break;
383         }
384
385         SAFE_FREE(ipc_username);
386         SAFE_FREE(ipc_domain);
387         SAFE_FREE(ipc_password);
388
389         if (!NT_STATUS_IS_OK(result)) {
390                 add_failed_connection_entry(new_conn, result);
391                 return result;
392         }
393         
394         if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
395                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
396                 /* 
397                  * only cache a failure if we are not trying to open the 
398                  * **win2k** specific lsarpc UUID.  This could be an NT PDC 
399                  * and therefore a failure is normal.  This should probably
400                  * be abstracted to a check for 2k specific pipes and wondering
401                  * if the PDC is an NT4 box.   but since there is only one 2k 
402                  * specific UUID right now, i'm not going to bother.  --jerry
403                  */
404                 if ( !is_win2k_pipe(pipe_index) )
405                         add_failed_connection_entry(new_conn, result);
406                 cli_shutdown(new_conn->cli);
407                 return result;
408         }
409
410         return NT_STATUS_OK;
411 }
412
413 /* Return true if a connection is still alive */
414
415 static BOOL connection_ok(struct winbindd_cm_conn *conn)
416 {
417         if (!conn) {
418                 smb_panic("Invalid paramater passed to conneciton_ok():  conn was NULL!\n");
419                 return False;
420         }
421
422         if (!conn->cli) {
423                 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
424                           conn->controller, conn->domain, conn->pipe_name));
425                 smb_panic("connection_ok: conn->cli was null!");
426                 return False;
427         }
428
429         if (!conn->cli->initialised) {
430                 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
431                           conn->controller, conn->domain, conn->pipe_name));
432                 smb_panic("connection_ok: conn->cli->initialised is False!");
433                 return False;
434         }
435
436         if (conn->cli->fd == -1) {
437                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
438                           conn->controller, conn->domain, conn->pipe_name));
439                 return False;
440         }
441         
442         return True;
443 }
444
445 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
446
447 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
448                 struct winbindd_cm_conn **conn_out) 
449 {
450         struct winbindd_cm_conn *conn, conn_temp;
451         NTSTATUS result;
452
453         for (conn = cm_conns; conn; conn = conn->next) {
454                 if (strequal(conn->domain, domain) && 
455                     strequal(conn->pipe_name, pipe_name)) {
456                         if (!connection_ok(conn)) {
457                                 if (conn->cli)
458                                         cli_shutdown(conn->cli);
459                                 ZERO_STRUCT(conn_temp);
460                                 conn_temp.next = conn->next;
461                                 DLIST_REMOVE(cm_conns, conn);
462                                 SAFE_FREE(conn);
463                                 conn = &conn_temp;  /* Just to keep the loop moving */
464                         } else {
465                                 break;
466                         }
467                 }
468         }
469         
470         if (!conn) {
471                 if (!(conn = malloc(sizeof(*conn))))
472                         return NT_STATUS_NO_MEMORY;
473                 
474                 ZERO_STRUCTP(conn);
475                 
476                 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
477                         DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
478                                   domain, pipe_name, nt_errstr(result)));
479                         SAFE_FREE(conn);
480                         return result;
481                 }
482                 DLIST_ADD(cm_conns, conn);              
483         }
484         
485         *conn_out = conn;
486         return NT_STATUS_OK;
487 }
488
489
490 /**********************************************************************************
491 **********************************************************************************/
492
493 BOOL cm_check_for_native_mode_win2k( const char *domain )
494 {
495         NTSTATUS                result;
496         struct winbindd_cm_conn conn;
497         DS_DOMINFO_CTR          ctr;
498         BOOL                    ret = False;
499         
500         ZERO_STRUCT( conn );
501         ZERO_STRUCT( ctr );
502         
503         
504         if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
505                 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n", 
506                           domain, nt_errstr(result)));
507                 return False;
508         }
509         
510         if ( conn.cli ) {
511                 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
512                                 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
513                         ret = False;
514                         goto done;
515                 }
516         }
517                                 
518         if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) 
519                         && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
520                 ret = True;
521
522 done:
523         if ( conn.cli )
524                 cli_shutdown( conn.cli );
525         
526         return ret;
527 }
528
529
530
531 /* Return a LSA policy handle on a domain */
532
533 CLI_POLICY_HND *cm_get_lsa_handle(const char *domain)
534 {
535         struct winbindd_cm_conn *conn;
536         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
537         NTSTATUS result;
538         static CLI_POLICY_HND hnd;
539
540         /* Look for existing connections */
541
542         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
543                 return NULL;
544
545         /* This *shitty* code needs scrapping ! JRA */
546         if (policy_handle_is_valid(&conn->pol)) {
547                 hnd.pol = conn->pol;
548                 hnd.cli = conn->cli;
549                 return &hnd;
550         }
551         
552         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
553                                      des_access, &conn->pol);
554
555         if (!NT_STATUS_IS_OK(result)) {
556                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
557                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
558                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
559                                 return NULL;
560
561                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
562                                                      des_access, &conn->pol);
563                 }
564
565                 if (!NT_STATUS_IS_OK(result)) {
566                         cli_shutdown(conn->cli);
567                         DLIST_REMOVE(cm_conns, conn);
568                         SAFE_FREE(conn);
569                         return NULL;
570                 }
571         }       
572
573         hnd.pol = conn->pol;
574         hnd.cli = conn->cli;
575
576         return &hnd;
577 }
578
579 /* Return a SAM policy handle on a domain */
580
581 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
582
583         struct winbindd_cm_conn *conn;
584         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
585         NTSTATUS result;
586         static CLI_POLICY_HND hnd;
587
588         /* Look for existing connections */
589
590         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
591                 return NULL;
592         
593         /* This *shitty* code needs scrapping ! JRA */
594         if (policy_handle_is_valid(&conn->pol)) {
595                 hnd.pol = conn->pol;
596                 hnd.cli = conn->cli;
597                 return &hnd;
598         }
599         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
600                                   des_access, &conn->pol);
601
602         if (!NT_STATUS_IS_OK(result)) {
603                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
604                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
605                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
606                                 return NULL;
607
608                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
609                                                   des_access, &conn->pol);
610                 }
611
612                 if (!NT_STATUS_IS_OK(result)) {
613                         cli_shutdown(conn->cli);
614                         DLIST_REMOVE(cm_conns, conn);
615                         SAFE_FREE(conn);
616                         return NULL;
617                 }
618         }       
619
620         hnd.pol = conn->pol;
621         hnd.cli = conn->cli;
622
623         return &hnd;
624 }
625
626 #if 0  /* This code now *well* out of date */
627
628 /* Return a SAM domain policy handle on a domain */
629
630 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
631 {
632         struct winbindd_cm_conn *conn, *basic_conn = NULL;
633         static CLI_POLICY_HND hnd;
634         NTSTATUS result;
635         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
636
637         /* Look for existing connections */
638
639         for (conn = cm_conns; conn; conn = conn->next) {
640                 if (strequal(conn->domain, domain) &&
641                     strequal(conn->pipe_name, PIPE_SAMR) &&
642                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
643
644                         if (!connection_ok(conn)) {
645                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
646                                 DLIST_REMOVE(cm_conns, conn);
647                                 return NULL;
648                         }
649
650                         goto ok;
651                 }
652         }
653
654         /* Create a basic handle to open a domain handle from */
655
656         if (!cm_get_sam_handle(domain))
657                 return False;
658
659         for (conn = cm_conns; conn; conn = conn->next) {
660                 if (strequal(conn->domain, domain) &&
661                     strequal(conn->pipe_name, PIPE_SAMR) &&
662                     conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
663                         basic_conn = conn;
664         }
665         
666         if (!(conn = (struct winbindd_cm_conn *)
667               malloc(sizeof(struct winbindd_cm_conn))))
668                 return NULL;
669         
670         ZERO_STRUCTP(conn);
671
672         fstrcpy(conn->domain, basic_conn->domain);
673         fstrcpy(conn->controller, basic_conn->controller);
674         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
675
676         conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
677         conn->cli = basic_conn->cli;
678
679         result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
680                                       &basic_conn->pol, des_access, 
681                                       domain_sid, &conn->pol);
682
683         if (!NT_STATUS_IS_OK(result))
684                 return NULL;
685
686         /* Add to list */
687
688         DLIST_ADD(cm_conns, conn);
689
690  ok:
691         hnd.pol = conn->pol;
692         hnd.cli = conn->cli;
693
694         return &hnd;
695 }
696
697 /* Return a SAM policy handle on a domain user */
698
699 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
700                                        uint32 user_rid)
701 {
702         struct winbindd_cm_conn *conn, *basic_conn = NULL;
703         static CLI_POLICY_HND hnd;
704         NTSTATUS result;
705         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
706
707         /* Look for existing connections */
708
709         for (conn = cm_conns; conn; conn = conn->next) {
710                 if (strequal(conn->domain, domain) &&
711                     strequal(conn->pipe_name, PIPE_SAMR) &&
712                     conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
713                     conn->pipe_data.samr.rid == user_rid) {
714
715                         if (!connection_ok(conn)) {
716                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
717                                 DLIST_REMOVE(cm_conns, conn);
718                                 return NULL;
719                         }
720                 
721                         goto ok;
722                 }
723         }
724
725         /* Create a domain handle to open a user handle from */
726
727         if (!cm_get_sam_dom_handle(domain, domain_sid))
728                 return NULL;
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                         basic_conn = conn;
735         }
736         
737         if (!basic_conn) {
738                 DEBUG(0, ("No domain sam handle was created!\n"));
739                 return NULL;
740         }
741
742         if (!(conn = (struct winbindd_cm_conn *)
743               malloc(sizeof(struct winbindd_cm_conn))))
744                 return NULL;
745         
746         ZERO_STRUCTP(conn);
747
748         fstrcpy(conn->domain, basic_conn->domain);
749         fstrcpy(conn->controller, basic_conn->controller);
750         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
751         
752         conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
753         conn->cli = basic_conn->cli;
754         conn->pipe_data.samr.rid = user_rid;
755
756         result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
757                                     &basic_conn->pol, des_access, user_rid,
758                                     &conn->pol);
759
760         if (!NT_STATUS_IS_OK(result))
761                 return NULL;
762
763         /* Add to list */
764
765         DLIST_ADD(cm_conns, conn);
766
767  ok:
768         hnd.pol = conn->pol;
769         hnd.cli = conn->cli;
770
771         return &hnd;
772 }
773
774 /* Return a SAM policy handle on a domain group */
775
776 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
777                                         uint32 group_rid)
778 {
779         struct winbindd_cm_conn *conn, *basic_conn = NULL;
780         static CLI_POLICY_HND hnd;
781         NTSTATUS result;
782         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
783
784         /* Look for existing connections */
785
786         for (conn = cm_conns; conn; conn = conn->next) {
787                 if (strequal(conn->domain, domain) &&
788                     strequal(conn->pipe_name, PIPE_SAMR) &&
789                     conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
790                     conn->pipe_data.samr.rid == group_rid) {
791
792                         if (!connection_ok(conn)) {
793                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
794                                 DLIST_REMOVE(cm_conns, conn);
795                                 return NULL;
796                         }
797                 
798                         goto ok;
799                 }
800         }
801
802         /* Create a domain handle to open a user handle from */
803
804         if (!cm_get_sam_dom_handle(domain, domain_sid))
805                 return NULL;
806
807         for (conn = cm_conns; conn; conn = conn->next) {
808                 if (strequal(conn->domain, domain) &&
809                     strequal(conn->pipe_name, PIPE_SAMR) &&
810                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
811                         basic_conn = conn;
812         }
813         
814         if (!basic_conn) {
815                 DEBUG(0, ("No domain sam handle was created!\n"));
816                 return NULL;
817         }
818
819         if (!(conn = (struct winbindd_cm_conn *)
820               malloc(sizeof(struct winbindd_cm_conn))))
821                 return NULL;
822         
823         ZERO_STRUCTP(conn);
824
825         fstrcpy(conn->domain, basic_conn->domain);
826         fstrcpy(conn->controller, basic_conn->controller);
827         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
828         
829         conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
830         conn->cli = basic_conn->cli;
831         conn->pipe_data.samr.rid = group_rid;
832
833         result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
834                                     &basic_conn->pol, des_access, group_rid,
835                                     &conn->pol);
836
837         if (!NT_STATUS_IS_OK(result))
838                 return NULL;
839
840         /* Add to list */
841
842         DLIST_ADD(cm_conns, conn);
843
844  ok:
845         hnd.pol = conn->pol;
846         hnd.cli = conn->cli;
847
848         return &hnd;
849 }
850
851 #endif
852
853 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
854    netlogon pipe as no handle is returned. */
855
856 NTSTATUS cm_get_netlogon_cli(const char *domain, 
857                              const unsigned char *trust_passwd, 
858                              uint32 sec_channel_type,
859                              struct cli_state **cli)
860 {
861         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
862         struct winbindd_cm_conn *conn;
863         uint32 neg_flags = 0x000001ff;
864         fstring lock_name;
865         BOOL got_mutex;
866
867         if (!cli)
868                 return NT_STATUS_INVALID_PARAMETER;
869
870         /* Open an initial conection - keep the mutex. */
871
872         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn)))
873                 return result;
874         
875         snprintf(lock_name, sizeof(lock_name), "NETLOGON\\%s", conn->controller);
876
877         if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
878                 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
879         }
880                         
881         result = cli_nt_setup_creds(conn->cli, sec_channel_type, trust_passwd, &neg_flags, 2);
882         
883         if (got_mutex)
884                 secrets_named_mutex_release(lock_name);
885                         
886         if (!NT_STATUS_IS_OK(result)) {
887                 DEBUG(0, ("error connecting to domain password server: %s\n",
888                           nt_errstr(result)));
889                 
890                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
891                 if (conn->cli->fd == -1) {
892                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn)))
893                                 return result;
894                         
895                         snprintf(lock_name, sizeof(lock_name), "NETLOGON\\%s", conn->controller);
896                         if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
897                                 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
898                         }
899                         
900                         /* Try again */
901                         result = cli_nt_setup_creds( conn->cli, sec_channel_type,trust_passwd, &neg_flags, 2);
902                         
903                         if (got_mutex)
904                                 secrets_named_mutex_release(lock_name);
905                 }
906                 
907                 if (!NT_STATUS_IS_OK(result)) {
908                         cli_shutdown(conn->cli);
909                         DLIST_REMOVE(cm_conns, conn);
910                         SAFE_FREE(conn);
911                         return result;
912                 }
913         }
914
915         *cli = conn->cli;
916
917         return result;
918 }
919
920 /* Dump the current connection status */
921
922 static void dump_conn_list(void)
923 {
924         struct winbindd_cm_conn *con;
925
926         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
927
928         for(con = cm_conns; con; con = con->next) {
929                 char *msg;
930
931                 /* Display pipe info */
932                 
933                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
934                         DEBUG(0, ("Error: not enough memory!\n"));
935                 } else {
936                         DEBUG(0, ("%s\n", msg));
937                         SAFE_FREE(msg);
938                 }
939         }
940 }
941
942 void winbindd_cm_status(void)
943 {
944         /* List open connections */
945
946         DEBUG(0, ("winbindd connection manager status:\n"));
947
948         if (cm_conns)
949                 dump_conn_list();
950         else
951                 DEBUG(0, ("\tNo active connections\n"));
952 }