2 Unix SMB/Netbios implementation.
5 Winbind daemon connection manager
7 Copyright (C) Tim Potter 2001
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.
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.
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.
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:
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
33 - manage re-entrancy for when winbindd becomes able to handle
34 multiple outstanding rpc requests
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. We actually cache policy handles - tng
42 caches connections to pipes.
44 The TNG design is quite good but I disagree with some aspects of the
52 - I'm pretty annoyed by all the make_nmb_name() stuff. It should be
53 moved down into another function.
55 - There needs to be a utility function in libsmb/namequery.c that does
58 - When closing down sam handles we need to close down user, group and
61 - Take care when destroying cli_structs as they can be shared between
68 /* We store lists of connections here */
71 SAM_PIPE_BASIC, /* A basic handle */
72 SAM_PIPE_DOM, /* A domain handle */
73 SAM_PIPE_USER, /* A handle on a user */
74 SAM_PIPE_GROUP /* A handle on a group */
77 /* Return a string description of a SAM pipe type */
79 static char *pipe_type(enum sam_pipe_type pt)
104 /* Global list of connections. Initially a DLIST but can become a hash
105 table or whatever later. */
107 struct winbindd_cm_conn {
108 struct winbindd_cm_conn *prev, *next;
112 struct cli_state *cli;
115 /* Pipe-specific properties for this instance */
119 enum sam_pipe_type pipe_type;
125 struct winbindd_cm_conn *cm_conns = NULL;
127 /* Get a domain controller name. Cache positive and negative lookups so we
128 don't go to the network too often when something is badly broken. */
130 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
132 struct get_dc_name_cache {
136 struct get_dc_name_cache *prev, *next;
139 static BOOL cm_get_dc_name(char *domain, fstring srv_name)
141 static struct get_dc_name_cache *get_dc_name_cache;
142 struct get_dc_name_cache *dcc;
143 struct in_addr *ip_list, dc_ip;
144 extern pstring global_myname;
147 /* Check the cache for previous lookups */
149 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
151 if (!strequal(domain, dcc->domain_name))
152 continue; /* Not our domain */
154 if ((time(NULL) - dcc->lookup_time) > GET_DC_NAME_CACHE_TIMEOUT) {
156 /* Cache entry has expired, delete it */
158 DEBUG(10, ("get_dc_name_cache entry expired for %s\n",
161 DLIST_REMOVE(get_dc_name_cache, dcc);
167 /* Return a positive or negative lookup for this domain */
169 if (dcc->srv_name[0]) {
170 DEBUG(10, ("returning positive get_dc_name_cache "
171 "entry for %s\n", domain));
172 fstrcpy(srv_name, dcc->srv_name);
175 DEBUG(10, ("returning negative get_dc_name_cache "
176 "entry for %s\n", domain));
181 /* Add cache entry for this lookup. */
183 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
185 if (!(dcc = (struct get_dc_name_cache *)
186 malloc(sizeof(struct get_dc_name_cache))))
191 fstrcpy(dcc->domain_name, domain);
192 dcc->lookup_time = time(NULL);
194 DLIST_ADD(get_dc_name_cache, dcc);
196 /* Lookup domain controller name */
198 if (!get_dc_list(False, domain, &ip_list, &count))
201 /* Firstly choose a PDC/BDC who has the same network address as any
202 of our interfaces. */
204 for (i = 0; i < count; i++) {
205 if(is_local_net(ip_list[i]))
209 i = (sys_random() % count);
215 if (!lookup_pdc_name(global_myname, domain, &dc_ip, srv_name))
218 /* We have a name so make the cache entry positive now */
220 fstrcpy(dcc->srv_name, srv_name);
225 /* Open a new smb pipe connection to a DC on a given domain. Cache
226 negative creation attempts so we don't try and connect to broken
227 machines too often. */
229 #define OPEN_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
231 struct open_connection_cache {
235 struct open_connection_cache *prev, *next;
238 static BOOL cm_open_connection(char *domain, char *pipe_name,
239 struct winbindd_cm_conn *new_conn)
241 static struct open_connection_cache *open_connection_cache;
242 struct open_connection_cache *occ;
243 struct nmb_name calling, called;
244 extern pstring global_myname;
246 struct in_addr dest_ip;
248 struct ntuser_creds creds;
250 fstrcpy(new_conn->domain, domain);
251 fstrcpy(new_conn->pipe_name, pipe_name);
253 /* Look for a domain controller for this domain. Negative results
254 are cached so don't bother applying the caching for this
255 function just yet. */
257 if (!cm_get_dc_name(domain, new_conn->controller))
260 /* Return false if we have tried to look up this domain and netbios
261 name before and failed. */
263 for (occ = open_connection_cache; occ; occ = occ->next) {
265 if (!(strequal(domain, occ->domain_name) &&
266 strequal(new_conn->controller, occ->controller)))
267 continue; /* Not our domain */
269 if ((time(NULL) - occ->lookup_time) > OPEN_CONNECTION_CACHE_TIMEOUT) {
270 /* Cache entry has expired, delete it */
272 DEBUG(10, ("cm_open_connection cache entry expired "
273 "for %s, %s\n", domain,
274 new_conn->controller));
276 DLIST_REMOVE(open_connection_cache, occ);
282 /* The timeout hasn't expired yet so return false */
284 DEBUG(10, ("returning negative open_connection_cache entry "
285 "for %s, %s\n", domain, new_conn->controller));
290 /* Initialise SMB connection */
292 if (!(new_conn->cli = cli_initialise(NULL)))
295 if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
298 make_nmb_name(&called, dns_to_netbios_name(new_conn->controller),
300 make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
303 creds.pwd.null_pwd = 1;
305 cli_init_creds(new_conn->cli, &creds);
307 if (!cli_establish_connection(new_conn->cli, new_conn->controller,
308 &dest_ip, &calling, &called, "IPC$",
312 if (!cli_nt_session_open (new_conn->cli, pipe_name))
318 /* Create negative lookup cache entry for this domain and
322 if (!(occ = (struct open_connection_cache *)
323 malloc(sizeof(struct open_connection_cache))))
328 fstrcpy(occ->domain_name, domain);
329 fstrcpy(occ->controller, new_conn->controller);
330 occ->lookup_time = time(NULL);
332 DLIST_ADD(open_connection_cache, occ);
335 if (!result && new_conn->cli)
336 cli_shutdown(new_conn->cli);
341 /* Return true if a connection is still alive */
343 static BOOL connection_ok(struct winbindd_cm_conn *conn)
345 if (!conn->cli->initialised)
348 if (conn->cli->fd == -1)
354 /* Return a LSA policy handle on a domain */
356 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
358 struct winbindd_cm_conn *conn;
359 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
361 static CLI_POLICY_HND hnd;
363 /* Look for existing connections */
365 for (conn = cm_conns; conn; conn = conn->next) {
366 if (strequal(conn->domain, domain) &&
367 strequal(conn->pipe_name, PIPE_LSARPC)) {
369 if (!connection_ok(conn)) {
370 DLIST_REMOVE(cm_conns, conn);
378 /* Create a new one */
380 if (!(conn = (struct winbindd_cm_conn *)
381 malloc(sizeof(struct winbindd_cm_conn))))
386 if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
387 DEBUG(3, ("Could not connect to a dc for domain %s\n",
392 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
393 des_access, &conn->pol);
395 if (!NT_STATUS_IS_OK(result))
400 DLIST_ADD(cm_conns, conn);
409 /* Return a SAM policy handle on a domain */
411 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
413 struct winbindd_cm_conn *conn;
414 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
416 static CLI_POLICY_HND hnd;
418 /* Look for existing connections */
420 for (conn = cm_conns; conn; conn = conn->next) {
421 if (strequal(conn->domain, domain) &&
422 strequal(conn->pipe_name, PIPE_SAMR) &&
423 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC) {
425 if (!connection_ok(conn)) {
426 DLIST_REMOVE(cm_conns, conn);
434 /* Create a new one */
436 if (!(conn = (struct winbindd_cm_conn *)
437 malloc(sizeof(struct winbindd_cm_conn))))
442 if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
443 DEBUG(3, ("Could not connect to a dc for domain %s\n",
448 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx, des_access,
451 if (!NT_STATUS_IS_OK(result))
456 DLIST_ADD(cm_conns, conn);
465 /* Return a SAM domain policy handle on a domain */
467 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
469 struct winbindd_cm_conn *conn, *basic_conn = NULL;
470 static CLI_POLICY_HND hnd;
472 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
474 /* Look for existing connections */
476 for (conn = cm_conns; conn; conn = conn->next) {
477 if (strequal(conn->domain, domain) &&
478 strequal(conn->pipe_name, PIPE_SAMR) &&
479 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
481 if (!connection_ok(conn)) {
482 DLIST_REMOVE(cm_conns, conn);
490 /* Create a basic handle to open a domain handle from */
492 if (!cm_get_sam_handle(domain))
495 for (conn = cm_conns; conn; conn = conn->next) {
496 if (strequal(conn->domain, domain) &&
497 strequal(conn->pipe_name, PIPE_SAMR) &&
498 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
502 if (!(conn = (struct winbindd_cm_conn *)
503 malloc(sizeof(struct winbindd_cm_conn))))
508 fstrcpy(conn->domain, basic_conn->domain);
509 fstrcpy(conn->controller, basic_conn->controller);
510 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
512 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
513 conn->cli = basic_conn->cli;
515 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
516 &basic_conn->pol, des_access,
517 domain_sid, &conn->pol);
519 if (!NT_STATUS_IS_OK(result))
524 DLIST_ADD(cm_conns, conn);
533 /* Return a SAM policy handle on a domain user */
535 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
538 struct winbindd_cm_conn *conn, *basic_conn = NULL;
539 static CLI_POLICY_HND hnd;
541 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
543 /* Look for existing connections */
545 for (conn = cm_conns; conn; conn = conn->next) {
546 if (strequal(conn->domain, domain) &&
547 strequal(conn->pipe_name, PIPE_SAMR) &&
548 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
549 conn->pipe_data.samr.rid == user_rid) {
551 if (!connection_ok(conn)) {
552 DLIST_REMOVE(cm_conns, conn);
560 /* Create a domain handle to open a user handle from */
562 if (!cm_get_sam_dom_handle(domain, domain_sid))
565 for (conn = cm_conns; conn; conn = conn->next) {
566 if (strequal(conn->domain, domain) &&
567 strequal(conn->pipe_name, PIPE_SAMR) &&
568 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
573 DEBUG(0, ("No domain sam handle was created!\n"));
577 if (!(conn = (struct winbindd_cm_conn *)
578 malloc(sizeof(struct winbindd_cm_conn))))
583 fstrcpy(conn->domain, basic_conn->domain);
584 fstrcpy(conn->controller, basic_conn->controller);
585 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
587 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
588 conn->cli = basic_conn->cli;
589 conn->pipe_data.samr.rid = user_rid;
591 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
592 &basic_conn->pol, des_access, user_rid,
595 if (!NT_STATUS_IS_OK(result))
600 DLIST_ADD(cm_conns, conn);
609 /* Return a SAM policy handle on a domain group */
611 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
614 struct winbindd_cm_conn *conn, *basic_conn = NULL;
615 static CLI_POLICY_HND hnd;
617 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
619 /* Look for existing connections */
621 for (conn = cm_conns; conn; conn = conn->next) {
622 if (strequal(conn->domain, domain) &&
623 strequal(conn->pipe_name, PIPE_SAMR) &&
624 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
625 conn->pipe_data.samr.rid == group_rid) {
627 if (!connection_ok(conn)) {
628 DLIST_REMOVE(cm_conns, conn);
636 /* Create a domain handle to open a user handle from */
638 if (!cm_get_sam_dom_handle(domain, domain_sid))
641 for (conn = cm_conns; conn; conn = conn->next) {
642 if (strequal(conn->domain, domain) &&
643 strequal(conn->pipe_name, PIPE_SAMR) &&
644 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
649 DEBUG(0, ("No domain sam handle was created!\n"));
653 if (!(conn = (struct winbindd_cm_conn *)
654 malloc(sizeof(struct winbindd_cm_conn))))
659 fstrcpy(conn->domain, basic_conn->domain);
660 fstrcpy(conn->controller, basic_conn->controller);
661 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
663 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
664 conn->cli = basic_conn->cli;
665 conn->pipe_data.samr.rid = group_rid;
667 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
668 &basic_conn->pol, des_access, group_rid,
671 if (!NT_STATUS_IS_OK(result))
676 DLIST_ADD(cm_conns, conn);
685 /* Get a handle on a netlogon pipe */
687 struct cli_state *cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd)
689 struct winbindd_cm_conn conn;
692 /* Open an initial conection */
696 if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
697 DEBUG(3, ("Could not open a connection to %s\n", domain));
701 result = cli_nt_setup_creds(conn.cli, trust_passwd);
703 if (!NT_STATUS_IS_OK(result)) {
704 DEBUG(0, ("error connecting to domain password server: %s\n",
705 get_nt_error_msg(result)));
706 cli_shutdown(conn.cli);
710 /* We only want the client handle from this structure */
715 /* Dump the current connection status */
717 static void dump_conn_list(void)
719 struct winbindd_cm_conn *con;
721 DEBUG(0, ("\tDomain Controller Pipe Handle type\n"));
723 for(con = cm_conns; con; con = con->next) {
726 /* Display pipe info */
728 asprintf(&msg, "\t%-15s %-15s %-16s", con->domain,
729 con->controller, con->pipe_name);
731 /* Display sam specific info */
733 if (strequal(con->pipe_name, PIPE_SAMR)) {
736 asprintf(&msg2, "%s %-7s", msg,
737 pipe_type(con->pipe_data.samr.pipe_type));
743 if (strequal(con->pipe_name, PIPE_SAMR) &&
744 (con->pipe_data.samr.pipe_type == SAM_PIPE_USER ||
745 con->pipe_data.samr.pipe_type == SAM_PIPE_GROUP)) {
748 asprintf(&msg2, "%s %4xh", msg,
749 con->pipe_data.samr.rid);
755 DEBUG(0, ("%s\n", msg));
760 void winbindd_cm_status(void)
762 /* List open connections */
764 DEBUG(0, ("winbindd connection manager status:\n"));
769 DEBUG(0, ("\tNo active connections\n"));