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