2 Unix SMB/Netbios implementation.
5 Winbind daemon connection manager
7 Copyright (C) Tim Potter 2001
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
64 /* Global list of connections. Initially a DLIST but can become a hash
65 table or whatever later. */
67 struct winbindd_cm_conn {
68 struct winbindd_cm_conn *prev, *next;
72 struct cli_state *cli;
76 static struct winbindd_cm_conn *cm_conns = NULL;
78 /* Get a domain controller name. Cache positive and negative lookups so we
79 don't go to the network too often when something is badly broken. */
81 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
83 struct get_dc_name_cache {
87 struct get_dc_name_cache *prev, *next;
90 static BOOL cm_get_dc_name(char *domain, fstring srv_name)
92 static struct get_dc_name_cache *get_dc_name_cache;
93 struct get_dc_name_cache *dcc;
94 struct in_addr *ip_list, dc_ip;
97 /* Check the cache for previous lookups */
99 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
101 if (!strequal(domain, dcc->domain_name))
102 continue; /* Not our domain */
104 if ((time(NULL) - dcc->lookup_time) >
105 GET_DC_NAME_CACHE_TIMEOUT) {
107 /* Cache entry has expired, delete it */
109 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
111 DLIST_REMOVE(get_dc_name_cache, dcc);
117 /* Return a positive or negative lookup for this domain */
119 if (dcc->srv_name[0]) {
120 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
121 fstrcpy(srv_name, dcc->srv_name);
124 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
129 /* Add cache entry for this lookup. */
131 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
133 if (!(dcc = (struct get_dc_name_cache *)
134 malloc(sizeof(struct get_dc_name_cache))))
139 fstrcpy(dcc->domain_name, domain);
140 dcc->lookup_time = time(NULL);
142 DLIST_ADD(get_dc_name_cache, dcc);
144 /* Lookup domain controller name */
146 if (!get_dc_list(False, domain, &ip_list, &count)) {
147 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
151 /* Pick a nice close server */
153 if (strequal(lp_passwordserver(), "*")) {
155 /* Look for DC on local net */
157 for (i = 0; i < count; i++) {
158 if (is_local_net(ip_list[i]) &&
159 name_status_find(domain, 0x1c, 0x20,
160 ip_list[i], srv_name)) {
164 zero_ip(&ip_list[i]);
167 /* Look for other DCs */
169 for (i = 0; i < count; i++) {
170 if (!is_zero_ip(ip_list[i]) &&
171 name_status_find(domain, 0x1c, 0x20,
172 ip_list[i], srv_name)) {
178 /* No-one to talk to )-: */
183 /* Return first DC that we can contact */
185 for (i = 0; i < count; i++) {
186 if (name_status_find(domain, 0x1c, 0x20, ip_list[i],
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, ("Returning DC %s (%s) for domain %s\n", srv_name,
206 inet_ntoa(dc_ip), domain));
211 /* Choose between anonymous or authenticated connections. We need to use
212 an authenticated connection if DCs have the RestrictAnonymous registry
213 entry set > 0, or the "Additional restrictions for anonymous
214 connections" set in the win2k Local Security Policy. */
216 void cm_init_creds(struct ntuser_creds *creds)
218 char *username, *password;
222 creds->pwd.null_pwd = True; /* anonymoose */
224 username = secrets_fetch(SECRETS_AUTH_USER, NULL);
225 password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
227 if (username && *username) {
228 pwd_set_cleartext(&creds->pwd, password);
230 fstrcpy(creds->user_name, username);
231 fstrcpy(creds->domain, lp_workgroup());
233 DEBUG(3, ("IPC$ connections done %s\\%s\n", creds->domain,
236 DEBUG(3, ("IPC$ connections done anonymously\n"));
239 /* Open a new smb pipe connection to a DC on a given domain. Cache
240 negative creation attempts so we don't try and connect to broken
241 machines too often. */
243 #define OPEN_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
245 struct open_connection_cache {
249 struct open_connection_cache *prev, *next;
252 static BOOL cm_open_connection(char *domain, char *pipe_name,
253 struct winbindd_cm_conn *new_conn)
255 static struct open_connection_cache *open_connection_cache;
256 struct open_connection_cache *occ;
257 struct nmb_name calling, called;
258 extern pstring global_myname;
260 struct in_addr dest_ip;
262 struct ntuser_creds creds;
264 fstrcpy(new_conn->domain, domain);
265 fstrcpy(new_conn->pipe_name, pipe_name);
267 /* Look for a domain controller for this domain. Negative results
268 are cached so don't bother applying the caching for this
269 function just yet. */
271 if (!cm_get_dc_name(domain, new_conn->controller))
274 /* Return false if we have tried to look up this domain and netbios
275 name before and failed. */
277 for (occ = open_connection_cache; occ; occ = occ->next) {
279 if (!(strequal(domain, occ->domain_name) &&
280 strequal(new_conn->controller, occ->controller)))
281 continue; /* Not our domain */
283 if ((time(NULL) - occ->lookup_time) >
284 OPEN_CONNECTION_CACHE_TIMEOUT) {
286 /* Cache entry has expired, delete it */
288 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
290 DLIST_REMOVE(open_connection_cache, occ);
296 /* The timeout hasn't expired yet so return false */
298 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
303 /* Initialise SMB connection */
305 if (!(new_conn->cli = cli_initialise(NULL)))
308 if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
311 make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 0x20);
312 make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
314 cm_init_creds(&creds);
316 cli_init_creds(new_conn->cli, &creds);
318 if (!cli_establish_connection(new_conn->cli, new_conn->controller,
319 &dest_ip, &calling, &called, "IPC$",
323 if (!cli_nt_session_open (new_conn->cli, pipe_name))
330 /* Create negative lookup cache entry for this domain and controller */
333 if (!(occ = (struct open_connection_cache *)
334 malloc(sizeof(struct open_connection_cache))))
339 fstrcpy(occ->domain_name, domain);
340 fstrcpy(occ->controller, new_conn->controller);
341 occ->lookup_time = time(NULL);
343 DLIST_ADD(open_connection_cache, occ);
346 if (!result && new_conn->cli)
347 cli_shutdown(new_conn->cli);
352 /* Return true if a connection is still alive */
354 static BOOL connection_ok(struct winbindd_cm_conn *conn)
356 if (!conn->cli->initialised)
359 if (conn->cli->fd == -1)
365 /* Return a LSA policy handle on a domain */
367 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
369 struct winbindd_cm_conn *conn;
370 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
372 static CLI_POLICY_HND hnd;
374 /* Look for existing connections */
376 for (conn = cm_conns; conn; conn = conn->next) {
377 if (strequal(conn->domain, domain) &&
378 strequal(conn->pipe_name, PIPE_LSARPC)) {
380 if (!connection_ok(conn)) {
381 DLIST_REMOVE(cm_conns, conn);
389 /* Create a new one */
391 if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
396 if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
397 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
401 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
402 des_access, &conn->pol);
404 if (!NT_STATUS_IS_OK(result))
409 DLIST_ADD(cm_conns, conn);
418 /* Return a SAM policy handle on a domain */
420 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
422 struct winbindd_cm_conn *conn;
423 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
425 static CLI_POLICY_HND hnd;
427 /* Look for existing connections */
429 for (conn = cm_conns; conn; conn = conn->next) {
430 if (strequal(conn->domain, domain) && strequal(conn->pipe_name, PIPE_SAMR)) {
432 if (!connection_ok(conn)) {
433 DLIST_REMOVE(cm_conns, conn);
441 /* Create a new one */
443 if (!(conn = (struct winbindd_cm_conn *)
444 malloc(sizeof(struct winbindd_cm_conn))))
449 if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
450 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
454 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
455 des_access, &conn->pol);
457 if (!NT_STATUS_IS_OK(result))
462 DLIST_ADD(cm_conns, conn);
473 /* Return a SAM domain policy handle on a domain */
475 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
477 struct winbindd_cm_conn *conn, *basic_conn = NULL;
478 static CLI_POLICY_HND hnd;
480 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
482 /* Look for existing connections */
484 for (conn = cm_conns; conn; conn = conn->next) {
485 if (strequal(conn->domain, domain) &&
486 strequal(conn->pipe_name, PIPE_SAMR) &&
487 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
489 if (!connection_ok(conn)) {
490 DLIST_REMOVE(cm_conns, conn);
498 /* Create a basic handle to open a domain handle from */
500 if (!cm_get_sam_handle(domain))
503 for (conn = cm_conns; conn; conn = conn->next) {
504 if (strequal(conn->domain, domain) &&
505 strequal(conn->pipe_name, PIPE_SAMR) &&
506 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
510 if (!(conn = (struct winbindd_cm_conn *)
511 malloc(sizeof(struct winbindd_cm_conn))))
516 fstrcpy(conn->domain, basic_conn->domain);
517 fstrcpy(conn->controller, basic_conn->controller);
518 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
520 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
521 conn->cli = basic_conn->cli;
523 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
524 &basic_conn->pol, des_access,
525 domain_sid, &conn->pol);
527 if (!NT_STATUS_IS_OK(result))
532 DLIST_ADD(cm_conns, conn);
541 /* Return a SAM policy handle on a domain user */
543 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
546 struct winbindd_cm_conn *conn, *basic_conn = NULL;
547 static CLI_POLICY_HND hnd;
549 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
551 /* Look for existing connections */
553 for (conn = cm_conns; conn; conn = conn->next) {
554 if (strequal(conn->domain, domain) &&
555 strequal(conn->pipe_name, PIPE_SAMR) &&
556 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
557 conn->pipe_data.samr.rid == user_rid) {
559 if (!connection_ok(conn)) {
560 DLIST_REMOVE(cm_conns, conn);
568 /* Create a domain handle to open a user handle from */
570 if (!cm_get_sam_dom_handle(domain, domain_sid))
573 for (conn = cm_conns; conn; conn = conn->next) {
574 if (strequal(conn->domain, domain) &&
575 strequal(conn->pipe_name, PIPE_SAMR) &&
576 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
581 DEBUG(0, ("No domain sam handle was created!\n"));
585 if (!(conn = (struct winbindd_cm_conn *)
586 malloc(sizeof(struct winbindd_cm_conn))))
591 fstrcpy(conn->domain, basic_conn->domain);
592 fstrcpy(conn->controller, basic_conn->controller);
593 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
595 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
596 conn->cli = basic_conn->cli;
597 conn->pipe_data.samr.rid = user_rid;
599 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
600 &basic_conn->pol, des_access, user_rid,
603 if (!NT_STATUS_IS_OK(result))
608 DLIST_ADD(cm_conns, conn);
617 /* Return a SAM policy handle on a domain group */
619 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
622 struct winbindd_cm_conn *conn, *basic_conn = NULL;
623 static CLI_POLICY_HND hnd;
625 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
627 /* Look for existing connections */
629 for (conn = cm_conns; conn; conn = conn->next) {
630 if (strequal(conn->domain, domain) &&
631 strequal(conn->pipe_name, PIPE_SAMR) &&
632 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
633 conn->pipe_data.samr.rid == group_rid) {
635 if (!connection_ok(conn)) {
636 DLIST_REMOVE(cm_conns, conn);
644 /* Create a domain handle to open a user handle from */
646 if (!cm_get_sam_dom_handle(domain, domain_sid))
649 for (conn = cm_conns; conn; conn = conn->next) {
650 if (strequal(conn->domain, domain) &&
651 strequal(conn->pipe_name, PIPE_SAMR) &&
652 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
657 DEBUG(0, ("No domain sam handle was created!\n"));
661 if (!(conn = (struct winbindd_cm_conn *)
662 malloc(sizeof(struct winbindd_cm_conn))))
667 fstrcpy(conn->domain, basic_conn->domain);
668 fstrcpy(conn->controller, basic_conn->controller);
669 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
671 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
672 conn->cli = basic_conn->cli;
673 conn->pipe_data.samr.rid = group_rid;
675 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
676 &basic_conn->pol, des_access, group_rid,
679 if (!NT_STATUS_IS_OK(result))
684 DLIST_ADD(cm_conns, conn);
695 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
696 netlogon pipe as no handle is returned. */
698 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
699 struct cli_state **cli)
701 struct winbindd_cm_conn conn;
702 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
704 /* Open an initial conection */
708 if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
709 DEBUG(3, ("Could not open a connection to %s\n", domain));
713 result = new_cli_nt_setup_creds(conn.cli, trust_passwd);
715 if (!NT_STATUS_IS_OK(result)) {
716 DEBUG(0, ("error connecting to domain password server: %s\n",
717 get_nt_error_msg(result)));
718 cli_shutdown(conn.cli);
728 /* Dump the current connection status */
730 static void dump_conn_list(void)
732 struct winbindd_cm_conn *con;
734 DEBUG(0, ("\tDomain Controller Pipe\n"));
736 for(con = cm_conns; con; con = con->next) {
739 /* Display pipe info */
741 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
742 DEBUG(0, ("Error: not enough memory!\n"));
744 DEBUG(0, ("%s\n", msg));
750 void winbindd_cm_status(void)
752 /* List open connections */
754 DEBUG(0, ("winbindd connection manager status:\n"));
759 DEBUG(0, ("\tNo active connections\n"));