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.
43 The TNG design is quite good but I disagree with some aspects of the
51 - I'm pretty annoyed by all the make_nmb_name() stuff. It should be
52 moved down into another function.
54 - There needs to be a utility function in libsmb/namequery.c that does
57 - Take care when destroying cli_structs as they can be shared between
64 /* Global list of connections. Initially a DLIST but can become a hash
65 table or whatever later. */
67 struct winbindd_cm_conn {
68 struct winbindd_cm_conn *prev, *next;
72 struct cli_state *cli;
76 struct winbindd_cm_conn *cm_conns = NULL;
78 /* Get a domain controller name. Cache positive and negative lookups so we
79 don't go to the network too often when something is badly broken. */
81 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
83 struct get_dc_name_cache {
87 struct get_dc_name_cache *prev, *next;
90 static BOOL cm_get_dc_name(char *domain, fstring srv_name)
92 static struct get_dc_name_cache *get_dc_name_cache;
93 struct get_dc_name_cache *dcc;
94 struct in_addr *ip_list, dc_ip;
95 extern pstring global_myname;
98 /* Check the cache for previous lookups */
100 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
102 if (!strequal(domain, dcc->domain_name))
103 continue; /* Not our domain */
105 if ((time(NULL) - dcc->lookup_time) > GET_DC_NAME_CACHE_TIMEOUT) {
107 /* Cache entry has expired, delete it */
109 DEBUG(10, ("get_dc_name_cache entry expired for %s\n",
112 DLIST_REMOVE(get_dc_name_cache, dcc);
118 /* Return a positive or negative lookup for this domain */
120 if (dcc->srv_name[0]) {
121 DEBUG(10, ("returning positive get_dc_name_cache "
122 "entry for %s\n", domain));
123 fstrcpy(srv_name, dcc->srv_name);
126 DEBUG(10, ("returning negative get_dc_name_cache "
127 "entry for %s\n", domain));
132 /* Add cache entry for this lookup. */
134 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
136 if (!(dcc = (struct get_dc_name_cache *)
137 malloc(sizeof(struct get_dc_name_cache))))
142 fstrcpy(dcc->domain_name, domain);
143 dcc->lookup_time = time(NULL);
145 DLIST_ADD(get_dc_name_cache, dcc);
147 /* Lookup domain controller name */
149 if (!get_dc_list(False, domain, &ip_list, &count))
152 /* Firstly choose a PDC/BDC who has the same network address as any
153 of our interfaces. */
155 for (i = 0; i < count; i++) {
156 if(is_local_net(ip_list[i]))
160 i = (sys_random() % count);
166 if (!lookup_pdc_name(global_myname, domain, &dc_ip, srv_name))
169 /* We have a name so make the cache entry positive now */
171 fstrcpy(dcc->srv_name, srv_name);
176 /* Open a new smb pipe connection to a DC on a given domain. Cache
177 negative creation attempts so we don't try and connect to broken
178 machines too often. */
180 #define OPEN_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
182 struct open_connection_cache {
186 struct open_connection_cache *prev, *next;
189 static BOOL cm_open_connection(char *domain, char *pipe_name,
190 struct winbindd_cm_conn *new_conn)
192 static struct open_connection_cache *open_connection_cache;
193 struct open_connection_cache *occ;
194 struct nmb_name calling, called;
195 extern pstring global_myname;
197 struct in_addr dest_ip;
199 struct ntuser_creds creds;
201 fstrcpy(new_conn->domain, domain);
202 fstrcpy(new_conn->pipe_name, pipe_name);
204 /* Look for a domain controller for this domain. Negative results
205 are cached so don't bother applying the caching for this
206 function just yet. */
208 if (!cm_get_dc_name(domain, new_conn->controller))
211 /* Return false if we have tried to look up this domain and netbios
212 name before and failed. */
214 for (occ = open_connection_cache; occ; occ = occ->next) {
216 if (!(strequal(domain, occ->domain_name) &&
217 strequal(new_conn->controller, occ->controller)))
218 continue; /* Not our domain */
220 if ((time(NULL) - occ->lookup_time) > OPEN_CONNECTION_CACHE_TIMEOUT) {
221 /* Cache entry has expired, delete it */
223 DEBUG(10, ("cm_open_connection cache entry expired "
224 "for %s, %s\n", domain,
225 new_conn->controller));
227 DLIST_REMOVE(open_connection_cache, occ);
233 /* The timeout hasn't expired yet so return false */
235 DEBUG(10, ("returning negative open_connection_cache entry "
236 "for %s, %s\n", domain, new_conn->controller));
241 /* Initialise SMB connection */
243 if (!(new_conn->cli = cli_initialise(NULL)))
246 if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
249 make_nmb_name(&called, dns_to_netbios_name(new_conn->controller),
251 make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
254 creds.pwd.null_pwd = 1;
256 cli_init_creds(new_conn->cli, &creds);
258 if (!cli_establish_connection(new_conn->cli, new_conn->controller,
259 &dest_ip, &calling, &called, "IPC$",
263 if (!cli_nt_session_open (new_conn->cli, pipe_name))
269 /* Create negative lookup cache entry for this domain and
273 if (!(occ = (struct open_connection_cache *)
274 malloc(sizeof(struct open_connection_cache))))
279 fstrcpy(occ->domain_name, domain);
280 fstrcpy(occ->controller, new_conn->controller);
281 occ->lookup_time = time(NULL);
283 DLIST_ADD(open_connection_cache, occ);
286 if (!result && new_conn->cli)
287 cli_shutdown(new_conn->cli);
292 /* Return true if a connection is still alive */
294 static BOOL connection_ok(struct winbindd_cm_conn *conn)
296 if (!conn->cli->initialised)
299 if (conn->cli->fd == -1)
305 /* Return a LSA policy handle on a domain */
307 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
309 struct winbindd_cm_conn *conn;
310 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
312 static CLI_POLICY_HND hnd;
314 /* Look for existing connections */
316 for (conn = cm_conns; conn; conn = conn->next) {
317 if (strequal(conn->domain, domain) &&
318 strequal(conn->pipe_name, PIPE_LSARPC)) {
320 if (!connection_ok(conn)) {
321 DLIST_REMOVE(cm_conns, conn);
329 /* Create a new one */
331 if (!(conn = (struct winbindd_cm_conn *)
332 malloc(sizeof(struct winbindd_cm_conn))))
337 if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
338 DEBUG(3, ("Could not connect to a dc for domain %s\n",
343 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
344 des_access, &conn->pol);
346 if (!NT_STATUS_IS_OK(result))
351 DLIST_ADD(cm_conns, conn);
360 /* Return a SAM policy handle on a domain */
362 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
364 struct winbindd_cm_conn *conn;
365 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
367 static CLI_POLICY_HND hnd;
369 /* Look for existing connections */
371 for (conn = cm_conns; conn; conn = conn->next) {
372 if (strequal(conn->domain, domain) &&
373 strequal(conn->pipe_name, PIPE_SAMR)) {
375 if (!connection_ok(conn)) {
376 DLIST_REMOVE(cm_conns, conn);
384 /* Create a new one */
386 if (!(conn = (struct winbindd_cm_conn *)
387 malloc(sizeof(struct winbindd_cm_conn))))
392 if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
393 DEBUG(3, ("Could not connect to a dc for domain %s\n",
398 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx, des_access,
401 if (!NT_STATUS_IS_OK(result))
406 DLIST_ADD(cm_conns, conn);
417 /* Return a SAM domain policy handle on a domain */
419 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
421 struct winbindd_cm_conn *conn, *basic_conn = NULL;
422 static CLI_POLICY_HND hnd;
424 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
426 /* Look for existing connections */
428 for (conn = cm_conns; conn; conn = conn->next) {
429 if (strequal(conn->domain, domain) &&
430 strequal(conn->pipe_name, PIPE_SAMR) &&
431 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
433 if (!connection_ok(conn)) {
434 DLIST_REMOVE(cm_conns, conn);
442 /* Create a basic handle to open a domain handle from */
444 if (!cm_get_sam_handle(domain))
447 for (conn = cm_conns; conn; conn = conn->next) {
448 if (strequal(conn->domain, domain) &&
449 strequal(conn->pipe_name, PIPE_SAMR) &&
450 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
454 if (!(conn = (struct winbindd_cm_conn *)
455 malloc(sizeof(struct winbindd_cm_conn))))
460 fstrcpy(conn->domain, basic_conn->domain);
461 fstrcpy(conn->controller, basic_conn->controller);
462 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
464 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
465 conn->cli = basic_conn->cli;
467 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
468 &basic_conn->pol, des_access,
469 domain_sid, &conn->pol);
471 if (!NT_STATUS_IS_OK(result))
476 DLIST_ADD(cm_conns, conn);
485 /* Return a SAM policy handle on a domain user */
487 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
490 struct winbindd_cm_conn *conn, *basic_conn = NULL;
491 static CLI_POLICY_HND hnd;
493 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
495 /* Look for existing connections */
497 for (conn = cm_conns; conn; conn = conn->next) {
498 if (strequal(conn->domain, domain) &&
499 strequal(conn->pipe_name, PIPE_SAMR) &&
500 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
501 conn->pipe_data.samr.rid == user_rid) {
503 if (!connection_ok(conn)) {
504 DLIST_REMOVE(cm_conns, conn);
512 /* Create a domain handle to open a user handle from */
514 if (!cm_get_sam_dom_handle(domain, domain_sid))
517 for (conn = cm_conns; conn; conn = conn->next) {
518 if (strequal(conn->domain, domain) &&
519 strequal(conn->pipe_name, PIPE_SAMR) &&
520 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
525 DEBUG(0, ("No domain sam handle was created!\n"));
529 if (!(conn = (struct winbindd_cm_conn *)
530 malloc(sizeof(struct winbindd_cm_conn))))
535 fstrcpy(conn->domain, basic_conn->domain);
536 fstrcpy(conn->controller, basic_conn->controller);
537 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
539 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
540 conn->cli = basic_conn->cli;
541 conn->pipe_data.samr.rid = user_rid;
543 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
544 &basic_conn->pol, des_access, user_rid,
547 if (!NT_STATUS_IS_OK(result))
552 DLIST_ADD(cm_conns, conn);
561 /* Return a SAM policy handle on a domain group */
563 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
566 struct winbindd_cm_conn *conn, *basic_conn = NULL;
567 static CLI_POLICY_HND hnd;
569 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
571 /* Look for existing connections */
573 for (conn = cm_conns; conn; conn = conn->next) {
574 if (strequal(conn->domain, domain) &&
575 strequal(conn->pipe_name, PIPE_SAMR) &&
576 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
577 conn->pipe_data.samr.rid == group_rid) {
579 if (!connection_ok(conn)) {
580 DLIST_REMOVE(cm_conns, conn);
588 /* Create a domain handle to open a user handle from */
590 if (!cm_get_sam_dom_handle(domain, domain_sid))
593 for (conn = cm_conns; conn; conn = conn->next) {
594 if (strequal(conn->domain, domain) &&
595 strequal(conn->pipe_name, PIPE_SAMR) &&
596 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
601 DEBUG(0, ("No domain sam handle was created!\n"));
605 if (!(conn = (struct winbindd_cm_conn *)
606 malloc(sizeof(struct winbindd_cm_conn))))
611 fstrcpy(conn->domain, basic_conn->domain);
612 fstrcpy(conn->controller, basic_conn->controller);
613 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
615 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
616 conn->cli = basic_conn->cli;
617 conn->pipe_data.samr.rid = group_rid;
619 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
620 &basic_conn->pol, des_access, group_rid,
623 if (!NT_STATUS_IS_OK(result))
628 DLIST_ADD(cm_conns, conn);
639 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
640 netlogon pipe as no handle is returned. */
642 struct cli_state *cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd)
644 struct winbindd_cm_conn conn;
647 /* Open an initial conection */
651 if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
652 DEBUG(3, ("Could not open a connection to %s\n", domain));
656 result = cli_nt_setup_creds(conn.cli, trust_passwd);
658 if (!NT_STATUS_IS_OK(result)) {
659 DEBUG(0, ("error connecting to domain password server: %s\n",
660 get_nt_error_msg(result)));
661 cli_shutdown(conn.cli);
665 /* We only want the client handle from this structure */
670 /* Dump the current connection status */
672 static void dump_conn_list(void)
674 struct winbindd_cm_conn *con;
676 DEBUG(0, ("\tDomain Controller Pipe\n"));
678 for(con = cm_conns; con; con = con->next) {
681 /* Display pipe info */
683 asprintf(&msg, "\t%-15s %-15s %-16s", con->domain,
684 con->controller, con->pipe_name);
686 DEBUG(0, ("%s\n", msg));
691 void winbindd_cm_status(void)
693 /* List open connections */
695 DEBUG(0, ("winbindd connection manager status:\n"));
700 DEBUG(0, ("\tNo active connections\n"));