2 Unix SMB/CIFS implementation.
4 Winbind daemon connection manager
6 Copyright (C) Tim Potter 2001
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.
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.
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.
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:
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
32 - manage re-entrancy for when winbindd becomes able to handle
33 multiple outstanding rpc requests
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.
42 The TNG design is quite good but I disagree with some aspects of the
50 - I'm pretty annoyed by all the make_nmb_name() stuff. It should be
51 moved down into another function.
53 - There needs to be a utility function in libsmb/namequery.c that does
56 - Take care when destroying cli_structs as they can be shared between
63 /* Global list of connections. Initially a DLIST but can become a hash
64 table or whatever later. */
66 struct winbindd_cm_conn {
67 struct winbindd_cm_conn *prev, *next;
71 struct cli_state *cli;
75 static struct winbindd_cm_conn *cm_conns = NULL;
77 /* Get a domain controller name. Cache positive and negative lookups so we
78 don't go to the network too often when something is badly broken. */
80 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
82 struct get_dc_name_cache {
86 struct get_dc_name_cache *prev, *next;
89 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
91 static struct get_dc_name_cache *get_dc_name_cache;
92 struct get_dc_name_cache *dcc;
93 struct in_addr *ip_list, dc_ip;
96 /* Check the cache for previous lookups */
98 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
100 if (!strequal(domain, dcc->domain_name))
101 continue; /* Not our domain */
103 if ((time(NULL) - dcc->lookup_time) >
104 GET_DC_NAME_CACHE_TIMEOUT) {
106 /* Cache entry has expired, delete it */
108 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
110 DLIST_REMOVE(get_dc_name_cache, dcc);
116 /* Return a positive or negative lookup for this domain */
118 if (dcc->srv_name[0]) {
119 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
120 fstrcpy(srv_name, dcc->srv_name);
123 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
128 /* Add cache entry for this lookup. */
130 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
132 if (!(dcc = (struct get_dc_name_cache *)
133 malloc(sizeof(struct get_dc_name_cache))))
138 fstrcpy(dcc->domain_name, domain);
139 dcc->lookup_time = time(NULL);
141 DLIST_ADD(get_dc_name_cache, dcc);
143 /* Lookup domain controller name. Try the real PDC first to avoid
145 if (!get_dc_list(True, domain, &ip_list, &count)) {
146 if (!get_dc_list(False, domain, &ip_list, &count)) {
147 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
152 /* Pick a nice close server */
153 /* Look for DC on local net */
155 for (i = 0; i < count; i++) {
156 if (!is_local_net(ip_list[i]))
159 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
163 zero_ip(&ip_list[i]);
167 * Secondly try and contact a random PDC/BDC.
170 i = (sys_random() % count);
172 if (!is_zero_ip(ip_list[i]) &&
173 name_status_find(domain, 0x1c, 0x20,
174 ip_list[i], srv_name)) {
178 zero_ip(&ip_list[i]); /* Tried and failed. */
180 /* Finally return first DC that we can contact */
182 for (i = 0; i < count; i++) {
183 if (is_zero_ip(ip_list[i]))
186 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
192 /* No-one to talk to )-: */
193 return False; /* Boo-hoo */
196 /* We have the netbios name and IP address of a domain controller.
197 Ideally we should sent a SAMLOGON request to determine whether
198 the DC is alive and kicking. If we can catch a dead DC before
199 performing a cli_connect() we can avoid a 30-second timeout. */
201 /* We have a name so make the cache entry positive now */
203 fstrcpy(dcc->srv_name, srv_name);
205 DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
206 inet_ntoa(dc_ip), domain));
215 /* Choose between anonymous or authenticated connections. We need to use
216 an authenticated connection if DCs have the RestrictAnonymous registry
217 entry set > 0, or the "Additional restrictions for anonymous
218 connections" set in the win2k Local Security Policy.
220 Caller to free() result in domain, username, password
223 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
225 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
226 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
227 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
229 if (*username && **username) {
230 if (!*domain || !**domain) {
231 *domain = smb_xstrdup(lp_workgroup());
234 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
236 DEBUG(3, ("IPC$ connections done anonymously\n"));
237 *username = smb_xstrdup("");
238 *domain = smb_xstrdup("");
239 *password = smb_xstrdup("");
243 /* Open a new smb pipe connection to a DC on a given domain. Cache
244 negative creation attempts so we don't try and connect to broken
245 machines too often. */
247 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
249 struct failed_connection_cache {
254 struct failed_connection_cache *prev, *next;
257 static struct failed_connection_cache *failed_connection_cache;
259 /* Add an entry to the failed conneciton cache */
261 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, NTSTATUS result) {
262 struct failed_connection_cache *fcc;
264 SMB_ASSERT(!NT_STATUS_IS_OK(result));
266 /* Create negative lookup cache entry for this domain and controller */
268 if (!(fcc = (struct failed_connection_cache *)
269 malloc(sizeof(struct failed_connection_cache)))) {
270 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
276 fstrcpy(fcc->domain_name, new_conn->domain);
277 fstrcpy(fcc->controller, new_conn->controller);
278 fcc->lookup_time = time(NULL);
279 fcc->nt_status = result;
281 DLIST_ADD(failed_connection_cache, fcc);
284 /* Open a connction to the remote server, cache failures for 30 seconds */
286 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
287 struct winbindd_cm_conn *new_conn)
289 struct failed_connection_cache *fcc;
290 extern pstring global_myname;
292 char *ipc_username, *ipc_domain, *ipc_password;
293 struct in_addr dc_ip;
297 fstrcpy(new_conn->domain, domain);
298 fstrcpy(new_conn->pipe_name, pipe_name);
300 /* Look for a domain controller for this domain. Negative results
301 are cached so don't bother applying the caching for this
302 function just yet. */
304 if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
305 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
306 add_failed_connection_entry(new_conn, result);
310 /* Return false if we have tried to look up this domain and netbios
311 name before and failed. */
313 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
315 if (!(strequal(domain, fcc->domain_name) &&
316 strequal(new_conn->controller, fcc->controller)))
317 continue; /* Not our domain */
319 if ((time(NULL) - fcc->lookup_time) >
320 FAILED_CONNECTION_CACHE_TIMEOUT) {
322 /* Cache entry has expired, delete it */
324 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
326 DLIST_REMOVE(failed_connection_cache, fcc);
332 /* The timeout hasn't expired yet so return false */
334 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
336 result = fcc->nt_status;
337 SMB_ASSERT(!NT_STATUS_IS_OK(result));
341 /* Initialise SMB connection */
343 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
345 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
346 new_conn->controller, global_myname, ipc_domain, ipc_username));
348 result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller,
350 "IPC", ipc_username, ipc_domain,
351 ipc_password, strlen(ipc_password));
353 SAFE_FREE(ipc_username);
354 SAFE_FREE(ipc_domain);
355 SAFE_FREE(ipc_password);
357 if (!NT_STATUS_IS_OK(result)) {
358 add_failed_connection_entry(new_conn, result);
362 if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
363 result = NT_STATUS_PIPE_NOT_AVAILABLE;
364 add_failed_connection_entry(new_conn, result);
365 cli_shutdown(new_conn->cli);
372 /* Return true if a connection is still alive */
374 static BOOL connection_ok(struct winbindd_cm_conn *conn)
377 smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
382 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
383 conn->controller, conn->domain, conn->pipe_name));
387 if (!conn->cli->initialised) {
388 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
389 conn->controller, conn->domain, conn->pipe_name));
393 if (conn->cli->fd == -1) {
394 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
395 conn->controller, conn->domain, conn->pipe_name));
402 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
404 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out)
406 struct winbindd_cm_conn *conn, conn_temp;
409 for (conn = cm_conns; conn; conn = conn->next) {
410 if (strequal(conn->domain, domain) &&
411 strequal(conn->pipe_name, pipe_name)) {
412 if (!connection_ok(conn)) {
414 cli_shutdown(conn->cli);
416 conn_temp.next = conn->next;
417 DLIST_REMOVE(cm_conns, conn);
419 conn = &conn_temp; /* Just to keep the loop moving */
427 if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
428 return NT_STATUS_NO_MEMORY;
432 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
433 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
434 domain, pipe_name, nt_errstr(result)));
438 DLIST_ADD(cm_conns, conn);
445 /* Return a LSA policy handle on a domain */
447 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
449 struct winbindd_cm_conn *conn;
450 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
452 static CLI_POLICY_HND hnd;
454 /* Look for existing connections */
456 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
460 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
461 des_access, &conn->pol);
463 if (!NT_STATUS_IS_OK(result)) {
464 /* Hit the cache code again. This cleans out the old connection and gets a new one */
465 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
466 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
470 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
471 des_access, &conn->pol);
474 if (!NT_STATUS_IS_OK(result)) {
475 cli_shutdown(conn->cli);
476 DLIST_REMOVE(cm_conns, conn);
488 /* Return a SAM policy handle on a domain */
490 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
492 struct winbindd_cm_conn *conn;
493 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
495 static CLI_POLICY_HND hnd;
497 /* Look for existing connections */
499 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
503 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
504 des_access, &conn->pol);
506 if (!NT_STATUS_IS_OK(result)) {
507 /* Hit the cache code again. This cleans out the old connection and gets a new one */
508 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
509 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
513 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
514 des_access, &conn->pol);
517 if (!NT_STATUS_IS_OK(result)) {
518 cli_shutdown(conn->cli);
519 DLIST_REMOVE(cm_conns, conn);
531 #if 0 /* This code now *well* out of date */
533 /* Return a SAM domain policy handle on a domain */
535 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
537 struct winbindd_cm_conn *conn, *basic_conn = NULL;
538 static CLI_POLICY_HND hnd;
540 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
542 /* Look for existing connections */
544 for (conn = cm_conns; conn; conn = conn->next) {
545 if (strequal(conn->domain, domain) &&
546 strequal(conn->pipe_name, PIPE_SAMR) &&
547 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
549 if (!connection_ok(conn)) {
550 /* Shutdown cli? Free conn? Allow retry of DC? */
551 DLIST_REMOVE(cm_conns, conn);
559 /* Create a basic handle to open a domain handle from */
561 if (!cm_get_sam_handle(domain))
564 for (conn = cm_conns; conn; conn = conn->next) {
565 if (strequal(conn->domain, domain) &&
566 strequal(conn->pipe_name, PIPE_SAMR) &&
567 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
571 if (!(conn = (struct winbindd_cm_conn *)
572 malloc(sizeof(struct winbindd_cm_conn))))
577 fstrcpy(conn->domain, basic_conn->domain);
578 fstrcpy(conn->controller, basic_conn->controller);
579 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
581 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
582 conn->cli = basic_conn->cli;
584 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
585 &basic_conn->pol, des_access,
586 domain_sid, &conn->pol);
588 if (!NT_STATUS_IS_OK(result))
593 DLIST_ADD(cm_conns, conn);
602 /* Return a SAM policy handle on a domain user */
604 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
607 struct winbindd_cm_conn *conn, *basic_conn = NULL;
608 static CLI_POLICY_HND hnd;
610 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
612 /* Look for existing connections */
614 for (conn = cm_conns; conn; conn = conn->next) {
615 if (strequal(conn->domain, domain) &&
616 strequal(conn->pipe_name, PIPE_SAMR) &&
617 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
618 conn->pipe_data.samr.rid == user_rid) {
620 if (!connection_ok(conn)) {
621 /* Shutdown cli? Free conn? Allow retry of DC? */
622 DLIST_REMOVE(cm_conns, conn);
630 /* Create a domain handle to open a user handle from */
632 if (!cm_get_sam_dom_handle(domain, domain_sid))
635 for (conn = cm_conns; conn; conn = conn->next) {
636 if (strequal(conn->domain, domain) &&
637 strequal(conn->pipe_name, PIPE_SAMR) &&
638 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
643 DEBUG(0, ("No domain sam handle was created!\n"));
647 if (!(conn = (struct winbindd_cm_conn *)
648 malloc(sizeof(struct winbindd_cm_conn))))
653 fstrcpy(conn->domain, basic_conn->domain);
654 fstrcpy(conn->controller, basic_conn->controller);
655 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
657 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
658 conn->cli = basic_conn->cli;
659 conn->pipe_data.samr.rid = user_rid;
661 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
662 &basic_conn->pol, des_access, user_rid,
665 if (!NT_STATUS_IS_OK(result))
670 DLIST_ADD(cm_conns, conn);
679 /* Return a SAM policy handle on a domain group */
681 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
684 struct winbindd_cm_conn *conn, *basic_conn = NULL;
685 static CLI_POLICY_HND hnd;
687 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
689 /* Look for existing connections */
691 for (conn = cm_conns; conn; conn = conn->next) {
692 if (strequal(conn->domain, domain) &&
693 strequal(conn->pipe_name, PIPE_SAMR) &&
694 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
695 conn->pipe_data.samr.rid == group_rid) {
697 if (!connection_ok(conn)) {
698 /* Shutdown cli? Free conn? Allow retry of DC? */
699 DLIST_REMOVE(cm_conns, conn);
707 /* Create a domain handle to open a user handle from */
709 if (!cm_get_sam_dom_handle(domain, domain_sid))
712 for (conn = cm_conns; conn; conn = conn->next) {
713 if (strequal(conn->domain, domain) &&
714 strequal(conn->pipe_name, PIPE_SAMR) &&
715 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
720 DEBUG(0, ("No domain sam handle was created!\n"));
724 if (!(conn = (struct winbindd_cm_conn *)
725 malloc(sizeof(struct winbindd_cm_conn))))
730 fstrcpy(conn->domain, basic_conn->domain);
731 fstrcpy(conn->controller, basic_conn->controller);
732 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
734 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
735 conn->cli = basic_conn->cli;
736 conn->pipe_data.samr.rid = group_rid;
738 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
739 &basic_conn->pol, des_access, group_rid,
742 if (!NT_STATUS_IS_OK(result))
747 DLIST_ADD(cm_conns, conn);
758 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
759 netlogon pipe as no handle is returned. */
761 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
762 struct cli_state **cli)
764 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
765 struct winbindd_cm_conn *conn;
768 return NT_STATUS_INVALID_PARAMETER;
771 /* Open an initial conection */
773 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
777 result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
778 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
780 if (!NT_STATUS_IS_OK(result)) {
781 DEBUG(0, ("error connecting to domain password server: %s\n",
784 /* Hit the cache code again. This cleans out the old connection and gets a new one */
785 if (conn->cli->fd == -1) {
786 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
791 result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
792 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
795 if (!NT_STATUS_IS_OK(result)) {
796 cli_shutdown(conn->cli);
797 DLIST_REMOVE(cm_conns, conn);
808 /* Dump the current connection status */
810 static void dump_conn_list(void)
812 struct winbindd_cm_conn *con;
814 DEBUG(0, ("\tDomain Controller Pipe\n"));
816 for(con = cm_conns; con; con = con->next) {
819 /* Display pipe info */
821 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
822 DEBUG(0, ("Error: not enough memory!\n"));
824 DEBUG(0, ("%s\n", msg));
830 void winbindd_cm_status(void)
832 /* List open connections */
834 DEBUG(0, ("winbindd connection manager status:\n"));
839 DEBUG(0, ("\tNo active connections\n"));