more debug classess activated
[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    
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 #undef DBGC_CLASS
64 #define DBGC_CLASS DBGC_WINBIND
65
66 /* Global list of connections.  Initially a DLIST but can become a hash
67    table or whatever later. */
68
69 struct winbindd_cm_conn {
70         struct winbindd_cm_conn *prev, *next;
71         fstring domain;
72         fstring controller;
73         fstring pipe_name;
74         struct cli_state *cli;
75         POLICY_HND pol;
76 };
77
78 static struct winbindd_cm_conn *cm_conns = NULL;
79
80 /* Get a domain controller name.  Cache positive and negative lookups so we
81    don't go to the network too often when something is badly broken. */
82
83 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
84
85 struct get_dc_name_cache {
86         fstring domain_name;
87         fstring srv_name;
88         time_t lookup_time;
89         struct get_dc_name_cache *prev, *next;
90 };
91
92 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
93 {
94         static struct get_dc_name_cache *get_dc_name_cache;
95         struct get_dc_name_cache *dcc;
96         struct in_addr *ip_list, dc_ip;
97         int count, i;
98
99         /* Check the cache for previous lookups */
100
101         for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
102
103                 if (!strequal(domain, dcc->domain_name))
104                         continue; /* Not our domain */
105
106                 if ((time(NULL) - dcc->lookup_time) > 
107                     GET_DC_NAME_CACHE_TIMEOUT) {
108
109                         /* Cache entry has expired, delete it */
110
111                         DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
112
113                         DLIST_REMOVE(get_dc_name_cache, dcc);
114                         SAFE_FREE(dcc);
115
116                         break;
117                 }
118
119                 /* Return a positive or negative lookup for this domain */
120
121                 if (dcc->srv_name[0]) {
122                         DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
123                         fstrcpy(srv_name, dcc->srv_name);
124                         return True;
125                 } else {
126                         DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
127                         return False;
128                 }
129         }
130
131         /* Add cache entry for this lookup. */
132
133         DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
134
135         if (!(dcc = (struct get_dc_name_cache *) 
136               malloc(sizeof(struct get_dc_name_cache))))
137                 return False;
138
139         ZERO_STRUCTP(dcc);
140
141         fstrcpy(dcc->domain_name, domain);
142         dcc->lookup_time = time(NULL);
143
144         DLIST_ADD(get_dc_name_cache, dcc);
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
158         for (i = 0; i < count; i++) {
159                 if (!is_local_net(ip_list[i]))
160                         continue;
161                 
162                 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
163                         dc_ip = ip_list[i];
164                         goto done;
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                 goto done;
180         }
181         zero_ip(&ip_list[i]); /* Tried and failed. */
182
183         /* Finally return first DC that we can contact */
184
185         for (i = 0; i < count; i++) {
186                 if (is_zero_ip(ip_list[i]))
187                         continue;
188
189                 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
190                         dc_ip = ip_list[i];
191                         goto done;
192                 }
193         }
194
195         /* No-one to talk to )-: */
196         return False;           /* Boo-hoo */
197         
198  done:
199         /* We have the netbios name and IP address of a domain controller.
200            Ideally we should sent a SAMLOGON request to determine whether
201            the DC is alive and kicking.  If we can catch a dead DC before
202            performing a cli_connect() we can avoid a 30-second timeout. */
203
204         /* We have a name so make the cache entry positive now */
205
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         SAFE_FREE(ip_list);
214
215         return True;
216 }
217
218 /* Choose between anonymous or authenticated connections.  We need to use
219    an authenticated connection if DCs have the RestrictAnonymous registry
220    entry set > 0, or the "Additional restrictions for anonymous
221    connections" set in the win2k Local Security Policy. 
222    
223    Caller to free() result in domain, username, password
224 */
225
226 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
227 {
228         *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
229         *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
230         *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
231         
232         if (*username && **username) {
233                 if (!*domain || !**domain) {
234                         *domain = smb_xstrdup(lp_workgroup());
235                 }
236                 
237                 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
238         } else {
239                 DEBUG(3, ("IPC$ connections done anonymously\n"));
240                 *username = smb_xstrdup("");
241                 *domain = smb_xstrdup("");
242                 *password = smb_xstrdup("");
243         }
244 }
245
246 /* Open a new smb pipe connection to a DC on a given domain.  Cache
247    negative creation attempts so we don't try and connect to broken
248    machines too often. */
249
250 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
251
252 struct failed_connection_cache {
253         fstring domain_name;
254         fstring controller;
255         time_t lookup_time;
256         NTSTATUS nt_status;
257         struct failed_connection_cache *prev, *next;
258 };
259
260 static struct failed_connection_cache *failed_connection_cache;
261
262 /* Add an entry to the failed conneciton cache */
263
264 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, NTSTATUS result) {
265         struct failed_connection_cache *fcc;
266
267         SMB_ASSERT(!NT_STATUS_IS_OK(result));
268
269         /* Create negative lookup cache entry for this domain and controller */
270
271         if (!(fcc = (struct failed_connection_cache *)
272               malloc(sizeof(struct failed_connection_cache)))) {
273                 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
274                 return;
275         }
276         
277         ZERO_STRUCTP(fcc);
278         
279         fstrcpy(fcc->domain_name, new_conn->domain);
280         fstrcpy(fcc->controller, new_conn->controller);
281         fcc->lookup_time = time(NULL);
282         fcc->nt_status = result;
283         
284         DLIST_ADD(failed_connection_cache, fcc);
285 }
286         
287 /* Open a connction to the remote server, cache failures for 30 seconds */
288
289 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
290                                struct winbindd_cm_conn *new_conn)
291 {
292         struct failed_connection_cache *fcc;
293         extern pstring global_myname;
294         NTSTATUS result;
295         char *ipc_username, *ipc_domain, *ipc_password;
296         struct in_addr dc_ip;
297
298         ZERO_STRUCT(dc_ip);
299
300         fstrcpy(new_conn->domain, domain);
301         fstrcpy(new_conn->pipe_name, pipe_name);
302         
303         /* Look for a domain controller for this domain.  Negative results
304            are cached so don't bother applying the caching for this
305            function just yet.  */
306
307         if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
308                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
309                 add_failed_connection_entry(new_conn, result);
310                 return result;
311         }
312                 
313         /* Return false if we have tried to look up this domain and netbios
314            name before and failed. */
315
316         for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
317                 
318                 if (!(strequal(domain, fcc->domain_name) &&
319                       strequal(new_conn->controller, fcc->controller)))
320                         continue; /* Not our domain */
321
322                 if ((time(NULL) - fcc->lookup_time) > 
323                     FAILED_CONNECTION_CACHE_TIMEOUT) {
324
325                         /* Cache entry has expired, delete it */
326
327                         DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
328
329                         DLIST_REMOVE(failed_connection_cache, fcc);
330                         free(fcc);
331
332                         break;
333                 }
334
335                 /* The timeout hasn't expired yet so return false */
336
337                 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
338
339                 result = fcc->nt_status;
340                 SMB_ASSERT(!NT_STATUS_IS_OK(result));
341                 return result;
342         }
343
344         /* Initialise SMB connection */
345
346         cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
347
348         DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
349               new_conn->controller, global_myname, ipc_domain, ipc_username));
350
351         result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller, 
352                                      &dc_ip, 0, "IPC$", 
353                                      "IPC", ipc_username, ipc_domain, 
354                                      ipc_password);
355
356         SAFE_FREE(ipc_username);
357         SAFE_FREE(ipc_domain);
358         SAFE_FREE(ipc_password);
359
360         if (!NT_STATUS_IS_OK(result)) {
361                 add_failed_connection_entry(new_conn, result);
362                 return result;
363         }
364         
365         if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
366                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
367                 add_failed_connection_entry(new_conn, result);
368                 cli_shutdown(new_conn->cli);
369                 return result;
370         }
371
372         return NT_STATUS_OK;
373 }
374
375 /* Return true if a connection is still alive */
376
377 static BOOL connection_ok(struct winbindd_cm_conn *conn)
378 {
379         if (!conn) {
380                 smb_panic("Invalid paramater passed to conneciton_ok():  conn was NULL!\n");
381                 return False;
382         }
383
384         if (!conn->cli) {
385                 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
386                           conn->controller, conn->domain, conn->pipe_name));
387                 smb_panic("connection_ok: conn->cli was null!");
388                 return False;
389         }
390
391         if (!conn->cli->initialised) {
392                 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
393                           conn->controller, conn->domain, conn->pipe_name));
394                 smb_panic("connection_ok: conn->cli->initialised is False!");
395                 return False;
396         }
397
398         if (conn->cli->fd == -1) {
399                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
400                           conn->controller, conn->domain, conn->pipe_name));
401                 return False;
402         }
403         
404         return True;
405 }
406
407 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
408
409 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out) 
410 {
411         struct winbindd_cm_conn *conn, conn_temp;
412         NTSTATUS result;
413
414         for (conn = cm_conns; conn; conn = conn->next) {
415                 if (strequal(conn->domain, domain) && 
416                     strequal(conn->pipe_name, pipe_name)) {
417                         if (!connection_ok(conn)) {
418                                 if (conn->cli) {
419                                         cli_shutdown(conn->cli);
420                                 }
421                                 ZERO_STRUCT(conn_temp);
422                                 conn_temp.next = conn->next;
423                                 DLIST_REMOVE(cm_conns, conn);
424                                 SAFE_FREE(conn);
425                                 conn = &conn_temp;  /* Just to keep the loop moving */
426                         } else {
427                                 break;
428                         }
429                 }
430         }
431         
432         if (!conn) {
433                 if (!(conn = malloc(sizeof(*conn))))
434                         return NT_STATUS_NO_MEMORY;
435                 
436                 ZERO_STRUCTP(conn);
437                 
438                 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
439                         DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
440                                   domain, pipe_name, nt_errstr(result)));
441                         SAFE_FREE(conn);
442                         return result;
443                 }
444                 DLIST_ADD(cm_conns, conn);              
445         }
446         
447         *conn_out = conn;
448         return NT_STATUS_OK;
449 }
450
451 /* Return a LSA policy handle on a domain */
452
453 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
454 {
455         struct winbindd_cm_conn *conn;
456         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
457         NTSTATUS result;
458         static CLI_POLICY_HND hnd;
459
460         /* Look for existing connections */
461
462         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
463                 return NULL;
464         }
465
466         /* This *shitty* code needs scrapping ! JRA */
467         if (policy_handle_is_valid(&conn->pol)) {
468                 hnd.pol = conn->pol;
469                 hnd.cli = conn->cli;
470                 return &hnd;
471         }
472         
473         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
474                                      des_access, &conn->pol);
475
476         if (!NT_STATUS_IS_OK(result)) {
477                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
478                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
479                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
480                                 return NULL;
481                         }
482
483                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
484                                                      des_access, &conn->pol);
485                 }
486
487                 if (!NT_STATUS_IS_OK(result)) {
488                         cli_shutdown(conn->cli);
489                         DLIST_REMOVE(cm_conns, conn);
490                         SAFE_FREE(conn);
491                         return NULL;
492                 }
493         }       
494
495         hnd.pol = conn->pol;
496         hnd.cli = conn->cli;
497
498         return &hnd;
499 }
500
501 /* Return a SAM policy handle on a domain */
502
503 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
504
505         struct winbindd_cm_conn *conn;
506         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
507         NTSTATUS result;
508         static CLI_POLICY_HND hnd;
509
510         /* Look for existing connections */
511
512         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
513                 return NULL;
514         }
515         
516         /* This *shitty* code needs scrapping ! JRA */
517         if (policy_handle_is_valid(&conn->pol)) {
518                 hnd.pol = conn->pol;
519                 hnd.cli = conn->cli;
520                 return &hnd;
521         }
522         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
523                                   des_access, &conn->pol);
524
525         if (!NT_STATUS_IS_OK(result)) {
526                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
527                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
528                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
529                                 return NULL;
530                         }
531
532                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
533                                                   des_access, &conn->pol);
534                 }
535
536                 if (!NT_STATUS_IS_OK(result)) {
537                         cli_shutdown(conn->cli);
538                         DLIST_REMOVE(cm_conns, conn);
539                         SAFE_FREE(conn);
540                         return NULL;
541                 }
542         }       
543
544         hnd.pol = conn->pol;
545         hnd.cli = conn->cli;
546
547         return &hnd;
548 }
549
550 #if 0  /* This code now *well* out of date */
551
552 /* Return a SAM domain policy handle on a domain */
553
554 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
555 {
556         struct winbindd_cm_conn *conn, *basic_conn = NULL;
557         static CLI_POLICY_HND hnd;
558         NTSTATUS result;
559         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
560
561         /* Look for existing connections */
562
563         for (conn = cm_conns; conn; conn = conn->next) {
564                 if (strequal(conn->domain, domain) &&
565                     strequal(conn->pipe_name, PIPE_SAMR) &&
566                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
567
568                         if (!connection_ok(conn)) {
569                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
570                                 DLIST_REMOVE(cm_conns, conn);
571                                 return NULL;
572                         }
573
574                         goto ok;
575                 }
576         }
577
578         /* Create a basic handle to open a domain handle from */
579
580         if (!cm_get_sam_handle(domain))
581                 return False;
582
583         for (conn = cm_conns; conn; conn = conn->next) {
584                 if (strequal(conn->domain, domain) &&
585                     strequal(conn->pipe_name, PIPE_SAMR) &&
586                     conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
587                         basic_conn = conn;
588         }
589         
590         if (!(conn = (struct winbindd_cm_conn *)
591               malloc(sizeof(struct winbindd_cm_conn))))
592                 return NULL;
593         
594         ZERO_STRUCTP(conn);
595
596         fstrcpy(conn->domain, basic_conn->domain);
597         fstrcpy(conn->controller, basic_conn->controller);
598         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
599
600         conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
601         conn->cli = basic_conn->cli;
602
603         result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
604                                       &basic_conn->pol, des_access, 
605                                       domain_sid, &conn->pol);
606
607         if (!NT_STATUS_IS_OK(result))
608                 return NULL;
609
610         /* Add to list */
611
612         DLIST_ADD(cm_conns, conn);
613
614  ok:
615         hnd.pol = conn->pol;
616         hnd.cli = conn->cli;
617
618         return &hnd;
619 }
620
621 /* Return a SAM policy handle on a domain user */
622
623 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
624                                        uint32 user_rid)
625 {
626         struct winbindd_cm_conn *conn, *basic_conn = NULL;
627         static CLI_POLICY_HND hnd;
628         NTSTATUS result;
629         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
630
631         /* Look for existing connections */
632
633         for (conn = cm_conns; conn; conn = conn->next) {
634                 if (strequal(conn->domain, domain) &&
635                     strequal(conn->pipe_name, PIPE_SAMR) &&
636                     conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
637                     conn->pipe_data.samr.rid == user_rid) {
638
639                         if (!connection_ok(conn)) {
640                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
641                                 DLIST_REMOVE(cm_conns, conn);
642                                 return NULL;
643                         }
644                 
645                         goto ok;
646                 }
647         }
648
649         /* Create a domain handle to open a user handle from */
650
651         if (!cm_get_sam_dom_handle(domain, domain_sid))
652                 return NULL;
653
654         for (conn = cm_conns; conn; conn = conn->next) {
655                 if (strequal(conn->domain, domain) &&
656                     strequal(conn->pipe_name, PIPE_SAMR) &&
657                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
658                         basic_conn = conn;
659         }
660         
661         if (!basic_conn) {
662                 DEBUG(0, ("No domain sam handle was created!\n"));
663                 return NULL;
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_USER;
677         conn->cli = basic_conn->cli;
678         conn->pipe_data.samr.rid = user_rid;
679
680         result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
681                                     &basic_conn->pol, des_access, user_rid,
682                                     &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 group */
699
700 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
701                                         uint32 group_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_GROUP &&
714                     conn->pipe_data.samr.rid == group_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_GROUP;
754         conn->cli = basic_conn->cli;
755         conn->pipe_data.samr.rid = group_rid;
756
757         result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
758                                     &basic_conn->pol, des_access, group_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 #endif
776
777 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
778    netlogon pipe as no handle is returned. */
779
780 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
781                              struct cli_state **cli)
782 {
783         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
784         struct winbindd_cm_conn *conn;
785
786         if (!cli) {
787                 return NT_STATUS_INVALID_PARAMETER;
788         }
789
790         /* Open an initial conection */
791
792         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
793                 return result;
794         }
795         
796         result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
797                                         SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
798
799         if (!NT_STATUS_IS_OK(result)) {
800                 DEBUG(0, ("error connecting to domain password server: %s\n",
801                           nt_errstr(result)));
802                 
803                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
804                 if (conn->cli->fd == -1) {
805                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
806                                 return result;
807                         }
808                         
809                         /* Try again */
810                         result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
811                                                         SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
812                 }
813                 
814                 if (!NT_STATUS_IS_OK(result)) {
815                         cli_shutdown(conn->cli);
816                         DLIST_REMOVE(cm_conns, conn);
817                         SAFE_FREE(conn);
818                         return result;
819                 }
820         }
821
822         *cli = conn->cli;
823
824         return result;
825 }
826
827 /* Dump the current connection status */
828
829 static void dump_conn_list(void)
830 {
831         struct winbindd_cm_conn *con;
832
833         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
834
835         for(con = cm_conns; con; con = con->next) {
836                 char *msg;
837
838                 /* Display pipe info */
839                 
840                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
841                         DEBUG(0, ("Error: not enough memory!\n"));
842                 } else {
843                         DEBUG(0, ("%s\n", msg));
844                         SAFE_FREE(msg);
845                 }
846         }
847 }
848
849 void winbindd_cm_status(void)
850 {
851         /* List open connections */
852
853         DEBUG(0, ("winbindd connection manager status:\n"));
854
855         if (cm_conns)
856                 dump_conn_list();
857         else
858                 DEBUG(0, ("\tNo active connections\n"));
859 }