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
63 #define DBGC_CLASS DBGC_WINBIND
65 /* Global list of connections. Initially a DLIST but can become a hash
66 table or whatever later. */
68 struct winbindd_cm_conn {
69 struct winbindd_cm_conn *prev, *next;
73 size_t mutex_ref_count;
74 struct cli_state *cli;
78 static struct winbindd_cm_conn *cm_conns = NULL;
81 /* Choose between anonymous or authenticated connections. We need to use
82 an authenticated connection if DCs have the RestrictAnonymous registry
83 entry set > 0, or the "Additional restrictions for anonymous
84 connections" set in the win2k Local Security Policy.
86 Caller to free() result in domain, username, password
89 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
91 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
92 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
93 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
95 if (*username && **username) {
97 if (!*domain || !**domain)
98 *domain = smb_xstrdup(lp_workgroup());
100 if (!*password || !**password)
101 *password = smb_xstrdup("");
103 DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
104 *domain, *username));
107 DEBUG(3, ("IPC$ connections done anonymously\n"));
108 *username = smb_xstrdup("");
109 *domain = smb_xstrdup("");
110 *password = smb_xstrdup("");
114 /* Open a connction to the remote server, cache failures for 30 seconds */
116 static NTSTATUS cm_open_connection(const struct winbindd_domain *domain, const int pipe_index,
117 struct winbindd_cm_conn *new_conn)
120 char *machine_password;
121 char *machine_krb5_principal, *ipc_username, *ipc_domain, *ipc_password;
122 struct in_addr dc_ip;
128 fstrcpy(new_conn->domain, domain->name);
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->name, domain->alt_name[0] ? domain->alt_name : NULL,
134 new_conn->controller, &dc_ip)) {
135 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
136 add_failed_connection_entry(domain->name, "", result);
140 /* Initialise SMB connection */
141 fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
143 /* grab stored passwords */
144 machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
146 if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), lp_realm()) == -1) {
147 SAFE_FREE(machine_password);
148 return NT_STATUS_NO_MEMORY;
151 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
153 for (i = 0; retry && (i < 3); i++) {
155 if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
156 DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
157 result = NT_STATUS_POSSIBLE_DEADLOCK;
161 new_conn->cli = NULL;
162 result = cli_start_connection(&new_conn->cli, global_myname(),
163 new_conn->controller,
164 &dc_ip, 0, Undefined,
165 CLI_FULL_CONNECTION_USE_KERBEROS,
168 if (NT_STATUS_IS_OK(result)) {
170 /* reset the error code */
171 result = NT_STATUS_UNSUCCESSFUL;
175 if ((lp_security() == SEC_ADS)
176 && (new_conn->cli->protocol >= PROTOCOL_NT1 && new_conn->cli->capabilities & CAP_EXTENDED_SECURITY)) {
177 new_conn->cli->use_kerberos = True;
178 DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n",
179 new_conn->controller, global_myname(), machine_krb5_principal));
181 result = NT_STATUS_OK;
183 if (!NT_STATUS_IS_OK(result = cli_session_setup_spnego(new_conn->cli, machine_krb5_principal,
186 DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result)));
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->name, 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->name );
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->name, 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(struct winbindd_domain *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(struct winbindd_domain *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->name) &&
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(struct winbindd_domain *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->name, 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(struct winbindd_domain *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( struct winbindd_domain *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->name, 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(struct winbindd_domain *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(struct winbindd_domain *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(struct winbindd_domain *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;
577 return NT_STATUS_INVALID_PARAMETER;
579 /* Open an initial conection - keep the mutex. */
581 find_cm_connection(domain, PIPE_NETLOGON, &conn);
583 if ( fresh && (conn != NULL) ) {
584 cli_shutdown(conn->cli);
589 /* purge connection from cache */
590 find_cm_connection(domain, PIPE_NETLOGON, &conn);
592 DEBUG(0,("Could not purge connection\n"));
593 return NT_STATUS_UNSUCCESSFUL;
602 result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
604 if (!NT_STATUS_IS_OK(result))
607 fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
609 if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
610 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
613 if ( sec_channel_type == SEC_CHAN_DOMAIN )
614 fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
617 fstrcpy( conn->cli->domain, domain->name);
620 result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
623 secrets_named_mutex_release(lock_name);
625 if (!NT_STATUS_IS_OK(result)) {
626 cli_shutdown(conn->cli);
627 DLIST_REMOVE(cm_conns, conn);
637 /* Dump the current connection status */
639 static void dump_conn_list(void)
641 struct winbindd_cm_conn *con;
643 DEBUG(0, ("\tDomain Controller Pipe\n"));
645 for(con = cm_conns; con; con = con->next) {
648 /* Display pipe info */
650 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
651 DEBUG(0, ("Error: not enough memory!\n"));
653 DEBUG(0, ("%s\n", msg));
659 void winbindd_cm_status(void)
661 /* List open connections */
663 DEBUG(0, ("winbindd connection manager status:\n"));
668 DEBUG(0, ("\tNo active connections\n"));
671 /* Close all cached connections */
673 void winbindd_cm_flush(void)
675 struct winbindd_cm_conn *conn, tmp;
677 /* Flush connection cache */
679 for (conn = cm_conns; conn; conn = conn->next) {
681 if (!connection_ok(conn))
684 DEBUG(10, ("Closing connection to %s on %s\n",
685 conn->pipe_name, conn->controller));
688 cli_shutdown(conn->cli);
690 tmp.next = conn->next;
692 DLIST_REMOVE(cm_conns, conn);
697 /* Flush failed connection cache */
699 flush_negative_conn_cache();