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 static 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;
97 /* Check the cache for previous lookups */
99 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
101 if (!strequal(domain, dcc->domain_name))
102 continue; /* Not our domain */
104 if ((time(NULL) - dcc->lookup_time) >
105 GET_DC_NAME_CACHE_TIMEOUT) {
107 /* Cache entry has expired, delete it */
109 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
111 DLIST_REMOVE(get_dc_name_cache, dcc);
117 /* Return a positive or negative lookup for this domain */
119 if (dcc->srv_name[0]) {
120 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
121 fstrcpy(srv_name, dcc->srv_name);
124 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
129 /* Add cache entry for this lookup. */
131 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
133 if (!(dcc = (struct get_dc_name_cache *)
134 malloc(sizeof(struct get_dc_name_cache))))
139 fstrcpy(dcc->domain_name, domain);
140 dcc->lookup_time = time(NULL);
142 DLIST_ADD(get_dc_name_cache, dcc);
144 /* Lookup domain controller name */
146 if (!get_dc_list(False, domain, &ip_list, &count)) {
147 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
151 /* Firstly choose a PDC/BDC who has the same network address as any
152 of our interfaces. */
154 for (i = 0; i < count; i++) {
155 if(is_local_net(ip_list[i]))
160 DEBUG(3, ("No domain controllers for domain %s\n", domain));
164 i = (sys_random() % count);
170 /* We really should be doing a GETDC call here rather than a node
173 if (!name_status_find(domain, 0x1c, 0x20, dc_ip, srv_name)) {
174 DEBUG(3, ("Error looking up DC name for %s in domain %s\n", inet_ntoa(dc_ip), domain));
178 /* We have a name so make the cache entry positive now */
180 fstrcpy(dcc->srv_name, srv_name);
185 /* Open a new smb pipe connection to a DC on a given domain. Cache
186 negative creation attempts so we don't try and connect to broken
187 machines too often. */
189 #define OPEN_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
191 struct open_connection_cache {
195 struct open_connection_cache *prev, *next;
198 static BOOL cm_open_connection(char *domain, char *pipe_name,
199 struct winbindd_cm_conn *new_conn)
201 static struct open_connection_cache *open_connection_cache;
202 struct open_connection_cache *occ;
203 struct nmb_name calling, called;
204 extern pstring global_myname;
206 struct in_addr dest_ip;
208 struct ntuser_creds creds;
210 fstrcpy(new_conn->domain, domain);
211 fstrcpy(new_conn->pipe_name, pipe_name);
213 /* Look for a domain controller for this domain. Negative results
214 are cached so don't bother applying the caching for this
215 function just yet. */
217 if (!cm_get_dc_name(domain, new_conn->controller))
220 /* Return false if we have tried to look up this domain and netbios
221 name before and failed. */
223 for (occ = open_connection_cache; occ; occ = occ->next) {
225 if (!(strequal(domain, occ->domain_name) &&
226 strequal(new_conn->controller, occ->controller)))
227 continue; /* Not our domain */
229 if ((time(NULL) - occ->lookup_time) >
230 OPEN_CONNECTION_CACHE_TIMEOUT) {
232 /* Cache entry has expired, delete it */
234 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
236 DLIST_REMOVE(open_connection_cache, occ);
242 /* The timeout hasn't expired yet so return false */
244 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
249 /* Initialise SMB connection */
251 if (!(new_conn->cli = cli_initialise(NULL)))
254 if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
257 make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 0x20);
258 make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
261 creds.pwd.null_pwd = 1;
263 cli_init_creds(new_conn->cli, &creds);
265 if (!cli_establish_connection(new_conn->cli, new_conn->controller,
266 &dest_ip, &calling, &called, "IPC$",
270 if (!cli_nt_session_open (new_conn->cli, pipe_name))
277 /* Create negative lookup cache entry for this domain and controller */
280 if (!(occ = (struct open_connection_cache *)
281 malloc(sizeof(struct open_connection_cache))))
286 fstrcpy(occ->domain_name, domain);
287 fstrcpy(occ->controller, new_conn->controller);
288 occ->lookup_time = time(NULL);
290 DLIST_ADD(open_connection_cache, occ);
293 if (!result && new_conn->cli)
294 cli_shutdown(new_conn->cli);
299 /* Return true if a connection is still alive */
301 static BOOL connection_ok(struct winbindd_cm_conn *conn)
303 if (!conn->cli->initialised)
306 if (conn->cli->fd == -1)
312 /* Return a LSA policy handle on a domain */
314 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
316 struct winbindd_cm_conn *conn;
317 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
319 static CLI_POLICY_HND hnd;
321 /* Look for existing connections */
323 for (conn = cm_conns; conn; conn = conn->next) {
324 if (strequal(conn->domain, domain) &&
325 strequal(conn->pipe_name, PIPE_LSARPC)) {
327 if (!connection_ok(conn)) {
328 DLIST_REMOVE(cm_conns, conn);
336 /* Create a new one */
338 if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
343 if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
344 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
348 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
349 des_access, &conn->pol);
351 if (!NT_STATUS_IS_OK(result))
356 DLIST_ADD(cm_conns, conn);
365 /* Return a SAM policy handle on a domain */
367 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
369 struct winbindd_cm_conn *conn;
370 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
372 static CLI_POLICY_HND hnd;
374 /* Look for existing connections */
376 for (conn = cm_conns; conn; conn = conn->next) {
377 if (strequal(conn->domain, domain) && strequal(conn->pipe_name, PIPE_SAMR)) {
379 if (!connection_ok(conn)) {
380 DLIST_REMOVE(cm_conns, conn);
388 /* Create a new one */
390 if (!(conn = (struct winbindd_cm_conn *)
391 malloc(sizeof(struct winbindd_cm_conn))))
396 if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
397 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
401 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
402 des_access, &conn->pol);
404 if (!NT_STATUS_IS_OK(result))
409 DLIST_ADD(cm_conns, conn);
420 /* Return a SAM domain policy handle on a domain */
422 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
424 struct winbindd_cm_conn *conn, *basic_conn = NULL;
425 static CLI_POLICY_HND hnd;
427 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
429 /* Look for existing connections */
431 for (conn = cm_conns; conn; conn = conn->next) {
432 if (strequal(conn->domain, domain) &&
433 strequal(conn->pipe_name, PIPE_SAMR) &&
434 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
436 if (!connection_ok(conn)) {
437 DLIST_REMOVE(cm_conns, conn);
445 /* Create a basic handle to open a domain handle from */
447 if (!cm_get_sam_handle(domain))
450 for (conn = cm_conns; conn; conn = conn->next) {
451 if (strequal(conn->domain, domain) &&
452 strequal(conn->pipe_name, PIPE_SAMR) &&
453 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
457 if (!(conn = (struct winbindd_cm_conn *)
458 malloc(sizeof(struct winbindd_cm_conn))))
463 fstrcpy(conn->domain, basic_conn->domain);
464 fstrcpy(conn->controller, basic_conn->controller);
465 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
467 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
468 conn->cli = basic_conn->cli;
470 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
471 &basic_conn->pol, des_access,
472 domain_sid, &conn->pol);
474 if (!NT_STATUS_IS_OK(result))
479 DLIST_ADD(cm_conns, conn);
488 /* Return a SAM policy handle on a domain user */
490 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
493 struct winbindd_cm_conn *conn, *basic_conn = NULL;
494 static CLI_POLICY_HND hnd;
496 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
498 /* Look for existing connections */
500 for (conn = cm_conns; conn; conn = conn->next) {
501 if (strequal(conn->domain, domain) &&
502 strequal(conn->pipe_name, PIPE_SAMR) &&
503 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
504 conn->pipe_data.samr.rid == user_rid) {
506 if (!connection_ok(conn)) {
507 DLIST_REMOVE(cm_conns, conn);
515 /* Create a domain handle to open a user handle from */
517 if (!cm_get_sam_dom_handle(domain, domain_sid))
520 for (conn = cm_conns; conn; conn = conn->next) {
521 if (strequal(conn->domain, domain) &&
522 strequal(conn->pipe_name, PIPE_SAMR) &&
523 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
528 DEBUG(0, ("No domain sam handle was created!\n"));
532 if (!(conn = (struct winbindd_cm_conn *)
533 malloc(sizeof(struct winbindd_cm_conn))))
538 fstrcpy(conn->domain, basic_conn->domain);
539 fstrcpy(conn->controller, basic_conn->controller);
540 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
542 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
543 conn->cli = basic_conn->cli;
544 conn->pipe_data.samr.rid = user_rid;
546 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
547 &basic_conn->pol, des_access, user_rid,
550 if (!NT_STATUS_IS_OK(result))
555 DLIST_ADD(cm_conns, conn);
564 /* Return a SAM policy handle on a domain group */
566 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
569 struct winbindd_cm_conn *conn, *basic_conn = NULL;
570 static CLI_POLICY_HND hnd;
572 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
574 /* Look for existing connections */
576 for (conn = cm_conns; conn; conn = conn->next) {
577 if (strequal(conn->domain, domain) &&
578 strequal(conn->pipe_name, PIPE_SAMR) &&
579 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
580 conn->pipe_data.samr.rid == group_rid) {
582 if (!connection_ok(conn)) {
583 DLIST_REMOVE(cm_conns, conn);
591 /* Create a domain handle to open a user handle from */
593 if (!cm_get_sam_dom_handle(domain, domain_sid))
596 for (conn = cm_conns; conn; conn = conn->next) {
597 if (strequal(conn->domain, domain) &&
598 strequal(conn->pipe_name, PIPE_SAMR) &&
599 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
604 DEBUG(0, ("No domain sam handle was created!\n"));
608 if (!(conn = (struct winbindd_cm_conn *)
609 malloc(sizeof(struct winbindd_cm_conn))))
614 fstrcpy(conn->domain, basic_conn->domain);
615 fstrcpy(conn->controller, basic_conn->controller);
616 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
618 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
619 conn->cli = basic_conn->cli;
620 conn->pipe_data.samr.rid = group_rid;
622 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
623 &basic_conn->pol, des_access, group_rid,
626 if (!NT_STATUS_IS_OK(result))
631 DLIST_ADD(cm_conns, conn);
642 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
643 netlogon pipe as no handle is returned. */
645 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
646 struct cli_state **cli)
648 struct winbindd_cm_conn conn;
649 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
651 /* Open an initial conection */
655 if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
656 DEBUG(3, ("Could not open a connection to %s\n", domain));
660 result = cli_nt_setup_creds(conn.cli, trust_passwd);
662 if (!NT_STATUS_IS_OK(result)) {
663 DEBUG(0, ("error connecting to domain password server: %s\n",
664 get_nt_error_msg(result)));
665 cli_shutdown(conn.cli);
675 /* Dump the current connection status */
677 static void dump_conn_list(void)
679 struct winbindd_cm_conn *con;
681 DEBUG(0, ("\tDomain Controller Pipe\n"));
683 for(con = cm_conns; con; con = con->next) {
686 /* Display pipe info */
688 asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name);
690 DEBUG(0, ("%s\n", msg));
695 void winbindd_cm_status(void)
697 /* List open connections */
699 DEBUG(0, ("winbindd connection manager status:\n"));
704 DEBUG(0, ("\tNo active connections\n"));