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;
93 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
95 static struct get_dc_name_cache *get_dc_name_cache;
96 struct get_dc_name_cache *dcc;
97 struct in_addr *ip_list, dc_ip;
100 /* Check the cache for previous lookups */
102 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
104 if (!strequal(domain, dcc->domain_name))
105 continue; /* Not our domain */
107 if ((time(NULL) - dcc->lookup_time) >
108 GET_DC_NAME_CACHE_TIMEOUT) {
110 /* Cache entry has expired, delete it */
112 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
114 DLIST_REMOVE(get_dc_name_cache, dcc);
120 /* Return a positive or negative lookup for this domain */
122 if (dcc->srv_name[0]) {
123 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
124 fstrcpy(srv_name, dcc->srv_name);
127 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
132 /* Add cache entry for this lookup. */
134 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
136 if (!(dcc = (struct get_dc_name_cache *)
137 malloc(sizeof(struct get_dc_name_cache))))
142 fstrcpy(dcc->domain_name, domain);
143 dcc->lookup_time = time(NULL);
145 DLIST_ADD(get_dc_name_cache, dcc);
147 /* Lookup domain controller name. Try the real PDC first to avoid
149 if (!get_dc_list(True, domain, &ip_list, &count)) {
150 if (!get_dc_list(False, domain, &ip_list, &count)) {
151 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
156 /* Pick a nice close server */
157 /* Look for DC on local net */
159 for (i = 0; i < count; i++) {
160 if (!is_local_net(ip_list[i]))
163 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
167 zero_ip(&ip_list[i]);
171 * Secondly try and contact a random PDC/BDC.
174 i = (sys_random() % count);
176 if (!is_zero_ip(ip_list[i]) &&
177 name_status_find(domain, 0x1c, 0x20,
178 ip_list[i], srv_name)) {
182 zero_ip(&ip_list[i]); /* Tried and failed. */
184 /* Finally return first DC that we can contact */
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)) {
196 /* No-one to talk to )-: */
197 return False; /* Boo-hoo */
200 /* We have the netbios name and IP address of a domain controller.
201 Ideally we should sent a SAMLOGON request to determine whether
202 the DC is alive and kicking. If we can catch a dead DC before
203 performing a cli_connect() we can avoid a 30-second timeout. */
205 /* We have a name so make the cache entry positive now */
207 fstrcpy(dcc->srv_name, srv_name);
209 DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
210 inet_ntoa(dc_ip), domain));
219 /* Choose between anonymous or authenticated connections. We need to use
220 an authenticated connection if DCs have the RestrictAnonymous registry
221 entry set > 0, or the "Additional restrictions for anonymous
222 connections" set in the win2k Local Security Policy.
224 Caller to free() result in domain, username, password
227 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
229 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
230 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
231 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
233 if (*username && **username) {
234 if (!*domain || !**domain) {
235 *domain = smb_xstrdup(lp_workgroup());
238 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
240 DEBUG(3, ("IPC$ connections done anonymously\n"));
241 *username = smb_xstrdup("");
242 *domain = smb_xstrdup("");
243 *password = smb_xstrdup("");
247 /* Open a new smb pipe connection to a DC on a given domain. Cache
248 negative creation attempts so we don't try and connect to broken
249 machines too often. */
251 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
253 struct failed_connection_cache {
258 struct failed_connection_cache *prev, *next;
261 static struct failed_connection_cache *failed_connection_cache;
263 /* Add an entry to the failed conneciton cache */
265 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, NTSTATUS result) {
266 struct failed_connection_cache *fcc;
268 SMB_ASSERT(!NT_STATUS_IS_OK(result));
270 /* Create negative lookup cache entry for this domain and controller */
272 if (!(fcc = (struct failed_connection_cache *)
273 malloc(sizeof(struct failed_connection_cache)))) {
274 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
280 fstrcpy(fcc->domain_name, new_conn->domain);
281 fstrcpy(fcc->controller, new_conn->controller);
282 fcc->lookup_time = time(NULL);
283 fcc->nt_status = result;
285 DLIST_ADD(failed_connection_cache, fcc);
288 /* Open a connction to the remote server, cache failures for 30 seconds */
290 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
291 struct winbindd_cm_conn *new_conn)
293 struct failed_connection_cache *fcc;
294 extern pstring global_myname;
296 char *ipc_username, *ipc_domain, *ipc_password;
297 struct in_addr dc_ip;
301 fstrcpy(new_conn->domain, domain);
302 fstrcpy(new_conn->pipe_name, pipe_name);
304 /* Look for a domain controller for this domain. Negative results
305 are cached so don't bother applying the caching for this
306 function just yet. */
308 if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
309 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
310 add_failed_connection_entry(new_conn, result);
314 /* Return false if we have tried to look up this domain and netbios
315 name before and failed. */
317 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
319 if (!(strequal(domain, fcc->domain_name) &&
320 strequal(new_conn->controller, fcc->controller)))
321 continue; /* Not our domain */
323 if ((time(NULL) - fcc->lookup_time) >
324 FAILED_CONNECTION_CACHE_TIMEOUT) {
326 /* Cache entry has expired, delete it */
328 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
330 DLIST_REMOVE(failed_connection_cache, fcc);
336 /* The timeout hasn't expired yet so return false */
338 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
340 result = fcc->nt_status;
341 SMB_ASSERT(!NT_STATUS_IS_OK(result));
345 /* Initialise SMB connection */
347 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
349 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
350 new_conn->controller, global_myname, ipc_domain, ipc_username));
352 result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller,
354 "IPC", ipc_username, ipc_domain,
357 SAFE_FREE(ipc_username);
358 SAFE_FREE(ipc_domain);
359 SAFE_FREE(ipc_password);
361 if (!NT_STATUS_IS_OK(result)) {
362 add_failed_connection_entry(new_conn, result);
366 if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
367 result = NT_STATUS_PIPE_NOT_AVAILABLE;
368 add_failed_connection_entry(new_conn, result);
369 cli_shutdown(new_conn->cli);
376 /* Return true if a connection is still alive */
378 static BOOL connection_ok(struct winbindd_cm_conn *conn)
381 smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
386 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
387 conn->controller, conn->domain, conn->pipe_name));
388 smb_panic("connection_ok: conn->cli was null!");
392 if (!conn->cli->initialised) {
393 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
394 conn->controller, conn->domain, conn->pipe_name));
395 smb_panic("connection_ok: conn->cli->initialised is False!");
399 if (conn->cli->fd == -1) {
400 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
401 conn->controller, conn->domain, conn->pipe_name));
408 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
410 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out)
412 struct winbindd_cm_conn *conn, conn_temp;
415 for (conn = cm_conns; conn; conn = conn->next) {
416 if (strequal(conn->domain, domain) &&
417 strequal(conn->pipe_name, pipe_name)) {
418 if (!connection_ok(conn)) {
420 cli_shutdown(conn->cli);
422 ZERO_STRUCT(conn_temp);
423 conn_temp.next = conn->next;
424 DLIST_REMOVE(cm_conns, conn);
426 conn = &conn_temp; /* Just to keep the loop moving */
434 if (!(conn = malloc(sizeof(*conn))))
435 return NT_STATUS_NO_MEMORY;
439 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
440 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
441 domain, pipe_name, nt_errstr(result)));
445 DLIST_ADD(cm_conns, conn);
452 /* Return a LSA policy handle on a domain */
454 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
456 struct winbindd_cm_conn *conn;
457 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
459 static CLI_POLICY_HND hnd;
461 /* Look for existing connections */
463 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
467 /* This *shitty* code needs scrapping ! JRA */
468 if (policy_handle_is_valid(&conn->pol)) {
474 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
475 des_access, &conn->pol);
477 if (!NT_STATUS_IS_OK(result)) {
478 /* Hit the cache code again. This cleans out the old connection and gets a new one */
479 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
480 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
484 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
485 des_access, &conn->pol);
488 if (!NT_STATUS_IS_OK(result)) {
489 cli_shutdown(conn->cli);
490 DLIST_REMOVE(cm_conns, conn);
502 /* Return a SAM policy handle on a domain */
504 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
506 struct winbindd_cm_conn *conn;
507 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
509 static CLI_POLICY_HND hnd;
511 /* Look for existing connections */
513 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
517 /* This *shitty* code needs scrapping ! JRA */
518 if (policy_handle_is_valid(&conn->pol)) {
523 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
524 des_access, &conn->pol);
526 if (!NT_STATUS_IS_OK(result)) {
527 /* Hit the cache code again. This cleans out the old connection and gets a new one */
528 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
529 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
533 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
534 des_access, &conn->pol);
537 if (!NT_STATUS_IS_OK(result)) {
538 cli_shutdown(conn->cli);
539 DLIST_REMOVE(cm_conns, conn);
551 #if 0 /* This code now *well* out of date */
553 /* Return a SAM domain policy handle on a domain */
555 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
557 struct winbindd_cm_conn *conn, *basic_conn = NULL;
558 static CLI_POLICY_HND hnd;
560 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
562 /* Look for existing connections */
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_DOM) {
569 if (!connection_ok(conn)) {
570 /* Shutdown cli? Free conn? Allow retry of DC? */
571 DLIST_REMOVE(cm_conns, conn);
579 /* Create a basic handle to open a domain handle from */
581 if (!cm_get_sam_handle(domain))
584 for (conn = cm_conns; conn; conn = conn->next) {
585 if (strequal(conn->domain, domain) &&
586 strequal(conn->pipe_name, PIPE_SAMR) &&
587 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
591 if (!(conn = (struct winbindd_cm_conn *)
592 malloc(sizeof(struct winbindd_cm_conn))))
597 fstrcpy(conn->domain, basic_conn->domain);
598 fstrcpy(conn->controller, basic_conn->controller);
599 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
601 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
602 conn->cli = basic_conn->cli;
604 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
605 &basic_conn->pol, des_access,
606 domain_sid, &conn->pol);
608 if (!NT_STATUS_IS_OK(result))
613 DLIST_ADD(cm_conns, conn);
622 /* Return a SAM policy handle on a domain user */
624 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
627 struct winbindd_cm_conn *conn, *basic_conn = NULL;
628 static CLI_POLICY_HND hnd;
630 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
632 /* Look for existing connections */
634 for (conn = cm_conns; conn; conn = conn->next) {
635 if (strequal(conn->domain, domain) &&
636 strequal(conn->pipe_name, PIPE_SAMR) &&
637 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
638 conn->pipe_data.samr.rid == user_rid) {
640 if (!connection_ok(conn)) {
641 /* Shutdown cli? Free conn? Allow retry of DC? */
642 DLIST_REMOVE(cm_conns, conn);
650 /* Create a domain handle to open a user handle from */
652 if (!cm_get_sam_dom_handle(domain, domain_sid))
655 for (conn = cm_conns; conn; conn = conn->next) {
656 if (strequal(conn->domain, domain) &&
657 strequal(conn->pipe_name, PIPE_SAMR) &&
658 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
663 DEBUG(0, ("No domain sam handle was created!\n"));
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_USER;
678 conn->cli = basic_conn->cli;
679 conn->pipe_data.samr.rid = user_rid;
681 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
682 &basic_conn->pol, des_access, user_rid,
685 if (!NT_STATUS_IS_OK(result))
690 DLIST_ADD(cm_conns, conn);
699 /* Return a SAM policy handle on a domain group */
701 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
704 struct winbindd_cm_conn *conn, *basic_conn = NULL;
705 static CLI_POLICY_HND hnd;
707 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
709 /* Look for existing connections */
711 for (conn = cm_conns; conn; conn = conn->next) {
712 if (strequal(conn->domain, domain) &&
713 strequal(conn->pipe_name, PIPE_SAMR) &&
714 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
715 conn->pipe_data.samr.rid == group_rid) {
717 if (!connection_ok(conn)) {
718 /* Shutdown cli? Free conn? Allow retry of DC? */
719 DLIST_REMOVE(cm_conns, conn);
727 /* Create a domain handle to open a user handle from */
729 if (!cm_get_sam_dom_handle(domain, domain_sid))
732 for (conn = cm_conns; conn; conn = conn->next) {
733 if (strequal(conn->domain, domain) &&
734 strequal(conn->pipe_name, PIPE_SAMR) &&
735 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
740 DEBUG(0, ("No domain sam handle was created!\n"));
744 if (!(conn = (struct winbindd_cm_conn *)
745 malloc(sizeof(struct winbindd_cm_conn))))
750 fstrcpy(conn->domain, basic_conn->domain);
751 fstrcpy(conn->controller, basic_conn->controller);
752 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
754 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
755 conn->cli = basic_conn->cli;
756 conn->pipe_data.samr.rid = group_rid;
758 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
759 &basic_conn->pol, des_access, group_rid,
762 if (!NT_STATUS_IS_OK(result))
767 DLIST_ADD(cm_conns, conn);
778 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
779 netlogon pipe as no handle is returned. */
781 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
782 struct cli_state **cli)
784 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
785 struct winbindd_cm_conn *conn;
788 return NT_STATUS_INVALID_PARAMETER;
791 /* Open an initial conection */
793 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
797 result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
798 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
800 if (!NT_STATUS_IS_OK(result)) {
801 DEBUG(0, ("error connecting to domain password server: %s\n",
804 /* Hit the cache code again. This cleans out the old connection and gets a new one */
805 if (conn->cli->fd == -1) {
806 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
811 result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
812 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
815 if (!NT_STATUS_IS_OK(result)) {
816 cli_shutdown(conn->cli);
817 DLIST_REMOVE(cm_conns, conn);
828 /* Dump the current connection status */
830 static void dump_conn_list(void)
832 struct winbindd_cm_conn *con;
834 DEBUG(0, ("\tDomain Controller Pipe\n"));
836 for(con = cm_conns; con; con = con->next) {
839 /* Display pipe info */
841 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
842 DEBUG(0, ("Error: not enough memory!\n"));
844 DEBUG(0, ("%s\n", msg));
850 void winbindd_cm_status(void)
852 /* List open connections */
854 DEBUG(0, ("winbindd connection manager status:\n"));
859 DEBUG(0, ("\tNo active connections\n"));