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 */
145 if (!get_dc_list(False, domain, &ip_list, &count)) {
146 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
150 /* Pick a nice close server */
152 if (strequal(lp_passwordserver(), "*")) {
154 /* Look for DC on local net */
156 for (i = 0; i < count; i++) {
157 if (is_local_net(ip_list[i]) &&
158 name_status_find(domain, 0x1c, 0x20,
159 ip_list[i], srv_name)) {
163 zero_ip(&ip_list[i]);
166 /* Look for other DCs */
168 for (i = 0; i < count; i++) {
169 if (!is_zero_ip(ip_list[i]) &&
170 name_status_find(domain, 0x1c, 0x20,
171 ip_list[i], srv_name)) {
177 /* No-one to talk to )-: */
182 /* Return first DC that we can contact */
184 for (i = 0; i < count; i++) {
185 if (name_status_find(domain, 0x1c, 0x20, ip_list[i],
192 return False; /* Boo-hoo */
195 /* We have the netbios name and IP address of a domain controller.
196 Ideally we should sent a SAMLOGON request to determine whether
197 the DC is alive and kicking. If we can catch a dead DC before
198 performing a cli_connect() we can avoid a 30-second timeout. */
200 /* We have a name so make the cache entry positive now */
202 fstrcpy(dcc->srv_name, srv_name);
204 DEBUG(3, ("Returning DC %s (%s) for domain %s\n", srv_name,
205 inet_ntoa(dc_ip), domain));
214 /* Choose between anonymous or authenticated connections. We need to use
215 an authenticated connection if DCs have the RestrictAnonymous registry
216 entry set > 0, or the "Additional restrictions for anonymous
217 connections" set in the win2k Local Security Policy.
219 Caller to free() result in domain, username, password
222 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
224 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
225 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
226 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
228 if (*username && **username) {
229 if (!*domain || !**domain) {
230 *domain = smb_xstrdup(lp_workgroup());
233 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
235 DEBUG(3, ("IPC$ connections done anonymously\n"));
236 *username = smb_xstrdup("");
237 *domain = smb_xstrdup("");
238 *password = smb_xstrdup("");
242 /* Open a new smb pipe connection to a DC on a given domain. Cache
243 negative creation attempts so we don't try and connect to broken
244 machines too often. */
246 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
248 struct failed_connection_cache {
253 struct failed_connection_cache *prev, *next;
256 static struct failed_connection_cache *failed_connection_cache;
258 /* Add an entry to the failed conneciton cache */
260 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, NTSTATUS result) {
261 struct failed_connection_cache *fcc;
263 SMB_ASSERT(!NT_STATUS_IS_OK(result));
265 /* Create negative lookup cache entry for this domain and controller */
267 if (!(fcc = (struct failed_connection_cache *)
268 malloc(sizeof(struct failed_connection_cache)))) {
269 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
275 fstrcpy(fcc->domain_name, new_conn->domain);
276 fstrcpy(fcc->controller, new_conn->controller);
277 fcc->lookup_time = time(NULL);
278 fcc->nt_status = result;
280 DLIST_ADD(failed_connection_cache, fcc);
283 /* Open a connction to the remote server, cache failures for 30 seconds */
285 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
286 struct winbindd_cm_conn *new_conn)
288 struct failed_connection_cache *fcc;
289 extern pstring global_myname;
291 char *ipc_username, *ipc_domain, *ipc_password;
292 struct in_addr dc_ip;
296 fstrcpy(new_conn->domain, domain);
297 fstrcpy(new_conn->pipe_name, pipe_name);
299 /* Look for a domain controller for this domain. Negative results
300 are cached so don't bother applying the caching for this
301 function just yet. */
303 if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
304 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
305 add_failed_connection_entry(new_conn, result);
309 /* Return false if we have tried to look up this domain and netbios
310 name before and failed. */
312 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
314 if (!(strequal(domain, fcc->domain_name) &&
315 strequal(new_conn->controller, fcc->controller)))
316 continue; /* Not our domain */
318 if ((time(NULL) - fcc->lookup_time) >
319 FAILED_CONNECTION_CACHE_TIMEOUT) {
321 /* Cache entry has expired, delete it */
323 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
325 DLIST_REMOVE(failed_connection_cache, fcc);
331 /* The timeout hasn't expired yet so return false */
333 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
335 result = fcc->nt_status;
336 SMB_ASSERT(!NT_STATUS_IS_OK(result));
340 /* Initialise SMB connection */
342 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
344 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
345 new_conn->controller, global_myname, ipc_domain, ipc_username));
347 result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller,
349 "IPC", ipc_username, ipc_domain,
350 ipc_password, strlen(ipc_password));
352 SAFE_FREE(ipc_username);
353 SAFE_FREE(ipc_domain);
354 SAFE_FREE(ipc_password);
356 if (!NT_STATUS_IS_OK(result)) {
357 add_failed_connection_entry(new_conn, result);
361 if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
362 result = NT_STATUS_PIPE_NOT_AVAILABLE;
363 add_failed_connection_entry(new_conn, result);
364 cli_shutdown(new_conn->cli);
371 /* Return true if a connection is still alive */
373 static BOOL connection_ok(struct winbindd_cm_conn *conn)
376 smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
380 if (!conn->cli || !conn->cli->initialised) {
381 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
382 conn->controller, conn->domain, conn->pipe_name));
386 if (conn->cli->fd == -1) {
387 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
388 conn->controller, conn->domain, conn->pipe_name));
395 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
397 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out)
399 struct winbindd_cm_conn *conn, conn_temp;
402 for (conn = cm_conns; conn; conn = conn->next) {
403 if (strequal(conn->domain, domain) &&
404 strequal(conn->pipe_name, pipe_name)) {
405 if (!connection_ok(conn)) {
407 cli_shutdown(conn->cli);
409 conn_temp.next = conn->next;
410 DLIST_REMOVE(cm_conns, conn);
412 conn = &conn_temp; /* Just to keep the loop moving */
420 if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
421 return NT_STATUS_NO_MEMORY;
425 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
426 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
427 domain, pipe_name, get_nt_error_msg(result)));
431 DLIST_ADD(cm_conns, conn);
438 /* Return a LSA policy handle on a domain */
440 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
442 struct winbindd_cm_conn *conn;
443 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
445 static CLI_POLICY_HND hnd;
447 /* Look for existing connections */
449 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
453 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
454 des_access, &conn->pol);
456 if (!NT_STATUS_IS_OK(result)) {
457 /* Hit the cache code again. This cleans out the old connection and gets a new one */
458 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
459 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
463 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
464 des_access, &conn->pol);
467 if (!NT_STATUS_IS_OK(result)) {
468 cli_shutdown(conn->cli);
469 DLIST_REMOVE(cm_conns, conn);
481 /* Return a SAM policy handle on a domain */
483 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
485 struct winbindd_cm_conn *conn;
486 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
488 static CLI_POLICY_HND hnd;
490 /* Look for existing connections */
492 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
496 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
497 des_access, &conn->pol);
499 if (!NT_STATUS_IS_OK(result)) {
500 /* Hit the cache code again. This cleans out the old connection and gets a new one */
501 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
502 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
506 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
507 des_access, &conn->pol);
510 if (!NT_STATUS_IS_OK(result)) {
511 cli_shutdown(conn->cli);
512 DLIST_REMOVE(cm_conns, conn);
524 #if 0 /* This code now *well* out of date */
526 /* Return a SAM domain policy handle on a domain */
528 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
530 struct winbindd_cm_conn *conn, *basic_conn = NULL;
531 static CLI_POLICY_HND hnd;
533 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
535 /* Look for existing connections */
537 for (conn = cm_conns; conn; conn = conn->next) {
538 if (strequal(conn->domain, domain) &&
539 strequal(conn->pipe_name, PIPE_SAMR) &&
540 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
542 if (!connection_ok(conn)) {
543 /* Shutdown cli? Free conn? Allow retry of DC? */
544 DLIST_REMOVE(cm_conns, conn);
552 /* Create a basic handle to open a domain handle from */
554 if (!cm_get_sam_handle(domain))
557 for (conn = cm_conns; conn; conn = conn->next) {
558 if (strequal(conn->domain, domain) &&
559 strequal(conn->pipe_name, PIPE_SAMR) &&
560 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
564 if (!(conn = (struct winbindd_cm_conn *)
565 malloc(sizeof(struct winbindd_cm_conn))))
570 fstrcpy(conn->domain, basic_conn->domain);
571 fstrcpy(conn->controller, basic_conn->controller);
572 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
574 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
575 conn->cli = basic_conn->cli;
577 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
578 &basic_conn->pol, des_access,
579 domain_sid, &conn->pol);
581 if (!NT_STATUS_IS_OK(result))
586 DLIST_ADD(cm_conns, conn);
595 /* Return a SAM policy handle on a domain user */
597 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
600 struct winbindd_cm_conn *conn, *basic_conn = NULL;
601 static CLI_POLICY_HND hnd;
603 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
605 /* Look for existing connections */
607 for (conn = cm_conns; conn; conn = conn->next) {
608 if (strequal(conn->domain, domain) &&
609 strequal(conn->pipe_name, PIPE_SAMR) &&
610 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
611 conn->pipe_data.samr.rid == user_rid) {
613 if (!connection_ok(conn)) {
614 /* Shutdown cli? Free conn? Allow retry of DC? */
615 DLIST_REMOVE(cm_conns, conn);
623 /* Create a domain handle to open a user handle from */
625 if (!cm_get_sam_dom_handle(domain, domain_sid))
628 for (conn = cm_conns; conn; conn = conn->next) {
629 if (strequal(conn->domain, domain) &&
630 strequal(conn->pipe_name, PIPE_SAMR) &&
631 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
636 DEBUG(0, ("No domain sam handle was created!\n"));
640 if (!(conn = (struct winbindd_cm_conn *)
641 malloc(sizeof(struct winbindd_cm_conn))))
646 fstrcpy(conn->domain, basic_conn->domain);
647 fstrcpy(conn->controller, basic_conn->controller);
648 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
650 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
651 conn->cli = basic_conn->cli;
652 conn->pipe_data.samr.rid = user_rid;
654 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
655 &basic_conn->pol, des_access, user_rid,
658 if (!NT_STATUS_IS_OK(result))
663 DLIST_ADD(cm_conns, conn);
672 /* Return a SAM policy handle on a domain group */
674 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
677 struct winbindd_cm_conn *conn, *basic_conn = NULL;
678 static CLI_POLICY_HND hnd;
680 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
682 /* Look for existing connections */
684 for (conn = cm_conns; conn; conn = conn->next) {
685 if (strequal(conn->domain, domain) &&
686 strequal(conn->pipe_name, PIPE_SAMR) &&
687 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
688 conn->pipe_data.samr.rid == group_rid) {
690 if (!connection_ok(conn)) {
691 /* Shutdown cli? Free conn? Allow retry of DC? */
692 DLIST_REMOVE(cm_conns, conn);
700 /* Create a domain handle to open a user handle from */
702 if (!cm_get_sam_dom_handle(domain, domain_sid))
705 for (conn = cm_conns; conn; conn = conn->next) {
706 if (strequal(conn->domain, domain) &&
707 strequal(conn->pipe_name, PIPE_SAMR) &&
708 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
713 DEBUG(0, ("No domain sam handle was created!\n"));
717 if (!(conn = (struct winbindd_cm_conn *)
718 malloc(sizeof(struct winbindd_cm_conn))))
723 fstrcpy(conn->domain, basic_conn->domain);
724 fstrcpy(conn->controller, basic_conn->controller);
725 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
727 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
728 conn->cli = basic_conn->cli;
729 conn->pipe_data.samr.rid = group_rid;
731 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
732 &basic_conn->pol, des_access, group_rid,
735 if (!NT_STATUS_IS_OK(result))
740 DLIST_ADD(cm_conns, conn);
751 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
752 netlogon pipe as no handle is returned. */
754 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
755 struct cli_state **cli)
757 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
758 struct winbindd_cm_conn *conn;
761 return NT_STATUS_INVALID_PARAMETER;
764 /* Open an initial conection */
766 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
770 result = new_cli_nt_setup_creds(conn->cli, trust_passwd);
772 if (!NT_STATUS_IS_OK(result)) {
773 DEBUG(0, ("error connecting to domain password server: %s\n",
774 get_nt_error_msg(result)));
776 /* Hit the cache code again. This cleans out the old connection and gets a new one */
777 if (conn->cli->fd == -1) {
778 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
783 result = new_cli_nt_setup_creds(conn->cli, trust_passwd);
786 if (!NT_STATUS_IS_OK(result)) {
787 cli_shutdown(conn->cli);
788 DLIST_REMOVE(cm_conns, conn);
799 /* Dump the current connection status */
801 static void dump_conn_list(void)
803 struct winbindd_cm_conn *con;
805 DEBUG(0, ("\tDomain Controller Pipe\n"));
807 for(con = cm_conns; con; con = con->next) {
810 /* Display pipe info */
812 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
813 DEBUG(0, ("Error: not enough memory!\n"));
815 DEBUG(0, ("%s\n", msg));
821 void winbindd_cm_status(void)
823 /* List open connections */
825 DEBUG(0, ("winbindd connection manager status:\n"));
830 DEBUG(0, ("\tNo active connections\n"));