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 char *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
);
129 fstrcpy(new_conn
->pipe_name
, get_pipe_name_from_index(pipe_index
));
131 /* connection failure cache has been moved inside of get_dc_name
132 so we can deal with half dead DC's --jerry */
134 if (!get_dc_name(domain
, new_conn
->controller
, &dc_ip
)) {
135 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
136 add_failed_connection_entry(domain
, "", result
);
140 /* Initialise SMB connection */
142 /* grab stored passwords */
143 machine_password
= secrets_fetch_machine_password(lp_workgroup(), NULL
, NULL
);
145 if (asprintf(&machine_krb5_principal
, "%s$@%s", global_myname(), lp_realm()) == -1) {
146 SAFE_FREE(machine_password
);
147 return NT_STATUS_NO_MEMORY
;
150 cm_get_ipc_userpass(&ipc_username
, &ipc_domain
, &ipc_password
);
152 for (i
= 0; retry
&& (i
< 3); i
++) {
154 if (!(got_mutex
= secrets_named_mutex(new_conn
->controller
, WINBIND_SERVER_MUTEX_WAIT_TIME
))) {
155 DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn
->controller
));
156 result
= NT_STATUS_POSSIBLE_DEADLOCK
;
160 new_conn
->cli
= NULL
;
161 result
= cli_start_connection(&new_conn
->cli
, global_myname(),
162 new_conn
->controller
,
163 &dc_ip
, 0, Undefined
,
164 CLI_FULL_CONNECTION_USE_KERBEROS
,
167 if (NT_STATUS_IS_OK(result
)) {
169 /* reset the error code */
170 result
= NT_STATUS_UNSUCCESSFUL
;
174 if ((lp_security() == SEC_ADS
)
175 && (new_conn
->cli
->protocol
>= PROTOCOL_NT1
&& new_conn
->cli
->capabilities
& CAP_EXTENDED_SECURITY
)) {
176 new_conn
->cli
->use_kerberos
= True
;
177 DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n",
178 new_conn
->controller
, global_myname(), machine_krb5_principal
));
180 result
= NT_STATUS_OK
;
182 if (!NT_STATUS_IS_OK(result
= cli_session_setup_spnego(new_conn
->cli
, machine_krb5_principal
,
185 DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result
)));
186 if (NT_STATUS_IS_OK(result
))
187 result
= NT_STATUS_UNSUCCESSFUL
;
190 new_conn
->cli
->use_kerberos
= False
;
192 /* only do this is we have a username/password for thr IPC$ connection */
194 if ( !NT_STATUS_IS_OK(result
)
195 && new_conn
->cli
->sec_mode
& NEGOTIATE_SECURITY_CHALLENGE_RESPONSE
196 && strlen(ipc_username
) )
198 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
199 new_conn
->controller
, global_myname(), ipc_domain
, ipc_username
));
201 result
= NT_STATUS_OK
;
203 if (!cli_session_setup(new_conn
->cli
, ipc_username
,
204 ipc_password
, strlen(ipc_password
)+1,
205 ipc_password
, strlen(ipc_password
)+1,
207 result
= cli_nt_error(new_conn
->cli
);
208 DEBUG(4,("failed authenticated session setup with %s\n", nt_errstr(result
)));
209 if (NT_STATUS_IS_OK(result
))
210 result
= NT_STATUS_UNSUCCESSFUL
;
214 /* anonymous is all that is left if we get to here */
216 if (!NT_STATUS_IS_OK(result
)) {
218 DEBUG(5, ("anonymous connection attempt to %s from %s\n",
219 new_conn
->controller
, global_myname()));
221 result
= NT_STATUS_OK
;
223 if (!cli_session_setup(new_conn
->cli
, "", NULL
, 0, NULL
, 0, ""))
225 result
= cli_nt_error(new_conn
->cli
);
226 DEBUG(4,("failed anonymous session setup with %s\n", nt_errstr(result
)));
227 if (NT_STATUS_IS_OK(result
))
228 result
= NT_STATUS_UNSUCCESSFUL
;
233 if (NT_STATUS_IS_OK(result
) && !cli_send_tconX(new_conn
->cli
, "IPC$", "IPC",
235 result
= cli_nt_error(new_conn
->cli
);
236 DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result
)));
237 cli_shutdown(new_conn
->cli
);
238 if (NT_STATUS_IS_OK(result
)) {
239 result
= NT_STATUS_UNSUCCESSFUL
;
244 if (NT_STATUS_IS_OK(result
)) {
245 struct ntuser_creds creds
;
246 init_creds(&creds
, ipc_username
, ipc_domain
, ipc_password
);
247 cli_init_creds(new_conn
->cli
, &creds
);
251 secrets_named_mutex_release(new_conn
->controller
);
253 if (NT_STATUS_IS_OK(result
))
257 SAFE_FREE(ipc_username
);
258 SAFE_FREE(ipc_domain
);
259 SAFE_FREE(ipc_password
);
260 SAFE_FREE(machine_password
);
262 if (!NT_STATUS_IS_OK(result
)) {
263 add_failed_connection_entry(domain
, new_conn
->controller
, result
);
267 /* set the domain if empty; needed for schannel connections */
268 if ( !*new_conn
->cli
->domain
)
269 fstrcpy( new_conn
->cli
->domain
, domain
);
272 if ( !cli_nt_session_open (new_conn
->cli
, pipe_index
) ) {
273 result
= NT_STATUS_PIPE_NOT_AVAILABLE
;
275 * only cache a failure if we are not trying to open the
276 * **win2k** specific lsarpc UUID. This could be an NT PDC
277 * and therefore a failure is normal. This should probably
278 * be abstracted to a check for 2k specific pipes and wondering
279 * if the PDC is an NT4 box. but since there is only one 2k
280 * specific UUID right now, i'm not going to bother. --jerry
282 if ( !is_win2k_pipe(pipe_index
) )
283 add_failed_connection_entry(domain
, new_conn
->controller
, result
);
284 cli_shutdown(new_conn
->cli
);
291 /************************************************************************
292 Wrapper around statuc cm_open_connection to retreive a freshly
293 setup cli_state struct
294 ************************************************************************/
296 NTSTATUS
cm_fresh_connection(const char *domain
, const int pipe_index
,
297 struct cli_state
**cli
)
300 struct winbindd_cm_conn conn
;
302 result
= cm_open_connection( domain
, pipe_index
, &conn
);
304 if ( NT_STATUS_IS_OK(result
) )
310 /* Return true if a connection is still alive */
312 static BOOL
connection_ok(struct winbindd_cm_conn
*conn
)
315 smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n");
320 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
321 conn
->controller
, conn
->domain
, conn
->pipe_name
));
325 if (!conn
->cli
->initialised
) {
326 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
327 conn
->controller
, conn
->domain
, conn
->pipe_name
));
331 if (conn
->cli
->fd
== -1) {
332 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
333 conn
->controller
, conn
->domain
, conn
->pipe_name
));
340 /* Search the cache for a connection. If there is a broken one,
341 shut it down properly and return NULL. */
343 static void find_cm_connection(const char *domain
, const char *pipe_name
,
344 struct winbindd_cm_conn
**conn_out
)
346 struct winbindd_cm_conn
*conn
;
348 for (conn
= cm_conns
; conn
; ) {
349 if (strequal(conn
->domain
, domain
) &&
350 strequal(conn
->pipe_name
, pipe_name
)) {
351 if (!connection_ok(conn
)) {
352 /* Dead connection - remove it. */
353 struct winbindd_cm_conn
*conn_temp
= conn
->next
;
355 cli_shutdown(conn
->cli
);
356 DLIST_REMOVE(cm_conns
, conn
);
358 conn
= conn_temp
; /* Keep the loop moving */
370 /* Initialize a new connection up to the RPC BIND. */
372 static NTSTATUS
new_cm_connection(const char *domain
, const char *pipe_name
,
373 struct winbindd_cm_conn
**conn_out
)
375 struct winbindd_cm_conn
*conn
;
378 if (!(conn
= malloc(sizeof(*conn
))))
379 return NT_STATUS_NO_MEMORY
;
383 if (!NT_STATUS_IS_OK(result
= cm_open_connection(domain
, get_pipe_index(pipe_name
), conn
))) {
384 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
385 domain
, pipe_name
, nt_errstr(result
)));
389 DLIST_ADD(cm_conns
, conn
);
395 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
397 static NTSTATUS
get_connection_from_cache(const char *domain
, const char *pipe_name
,
398 struct winbindd_cm_conn
**conn_out
)
400 find_cm_connection(domain
, pipe_name
, conn_out
);
402 if (*conn_out
!= NULL
)
405 return new_cm_connection(domain
, pipe_name
, conn_out
);
408 /**********************************************************************************
409 **********************************************************************************/
411 BOOL
cm_check_for_native_mode_win2k( const char *domain
)
414 struct winbindd_cm_conn conn
;
422 if ( !NT_STATUS_IS_OK(result
= cm_open_connection(domain
, PI_LSARPC_DS
, &conn
)) ) {
423 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
424 domain
, nt_errstr(result
)));
429 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn
.cli
,
430 conn
.cli
->mem_ctx
, DsRolePrimaryDomainInfoBasic
, &ctr
)) ) {
436 if ( (ctr
.basic
->flags
& DSROLE_PRIMARY_DS_RUNNING
)
437 && !(ctr
.basic
->flags
& DSROLE_PRIMARY_DS_MIXED_MODE
) )
442 /* close the connection; no other cals use this pipe and it is called only
443 on reestablishing the domain list --jerry */
446 cli_shutdown( conn
.cli
);
453 /* Return a LSA policy handle on a domain */
455 NTSTATUS
cm_get_lsa_handle(const char *domain
, CLI_POLICY_HND
**return_hnd
)
457 struct winbindd_cm_conn
*conn
;
458 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
460 static CLI_POLICY_HND hnd
;
462 /* Look for existing connections */
464 if (!NT_STATUS_IS_OK(result
= get_connection_from_cache(domain
, PIPE_LSARPC
, &conn
)))
467 /* This *shitty* code needs scrapping ! JRA */
469 if (policy_handle_is_valid(&conn
->pol
)) {
477 result
= cli_lsa_open_policy(conn
->cli
, conn
->cli
->mem_ctx
, False
,
478 des_access
, &conn
->pol
);
480 if (!NT_STATUS_IS_OK(result
)) {
481 /* Hit the cache code again. This cleans out the old connection and gets a new one */
482 if (conn
->cli
->fd
== -1) { /* Try again, if the remote host disapeared */
483 if (!NT_STATUS_IS_OK(result
= get_connection_from_cache(domain
, PIPE_LSARPC
, &conn
)))
486 result
= cli_lsa_open_policy(conn
->cli
, conn
->cli
->mem_ctx
, False
,
487 des_access
, &conn
->pol
);
490 if (!NT_STATUS_IS_OK(result
)) {
491 cli_shutdown(conn
->cli
);
492 DLIST_REMOVE(cm_conns
, conn
);
506 /* Return a SAM policy handle on a domain */
508 NTSTATUS
cm_get_sam_handle(char *domain
, CLI_POLICY_HND
**return_hnd
)
510 struct winbindd_cm_conn
*conn
;
511 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
513 static CLI_POLICY_HND hnd
;
515 /* Look for existing connections */
517 if (!NT_STATUS_IS_OK(result
= get_connection_from_cache(domain
, PIPE_SAMR
, &conn
)))
520 /* This *shitty* code needs scrapping ! JRA */
522 if (policy_handle_is_valid(&conn
->pol
)) {
531 result
= cli_samr_connect(conn
->cli
, conn
->cli
->mem_ctx
,
532 des_access
, &conn
->pol
);
534 if (!NT_STATUS_IS_OK(result
)) {
535 /* Hit the cache code again. This cleans out the old connection and gets a new one */
536 if (conn
->cli
->fd
== -1) { /* Try again, if the remote host disapeared */
538 if (!NT_STATUS_IS_OK(result
= get_connection_from_cache(domain
, PIPE_SAMR
, &conn
)))
541 result
= cli_samr_connect(conn
->cli
, conn
->cli
->mem_ctx
,
542 des_access
, &conn
->pol
);
545 if (!NT_STATUS_IS_OK(result
)) {
547 cli_shutdown(conn
->cli
);
548 DLIST_REMOVE(cm_conns
, conn
);
563 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
564 netlogon pipe as no handle is returned. */
566 NTSTATUS
cm_get_netlogon_cli(const char *domain
,
567 const unsigned char *trust_passwd
,
568 uint32 sec_channel_type
,
570 struct cli_state
**cli
)
572 NTSTATUS result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
573 struct winbindd_cm_conn
*conn
;
576 struct winbindd_domain
*wb_domain
= NULL
;
579 return NT_STATUS_INVALID_PARAMETER
;
581 /* Open an initial conection - keep the mutex. */
583 find_cm_connection(domain
, PIPE_NETLOGON
, &conn
);
585 if ( fresh
&& (conn
!= NULL
) ) {
586 cli_shutdown(conn
->cli
);
591 /* purge connection from cache */
592 find_cm_connection(domain
, PIPE_NETLOGON
, &conn
);
594 DEBUG(0,("Could not purge connection\n"));
595 return NT_STATUS_UNSUCCESSFUL
;
604 result
= new_cm_connection(domain
, PIPE_NETLOGON
, &conn
);
606 if (!NT_STATUS_IS_OK(result
))
609 fstr_sprintf(lock_name
, "NETLOGON\\%s", conn
->controller
);
611 if (!(got_mutex
= secrets_named_mutex(lock_name
, WINBIND_SERVER_MUTEX_WAIT_TIME
))) {
612 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn
->controller
));
615 if ( sec_channel_type
== SEC_CHAN_DOMAIN
)
616 fstr_sprintf(conn
->cli
->mach_acct
, "%s$", lp_workgroup());
618 /* we need the short form of the domain name for the schanel
619 rpc bind. What if we fail? I don't think we should ever get
620 a request for a domain name not in our list but I'm not bailing
621 out if we do since I'm not 10% certain about this --jerry */
623 if ( (wb_domain
= find_domain_from_name( domain
)) != NULL
) {
624 DEBUG(5,("cm_get_netlogon_cli: Using short for of domain name [%s] for netlogon rpc bind\n",
626 fstrcpy( conn
->cli
->domain
, wb_domain
->name
);
629 result
= cli_nt_establish_netlogon(conn
->cli
, sec_channel_type
, trust_passwd
);
632 secrets_named_mutex_release(lock_name
);
634 if (!NT_STATUS_IS_OK(result
)) {
635 cli_shutdown(conn
->cli
);
636 DLIST_REMOVE(cm_conns
, conn
);
646 /* Dump the current connection status */
648 static void dump_conn_list(void)
650 struct winbindd_cm_conn
*con
;
652 DEBUG(0, ("\tDomain Controller Pipe\n"));
654 for(con
= cm_conns
; con
; con
= con
->next
) {
657 /* Display pipe info */
659 if (asprintf(&msg
, "\t%-15s %-15s %-16s", con
->domain
, con
->controller
, con
->pipe_name
) < 0) {
660 DEBUG(0, ("Error: not enough memory!\n"));
662 DEBUG(0, ("%s\n", msg
));
668 void winbindd_cm_status(void)
670 /* List open connections */
672 DEBUG(0, ("winbindd connection manager status:\n"));
677 DEBUG(0, ("\tNo active connections\n"));
680 /* Close all cached connections */
682 void winbindd_cm_flush(void)
684 struct winbindd_cm_conn
*conn
, tmp
;
686 /* Flush connection cache */
688 for (conn
= cm_conns
; conn
; conn
= conn
->next
) {
690 if (!connection_ok(conn
))
693 DEBUG(10, ("Closing connection to %s on %s\n",
694 conn
->pipe_name
, conn
->controller
));
697 cli_shutdown(conn
->cli
);
699 tmp
.next
= conn
->next
;
701 DLIST_REMOVE(cm_conns
, conn
);
706 /* Flush failed connection cache */
708 flush_negative_conn_cache();