2 Unix SMB/CIFS implementation.
4 Winbind daemon connection manager
6 Copyright (C) Tim Potter 2001
7 Copyright (C) Andrew Bartlett 2002
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
65 #define DBGC_CLASS DBGC_WINBIND
67 /* Global list of connections. Initially a DLIST but can become a hash
68 table or whatever later. */
70 struct winbindd_cm_conn {
71 struct winbindd_cm_conn *prev, *next;
75 struct cli_state *cli;
79 static struct winbindd_cm_conn *cm_conns = NULL;
81 /* Get a domain controller name. Cache positive and negative lookups so we
82 don't go to the network too often when something is badly broken. */
84 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
86 struct get_dc_name_cache {
90 struct get_dc_name_cache *prev, *next;
95 find the DC for a domain using methods appropriate for a ADS domain
97 static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
100 ads = ads_init_simple();
105 DEBUG(4,("cm_ads_find_dc: realm=%s\n", ads->realm));
108 /* a full ads_connect() is actually overkill, as we don't srictly need
109 to do the SASL auth in order to get the info we need, but libads
110 doesn't offer a better way right now */
114 fstrcpy(srv_name, ads->ldap_server_name);
116 *dc_ip = ads->ldap_ip;
119 if (!*srv_name || is_zero_ip(*dc_ip)) {
123 DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
124 srv_name, inet_ntoa(*dc_ip)));
130 find the DC for a domain using methods appropriate for a RPC domain
132 static BOOL cm_rpc_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
134 struct in_addr *ip_list = NULL;
137 /* Lookup domain controller name. Try the real PDC first to avoid
139 if (!get_dc_list(True, domain, &ip_list, &count)) {
140 if (!get_dc_list(False, domain, &ip_list, &count)) {
141 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
146 /* Pick a nice close server */
147 /* Look for DC on local net */
148 for (i = 0; i < count; i++) {
149 if (!is_local_net(ip_list[i]))
152 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
157 zero_ip(&ip_list[i]);
161 * Secondly try and contact a random PDC/BDC.
164 i = (sys_random() % count);
166 if (!is_zero_ip(ip_list[i]) &&
167 name_status_find(domain, 0x1c, 0x20,
168 ip_list[i], srv_name)) {
173 zero_ip(&ip_list[i]); /* Tried and failed. */
175 /* Finally return first DC that we can contact using a node
177 for (i = 0; i < count; i++) {
178 if (is_zero_ip(ip_list[i]))
181 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
194 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
196 static struct get_dc_name_cache *get_dc_name_cache;
197 struct get_dc_name_cache *dcc;
198 struct in_addr dc_ip;
201 /* Check the cache for previous lookups */
203 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
205 if (!strequal(domain, dcc->domain_name))
206 continue; /* Not our domain */
208 if ((time(NULL) - dcc->lookup_time) >
209 GET_DC_NAME_CACHE_TIMEOUT) {
211 /* Cache entry has expired, delete it */
213 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
215 DLIST_REMOVE(get_dc_name_cache, dcc);
221 /* Return a positive or negative lookup for this domain */
223 if (dcc->srv_name[0]) {
224 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
225 fstrcpy(srv_name, dcc->srv_name);
228 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
233 /* Add cache entry for this lookup. */
235 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
237 if (!(dcc = (struct get_dc_name_cache *)
238 malloc(sizeof(struct get_dc_name_cache))))
243 fstrcpy(dcc->domain_name, domain);
244 dcc->lookup_time = time(NULL);
246 DLIST_ADD(get_dc_name_cache, dcc);
250 if (lp_security() == SEC_ADS) {
251 ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
253 ret = cm_rpc_find_dc(domain, &dc_ip, srv_name);
260 /* We have a name so make the cache entry positive now */
261 fstrcpy(dcc->srv_name, srv_name);
263 DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
264 inet_ntoa(dc_ip), domain));
271 /* Choose between anonymous or authenticated connections. We need to use
272 an authenticated connection if DCs have the RestrictAnonymous registry
273 entry set > 0, or the "Additional restrictions for anonymous
274 connections" set in the win2k Local Security Policy.
276 Caller to free() result in domain, username, password
279 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
281 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
282 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
283 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
285 if (*username && **username) {
286 if (!*domain || !**domain) {
287 *domain = smb_xstrdup(lp_workgroup());
290 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
292 DEBUG(3, ("IPC$ connections done anonymously\n"));
293 *username = smb_xstrdup("");
294 *domain = smb_xstrdup("");
295 *password = smb_xstrdup("");
299 /* Open a new smb pipe connection to a DC on a given domain. Cache
300 negative creation attempts so we don't try and connect to broken
301 machines too often. */
303 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
305 struct failed_connection_cache {
310 struct failed_connection_cache *prev, *next;
313 static struct failed_connection_cache *failed_connection_cache;
315 /* Add an entry to the failed conneciton cache */
317 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn,
320 struct failed_connection_cache *fcc;
322 SMB_ASSERT(!NT_STATUS_IS_OK(result));
324 /* Check we already aren't in the cache */
326 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
327 if (strequal(fcc->domain_name, new_conn->domain)) {
328 DEBUG(10, ("domain %s already tried and failed\n",
334 /* Create negative lookup cache entry for this domain and controller */
336 if (!(fcc = (struct failed_connection_cache *)
337 malloc(sizeof(struct failed_connection_cache)))) {
338 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
344 fstrcpy(fcc->domain_name, new_conn->domain);
345 fstrcpy(fcc->controller, new_conn->controller);
346 fcc->lookup_time = time(NULL);
347 fcc->nt_status = result;
349 DLIST_ADD(failed_connection_cache, fcc);
352 /* Open a connction to the remote server, cache failures for 30 seconds */
354 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
355 struct winbindd_cm_conn *new_conn)
357 struct failed_connection_cache *fcc;
358 extern pstring global_myname;
360 char *ipc_username, *ipc_domain, *ipc_password;
361 struct in_addr dc_ip;
365 fstrcpy(new_conn->domain, domain);
366 fstrcpy(new_conn->pipe_name, pipe_name);
368 /* Look for a domain controller for this domain. Negative results
369 are cached so don't bother applying the caching for this
370 function just yet. */
372 if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
373 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
374 add_failed_connection_entry(new_conn, result);
378 /* Return false if we have tried to look up this domain and netbios
379 name before and failed. */
381 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
383 if (!(strequal(domain, fcc->domain_name) &&
384 strequal(new_conn->controller, fcc->controller)))
385 continue; /* Not our domain */
387 if ((time(NULL) - fcc->lookup_time) >
388 FAILED_CONNECTION_CACHE_TIMEOUT) {
390 /* Cache entry has expired, delete it */
392 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
394 DLIST_REMOVE(failed_connection_cache, fcc);
400 /* The timeout hasn't expired yet so return false */
402 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
404 result = fcc->nt_status;
405 SMB_ASSERT(!NT_STATUS_IS_OK(result));
409 /* Initialise SMB connection */
411 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
413 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
414 new_conn->controller, global_myname, ipc_domain, ipc_username));
416 result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller,
418 "IPC", ipc_username, ipc_domain,
421 SAFE_FREE(ipc_username);
422 SAFE_FREE(ipc_domain);
423 SAFE_FREE(ipc_password);
425 if (!NT_STATUS_IS_OK(result)) {
426 add_failed_connection_entry(new_conn, result);
430 if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
431 result = NT_STATUS_PIPE_NOT_AVAILABLE;
432 add_failed_connection_entry(new_conn, result);
433 cli_shutdown(new_conn->cli);
440 /* Return true if a connection is still alive */
442 static BOOL connection_ok(struct winbindd_cm_conn *conn)
445 smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
450 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
451 conn->controller, conn->domain, conn->pipe_name));
452 smb_panic("connection_ok: conn->cli was null!");
456 if (!conn->cli->initialised) {
457 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
458 conn->controller, conn->domain, conn->pipe_name));
459 smb_panic("connection_ok: conn->cli->initialised is False!");
463 if (conn->cli->fd == -1) {
464 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
465 conn->controller, conn->domain, conn->pipe_name));
472 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
474 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out)
476 struct winbindd_cm_conn *conn, conn_temp;
479 for (conn = cm_conns; conn; conn = conn->next) {
480 if (strequal(conn->domain, domain) &&
481 strequal(conn->pipe_name, pipe_name)) {
482 if (!connection_ok(conn)) {
484 cli_shutdown(conn->cli);
486 ZERO_STRUCT(conn_temp);
487 conn_temp.next = conn->next;
488 DLIST_REMOVE(cm_conns, conn);
490 conn = &conn_temp; /* Just to keep the loop moving */
498 if (!(conn = malloc(sizeof(*conn))))
499 return NT_STATUS_NO_MEMORY;
503 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
504 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
505 domain, pipe_name, nt_errstr(result)));
509 DLIST_ADD(cm_conns, conn);
516 /* Return a LSA policy handle on a domain */
518 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
520 struct winbindd_cm_conn *conn;
521 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
523 static CLI_POLICY_HND hnd;
525 /* Look for existing connections */
527 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
531 /* This *shitty* code needs scrapping ! JRA */
532 if (policy_handle_is_valid(&conn->pol)) {
538 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
539 des_access, &conn->pol);
541 if (!NT_STATUS_IS_OK(result)) {
542 /* Hit the cache code again. This cleans out the old connection and gets a new one */
543 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
544 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
548 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
549 des_access, &conn->pol);
552 if (!NT_STATUS_IS_OK(result)) {
553 cli_shutdown(conn->cli);
554 DLIST_REMOVE(cm_conns, conn);
566 /* Return a SAM policy handle on a domain */
568 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
570 struct winbindd_cm_conn *conn;
571 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
573 static CLI_POLICY_HND hnd;
575 /* Look for existing connections */
577 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
581 /* This *shitty* code needs scrapping ! JRA */
582 if (policy_handle_is_valid(&conn->pol)) {
587 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
588 des_access, &conn->pol);
590 if (!NT_STATUS_IS_OK(result)) {
591 /* Hit the cache code again. This cleans out the old connection and gets a new one */
592 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
593 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
597 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
598 des_access, &conn->pol);
601 if (!NT_STATUS_IS_OK(result)) {
602 cli_shutdown(conn->cli);
603 DLIST_REMOVE(cm_conns, conn);
615 #if 0 /* This code now *well* out of date */
617 /* Return a SAM domain policy handle on a domain */
619 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
621 struct winbindd_cm_conn *conn, *basic_conn = NULL;
622 static CLI_POLICY_HND hnd;
624 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
626 /* Look for existing connections */
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) {
633 if (!connection_ok(conn)) {
634 /* Shutdown cli? Free conn? Allow retry of DC? */
635 DLIST_REMOVE(cm_conns, conn);
643 /* Create a basic handle to open a domain handle from */
645 if (!cm_get_sam_handle(domain))
648 for (conn = cm_conns; conn; conn = conn->next) {
649 if (strequal(conn->domain, domain) &&
650 strequal(conn->pipe_name, PIPE_SAMR) &&
651 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
655 if (!(conn = (struct winbindd_cm_conn *)
656 malloc(sizeof(struct winbindd_cm_conn))))
661 fstrcpy(conn->domain, basic_conn->domain);
662 fstrcpy(conn->controller, basic_conn->controller);
663 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
665 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
666 conn->cli = basic_conn->cli;
668 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
669 &basic_conn->pol, des_access,
670 domain_sid, &conn->pol);
672 if (!NT_STATUS_IS_OK(result))
677 DLIST_ADD(cm_conns, conn);
686 /* Return a SAM policy handle on a domain user */
688 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
691 struct winbindd_cm_conn *conn, *basic_conn = NULL;
692 static CLI_POLICY_HND hnd;
694 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
696 /* Look for existing connections */
698 for (conn = cm_conns; conn; conn = conn->next) {
699 if (strequal(conn->domain, domain) &&
700 strequal(conn->pipe_name, PIPE_SAMR) &&
701 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
702 conn->pipe_data.samr.rid == user_rid) {
704 if (!connection_ok(conn)) {
705 /* Shutdown cli? Free conn? Allow retry of DC? */
706 DLIST_REMOVE(cm_conns, conn);
714 /* Create a domain handle to open a user handle from */
716 if (!cm_get_sam_dom_handle(domain, domain_sid))
719 for (conn = cm_conns; conn; conn = conn->next) {
720 if (strequal(conn->domain, domain) &&
721 strequal(conn->pipe_name, PIPE_SAMR) &&
722 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
727 DEBUG(0, ("No domain sam handle was created!\n"));
731 if (!(conn = (struct winbindd_cm_conn *)
732 malloc(sizeof(struct winbindd_cm_conn))))
737 fstrcpy(conn->domain, basic_conn->domain);
738 fstrcpy(conn->controller, basic_conn->controller);
739 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
741 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
742 conn->cli = basic_conn->cli;
743 conn->pipe_data.samr.rid = user_rid;
745 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
746 &basic_conn->pol, des_access, user_rid,
749 if (!NT_STATUS_IS_OK(result))
754 DLIST_ADD(cm_conns, conn);
763 /* Return a SAM policy handle on a domain group */
765 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
768 struct winbindd_cm_conn *conn, *basic_conn = NULL;
769 static CLI_POLICY_HND hnd;
771 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
773 /* Look for existing connections */
775 for (conn = cm_conns; conn; conn = conn->next) {
776 if (strequal(conn->domain, domain) &&
777 strequal(conn->pipe_name, PIPE_SAMR) &&
778 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
779 conn->pipe_data.samr.rid == group_rid) {
781 if (!connection_ok(conn)) {
782 /* Shutdown cli? Free conn? Allow retry of DC? */
783 DLIST_REMOVE(cm_conns, conn);
791 /* Create a domain handle to open a user handle from */
793 if (!cm_get_sam_dom_handle(domain, domain_sid))
796 for (conn = cm_conns; conn; conn = conn->next) {
797 if (strequal(conn->domain, domain) &&
798 strequal(conn->pipe_name, PIPE_SAMR) &&
799 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
804 DEBUG(0, ("No domain sam handle was created!\n"));
808 if (!(conn = (struct winbindd_cm_conn *)
809 malloc(sizeof(struct winbindd_cm_conn))))
814 fstrcpy(conn->domain, basic_conn->domain);
815 fstrcpy(conn->controller, basic_conn->controller);
816 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
818 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
819 conn->cli = basic_conn->cli;
820 conn->pipe_data.samr.rid = group_rid;
822 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
823 &basic_conn->pol, des_access, group_rid,
826 if (!NT_STATUS_IS_OK(result))
831 DLIST_ADD(cm_conns, conn);
842 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
843 netlogon pipe as no handle is returned. */
845 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
846 struct cli_state **cli)
848 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
849 struct winbindd_cm_conn *conn;
852 return NT_STATUS_INVALID_PARAMETER;
855 /* Open an initial conection */
857 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
861 result = cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
862 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
864 if (!NT_STATUS_IS_OK(result)) {
865 DEBUG(0, ("error connecting to domain password server: %s\n",
868 /* Hit the cache code again. This cleans out the old connection and gets a new one */
869 if (conn->cli->fd == -1) {
870 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
875 result = cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
876 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
879 if (!NT_STATUS_IS_OK(result)) {
880 cli_shutdown(conn->cli);
881 DLIST_REMOVE(cm_conns, conn);
892 /* Dump the current connection status */
894 static void dump_conn_list(void)
896 struct winbindd_cm_conn *con;
898 DEBUG(0, ("\tDomain Controller Pipe\n"));
900 for(con = cm_conns; con; con = con->next) {
903 /* Display pipe info */
905 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
906 DEBUG(0, ("Error: not enough memory!\n"));
908 DEBUG(0, ("%s\n", msg));
914 void winbindd_cm_status(void)
916 /* List open connections */
918 DEBUG(0, ("winbindd connection manager status:\n"));
923 DEBUG(0, ("\tNo active connections\n"));