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