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 - Take care when destroying cli_structs as they can be shared between
62 #define DBGC_CLASS DBGC_WINBIND
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 size_t mutex_ref_count;
73 struct cli_state *cli;
77 static struct winbindd_cm_conn *cm_conns = NULL;
80 /* Choose between anonymous or authenticated connections. We need to use
81 an authenticated connection if DCs have the RestrictAnonymous registry
82 entry set > 0, or the "Additional restrictions for anonymous
83 connections" set in the win2k Local Security Policy.
85 Caller to free() result in domain, username, password
88 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
90 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
91 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
92 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
94 if (*username && **username) {
96 if (!*domain || !**domain)
97 *domain = smb_xstrdup(lp_workgroup());
99 if (!*password || !**password)
100 *password = smb_xstrdup("");
102 DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
103 *domain, *username));
106 DEBUG(3, ("IPC$ connections done anonymously\n"));
107 *username = smb_xstrdup("");
108 *domain = smb_xstrdup("");
109 *password = smb_xstrdup("");
113 /* Open a connction to the remote server, cache failures for 30 seconds */
115 static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
116 struct winbindd_cm_conn *new_conn)
119 char *machine_password;
120 char *machine_krb5_principal, *ipc_username, *ipc_domain, *ipc_password;
121 struct in_addr dc_ip;
127 fstrcpy(new_conn->domain, domain);
128 fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
130 /* connection failure cache has been moved inside of get_dc_name
131 so we can deal with half dead DC's --jerry */
133 if (!get_dc_name(domain, new_conn->controller, &dc_ip)) {
134 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
135 add_failed_connection_entry(domain, "", result);
139 /* Initialise SMB connection */
141 /* grab stored passwords */
142 machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
144 if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), lp_realm()) == -1) {
145 SAFE_FREE(machine_password);
146 return NT_STATUS_NO_MEMORY;
149 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
151 for (i = 0; retry && (i < 3); i++) {
153 if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
154 DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
155 result = NT_STATUS_POSSIBLE_DEADLOCK;
159 new_conn->cli = NULL;
160 result = cli_start_connection(&new_conn->cli, global_myname(),
161 new_conn->controller,
162 &dc_ip, 0, Undefined,
163 CLI_FULL_CONNECTION_USE_KERBEROS,
166 if (NT_STATUS_IS_OK(result)) {
168 /* reset the error code */
169 result = NT_STATUS_UNSUCCESSFUL;
173 if ((lp_security() == SEC_ADS)
174 && (new_conn->cli->protocol >= PROTOCOL_NT1 && new_conn->cli->capabilities & CAP_EXTENDED_SECURITY)) {
175 new_conn->cli->use_kerberos = True;
176 DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n",
177 new_conn->controller, global_myname(), machine_krb5_principal));
179 result = NT_STATUS_OK;
181 if (!NT_STATUS_IS_OK(result = cli_session_setup_spnego(new_conn->cli, machine_krb5_principal,
184 DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result)));
185 if (NT_STATUS_IS_OK(result))
186 result = NT_STATUS_UNSUCCESSFUL;
189 new_conn->cli->use_kerberos = False;
191 /* only do this is we have a username/password for thr IPC$ connection */
193 if ( !NT_STATUS_IS_OK(result)
194 && new_conn->cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE
195 && strlen(ipc_username) )
197 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
198 new_conn->controller, global_myname(), ipc_domain, ipc_username));
200 result = NT_STATUS_OK;
202 if (!cli_session_setup(new_conn->cli, ipc_username,
203 ipc_password, strlen(ipc_password)+1,
204 ipc_password, strlen(ipc_password)+1,
206 result = cli_nt_error(new_conn->cli);
207 DEBUG(4,("failed authenticated session setup with %s\n", nt_errstr(result)));
208 if (NT_STATUS_IS_OK(result))
209 result = NT_STATUS_UNSUCCESSFUL;
213 /* anonymous is all that is left if we get to here */
215 if (!NT_STATUS_IS_OK(result)) {
217 DEBUG(5, ("anonymous connection attempt to %s from %s\n",
218 new_conn->controller, global_myname()));
220 result = NT_STATUS_OK;
222 if (!cli_session_setup(new_conn->cli, "", NULL, 0, NULL, 0, ""))
224 result = cli_nt_error(new_conn->cli);
225 DEBUG(4,("failed anonymous session setup with %s\n", nt_errstr(result)));
226 if (NT_STATUS_IS_OK(result))
227 result = NT_STATUS_UNSUCCESSFUL;
232 if (NT_STATUS_IS_OK(result) && !cli_send_tconX(new_conn->cli, "IPC$", "IPC",
234 result = cli_nt_error(new_conn->cli);
235 DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
236 cli_shutdown(new_conn->cli);
237 if (NT_STATUS_IS_OK(result)) {
238 result = NT_STATUS_UNSUCCESSFUL;
243 if (NT_STATUS_IS_OK(result)) {
244 struct ntuser_creds creds;
245 init_creds(&creds, ipc_username, ipc_domain, ipc_password);
246 cli_init_creds(new_conn->cli, &creds);
250 secrets_named_mutex_release(new_conn->controller);
252 if (NT_STATUS_IS_OK(result))
256 SAFE_FREE(ipc_username);
257 SAFE_FREE(ipc_domain);
258 SAFE_FREE(ipc_password);
259 SAFE_FREE(machine_password);
261 if (!NT_STATUS_IS_OK(result)) {
262 add_failed_connection_entry(domain, new_conn->controller, result);
266 /* set the domain if empty; needed for schannel connections */
267 if ( !*new_conn->cli->domain )
268 fstrcpy( new_conn->cli->domain, domain );
271 if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
272 result = NT_STATUS_PIPE_NOT_AVAILABLE;
274 * only cache a failure if we are not trying to open the
275 * **win2k** specific lsarpc UUID. This could be an NT PDC
276 * and therefore a failure is normal. This should probably
277 * be abstracted to a check for 2k specific pipes and wondering
278 * if the PDC is an NT4 box. but since there is only one 2k
279 * specific UUID right now, i'm not going to bother. --jerry
281 if ( !is_win2k_pipe(pipe_index) )
282 add_failed_connection_entry(domain, new_conn->controller, result);
283 cli_shutdown(new_conn->cli);
290 /************************************************************************
291 Wrapper around statuc cm_open_connection to retreive a freshly
292 setup cli_state struct
293 ************************************************************************/
295 NTSTATUS cm_fresh_connection(const char *domain, const int pipe_index,
296 struct cli_state **cli)
299 struct winbindd_cm_conn conn;
301 result = cm_open_connection( domain, pipe_index, &conn );
303 if ( NT_STATUS_IS_OK(result) )
309 /* Return true if a connection is still alive */
311 static BOOL connection_ok(struct winbindd_cm_conn *conn)
314 smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n");
319 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
320 conn->controller, conn->domain, conn->pipe_name));
324 if (!conn->cli->initialised) {
325 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
326 conn->controller, conn->domain, conn->pipe_name));
330 if (conn->cli->fd == -1) {
331 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
332 conn->controller, conn->domain, conn->pipe_name));
339 /* Search the cache for a connection. If there is a broken one,
340 shut it down properly and return NULL. */
342 static void find_cm_connection(const char *domain, const char *pipe_name,
343 struct winbindd_cm_conn **conn_out)
345 struct winbindd_cm_conn *conn;
347 for (conn = cm_conns; conn; ) {
348 if (strequal(conn->domain, domain) &&
349 strequal(conn->pipe_name, pipe_name)) {
350 if (!connection_ok(conn)) {
351 /* Dead connection - remove it. */
352 struct winbindd_cm_conn *conn_temp = conn->next;
354 cli_shutdown(conn->cli);
355 DLIST_REMOVE(cm_conns, conn);
357 conn = conn_temp; /* Keep the loop moving */
369 /* Initialize a new connection up to the RPC BIND. */
371 static NTSTATUS new_cm_connection(const char *domain, const char *pipe_name,
372 struct winbindd_cm_conn **conn_out)
374 struct winbindd_cm_conn *conn;
377 if (!(conn = malloc(sizeof(*conn))))
378 return NT_STATUS_NO_MEMORY;
382 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
383 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
384 domain, pipe_name, nt_errstr(result)));
388 DLIST_ADD(cm_conns, conn);
394 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
396 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
397 struct winbindd_cm_conn **conn_out)
399 find_cm_connection(domain, pipe_name, conn_out);
401 if (*conn_out != NULL)
404 return new_cm_connection(domain, pipe_name, conn_out);
407 /**********************************************************************************
408 **********************************************************************************/
410 BOOL cm_check_for_native_mode_win2k( const char *domain )
413 struct winbindd_cm_conn conn;
421 if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
422 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
423 domain, nt_errstr(result)));
428 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
429 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
435 if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
436 && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
441 /* close the connection; no other cals use this pipe and it is called only
442 on reestablishing the domain list --jerry */
445 cli_shutdown( conn.cli );
452 /* Return a LSA policy handle on a domain */
454 NTSTATUS cm_get_lsa_handle(const char *domain, CLI_POLICY_HND **return_hnd)
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)))
466 /* This *shitty* code needs scrapping ! JRA */
468 if (policy_handle_is_valid(&conn->pol)) {
476 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
477 des_access, &conn->pol);
479 if (!NT_STATUS_IS_OK(result)) {
480 /* Hit the cache code again. This cleans out the old connection and gets a new one */
481 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
482 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
485 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
486 des_access, &conn->pol);
489 if (!NT_STATUS_IS_OK(result)) {
490 cli_shutdown(conn->cli);
491 DLIST_REMOVE(cm_conns, conn);
505 /* Return a SAM policy handle on a domain */
507 NTSTATUS cm_get_sam_handle(char *domain, CLI_POLICY_HND **return_hnd)
509 struct winbindd_cm_conn *conn;
510 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
512 static CLI_POLICY_HND hnd;
514 /* Look for existing connections */
516 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
519 /* This *shitty* code needs scrapping ! JRA */
521 if (policy_handle_is_valid(&conn->pol)) {
530 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
531 des_access, &conn->pol);
533 if (!NT_STATUS_IS_OK(result)) {
534 /* Hit the cache code again. This cleans out the old connection and gets a new one */
535 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
537 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
540 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
541 des_access, &conn->pol);
544 if (!NT_STATUS_IS_OK(result)) {
546 cli_shutdown(conn->cli);
547 DLIST_REMOVE(cm_conns, conn);
562 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
563 netlogon pipe as no handle is returned. */
565 NTSTATUS cm_get_netlogon_cli(const char *domain,
566 const unsigned char *trust_passwd,
567 uint32 sec_channel_type,
569 struct cli_state **cli)
571 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
572 struct winbindd_cm_conn *conn;
575 struct winbindd_domain *wb_domain = NULL;
578 return NT_STATUS_INVALID_PARAMETER;
580 /* Open an initial conection - keep the mutex. */
582 find_cm_connection(domain, PIPE_NETLOGON, &conn);
584 if ( fresh && (conn != NULL) ) {
585 cli_shutdown(conn->cli);
590 /* purge connection from cache */
591 find_cm_connection(domain, PIPE_NETLOGON, &conn);
593 DEBUG(0,("Could not purge connection\n"));
594 return NT_STATUS_UNSUCCESSFUL;
603 result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
605 if (!NT_STATUS_IS_OK(result))
608 fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
610 if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
611 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
614 if ( sec_channel_type == SEC_CHAN_DOMAIN )
615 fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
617 /* we need the short form of the domain name for the schanel
618 rpc bind. What if we fail? I don't think we should ever get
619 a request for a domain name not in our list but I'm not bailing
620 out if we do since I'm not 10% certain about this --jerry */
622 if ( (wb_domain = find_domain_from_name( domain )) != NULL ) {
623 DEBUG(5,("cm_get_netlogon_cli: Using short for of domain name [%s] for netlogon rpc bind\n",
625 fstrcpy( conn->cli->domain, wb_domain->name);
628 result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
631 secrets_named_mutex_release(lock_name);
633 if (!NT_STATUS_IS_OK(result)) {
634 cli_shutdown(conn->cli);
635 DLIST_REMOVE(cm_conns, conn);
645 /* Dump the current connection status */
647 static void dump_conn_list(void)
649 struct winbindd_cm_conn *con;
651 DEBUG(0, ("\tDomain Controller Pipe\n"));
653 for(con = cm_conns; con; con = con->next) {
656 /* Display pipe info */
658 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
659 DEBUG(0, ("Error: not enough memory!\n"));
661 DEBUG(0, ("%s\n", msg));
667 void winbindd_cm_status(void)
669 /* List open connections */
671 DEBUG(0, ("winbindd connection manager status:\n"));
676 DEBUG(0, ("\tNo active connections\n"));
679 /* Close all cached connections */
681 void winbindd_cm_flush(void)
683 struct winbindd_cm_conn *conn, tmp;
685 /* Flush connection cache */
687 for (conn = cm_conns; conn; conn = conn->next) {
689 if (!connection_ok(conn))
692 DEBUG(10, ("Closing connection to %s on %s\n",
693 conn->pipe_name, conn->controller));
696 cli_shutdown(conn->cli);
698 tmp.next = conn->next;
700 DLIST_REMOVE(cm_conns, conn);
705 /* Flush failed connection cache */
707 flush_negative_conn_cache();