Minor comment updates ...
[Samba/gebeck_regimport.git] / source3 / nsswitch / winbindd_cm.c
blob8513a46f8f2ebb2514a43f7181c98440c2e55193
1 /*
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
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 - Take care when destroying cli_structs as they can be shared between
55 various sam handles.
59 #include "winbindd.h"
61 #undef DBGC_CLASS
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;
69 fstring domain;
70 fstring controller;
71 fstring pipe_name;
72 size_t mutex_ref_count;
73 struct cli_state *cli;
74 POLICY_HND pol;
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));
105 } else {
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)
118 NTSTATUS result;
119 char *machine_password;
120 char *machine_krb5_principal, *ipc_username, *ipc_domain, *ipc_password;
121 struct in_addr dc_ip;
122 int i;
123 BOOL retry = True;
125 ZERO_STRUCT(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);
136 return 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++) {
152 BOOL got_mutex;
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;
156 continue;
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,
164 &retry);
166 if (NT_STATUS_IS_OK(result)) {
168 /* reset the error code */
169 result = NT_STATUS_UNSUCCESSFUL;
171 /* Krb5 session */
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 (!cli_session_setup_spnego(new_conn->cli, machine_krb5_principal,
182 machine_password,
183 domain)) {
184 result = cli_nt_error(new_conn->cli);
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,
206 domain)) {
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",
234 "", 0)) {
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);
250 if (got_mutex)
251 secrets_named_mutex_release(new_conn->controller);
253 if (NT_STATUS_IS_OK(result))
254 break;
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);
264 return 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);
285 return result;
288 return NT_STATUS_OK;
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)
299 NTSTATUS result;
300 struct winbindd_cm_conn conn;
302 result = cm_open_connection( domain, pipe_index, &conn );
304 if ( NT_STATUS_IS_OK(result) )
305 *cli = conn.cli;
307 return result;
310 /* Return true if a connection is still alive */
312 static BOOL connection_ok(struct winbindd_cm_conn *conn)
314 if (!conn) {
315 smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n");
316 return False;
319 if (!conn->cli) {
320 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
321 conn->controller, conn->domain, conn->pipe_name));
322 return False;
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));
328 return False;
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));
334 return False;
337 return True;
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;
354 if (conn->cli)
355 cli_shutdown(conn->cli);
356 DLIST_REMOVE(cm_conns, conn);
357 SAFE_FREE(conn);
358 conn = conn_temp; /* Keep the loop moving */
359 continue;
360 } else {
361 break;
364 conn = conn->next;
367 *conn_out = conn;
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;
376 NTSTATUS result;
378 if (!(conn = malloc(sizeof(*conn))))
379 return NT_STATUS_NO_MEMORY;
381 ZERO_STRUCTP(conn);
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)));
386 SAFE_FREE(conn);
387 return result;
389 DLIST_ADD(cm_conns, conn);
391 *conn_out = conn;
392 return NT_STATUS_OK;
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)
403 return NT_STATUS_OK;
405 return new_cm_connection(domain, pipe_name, conn_out);
408 /**********************************************************************************
409 **********************************************************************************/
411 BOOL cm_check_for_native_mode_win2k( const char *domain )
413 NTSTATUS result;
414 struct winbindd_cm_conn conn;
415 DS_DOMINFO_CTR ctr;
416 BOOL ret = False;
418 ZERO_STRUCT( conn );
419 ZERO_STRUCT( ctr );
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)));
425 return False;
428 if ( conn.cli ) {
429 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
430 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
431 ret = False;
432 goto done;
436 if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
437 && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
438 ret = True;
440 done:
442 /* close the connection; no other cals use this pipe and it is called only
443 on reestablishing the domain list --jerry */
445 if ( conn.cli )
446 cli_shutdown( conn.cli );
448 return ret;
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;
459 NTSTATUS result;
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)))
465 return result;
467 /* This *shitty* code needs scrapping ! JRA */
469 if (policy_handle_is_valid(&conn->pol)) {
470 hnd.pol = conn->pol;
471 hnd.cli = conn->cli;
472 *return_hnd = &hnd;
474 return NT_STATUS_OK;
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)))
484 return result;
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);
493 SAFE_FREE(conn);
494 return result;
498 hnd.pol = conn->pol;
499 hnd.cli = conn->cli;
501 *return_hnd = &hnd;
503 return NT_STATUS_OK;
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;
512 NTSTATUS result;
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)))
518 return result;
520 /* This *shitty* code needs scrapping ! JRA */
522 if (policy_handle_is_valid(&conn->pol)) {
523 hnd.pol = conn->pol;
524 hnd.cli = conn->cli;
526 *return_hnd = &hnd;
528 return NT_STATUS_OK;
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)))
539 return result;
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);
549 SAFE_FREE(conn);
551 return result;
555 hnd.pol = conn->pol;
556 hnd.cli = conn->cli;
558 *return_hnd = &hnd;
560 return NT_STATUS_OK;
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,
569 BOOL fresh,
570 struct cli_state **cli)
572 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
573 struct winbindd_cm_conn *conn;
574 fstring lock_name;
575 BOOL got_mutex;
576 struct winbindd_domain *wb_domain = NULL;
578 if (!cli)
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);
587 conn->cli = NULL;
589 conn = NULL;
591 /* purge connection from cache */
592 find_cm_connection(domain, PIPE_NETLOGON, &conn);
593 if (conn != NULL) {
594 DEBUG(0,("Could not purge connection\n"));
595 return NT_STATUS_UNSUCCESSFUL;
599 if (conn != NULL) {
600 *cli = conn->cli;
601 return NT_STATUS_OK;
604 result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
606 if (!NT_STATUS_IS_OK(result))
607 return 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",
625 wb_domain->name));
626 fstrcpy( conn->cli->domain, wb_domain->name);
629 result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
631 if (got_mutex)
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);
637 SAFE_FREE(conn);
638 return result;
641 *cli = conn->cli;
643 return result;
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) {
655 char *msg;
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"));
661 } else {
662 DEBUG(0, ("%s\n", msg));
663 SAFE_FREE(msg);
668 void winbindd_cm_status(void)
670 /* List open connections */
672 DEBUG(0, ("winbindd connection manager status:\n"));
674 if (cm_conns)
675 dump_conn_list();
676 else
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))
691 continue;
693 DEBUG(10, ("Closing connection to %s on %s\n",
694 conn->pipe_name, conn->controller));
696 if (conn->cli)
697 cli_shutdown(conn->cli);
699 tmp.next = conn->next;
701 DLIST_REMOVE(cm_conns, conn);
702 SAFE_FREE(conn);
703 conn = &tmp;
706 /* Flush failed connection cache */
708 flush_negative_conn_cache();