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 /* Choose between anonymous or authenticated connections. We need to use
186 an authenticated connection if DCs have the RestrictAnonymous registry
187 entry set > 0, or the "Additional restrictions for anonymous
188 connections" set in the win2k Local Security Policy. */
190 void cm_init_creds(struct ntuser_creds *creds)
192 char *username, *password;
196 creds->pwd.null_pwd = True; /* anonymoose */
198 username = secrets_fetch(SECRETS_AUTH_USER, NULL);
199 password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
201 if (username && *username) {
202 pwd_set_cleartext(&creds->pwd, password);
204 fstrcpy(creds->user_name, username);
205 fstrcpy(creds->domain, lp_workgroup());
207 DEBUG(3, ("IPC$ connections done %s\\%s\n", creds->domain,
210 DEBUG(3, ("IPC$ connections done anonymously\n"));
213 /* Open a new smb pipe connection to a DC on a given domain. Cache
214 negative creation attempts so we don't try and connect to broken
215 machines too often. */
217 #define OPEN_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
219 struct open_connection_cache {
223 struct open_connection_cache *prev, *next;
226 static BOOL cm_open_connection(char *domain, char *pipe_name,
227 struct winbindd_cm_conn *new_conn)
229 static struct open_connection_cache *open_connection_cache;
230 struct open_connection_cache *occ;
231 struct nmb_name calling, called;
232 extern pstring global_myname;
234 struct in_addr dest_ip;
236 struct ntuser_creds creds;
238 fstrcpy(new_conn->domain, domain);
239 fstrcpy(new_conn->pipe_name, pipe_name);
241 /* Look for a domain controller for this domain. Negative results
242 are cached so don't bother applying the caching for this
243 function just yet. */
245 if (!cm_get_dc_name(domain, new_conn->controller))
248 /* Return false if we have tried to look up this domain and netbios
249 name before and failed. */
251 for (occ = open_connection_cache; occ; occ = occ->next) {
253 if (!(strequal(domain, occ->domain_name) &&
254 strequal(new_conn->controller, occ->controller)))
255 continue; /* Not our domain */
257 if ((time(NULL) - occ->lookup_time) >
258 OPEN_CONNECTION_CACHE_TIMEOUT) {
260 /* Cache entry has expired, delete it */
262 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
264 DLIST_REMOVE(open_connection_cache, occ);
270 /* The timeout hasn't expired yet so return false */
272 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
277 /* Initialise SMB connection */
279 if (!(new_conn->cli = cli_initialise(NULL)))
282 if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
285 make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 0x20);
286 make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
288 cm_init_creds(&creds);
290 cli_init_creds(new_conn->cli, &creds);
292 if (!cli_establish_connection(new_conn->cli, new_conn->controller,
293 &dest_ip, &calling, &called, "IPC$",
297 if (!cli_nt_session_open (new_conn->cli, pipe_name))
304 /* Create negative lookup cache entry for this domain and controller */
307 if (!(occ = (struct open_connection_cache *)
308 malloc(sizeof(struct open_connection_cache))))
313 fstrcpy(occ->domain_name, domain);
314 fstrcpy(occ->controller, new_conn->controller);
315 occ->lookup_time = time(NULL);
317 DLIST_ADD(open_connection_cache, occ);
320 if (!result && new_conn->cli)
321 cli_shutdown(new_conn->cli);
326 /* Return true if a connection is still alive */
328 static BOOL connection_ok(struct winbindd_cm_conn *conn)
330 if (!conn->cli->initialised)
333 if (conn->cli->fd == -1)
339 /* Return a LSA policy handle on a domain */
341 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
343 struct winbindd_cm_conn *conn;
344 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
346 static CLI_POLICY_HND hnd;
348 /* Look for existing connections */
350 for (conn = cm_conns; conn; conn = conn->next) {
351 if (strequal(conn->domain, domain) &&
352 strequal(conn->pipe_name, PIPE_LSARPC)) {
354 if (!connection_ok(conn)) {
355 DLIST_REMOVE(cm_conns, conn);
363 /* Create a new one */
365 if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
370 if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
371 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
375 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
376 des_access, &conn->pol);
378 if (!NT_STATUS_IS_OK(result))
383 DLIST_ADD(cm_conns, conn);
392 /* Return a SAM policy handle on a domain */
394 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
396 struct winbindd_cm_conn *conn;
397 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
399 static CLI_POLICY_HND hnd;
401 /* Look for existing connections */
403 for (conn = cm_conns; conn; conn = conn->next) {
404 if (strequal(conn->domain, domain) && strequal(conn->pipe_name, PIPE_SAMR)) {
406 if (!connection_ok(conn)) {
407 DLIST_REMOVE(cm_conns, conn);
415 /* Create a new one */
417 if (!(conn = (struct winbindd_cm_conn *)
418 malloc(sizeof(struct winbindd_cm_conn))))
423 if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
424 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
428 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
429 des_access, &conn->pol);
431 if (!NT_STATUS_IS_OK(result))
436 DLIST_ADD(cm_conns, conn);
447 /* Return a SAM domain policy handle on a domain */
449 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
451 struct winbindd_cm_conn *conn, *basic_conn = NULL;
452 static CLI_POLICY_HND hnd;
454 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
456 /* Look for existing connections */
458 for (conn = cm_conns; conn; conn = conn->next) {
459 if (strequal(conn->domain, domain) &&
460 strequal(conn->pipe_name, PIPE_SAMR) &&
461 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
463 if (!connection_ok(conn)) {
464 DLIST_REMOVE(cm_conns, conn);
472 /* Create a basic handle to open a domain handle from */
474 if (!cm_get_sam_handle(domain))
477 for (conn = cm_conns; conn; conn = conn->next) {
478 if (strequal(conn->domain, domain) &&
479 strequal(conn->pipe_name, PIPE_SAMR) &&
480 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
484 if (!(conn = (struct winbindd_cm_conn *)
485 malloc(sizeof(struct winbindd_cm_conn))))
490 fstrcpy(conn->domain, basic_conn->domain);
491 fstrcpy(conn->controller, basic_conn->controller);
492 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
494 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
495 conn->cli = basic_conn->cli;
497 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
498 &basic_conn->pol, des_access,
499 domain_sid, &conn->pol);
501 if (!NT_STATUS_IS_OK(result))
506 DLIST_ADD(cm_conns, conn);
515 /* Return a SAM policy handle on a domain user */
517 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
520 struct winbindd_cm_conn *conn, *basic_conn = NULL;
521 static CLI_POLICY_HND hnd;
523 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
525 /* Look for existing connections */
527 for (conn = cm_conns; conn; conn = conn->next) {
528 if (strequal(conn->domain, domain) &&
529 strequal(conn->pipe_name, PIPE_SAMR) &&
530 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
531 conn->pipe_data.samr.rid == user_rid) {
533 if (!connection_ok(conn)) {
534 DLIST_REMOVE(cm_conns, conn);
542 /* Create a domain handle to open a user handle from */
544 if (!cm_get_sam_dom_handle(domain, domain_sid))
547 for (conn = cm_conns; conn; conn = conn->next) {
548 if (strequal(conn->domain, domain) &&
549 strequal(conn->pipe_name, PIPE_SAMR) &&
550 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
555 DEBUG(0, ("No domain sam handle was created!\n"));
559 if (!(conn = (struct winbindd_cm_conn *)
560 malloc(sizeof(struct winbindd_cm_conn))))
565 fstrcpy(conn->domain, basic_conn->domain);
566 fstrcpy(conn->controller, basic_conn->controller);
567 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
569 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
570 conn->cli = basic_conn->cli;
571 conn->pipe_data.samr.rid = user_rid;
573 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
574 &basic_conn->pol, des_access, user_rid,
577 if (!NT_STATUS_IS_OK(result))
582 DLIST_ADD(cm_conns, conn);
591 /* Return a SAM policy handle on a domain group */
593 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
596 struct winbindd_cm_conn *conn, *basic_conn = NULL;
597 static CLI_POLICY_HND hnd;
599 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
601 /* Look for existing connections */
603 for (conn = cm_conns; conn; conn = conn->next) {
604 if (strequal(conn->domain, domain) &&
605 strequal(conn->pipe_name, PIPE_SAMR) &&
606 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
607 conn->pipe_data.samr.rid == group_rid) {
609 if (!connection_ok(conn)) {
610 DLIST_REMOVE(cm_conns, conn);
618 /* Create a domain handle to open a user handle from */
620 if (!cm_get_sam_dom_handle(domain, domain_sid))
623 for (conn = cm_conns; conn; conn = conn->next) {
624 if (strequal(conn->domain, domain) &&
625 strequal(conn->pipe_name, PIPE_SAMR) &&
626 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
631 DEBUG(0, ("No domain sam handle was created!\n"));
635 if (!(conn = (struct winbindd_cm_conn *)
636 malloc(sizeof(struct winbindd_cm_conn))))
641 fstrcpy(conn->domain, basic_conn->domain);
642 fstrcpy(conn->controller, basic_conn->controller);
643 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
645 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
646 conn->cli = basic_conn->cli;
647 conn->pipe_data.samr.rid = group_rid;
649 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
650 &basic_conn->pol, des_access, group_rid,
653 if (!NT_STATUS_IS_OK(result))
658 DLIST_ADD(cm_conns, conn);
669 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
670 netlogon pipe as no handle is returned. */
672 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
673 struct cli_state **cli)
675 struct winbindd_cm_conn conn;
676 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
678 /* Open an initial conection */
682 if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
683 DEBUG(3, ("Could not open a connection to %s\n", domain));
687 result = new_cli_nt_setup_creds(conn.cli, trust_passwd);
689 if (!NT_STATUS_IS_OK(result)) {
690 DEBUG(0, ("error connecting to domain password server: %s\n",
691 get_nt_error_msg(result)));
692 cli_shutdown(conn.cli);
702 /* Dump the current connection status */
704 static void dump_conn_list(void)
706 struct winbindd_cm_conn *con;
708 DEBUG(0, ("\tDomain Controller Pipe\n"));
710 for(con = cm_conns; con; con = con->next) {
713 /* Display pipe info */
715 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
716 DEBUG(0, ("Error: not enough memory!\n"));
718 DEBUG(0, ("%s\n", msg));
724 void winbindd_cm_status(void)
726 /* List open connections */
728 DEBUG(0, ("winbindd connection manager status:\n"));
733 DEBUG(0, ("\tNo active connections\n"));