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(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
383 conn->controller, conn->domain, conn->pipe_name));
384 smb_panic("connection_ok: conn->cli was null!");
388 if (!conn->cli->initialised) {
389 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
390 conn->controller, conn->domain, conn->pipe_name));
391 smb_panic("connection_ok: conn->cli->initialised is False!");
395 if (conn->cli->fd == -1) {
396 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
397 conn->controller, conn->domain, conn->pipe_name));
404 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
406 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out)
408 struct winbindd_cm_conn *conn, conn_temp;
411 for (conn = cm_conns; conn; conn = conn->next) {
412 if (strequal(conn->domain, domain) &&
413 strequal(conn->pipe_name, pipe_name)) {
414 if (!connection_ok(conn)) {
416 cli_shutdown(conn->cli);
418 ZERO_STRUCT(conn_temp);
419 conn_temp.next = conn->next;
420 DLIST_REMOVE(cm_conns, conn);
422 conn = &conn_temp; /* Just to keep the loop moving */
430 if (!(conn = malloc(sizeof(*conn))))
431 return NT_STATUS_NO_MEMORY;
435 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
436 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
437 domain, pipe_name, nt_errstr(result)));
441 DLIST_ADD(cm_conns, conn);
448 /* Return a LSA policy handle on a domain */
450 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
452 struct winbindd_cm_conn *conn;
453 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
455 static CLI_POLICY_HND hnd;
457 /* Look for existing connections */
459 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
463 /* This *shitty* code needs scrapping ! JRA */
464 if (policy_handle_is_valid(&conn->pol)) {
470 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
471 des_access, &conn->pol);
473 if (!NT_STATUS_IS_OK(result)) {
474 /* Hit the cache code again. This cleans out the old connection and gets a new one */
475 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
476 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
480 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
481 des_access, &conn->pol);
484 if (!NT_STATUS_IS_OK(result)) {
485 cli_shutdown(conn->cli);
486 DLIST_REMOVE(cm_conns, conn);
498 /* Return a SAM policy handle on a domain */
500 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
502 struct winbindd_cm_conn *conn;
503 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
505 static CLI_POLICY_HND hnd;
507 /* Look for existing connections */
509 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
513 /* This *shitty* code needs scrapping ! JRA */
514 if (policy_handle_is_valid(&conn->pol)) {
519 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
520 des_access, &conn->pol);
522 if (!NT_STATUS_IS_OK(result)) {
523 /* Hit the cache code again. This cleans out the old connection and gets a new one */
524 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
525 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
529 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
530 des_access, &conn->pol);
533 if (!NT_STATUS_IS_OK(result)) {
534 cli_shutdown(conn->cli);
535 DLIST_REMOVE(cm_conns, conn);
547 #if 0 /* This code now *well* out of date */
549 /* Return a SAM domain policy handle on a domain */
551 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
553 struct winbindd_cm_conn *conn, *basic_conn = NULL;
554 static CLI_POLICY_HND hnd;
556 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
558 /* Look for existing connections */
560 for (conn = cm_conns; conn; conn = conn->next) {
561 if (strequal(conn->domain, domain) &&
562 strequal(conn->pipe_name, PIPE_SAMR) &&
563 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
565 if (!connection_ok(conn)) {
566 /* Shutdown cli? Free conn? Allow retry of DC? */
567 DLIST_REMOVE(cm_conns, conn);
575 /* Create a basic handle to open a domain handle from */
577 if (!cm_get_sam_handle(domain))
580 for (conn = cm_conns; conn; conn = conn->next) {
581 if (strequal(conn->domain, domain) &&
582 strequal(conn->pipe_name, PIPE_SAMR) &&
583 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
587 if (!(conn = (struct winbindd_cm_conn *)
588 malloc(sizeof(struct winbindd_cm_conn))))
593 fstrcpy(conn->domain, basic_conn->domain);
594 fstrcpy(conn->controller, basic_conn->controller);
595 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
597 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
598 conn->cli = basic_conn->cli;
600 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
601 &basic_conn->pol, des_access,
602 domain_sid, &conn->pol);
604 if (!NT_STATUS_IS_OK(result))
609 DLIST_ADD(cm_conns, conn);
618 /* Return a SAM policy handle on a domain user */
620 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
623 struct winbindd_cm_conn *conn, *basic_conn = NULL;
624 static CLI_POLICY_HND hnd;
626 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
628 /* Look for existing connections */
630 for (conn = cm_conns; conn; conn = conn->next) {
631 if (strequal(conn->domain, domain) &&
632 strequal(conn->pipe_name, PIPE_SAMR) &&
633 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
634 conn->pipe_data.samr.rid == user_rid) {
636 if (!connection_ok(conn)) {
637 /* Shutdown cli? Free conn? Allow retry of DC? */
638 DLIST_REMOVE(cm_conns, conn);
646 /* Create a domain handle to open a user handle from */
648 if (!cm_get_sam_dom_handle(domain, domain_sid))
651 for (conn = cm_conns; conn; conn = conn->next) {
652 if (strequal(conn->domain, domain) &&
653 strequal(conn->pipe_name, PIPE_SAMR) &&
654 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
659 DEBUG(0, ("No domain sam handle was created!\n"));
663 if (!(conn = (struct winbindd_cm_conn *)
664 malloc(sizeof(struct winbindd_cm_conn))))
669 fstrcpy(conn->domain, basic_conn->domain);
670 fstrcpy(conn->controller, basic_conn->controller);
671 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
673 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
674 conn->cli = basic_conn->cli;
675 conn->pipe_data.samr.rid = user_rid;
677 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
678 &basic_conn->pol, des_access, user_rid,
681 if (!NT_STATUS_IS_OK(result))
686 DLIST_ADD(cm_conns, conn);
695 /* Return a SAM policy handle on a domain group */
697 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
700 struct winbindd_cm_conn *conn, *basic_conn = NULL;
701 static CLI_POLICY_HND hnd;
703 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
705 /* Look for existing connections */
707 for (conn = cm_conns; conn; conn = conn->next) {
708 if (strequal(conn->domain, domain) &&
709 strequal(conn->pipe_name, PIPE_SAMR) &&
710 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
711 conn->pipe_data.samr.rid == group_rid) {
713 if (!connection_ok(conn)) {
714 /* Shutdown cli? Free conn? Allow retry of DC? */
715 DLIST_REMOVE(cm_conns, conn);
723 /* Create a domain handle to open a user handle from */
725 if (!cm_get_sam_dom_handle(domain, domain_sid))
728 for (conn = cm_conns; conn; conn = conn->next) {
729 if (strequal(conn->domain, domain) &&
730 strequal(conn->pipe_name, PIPE_SAMR) &&
731 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
736 DEBUG(0, ("No domain sam handle was created!\n"));
740 if (!(conn = (struct winbindd_cm_conn *)
741 malloc(sizeof(struct winbindd_cm_conn))))
746 fstrcpy(conn->domain, basic_conn->domain);
747 fstrcpy(conn->controller, basic_conn->controller);
748 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
750 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
751 conn->cli = basic_conn->cli;
752 conn->pipe_data.samr.rid = group_rid;
754 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
755 &basic_conn->pol, des_access, group_rid,
758 if (!NT_STATUS_IS_OK(result))
763 DLIST_ADD(cm_conns, conn);
774 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
775 netlogon pipe as no handle is returned. */
777 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
778 struct cli_state **cli)
780 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
781 struct winbindd_cm_conn *conn;
784 return NT_STATUS_INVALID_PARAMETER;
787 /* Open an initial conection */
789 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
793 result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
794 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
796 if (!NT_STATUS_IS_OK(result)) {
797 DEBUG(0, ("error connecting to domain password server: %s\n",
800 /* Hit the cache code again. This cleans out the old connection and gets a new one */
801 if (conn->cli->fd == -1) {
802 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
807 result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
808 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
811 if (!NT_STATUS_IS_OK(result)) {
812 cli_shutdown(conn->cli);
813 DLIST_REMOVE(cm_conns, conn);
824 /* Dump the current connection status */
826 static void dump_conn_list(void)
828 struct winbindd_cm_conn *con;
830 DEBUG(0, ("\tDomain Controller Pipe\n"));
832 for(con = cm_conns; con; con = con->next) {
835 /* Display pipe info */
837 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
838 DEBUG(0, ("Error: not enough memory!\n"));
840 DEBUG(0, ("%s\n", msg));
846 void winbindd_cm_status(void)
848 /* List open connections */
850 DEBUG(0, ("winbindd connection manager status:\n"));
855 DEBUG(0, ("\tNo active connections\n"));