preparing for release of 2.2.3a
[Samba.git] / source / nsswitch / winbindd_cm.c
blobaf03826ad0766e67f69812d390e2b2089f2729d9
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
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
32 selection etc
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
44 implementation. -tpot
49 TODO:
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
55 cm_get_dc_name()
57 - Take care when destroying cli_structs as they can be shared between
58 various sam handles.
62 #include "winbindd.h"
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;
69 fstring domain;
70 fstring controller;
71 fstring pipe_name;
72 struct cli_state *cli;
73 POLICY_HND pol;
76 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 {
84 fstring domain_name;
85 fstring srv_name;
86 time_t lookup_time;
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;
95 int count, i;
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);
112 SAFE_FREE(dcc);
114 break;
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);
122 return True;
123 } else {
124 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
125 return False;
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))))
135 return False;
137 ZERO_STRUCTP(dcc);
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));
148 return False;
151 /* Firstly choose a PDC/BDC who has the same network address as any
152 of our interfaces. */
154 for (i = 0; i < count; i++) {
155 if(is_local_net(ip_list[i]))
156 goto got_ip;
159 if (count == 0) {
160 DEBUG(3, ("No domain controllers for domain %s\n", domain));
161 return False;
164 i = (sys_random() % count);
166 got_ip:
167 dc_ip = ip_list[i];
168 SAFE_FREE(ip_list);
170 /* We really should be doing a GETDC call here rather than a node
171 status lookup. */
173 if (!name_status_find(domain, 0x1c, 0x20, dc_ip, srv_name)) {
174 DEBUG(3, ("Error looking up DC name for %s in domain %s\n", inet_ntoa(dc_ip), domain));
175 return False;
178 /* We have a name so make the cache entry positive now */
180 fstrcpy(dcc->srv_name, srv_name);
182 return True;
185 /* Choose between anonymous or authenticated connections. We need to use
186 an authenticated connection if DCs have the RestrictAnonymous registry
187 entry set > 0, or the "Additional restrictions for anonymous
188 connections" set in the win2k Local Security Policy. */
190 void cm_init_creds(struct ntuser_creds *creds)
192 char *username, *password;
194 ZERO_STRUCTP(creds);
196 creds->pwd.null_pwd = True; /* anonymoose */
198 username = secrets_fetch(SECRETS_AUTH_USER, NULL);
199 password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
201 if (username && *username) {
202 pwd_set_cleartext(&creds->pwd, password);
203 pwd_make_lm_nt_16(&creds->pwd, password);
205 fstrcpy(creds->user_name, username);
206 fstrcpy(creds->domain, lp_workgroup());
208 DEBUG(3, ("IPC$ connections done %s\\%s\n", creds->domain,
209 creds->user_name));
210 } else
211 DEBUG(3, ("IPC$ connections done anonymously\n"));
214 /* Open a new smb pipe connection to a DC on a given domain. Cache
215 negative creation attempts so we don't try and connect to broken
216 machines too often. */
218 #define OPEN_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
220 struct open_connection_cache {
221 fstring domain_name;
222 fstring controller;
223 time_t lookup_time;
224 struct open_connection_cache *prev, *next;
227 static BOOL cm_open_connection(char *domain, char *pipe_name,
228 struct winbindd_cm_conn *new_conn)
230 static struct open_connection_cache *open_connection_cache;
231 struct open_connection_cache *occ;
232 struct nmb_name calling, called;
233 extern pstring global_myname;
234 fstring dest_host;
235 struct in_addr dest_ip;
236 BOOL result = False;
237 struct ntuser_creds creds;
239 fstrcpy(new_conn->domain, domain);
240 fstrcpy(new_conn->pipe_name, pipe_name);
242 /* Look for a domain controller for this domain. Negative results
243 are cached so don't bother applying the caching for this
244 function just yet. */
246 if (!cm_get_dc_name(domain, new_conn->controller))
247 goto done;
249 /* Return false if we have tried to look up this domain and netbios
250 name before and failed. */
252 for (occ = open_connection_cache; occ; occ = occ->next) {
254 if (!(strequal(domain, occ->domain_name) &&
255 strequal(new_conn->controller, occ->controller)))
256 continue; /* Not our domain */
258 if ((time(NULL) - occ->lookup_time) >
259 OPEN_CONNECTION_CACHE_TIMEOUT) {
261 /* Cache entry has expired, delete it */
263 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
265 DLIST_REMOVE(open_connection_cache, occ);
266 SAFE_FREE(occ);
268 break;
271 /* The timeout hasn't expired yet so return false */
273 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
275 goto done;
278 /* Initialise SMB connection */
280 if (!(new_conn->cli = cli_initialise(NULL)))
281 goto done;
283 if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
284 goto done;
286 make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 0x20);
287 make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
289 cm_init_creds(&creds);
291 cli_init_creds(new_conn->cli, &creds);
293 if (!cli_establish_connection(new_conn->cli, new_conn->controller,
294 &dest_ip, &calling, &called, "IPC$",
295 "IPC", False, True))
296 goto done;
298 if (!cli_nt_session_open (new_conn->cli, pipe_name))
299 goto done;
301 result = True;
303 done:
305 /* Create negative lookup cache entry for this domain and controller */
307 if (!result) {
308 if (!(occ = (struct open_connection_cache *)
309 malloc(sizeof(struct open_connection_cache))))
310 return False;
312 ZERO_STRUCTP(occ);
314 fstrcpy(occ->domain_name, domain);
315 fstrcpy(occ->controller, new_conn->controller);
316 occ->lookup_time = time(NULL);
318 DLIST_ADD(open_connection_cache, occ);
321 if (!result && new_conn->cli)
322 cli_shutdown(new_conn->cli);
324 return result;
327 /* Return true if a connection is still alive */
329 static BOOL connection_ok(struct winbindd_cm_conn *conn)
331 if (!conn->cli->initialised)
332 return False;
334 if (conn->cli->fd == -1)
335 return False;
337 return True;
340 /* Return a LSA policy handle on a domain */
342 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
344 struct winbindd_cm_conn *conn;
345 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
346 NTSTATUS result;
347 static CLI_POLICY_HND hnd;
349 /* Look for existing connections */
351 for (conn = cm_conns; conn; conn = conn->next) {
352 if (strequal(conn->domain, domain) &&
353 strequal(conn->pipe_name, PIPE_LSARPC)) {
355 if (!connection_ok(conn)) {
356 DLIST_REMOVE(cm_conns, conn);
357 return NULL;
360 goto ok;
364 /* Create a new one */
366 if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
367 return NULL;
369 ZERO_STRUCTP(conn);
371 if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
372 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
373 return NULL;
376 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
377 des_access, &conn->pol);
379 if (!NT_STATUS_IS_OK(result))
380 return NULL;
382 /* Add to list */
384 DLIST_ADD(cm_conns, conn);
387 hnd.pol = conn->pol;
388 hnd.cli = conn->cli;
390 return &hnd;
393 /* Return a SAM policy handle on a domain */
395 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
397 struct winbindd_cm_conn *conn;
398 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
399 NTSTATUS result;
400 static CLI_POLICY_HND hnd;
402 /* Look for existing connections */
404 for (conn = cm_conns; conn; conn = conn->next) {
405 if (strequal(conn->domain, domain) && strequal(conn->pipe_name, PIPE_SAMR)) {
407 if (!connection_ok(conn)) {
408 DLIST_REMOVE(cm_conns, conn);
409 return NULL;
412 goto ok;
416 /* Create a new one */
418 if (!(conn = (struct winbindd_cm_conn *)
419 malloc(sizeof(struct winbindd_cm_conn))))
420 return NULL;
422 ZERO_STRUCTP(conn);
424 if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
425 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
426 return NULL;
429 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
430 des_access, &conn->pol);
432 if (!NT_STATUS_IS_OK(result))
433 return NULL;
435 /* Add to list */
437 DLIST_ADD(cm_conns, conn);
440 hnd.pol = conn->pol;
441 hnd.cli = conn->cli;
443 return &hnd;
446 #if 0
448 /* Return a SAM domain policy handle on a domain */
450 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
452 struct winbindd_cm_conn *conn, *basic_conn = NULL;
453 static CLI_POLICY_HND hnd;
454 NTSTATUS result;
455 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
457 /* Look for existing connections */
459 for (conn = cm_conns; conn; conn = conn->next) {
460 if (strequal(conn->domain, domain) &&
461 strequal(conn->pipe_name, PIPE_SAMR) &&
462 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
464 if (!connection_ok(conn)) {
465 DLIST_REMOVE(cm_conns, conn);
466 return NULL;
469 goto ok;
473 /* Create a basic handle to open a domain handle from */
475 if (!cm_get_sam_handle(domain))
476 return False;
478 for (conn = cm_conns; conn; conn = conn->next) {
479 if (strequal(conn->domain, domain) &&
480 strequal(conn->pipe_name, PIPE_SAMR) &&
481 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
482 basic_conn = conn;
485 if (!(conn = (struct winbindd_cm_conn *)
486 malloc(sizeof(struct winbindd_cm_conn))))
487 return NULL;
489 ZERO_STRUCTP(conn);
491 fstrcpy(conn->domain, basic_conn->domain);
492 fstrcpy(conn->controller, basic_conn->controller);
493 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
495 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
496 conn->cli = basic_conn->cli;
498 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
499 &basic_conn->pol, des_access,
500 domain_sid, &conn->pol);
502 if (!NT_STATUS_IS_OK(result))
503 return NULL;
505 /* Add to list */
507 DLIST_ADD(cm_conns, conn);
510 hnd.pol = conn->pol;
511 hnd.cli = conn->cli;
513 return &hnd;
516 /* Return a SAM policy handle on a domain user */
518 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
519 uint32 user_rid)
521 struct winbindd_cm_conn *conn, *basic_conn = NULL;
522 static CLI_POLICY_HND hnd;
523 NTSTATUS result;
524 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
526 /* Look for existing connections */
528 for (conn = cm_conns; conn; conn = conn->next) {
529 if (strequal(conn->domain, domain) &&
530 strequal(conn->pipe_name, PIPE_SAMR) &&
531 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
532 conn->pipe_data.samr.rid == user_rid) {
534 if (!connection_ok(conn)) {
535 DLIST_REMOVE(cm_conns, conn);
536 return NULL;
539 goto ok;
543 /* Create a domain handle to open a user handle from */
545 if (!cm_get_sam_dom_handle(domain, domain_sid))
546 return NULL;
548 for (conn = cm_conns; conn; conn = conn->next) {
549 if (strequal(conn->domain, domain) &&
550 strequal(conn->pipe_name, PIPE_SAMR) &&
551 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
552 basic_conn = conn;
555 if (!basic_conn) {
556 DEBUG(0, ("No domain sam handle was created!\n"));
557 return NULL;
560 if (!(conn = (struct winbindd_cm_conn *)
561 malloc(sizeof(struct winbindd_cm_conn))))
562 return NULL;
564 ZERO_STRUCTP(conn);
566 fstrcpy(conn->domain, basic_conn->domain);
567 fstrcpy(conn->controller, basic_conn->controller);
568 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
570 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
571 conn->cli = basic_conn->cli;
572 conn->pipe_data.samr.rid = user_rid;
574 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
575 &basic_conn->pol, des_access, user_rid,
576 &conn->pol);
578 if (!NT_STATUS_IS_OK(result))
579 return NULL;
581 /* Add to list */
583 DLIST_ADD(cm_conns, conn);
586 hnd.pol = conn->pol;
587 hnd.cli = conn->cli;
589 return &hnd;
592 /* Return a SAM policy handle on a domain group */
594 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
595 uint32 group_rid)
597 struct winbindd_cm_conn *conn, *basic_conn = NULL;
598 static CLI_POLICY_HND hnd;
599 NTSTATUS result;
600 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
602 /* Look for existing connections */
604 for (conn = cm_conns; conn; conn = conn->next) {
605 if (strequal(conn->domain, domain) &&
606 strequal(conn->pipe_name, PIPE_SAMR) &&
607 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
608 conn->pipe_data.samr.rid == group_rid) {
610 if (!connection_ok(conn)) {
611 DLIST_REMOVE(cm_conns, conn);
612 return NULL;
615 goto ok;
619 /* Create a domain handle to open a user handle from */
621 if (!cm_get_sam_dom_handle(domain, domain_sid))
622 return NULL;
624 for (conn = cm_conns; conn; conn = conn->next) {
625 if (strequal(conn->domain, domain) &&
626 strequal(conn->pipe_name, PIPE_SAMR) &&
627 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
628 basic_conn = conn;
631 if (!basic_conn) {
632 DEBUG(0, ("No domain sam handle was created!\n"));
633 return NULL;
636 if (!(conn = (struct winbindd_cm_conn *)
637 malloc(sizeof(struct winbindd_cm_conn))))
638 return NULL;
640 ZERO_STRUCTP(conn);
642 fstrcpy(conn->domain, basic_conn->domain);
643 fstrcpy(conn->controller, basic_conn->controller);
644 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
646 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
647 conn->cli = basic_conn->cli;
648 conn->pipe_data.samr.rid = group_rid;
650 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
651 &basic_conn->pol, des_access, group_rid,
652 &conn->pol);
654 if (!NT_STATUS_IS_OK(result))
655 return NULL;
657 /* Add to list */
659 DLIST_ADD(cm_conns, conn);
662 hnd.pol = conn->pol;
663 hnd.cli = conn->cli;
665 return &hnd;
668 #endif
670 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
671 netlogon pipe as no handle is returned. */
673 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
674 struct cli_state **cli)
676 struct winbindd_cm_conn conn;
677 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
679 /* Open an initial conection */
681 ZERO_STRUCT(conn);
683 if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
684 DEBUG(3, ("Could not open a connection to %s\n", domain));
685 return result;
688 result = cli_nt_setup_creds(conn.cli, trust_passwd);
690 if (!NT_STATUS_IS_OK(result)) {
691 DEBUG(0, ("error connecting to domain password server: %s\n",
692 get_nt_error_msg(result)));
693 cli_shutdown(conn.cli);
694 return result;
697 if (cli)
698 *cli = conn.cli;
700 return result;
703 /* Dump the current connection status */
705 static void dump_conn_list(void)
707 struct winbindd_cm_conn *con;
709 DEBUG(0, ("\tDomain Controller Pipe\n"));
711 for(con = cm_conns; con; con = con->next) {
712 char *msg;
714 /* Display pipe info */
716 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
717 DEBUG(0, ("Error: not enough memory!\n"));
718 } else {
719 DEBUG(0, ("%s\n", msg));
720 SAFE_FREE(msg);
725 void winbindd_cm_status(void)
727 /* List open connections */
729 DEBUG(0, ("winbindd connection manager status:\n"));
731 if (cm_conns)
732 dump_conn_list();
733 else
734 DEBUG(0, ("\tNo active connections\n"));