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 const char *realm = domain;
102 if (strcasecmp(realm, lp_workgroup()) == 0) {
106 ads = ads_init(realm, domain, NULL);
111 /* we don't need to bind, just connect */
112 ads->auth.no_bind = 1;
114 DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
117 /* a full ads_connect() is actually overkill, as we don't srictly need
118 to do the SASL auth in order to get the info we need, but libads
119 doesn't offer a better way right now */
123 if (!ads->config.realm) {
127 fstrcpy(srv_name, ads->config.ldap_server_name);
129 *dc_ip = ads->ldap_ip;
132 DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
133 srv_name, inet_ntoa(*dc_ip)));
139 find the DC for a domain using methods appropriate for a RPC domain
141 static BOOL cm_rpc_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
143 struct in_addr *ip_list = NULL;
146 /* Lookup domain controller name. Try the real PDC first to avoid
148 if (!get_dc_list(True, domain, &ip_list, &count)) {
149 if (!get_dc_list(False, domain, &ip_list, &count)) {
150 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
155 /* Pick a nice close server */
156 /* Look for DC on local net */
157 for (i = 0; i < count; i++) {
158 if (!is_local_net(ip_list[i]))
161 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
166 zero_ip(&ip_list[i]);
170 * Secondly try and contact a random PDC/BDC.
173 i = (sys_random() % count);
175 if (!is_zero_ip(ip_list[i]) &&
176 name_status_find(domain, 0x1c, 0x20,
177 ip_list[i], srv_name)) {
182 zero_ip(&ip_list[i]); /* Tried and failed. */
184 /* Finally return first DC that we can contact using a node
186 for (i = 0; i < count; i++) {
187 if (is_zero_ip(ip_list[i]))
190 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
203 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
205 static struct get_dc_name_cache *get_dc_name_cache;
206 struct get_dc_name_cache *dcc;
207 struct in_addr dc_ip;
210 /* Check the cache for previous lookups */
212 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
214 if (!strequal(domain, dcc->domain_name))
215 continue; /* Not our domain */
217 if ((time(NULL) - dcc->lookup_time) >
218 GET_DC_NAME_CACHE_TIMEOUT) {
220 /* Cache entry has expired, delete it */
222 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
224 DLIST_REMOVE(get_dc_name_cache, dcc);
230 /* Return a positive or negative lookup for this domain */
232 if (dcc->srv_name[0]) {
233 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
234 fstrcpy(srv_name, dcc->srv_name);
237 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
242 /* Add cache entry for this lookup. */
244 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
246 if (!(dcc = (struct get_dc_name_cache *)
247 malloc(sizeof(struct get_dc_name_cache))))
252 fstrcpy(dcc->domain_name, domain);
253 dcc->lookup_time = time(NULL);
255 DLIST_ADD(get_dc_name_cache, dcc);
260 if (lp_security() == SEC_ADS) {
261 ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
264 /* fall back on rpc methods if the ADS methods fail */
265 ret = cm_rpc_find_dc(domain, &dc_ip, srv_name);
272 /* We have a name so make the cache entry positive now */
273 fstrcpy(dcc->srv_name, srv_name);
275 DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
276 inet_ntoa(dc_ip), domain));
283 /* Choose between anonymous or authenticated connections. We need to use
284 an authenticated connection if DCs have the RestrictAnonymous registry
285 entry set > 0, or the "Additional restrictions for anonymous
286 connections" set in the win2k Local Security Policy.
288 Caller to free() result in domain, username, password
291 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
293 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
294 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
295 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
297 if (*username && **username) {
298 if (!*domain || !**domain) {
299 *domain = smb_xstrdup(lp_workgroup());
302 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
304 DEBUG(3, ("IPC$ connections done anonymously\n"));
305 *username = smb_xstrdup("");
306 *domain = smb_xstrdup("");
307 *password = smb_xstrdup("");
311 /* Open a new smb pipe connection to a DC on a given domain. Cache
312 negative creation attempts so we don't try and connect to broken
313 machines too often. */
315 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
317 struct failed_connection_cache {
322 struct failed_connection_cache *prev, *next;
325 static struct failed_connection_cache *failed_connection_cache;
327 /* Add an entry to the failed conneciton cache */
329 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn,
332 struct failed_connection_cache *fcc;
334 SMB_ASSERT(!NT_STATUS_IS_OK(result));
336 /* Check we already aren't in the cache */
338 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
339 if (strequal(fcc->domain_name, new_conn->domain)) {
340 DEBUG(10, ("domain %s already tried and failed\n",
346 /* Create negative lookup cache entry for this domain and controller */
348 if (!(fcc = (struct failed_connection_cache *)
349 malloc(sizeof(struct failed_connection_cache)))) {
350 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
356 fstrcpy(fcc->domain_name, new_conn->domain);
357 fstrcpy(fcc->controller, new_conn->controller);
358 fcc->lookup_time = time(NULL);
359 fcc->nt_status = result;
361 DLIST_ADD(failed_connection_cache, fcc);
364 /* Open a connction to the remote server, cache failures for 30 seconds */
366 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
367 struct winbindd_cm_conn *new_conn)
369 struct failed_connection_cache *fcc;
370 extern pstring global_myname;
372 char *ipc_username, *ipc_domain, *ipc_password;
373 struct in_addr dc_ip;
377 fstrcpy(new_conn->domain, domain);
378 fstrcpy(new_conn->pipe_name, pipe_name);
380 /* Look for a domain controller for this domain. Negative results
381 are cached so don't bother applying the caching for this
382 function just yet. */
384 if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
385 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
386 add_failed_connection_entry(new_conn, result);
390 /* Return false if we have tried to look up this domain and netbios
391 name before and failed. */
393 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
395 if (!(strequal(domain, fcc->domain_name) &&
396 strequal(new_conn->controller, fcc->controller)))
397 continue; /* Not our domain */
399 if ((time(NULL) - fcc->lookup_time) >
400 FAILED_CONNECTION_CACHE_TIMEOUT) {
402 /* Cache entry has expired, delete it */
404 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
406 DLIST_REMOVE(failed_connection_cache, fcc);
412 /* The timeout hasn't expired yet so return false */
414 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
416 result = fcc->nt_status;
417 SMB_ASSERT(!NT_STATUS_IS_OK(result));
421 /* Initialise SMB connection */
423 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
425 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
426 new_conn->controller, global_myname, ipc_domain, ipc_username));
428 result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller,
430 "IPC", ipc_username, ipc_domain,
433 SAFE_FREE(ipc_username);
434 SAFE_FREE(ipc_domain);
435 SAFE_FREE(ipc_password);
437 if (!NT_STATUS_IS_OK(result)) {
438 add_failed_connection_entry(new_conn, result);
442 if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
443 result = NT_STATUS_PIPE_NOT_AVAILABLE;
444 add_failed_connection_entry(new_conn, result);
445 cli_shutdown(new_conn->cli);
452 /* Return true if a connection is still alive */
454 static BOOL connection_ok(struct winbindd_cm_conn *conn)
457 smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
462 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
463 conn->controller, conn->domain, conn->pipe_name));
464 smb_panic("connection_ok: conn->cli was null!");
468 if (!conn->cli->initialised) {
469 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
470 conn->controller, conn->domain, conn->pipe_name));
471 smb_panic("connection_ok: conn->cli->initialised is False!");
475 if (conn->cli->fd == -1) {
476 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
477 conn->controller, conn->domain, conn->pipe_name));
484 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
486 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out)
488 struct winbindd_cm_conn *conn, conn_temp;
491 for (conn = cm_conns; conn; conn = conn->next) {
492 if (strequal(conn->domain, domain) &&
493 strequal(conn->pipe_name, pipe_name)) {
494 if (!connection_ok(conn)) {
496 cli_shutdown(conn->cli);
498 ZERO_STRUCT(conn_temp);
499 conn_temp.next = conn->next;
500 DLIST_REMOVE(cm_conns, conn);
502 conn = &conn_temp; /* Just to keep the loop moving */
510 if (!(conn = malloc(sizeof(*conn))))
511 return NT_STATUS_NO_MEMORY;
515 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
516 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
517 domain, pipe_name, nt_errstr(result)));
521 DLIST_ADD(cm_conns, conn);
528 /* Return a LSA policy handle on a domain */
530 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
532 struct winbindd_cm_conn *conn;
533 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
535 static CLI_POLICY_HND hnd;
537 /* Look for existing connections */
539 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
543 /* This *shitty* code needs scrapping ! JRA */
544 if (policy_handle_is_valid(&conn->pol)) {
550 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
551 des_access, &conn->pol);
553 if (!NT_STATUS_IS_OK(result)) {
554 /* Hit the cache code again. This cleans out the old connection and gets a new one */
555 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
556 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
560 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
561 des_access, &conn->pol);
564 if (!NT_STATUS_IS_OK(result)) {
565 cli_shutdown(conn->cli);
566 DLIST_REMOVE(cm_conns, conn);
578 /* Return a SAM policy handle on a domain */
580 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
582 struct winbindd_cm_conn *conn;
583 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
585 static CLI_POLICY_HND hnd;
587 /* Look for existing connections */
589 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
593 /* This *shitty* code needs scrapping ! JRA */
594 if (policy_handle_is_valid(&conn->pol)) {
599 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
600 des_access, &conn->pol);
602 if (!NT_STATUS_IS_OK(result)) {
603 /* Hit the cache code again. This cleans out the old connection and gets a new one */
604 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
605 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
609 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
610 des_access, &conn->pol);
613 if (!NT_STATUS_IS_OK(result)) {
614 cli_shutdown(conn->cli);
615 DLIST_REMOVE(cm_conns, conn);
627 #if 0 /* This code now *well* out of date */
629 /* Return a SAM domain policy handle on a domain */
631 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
633 struct winbindd_cm_conn *conn, *basic_conn = NULL;
634 static CLI_POLICY_HND hnd;
636 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
638 /* Look for existing connections */
640 for (conn = cm_conns; conn; conn = conn->next) {
641 if (strequal(conn->domain, domain) &&
642 strequal(conn->pipe_name, PIPE_SAMR) &&
643 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
645 if (!connection_ok(conn)) {
646 /* Shutdown cli? Free conn? Allow retry of DC? */
647 DLIST_REMOVE(cm_conns, conn);
655 /* Create a basic handle to open a domain handle from */
657 if (!cm_get_sam_handle(domain))
660 for (conn = cm_conns; conn; conn = conn->next) {
661 if (strequal(conn->domain, domain) &&
662 strequal(conn->pipe_name, PIPE_SAMR) &&
663 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
667 if (!(conn = (struct winbindd_cm_conn *)
668 malloc(sizeof(struct winbindd_cm_conn))))
673 fstrcpy(conn->domain, basic_conn->domain);
674 fstrcpy(conn->controller, basic_conn->controller);
675 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
677 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
678 conn->cli = basic_conn->cli;
680 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
681 &basic_conn->pol, des_access,
682 domain_sid, &conn->pol);
684 if (!NT_STATUS_IS_OK(result))
689 DLIST_ADD(cm_conns, conn);
698 /* Return a SAM policy handle on a domain user */
700 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
703 struct winbindd_cm_conn *conn, *basic_conn = NULL;
704 static CLI_POLICY_HND hnd;
706 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
708 /* Look for existing connections */
710 for (conn = cm_conns; conn; conn = conn->next) {
711 if (strequal(conn->domain, domain) &&
712 strequal(conn->pipe_name, PIPE_SAMR) &&
713 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
714 conn->pipe_data.samr.rid == user_rid) {
716 if (!connection_ok(conn)) {
717 /* Shutdown cli? Free conn? Allow retry of DC? */
718 DLIST_REMOVE(cm_conns, conn);
726 /* Create a domain handle to open a user handle from */
728 if (!cm_get_sam_dom_handle(domain, domain_sid))
731 for (conn = cm_conns; conn; conn = conn->next) {
732 if (strequal(conn->domain, domain) &&
733 strequal(conn->pipe_name, PIPE_SAMR) &&
734 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
739 DEBUG(0, ("No domain sam handle was created!\n"));
743 if (!(conn = (struct winbindd_cm_conn *)
744 malloc(sizeof(struct winbindd_cm_conn))))
749 fstrcpy(conn->domain, basic_conn->domain);
750 fstrcpy(conn->controller, basic_conn->controller);
751 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
753 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
754 conn->cli = basic_conn->cli;
755 conn->pipe_data.samr.rid = user_rid;
757 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
758 &basic_conn->pol, des_access, user_rid,
761 if (!NT_STATUS_IS_OK(result))
766 DLIST_ADD(cm_conns, conn);
775 /* Return a SAM policy handle on a domain group */
777 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
780 struct winbindd_cm_conn *conn, *basic_conn = NULL;
781 static CLI_POLICY_HND hnd;
783 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
785 /* Look for existing connections */
787 for (conn = cm_conns; conn; conn = conn->next) {
788 if (strequal(conn->domain, domain) &&
789 strequal(conn->pipe_name, PIPE_SAMR) &&
790 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
791 conn->pipe_data.samr.rid == group_rid) {
793 if (!connection_ok(conn)) {
794 /* Shutdown cli? Free conn? Allow retry of DC? */
795 DLIST_REMOVE(cm_conns, conn);
803 /* Create a domain handle to open a user handle from */
805 if (!cm_get_sam_dom_handle(domain, domain_sid))
808 for (conn = cm_conns; conn; conn = conn->next) {
809 if (strequal(conn->domain, domain) &&
810 strequal(conn->pipe_name, PIPE_SAMR) &&
811 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
816 DEBUG(0, ("No domain sam handle was created!\n"));
820 if (!(conn = (struct winbindd_cm_conn *)
821 malloc(sizeof(struct winbindd_cm_conn))))
826 fstrcpy(conn->domain, basic_conn->domain);
827 fstrcpy(conn->controller, basic_conn->controller);
828 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
830 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
831 conn->cli = basic_conn->cli;
832 conn->pipe_data.samr.rid = group_rid;
834 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
835 &basic_conn->pol, des_access, group_rid,
838 if (!NT_STATUS_IS_OK(result))
843 DLIST_ADD(cm_conns, conn);
854 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
855 netlogon pipe as no handle is returned. */
857 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
858 struct cli_state **cli)
860 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
861 struct winbindd_cm_conn *conn;
864 return NT_STATUS_INVALID_PARAMETER;
867 /* Open an initial conection */
869 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
873 result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd);
875 if (!NT_STATUS_IS_OK(result)) {
876 DEBUG(0, ("error connecting to domain password server: %s\n",
879 /* Hit the cache code again. This cleans out the old connection and gets a new one */
880 if (conn->cli->fd == -1) {
881 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
886 result = cli_nt_setup_creds(
887 conn->cli, get_sec_chan(),trust_passwd);
890 if (!NT_STATUS_IS_OK(result)) {
891 cli_shutdown(conn->cli);
892 DLIST_REMOVE(cm_conns, conn);
903 /* Dump the current connection status */
905 static void dump_conn_list(void)
907 struct winbindd_cm_conn *con;
909 DEBUG(0, ("\tDomain Controller Pipe\n"));
911 for(con = cm_conns; con; con = con->next) {
914 /* Display pipe info */
916 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
917 DEBUG(0, ("Error: not enough memory!\n"));
919 DEBUG(0, ("%s\n", msg));
925 void winbindd_cm_status(void)
927 /* List open connections */
929 DEBUG(0, ("winbindd connection manager status:\n"));
934 DEBUG(0, ("\tNo active connections\n"));