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(char *domain, fstring srv_name)
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));
210 /* Choose between anonymous or authenticated connections. We need to use
211 an authenticated connection if DCs have the RestrictAnonymous registry
212 entry set > 0, or the "Additional restrictions for anonymous
213 connections" set in the win2k Local Security Policy. */
215 void cm_init_creds(struct ntuser_creds *creds)
217 char *username, *password;
221 creds->pwd.null_pwd = True; /* anonymoose */
223 username = secrets_fetch(SECRETS_AUTH_USER, NULL);
224 password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
226 if (username && *username) {
227 pwd_set_cleartext(&creds->pwd, password);
229 fstrcpy(creds->user_name, username);
230 fstrcpy(creds->domain, lp_workgroup());
232 DEBUG(3, ("IPC$ connections done %s\\%s\n", creds->domain,
235 DEBUG(3, ("IPC$ connections done anonymously\n"));
238 /* Open a new smb pipe connection to a DC on a given domain. Cache
239 negative creation attempts so we don't try and connect to broken
240 machines too often. */
242 #define OPEN_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
244 struct open_connection_cache {
248 struct open_connection_cache *prev, *next;
251 static BOOL cm_open_connection(char *domain, char *pipe_name,
252 struct winbindd_cm_conn *new_conn)
254 static struct open_connection_cache *open_connection_cache;
255 struct open_connection_cache *occ;
256 struct nmb_name calling, called;
257 extern pstring global_myname;
259 struct in_addr dest_ip;
261 struct ntuser_creds creds;
263 fstrcpy(new_conn->domain, domain);
264 fstrcpy(new_conn->pipe_name, pipe_name);
266 /* Look for a domain controller for this domain. Negative results
267 are cached so don't bother applying the caching for this
268 function just yet. */
270 if (!cm_get_dc_name(domain, new_conn->controller))
273 /* Return false if we have tried to look up this domain and netbios
274 name before and failed. */
276 for (occ = open_connection_cache; occ; occ = occ->next) {
278 if (!(strequal(domain, occ->domain_name) &&
279 strequal(new_conn->controller, occ->controller)))
280 continue; /* Not our domain */
282 if ((time(NULL) - occ->lookup_time) >
283 OPEN_CONNECTION_CACHE_TIMEOUT) {
285 /* Cache entry has expired, delete it */
287 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
289 DLIST_REMOVE(open_connection_cache, occ);
295 /* The timeout hasn't expired yet so return false */
297 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
302 /* Initialise SMB connection */
304 if (!(new_conn->cli = cli_initialise(NULL)))
307 if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
310 make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 0x20);
311 make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
313 cm_init_creds(&creds);
315 cli_init_creds(new_conn->cli, &creds);
317 if (!cli_establish_connection(new_conn->cli, new_conn->controller,
318 &dest_ip, &calling, &called, "IPC$",
322 if (!cli_nt_session_open (new_conn->cli, pipe_name))
329 /* Create negative lookup cache entry for this domain and controller */
332 if (!(occ = (struct open_connection_cache *)
333 malloc(sizeof(struct open_connection_cache))))
338 fstrcpy(occ->domain_name, domain);
339 fstrcpy(occ->controller, new_conn->controller);
340 occ->lookup_time = time(NULL);
342 DLIST_ADD(open_connection_cache, occ);
345 if (!result && new_conn->cli)
346 cli_shutdown(new_conn->cli);
351 /* Return true if a connection is still alive */
353 static BOOL connection_ok(struct winbindd_cm_conn *conn)
355 if (!conn->cli->initialised)
358 if (conn->cli->fd == -1)
364 /* Return a LSA policy handle on a domain */
366 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
368 struct winbindd_cm_conn *conn;
369 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
371 static CLI_POLICY_HND hnd;
373 /* Look for existing connections */
375 for (conn = cm_conns; conn; conn = conn->next) {
376 if (strequal(conn->domain, domain) &&
377 strequal(conn->pipe_name, PIPE_LSARPC)) {
379 if (!connection_ok(conn)) {
380 DLIST_REMOVE(cm_conns, conn);
388 /* Create a new one */
390 if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
395 if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
396 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
400 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
401 des_access, &conn->pol);
403 if (!NT_STATUS_IS_OK(result))
408 DLIST_ADD(cm_conns, conn);
417 /* Return a SAM policy handle on a domain */
419 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
421 struct winbindd_cm_conn *conn;
422 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
424 static CLI_POLICY_HND hnd;
426 /* Look for existing connections */
428 for (conn = cm_conns; conn; conn = conn->next) {
429 if (strequal(conn->domain, domain) && strequal(conn->pipe_name, PIPE_SAMR)) {
431 if (!connection_ok(conn)) {
432 DLIST_REMOVE(cm_conns, conn);
440 /* Create a new one */
442 if (!(conn = (struct winbindd_cm_conn *)
443 malloc(sizeof(struct winbindd_cm_conn))))
448 if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
449 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
453 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
454 des_access, &conn->pol);
456 if (!NT_STATUS_IS_OK(result))
461 DLIST_ADD(cm_conns, conn);
472 /* Return a SAM domain policy handle on a domain */
474 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
476 struct winbindd_cm_conn *conn, *basic_conn = NULL;
477 static CLI_POLICY_HND hnd;
479 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
481 /* Look for existing connections */
483 for (conn = cm_conns; conn; conn = conn->next) {
484 if (strequal(conn->domain, domain) &&
485 strequal(conn->pipe_name, PIPE_SAMR) &&
486 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
488 if (!connection_ok(conn)) {
489 DLIST_REMOVE(cm_conns, conn);
497 /* Create a basic handle to open a domain handle from */
499 if (!cm_get_sam_handle(domain))
502 for (conn = cm_conns; conn; conn = conn->next) {
503 if (strequal(conn->domain, domain) &&
504 strequal(conn->pipe_name, PIPE_SAMR) &&
505 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
509 if (!(conn = (struct winbindd_cm_conn *)
510 malloc(sizeof(struct winbindd_cm_conn))))
515 fstrcpy(conn->domain, basic_conn->domain);
516 fstrcpy(conn->controller, basic_conn->controller);
517 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
519 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
520 conn->cli = basic_conn->cli;
522 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
523 &basic_conn->pol, des_access,
524 domain_sid, &conn->pol);
526 if (!NT_STATUS_IS_OK(result))
531 DLIST_ADD(cm_conns, conn);
540 /* Return a SAM policy handle on a domain user */
542 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
545 struct winbindd_cm_conn *conn, *basic_conn = NULL;
546 static CLI_POLICY_HND hnd;
548 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
550 /* Look for existing connections */
552 for (conn = cm_conns; conn; conn = conn->next) {
553 if (strequal(conn->domain, domain) &&
554 strequal(conn->pipe_name, PIPE_SAMR) &&
555 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
556 conn->pipe_data.samr.rid == user_rid) {
558 if (!connection_ok(conn)) {
559 DLIST_REMOVE(cm_conns, conn);
567 /* Create a domain handle to open a user handle from */
569 if (!cm_get_sam_dom_handle(domain, domain_sid))
572 for (conn = cm_conns; conn; conn = conn->next) {
573 if (strequal(conn->domain, domain) &&
574 strequal(conn->pipe_name, PIPE_SAMR) &&
575 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
580 DEBUG(0, ("No domain sam handle was created!\n"));
584 if (!(conn = (struct winbindd_cm_conn *)
585 malloc(sizeof(struct winbindd_cm_conn))))
590 fstrcpy(conn->domain, basic_conn->domain);
591 fstrcpy(conn->controller, basic_conn->controller);
592 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
594 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
595 conn->cli = basic_conn->cli;
596 conn->pipe_data.samr.rid = user_rid;
598 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
599 &basic_conn->pol, des_access, user_rid,
602 if (!NT_STATUS_IS_OK(result))
607 DLIST_ADD(cm_conns, conn);
616 /* Return a SAM policy handle on a domain group */
618 CLI_POLICY_HND *cm_get_sam_group_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_GROUP &&
632 conn->pipe_data.samr.rid == group_rid) {
634 if (!connection_ok(conn)) {
635 DLIST_REMOVE(cm_conns, conn);
643 /* Create a domain handle to open a user handle from */
645 if (!cm_get_sam_dom_handle(domain, domain_sid))
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_DOM)
656 DEBUG(0, ("No domain sam handle was created!\n"));
660 if (!(conn = (struct winbindd_cm_conn *)
661 malloc(sizeof(struct winbindd_cm_conn))))
666 fstrcpy(conn->domain, basic_conn->domain);
667 fstrcpy(conn->controller, basic_conn->controller);
668 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
670 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
671 conn->cli = basic_conn->cli;
672 conn->pipe_data.samr.rid = group_rid;
674 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
675 &basic_conn->pol, des_access, group_rid,
678 if (!NT_STATUS_IS_OK(result))
683 DLIST_ADD(cm_conns, conn);
694 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
695 netlogon pipe as no handle is returned. */
697 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
698 struct cli_state **cli)
700 struct winbindd_cm_conn conn;
701 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
703 /* Open an initial conection */
707 if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
708 DEBUG(3, ("Could not open a connection to %s\n", domain));
712 result = new_cli_nt_setup_creds(conn.cli, trust_passwd);
714 if (!NT_STATUS_IS_OK(result)) {
715 DEBUG(0, ("error connecting to domain password server: %s\n",
716 get_nt_error_msg(result)));
717 cli_shutdown(conn.cli);
727 /* Dump the current connection status */
729 static void dump_conn_list(void)
731 struct winbindd_cm_conn *con;
733 DEBUG(0, ("\tDomain Controller Pipe\n"));
735 for(con = cm_conns; con; con = con->next) {
738 /* Display pipe info */
740 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
741 DEBUG(0, ("Error: not enough memory!\n"));
743 DEBUG(0, ("%s\n", msg));
749 void winbindd_cm_status(void)
751 /* List open connections */
753 DEBUG(0, ("winbindd connection manager status:\n"));
758 DEBUG(0, ("\tNo active connections\n"));