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 struct cli_state *cli;
77 static struct winbindd_cm_conn *cm_conns = NULL;
79 static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain,
80 const char *pipe_name,
81 struct winbindd_cm_conn **conn_out);
83 /* Choose between anonymous or authenticated connections. We need to use
84 an authenticated connection if DCs have the RestrictAnonymous registry
85 entry set > 0, or the "Additional restrictions for anonymous
86 connections" set in the win2k Local Security Policy.
88 Caller to free() result in domain, username, password
91 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
93 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
94 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
95 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
97 if (*username && **username) {
99 if (!*domain || !**domain)
100 *domain = smb_xstrdup(lp_workgroup());
102 if (!*password || !**password)
103 *password = smb_xstrdup("");
105 DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
106 *domain, *username));
109 DEBUG(3, ("IPC$ connections done anonymously\n"));
110 *username = smb_xstrdup("");
111 *domain = smb_xstrdup("");
112 *password = smb_xstrdup("");
117 setup for schannel on any pipes opened on this connection
119 static NTSTATUS setup_schannel( struct cli_state *cli, const char *domain )
122 uchar trust_password[16];
123 uint32 sec_channel_type;
127 /* use the domain trust password if we're on a DC
128 and this is not our domain */
130 if ( IS_DC && !strequal(domain, lp_workgroup()) ) {
133 if ( !secrets_fetch_trusted_domain_password( domain,
136 return NT_STATUS_UNSUCCESSFUL;
139 sec_channel_type = SEC_CHAN_DOMAIN;
140 E_md4hash(pass, trust_password);
144 if (!secrets_fetch_trust_account_password(lp_workgroup(),
145 trust_password, NULL, &sec_channel_type))
147 return NT_STATUS_UNSUCCESSFUL;
151 ret = cli_nt_setup_netsec(cli, sec_channel_type,
152 AUTH_PIPE_NETSEC | AUTH_PIPE_SIGN, trust_password);
157 static BOOL get_dc_name_via_netlogon(const struct winbindd_domain *domain,
158 fstring dcname, struct in_addr *dc_ip)
160 struct winbindd_domain *our_domain;
162 struct winbindd_cm_conn *conn;
174 if ((our_domain = find_our_domain()) == NULL)
177 result = get_connection_from_cache(our_domain, PIPE_NETLOGON, &conn);
178 if (!NT_STATUS_IS_OK(result))
181 if ((mem_ctx = talloc_init("get_dc_name_via_netlogon")) == NULL)
184 result = cli_netlogon_getdcname(conn->cli, mem_ctx, domain->name, tmp);
186 talloc_destroy(mem_ctx);
188 if (!NT_STATUS_IS_OK(result))
191 /* cli_netlogon_getdcname gives us a name with \\ */
193 if (*p == '\\') p+=1;
194 if (*p == '\\') p+=1;
198 if (!resolve_name(dcname, dc_ip, 0x20))
204 /************************************************************************
205 Given a fd with a just-connected TCP connection to a DC, open a connection
207 ************************************************************************/
209 static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
211 const int pipe_index,
212 const char *controller,
213 struct cli_state **cli,
216 char *machine_password, *machine_krb5_principal;
217 char *ipc_username, *ipc_domain, *ipc_password;
218 struct ntuser_creds creds;
221 BOOL add_failed_connection = True;
223 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
225 struct sockaddr peeraddr;
226 socklen_t peeraddr_len;
228 struct sockaddr_in *peeraddr_in = (struct sockaddr_in *)&peeraddr;
230 machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL,
233 if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(),
235 SAFE_FREE(machine_password);
236 return NT_STATUS_NO_MEMORY;
239 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
243 got_mutex = secrets_named_mutex(controller,
244 WINBIND_SERVER_MUTEX_WAIT_TIME);
247 DEBUG(0,("cm_open_connection: mutex grab failed for %s\n",
249 result = NT_STATUS_POSSIBLE_DEADLOCK;
253 if ((*cli = cli_initialise(NULL)) == NULL) {
254 DEBUG(1, ("Could not cli_initialize\n"));
255 result = NT_STATUS_NO_MEMORY;
259 (*cli)->timeout = 10000; /* 10 seconds */
261 fstrcpy((*cli)->desthost, controller);
262 (*cli)->use_kerberos = True;
264 peeraddr_len = sizeof(peeraddr);
266 if ((getpeername((*cli)->fd, &peeraddr, &peeraddr_len) != 0) ||
267 (peeraddr_len != sizeof(struct sockaddr_in)) ||
268 (peeraddr_in->sin_family != PF_INET))
270 DEBUG(0,("cm_prepare_connection: %s\n", strerror(errno)));
274 if (ntohs(peeraddr_in->sin_port) == 139) {
275 struct nmb_name calling;
276 struct nmb_name called;
278 make_nmb_name(&calling, global_myname(), 0x0);
279 make_nmb_name(&called, "*SMBSERVER", 0x20);
281 if (!cli_session_request(*cli, &calling, &called)) {
282 DEBUG(8, ("cli_session_request failed for %s\n",
288 cli_setup_signing_state(*cli, Undefined);
290 if (!cli_negprot(*cli)) {
291 DEBUG(1, ("cli_negprot failed\n"));
298 if ((lp_security() == SEC_ADS)
299 && ((*cli)->protocol >= PROTOCOL_NT1 &&
300 (*cli)->capabilities & CAP_EXTENDED_SECURITY)) {
302 ADS_STATUS ads_status;
303 (*cli)->use_kerberos = True;
304 DEBUG(5, ("connecting to %s from %s with kerberos principal "
305 "[%s]\n", controller, global_myname(),
306 machine_krb5_principal));
308 ads_status = cli_session_setup_spnego(*cli,
309 machine_krb5_principal,
313 if (!ADS_ERR_OK(ads_status))
314 DEBUG(4,("failed kerberos session setup with %s\n",
315 ads_errstr(ads_status)));
317 result = ads_ntstatus(ads_status);
320 if (NT_STATUS_IS_OK(result))
321 goto session_setup_done;
323 /* Fall back to non-kerberos session setup */
325 (*cli)->use_kerberos = False;
327 if ((((*cli)->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) != 0) &&
328 (strlen(ipc_username) > 0)) {
330 /* Only try authenticated if we have a username */
332 DEBUG(5, ("connecting to %s from %s with username "
333 "[%s]\\[%s]\n", controller, global_myname(),
334 ipc_domain, ipc_username));
336 if (cli_session_setup(*cli, ipc_username,
337 ipc_password, strlen(ipc_password)+1,
338 ipc_password, strlen(ipc_password)+1,
340 DEBUG(5, ("authenticated session setup failed\n"));
341 goto session_setup_done;
345 /* Fall back to anonymous connection, this might fail later */
347 if (cli_session_setup(*cli, "", NULL, 0, NULL, 0, "")) {
348 DEBUG(5, ("Connected anonymously\n"));
349 goto session_setup_done;
352 result = cli_nt_error(*cli);
354 if (NT_STATUS_IS_OK(result))
355 result = NT_STATUS_UNSUCCESSFUL;
357 /* We can't session setup */
363 if (!cli_send_tconX(*cli, "IPC$", "IPC", "", 0)) {
365 result = cli_nt_error(*cli);
367 DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
369 if (NT_STATUS_IS_OK(result))
370 result = NT_STATUS_UNSUCCESSFUL;
376 init_creds(&creds, ipc_username, ipc_domain, ipc_password);
377 cli_init_creds(*cli, &creds);
379 secrets_named_mutex_release(controller);
383 if (domain->primary || IS_DC) {
384 NTSTATUS status = setup_schannel( *cli, domain->name );
385 if (!NT_STATUS_IS_OK(status)) {
386 DEBUG(3,("schannel refused - continuing without "
387 "schannel (%s)\n", nt_errstr(status)));
391 /* set the domain if empty; needed for schannel connections */
392 if ( !*(*cli)->domain )
393 fstrcpy( (*cli)->domain, domain->name );
395 if ( !cli_nt_session_open (*cli, pipe_index) ) {
397 result = NT_STATUS_PIPE_NOT_AVAILABLE;
399 /* This might be a NT4 DC */
400 if ( is_win2k_pipe(pipe_index) )
401 add_failed_connection = False;
407 result = NT_STATUS_OK;
408 add_failed_connection = False;
412 secrets_named_mutex_release(controller);
414 SAFE_FREE(machine_password);
415 SAFE_FREE(machine_krb5_principal);
416 SAFE_FREE(ipc_username);
417 SAFE_FREE(ipc_domain);
418 SAFE_FREE(ipc_password);
420 if (add_failed_connection)
421 add_failed_connection_entry(domain->name, controller, result);
431 static BOOL add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name,
432 const char *dcname, struct in_addr ip,
433 struct dc_name_ip **dcs, int *num)
435 if (!NT_STATUS_IS_OK(check_negative_conn_cache(domain_name, dcname)))
438 *dcs = TALLOC_REALLOC_ARRAY(mem_ctx, *dcs, struct dc_name_ip, (*num)+1);
443 fstrcpy((*dcs)[*num].name, dcname);
444 (*dcs)[*num].ip = ip;
449 static BOOL add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
450 struct in_addr ip, uint16 port,
451 struct sockaddr_in **addrs, int *num)
453 *addrs = TALLOC_REALLOC_ARRAY(mem_ctx, *addrs, struct sockaddr_in, (*num)+1);
458 (*addrs)[*num].sin_family = PF_INET;
459 putip((char *)&((*addrs)[*num].sin_addr), (char *)&ip);
460 (*addrs)[*num].sin_port = htons(port);
466 static BOOL get_dcs_1c(TALLOC_CTX *mem_ctx,
467 const struct winbindd_domain *domain,
468 struct dc_name_ip **dcs, int *num_dcs)
470 struct ip_service *iplist = NULL;
473 if (!internal_resolve_name(domain->name, 0x1c, &iplist, &num,
474 lp_name_resolve_order()))
477 /* Now try to find the server names of at least one IP address, hosts
478 * not replying are cached as such */
480 for (i=0; i<num; i++) {
484 if (!name_status_find(domain->name, 0x1c, 0x20, iplist[i].ip,
488 if (add_one_dc_unique(mem_ctx, domain->name, dcname,
489 iplist[i].ip, dcs, num_dcs)) {
490 /* One DC responded, so we assume that he will also
499 static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain,
500 struct dc_name_ip **dcs, int *num_dcs)
508 is_our_domain = strequal(domain->name, lp_workgroup());
510 if (!is_our_domain && get_dc_name_via_netlogon(domain, dcname, &ip) &&
511 add_one_dc_unique(mem_ctx, domain->name, dcname, ip, dcs, num_dcs))
514 if (!is_our_domain) {
515 /* NETLOGON to our own domain could not give us a DC name
516 * (which is an error), fall back to looking up domain#1c */
517 return get_dcs_1c(mem_ctx, domain, dcs, num_dcs);
520 if (must_use_pdc(domain->name) && get_pdc_ip(domain->name, &ip)) {
522 if (!name_status_find(domain->name, 0x1b, 0x20, ip, dcname))
525 if (add_one_dc_unique(mem_ctx, domain->name,
526 dcname, ip, dcs, num_dcs))
530 p = lp_passwordserver();
533 return get_dcs_1c(mem_ctx, domain, dcs, num_dcs);
535 while (next_token(&p, dcname, LIST_SEP, sizeof(dcname))) {
537 if (strequal(dcname, "*")) {
538 get_dcs_1c(mem_ctx, domain, dcs, num_dcs);
542 if (!resolve_name(dcname, &ip, 0x20))
545 /* Even if we got the dcname, double check the name to use for
546 * the netlogon auth2 */
548 if (!name_status_find(domain->name, 0x1c, 0x20, ip, dcname))
551 add_one_dc_unique(mem_ctx, domain->name, dcname, ip,
558 static BOOL find_new_dc(TALLOC_CTX *mem_ctx,
559 const struct winbindd_domain *domain,
560 fstring dcname, struct sockaddr_in *addr, int *fd)
562 struct dc_name_ip *dcs = NULL;
565 char **dcnames = NULL;
568 struct sockaddr_in *addrs = NULL;
573 if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs) || (num_dcs == 0))
576 for (i=0; i<num_dcs; i++) {
578 add_string_to_array(mem_ctx, dcs[i].name,
579 &dcnames, &num_dcnames);
580 add_sockaddr_to_array(mem_ctx, dcs[i].ip, 445,
583 add_string_to_array(mem_ctx, dcs[i].name,
584 &dcnames, &num_dcnames);
585 add_sockaddr_to_array(mem_ctx, dcs[i].ip, 139,
589 if ((num_dcnames == 0) || (num_dcnames != num_addrs))
592 if (!open_any_socket_out(addrs, num_addrs, 10000, &fd_index, fd)) {
593 for (i=0; i<num_dcs; i++) {
594 add_failed_connection_entry(domain->name,
596 NT_STATUS_UNSUCCESSFUL);
601 fstrcpy(dcname, dcnames[fd_index]);
602 *addr = addrs[fd_index];
607 static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
608 const int pipe_index,
609 struct winbindd_cm_conn *new_conn)
616 if ((mem_ctx = talloc_init("cm_open_connection")) == NULL)
617 return NT_STATUS_NO_MEMORY;
619 for (retries = 0; retries < 3; retries++) {
624 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
626 if ((strlen(domain->dcname) > 0) &&
627 NT_STATUS_IS_OK(check_negative_conn_cache(domain->name,
630 if (!open_any_socket_out(&domain->dcaddr, 1, 10000,
637 !find_new_dc(mem_ctx, domain, domain->dcname,
638 &domain->dcaddr, &fd))
641 new_conn->cli = NULL;
643 result = cm_prepare_connection(domain, fd, pipe_index,
645 &new_conn->cli, &retry);
647 if (NT_STATUS_IS_OK(result)) {
648 fstrcpy(new_conn->domain, domain->name);
649 /* Initialise SMB connection */
650 fstrcpy(new_conn->pipe_name,
651 get_pipe_name_from_index(pipe_index));
659 talloc_destroy(mem_ctx);
663 /************************************************************************
664 Wrapper around statuc cm_open_connection to retreive a freshly
665 setup cli_state struct
666 ************************************************************************/
668 NTSTATUS cm_fresh_connection(struct winbindd_domain *domain, const int pipe_index,
669 struct cli_state **cli)
672 struct winbindd_cm_conn conn;
674 result = cm_open_connection( domain, pipe_index, &conn );
676 if ( NT_STATUS_IS_OK(result) )
682 /* Return true if a connection is still alive */
684 static BOOL connection_ok(struct winbindd_cm_conn *conn)
687 smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n");
692 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
693 conn->controller, conn->domain, conn->pipe_name));
697 if (!conn->cli->initialised) {
698 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
699 conn->controller, conn->domain, conn->pipe_name));
703 if (conn->cli->fd == -1) {
704 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
705 conn->controller, conn->domain, conn->pipe_name));
712 /* Search the cache for a connection. If there is a broken one,
713 shut it down properly and return NULL. */
715 static void find_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
716 struct winbindd_cm_conn **conn_out)
718 struct winbindd_cm_conn *conn;
720 for (conn = cm_conns; conn; ) {
721 if (strequal(conn->domain, domain->name) &&
722 strequal(conn->pipe_name, pipe_name)) {
723 if (!connection_ok(conn)) {
724 /* Dead connection - remove it. */
725 struct winbindd_cm_conn *conn_temp = conn->next;
727 cli_shutdown(conn->cli);
728 DLIST_REMOVE(cm_conns, conn);
730 conn = conn_temp; /* Keep the loop moving */
742 /* Initialize a new connection up to the RPC BIND. */
744 static NTSTATUS new_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
745 struct winbindd_cm_conn **conn_out)
747 struct winbindd_cm_conn *conn;
750 if (!(conn = SMB_MALLOC_P(struct winbindd_cm_conn)))
751 return NT_STATUS_NO_MEMORY;
755 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
756 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
757 domain->name, pipe_name, nt_errstr(result)));
761 DLIST_ADD(cm_conns, conn);
767 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
769 static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain, const char *pipe_name,
770 struct winbindd_cm_conn **conn_out)
772 find_cm_connection(domain, pipe_name, conn_out);
774 if (*conn_out != NULL)
777 return new_cm_connection(domain, pipe_name, conn_out);
780 /**********************************************************************************
781 We can 'sense' certain things about the DC by it's replies to certain questions.
783 This tells us if this particular remote server is Active Directory, and if it is
785 **********************************************************************************/
787 void set_dc_type_and_flags( struct winbindd_domain *domain )
790 struct winbindd_cm_conn conn;
792 TALLOC_CTX *mem_ctx = NULL;
797 domain->native_mode = False;
798 domain->active_directory = False;
800 if (domain->internal) {
801 domain->initialized = True;
805 if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
806 DEBUG(5, ("set_dc_type_and_flags: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
807 domain->name, nt_errstr(result)));
808 domain->initialized = True;
813 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
814 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
819 if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
820 && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
821 domain->native_mode = True;
823 /* Cheat - shut down the DS pipe, and open LSA */
825 cli_nt_session_close(conn.cli);
827 if ( cli_nt_session_open (conn.cli, PI_LSARPC) ) {
828 char *domain_name = NULL;
829 char *dns_name = NULL;
830 DOM_SID *dom_sid = NULL;
832 mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", domain->name);
834 DEBUG(1, ("set_dc_type_and_flags: talloc_init() failed\n"));
838 result = cli_lsa_open_policy2(conn.cli, mem_ctx, True,
839 SEC_RIGHTS_MAXIMUM_ALLOWED,
842 if (NT_STATUS_IS_OK(result)) {
843 /* This particular query is exactly what Win2k clients use
844 to determine that the DC is active directory */
845 result = cli_lsa_query_info_policy2(conn.cli, mem_ctx,
852 if (NT_STATUS_IS_OK(result)) {
854 fstrcpy(domain->name, domain_name);
857 fstrcpy(domain->alt_name, dns_name);
860 sid_copy(&domain->sid, dom_sid);
862 domain->active_directory = True;
865 result = cli_lsa_open_policy(conn.cli, mem_ctx, True,
866 SEC_RIGHTS_MAXIMUM_ALLOWED,
869 if (!NT_STATUS_IS_OK(result))
872 result = cli_lsa_query_info_policy(conn.cli, mem_ctx,
873 &conn.pol, 5, &domain_name,
876 if (NT_STATUS_IS_OK(result)) {
878 fstrcpy(domain->name, domain_name);
881 sid_copy(&domain->sid, dom_sid);
888 /* close the connection; no other calls use this pipe and it is called only
889 on reestablishing the domain list --jerry */
892 cli_shutdown( conn.cli );
894 talloc_destroy(mem_ctx);
896 domain->initialized = True;
903 /* Return a LSA policy handle on a domain */
905 NTSTATUS cm_get_lsa_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
907 struct winbindd_cm_conn *conn;
908 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
910 static CLI_POLICY_HND hnd;
912 /* Look for existing connections */
914 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
917 /* This *shitty* code needs scrapping ! JRA */
919 if (policy_handle_is_valid(&conn->pol)) {
927 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
928 des_access, &conn->pol);
930 if (!NT_STATUS_IS_OK(result)) {
931 /* Hit the cache code again. This cleans out the old connection and gets a new one */
932 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
933 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
936 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
937 des_access, &conn->pol);
940 if (!NT_STATUS_IS_OK(result)) {
941 cli_shutdown(conn->cli);
942 DLIST_REMOVE(cm_conns, conn);
956 /* Return a SAM policy handle on a domain */
958 NTSTATUS cm_get_sam_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
960 struct winbindd_cm_conn *conn;
961 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
963 static CLI_POLICY_HND hnd;
965 /* Look for existing connections */
967 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
970 /* This *shitty* code needs scrapping ! JRA */
972 if (policy_handle_is_valid(&conn->pol)) {
981 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
982 des_access, &conn->pol);
984 if (!NT_STATUS_IS_OK(result)) {
985 /* Hit the cache code again. This cleans out the old connection and gets a new one */
986 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
988 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
991 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
992 des_access, &conn->pol);
995 if (!NT_STATUS_IS_OK(result)) {
997 cli_shutdown(conn->cli);
998 DLIST_REMOVE(cm_conns, conn);
1005 hnd.pol = conn->pol;
1006 hnd.cli = conn->cli;
1010 return NT_STATUS_OK;
1013 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
1014 netlogon pipe as no handle is returned. */
1016 NTSTATUS cm_get_netlogon_cli(struct winbindd_domain *domain,
1017 const unsigned char *trust_passwd,
1018 uint32 sec_channel_type,
1020 struct cli_state **cli)
1022 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1023 struct winbindd_cm_conn *conn;
1028 return NT_STATUS_INVALID_PARAMETER;
1030 /* Open an initial conection - keep the mutex. */
1032 find_cm_connection(domain, PIPE_NETLOGON, &conn);
1034 if ( fresh && (conn != NULL) ) {
1035 cli_shutdown(conn->cli);
1040 /* purge connection from cache */
1041 find_cm_connection(domain, PIPE_NETLOGON, &conn);
1043 DEBUG(0,("Could not purge connection\n"));
1044 return NT_STATUS_UNSUCCESSFUL;
1050 return NT_STATUS_OK;
1053 result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
1055 if (!NT_STATUS_IS_OK(result))
1058 fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
1060 if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
1061 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
1064 if ( sec_channel_type == SEC_CHAN_DOMAIN )
1065 fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
1067 /* This must be the remote domain (not ours) for schannel */
1069 fstrcpy( conn->cli->domain, domain->name);
1071 result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
1074 secrets_named_mutex_release(lock_name);
1076 if (!NT_STATUS_IS_OK(result)) {
1077 cli_shutdown(conn->cli);
1078 DLIST_REMOVE(cm_conns, conn);
1088 /* Dump the current connection status */
1090 static void dump_conn_list(void)
1092 struct winbindd_cm_conn *con;
1094 DEBUG(0, ("\tDomain Controller Pipe\n"));
1096 for(con = cm_conns; con; con = con->next) {
1099 /* Display pipe info */
1101 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
1102 DEBUG(0, ("Error: not enough memory!\n"));
1104 DEBUG(0, ("%s\n", msg));
1110 void winbindd_cm_status(void)
1112 /* List open connections */
1114 DEBUG(0, ("winbindd connection manager status:\n"));
1119 DEBUG(0, ("\tNo active connections\n"));
1122 /* Close all cached connections */
1124 void winbindd_cm_flush(void)
1126 struct winbindd_cm_conn *conn, tmp;
1128 /* Flush connection cache */
1130 for (conn = cm_conns; conn; conn = conn->next) {
1132 if (!connection_ok(conn))
1135 DEBUG(10, ("Closing connection to %s on %s\n",
1136 conn->pipe_name, conn->controller));
1139 cli_shutdown(conn->cli);
1141 tmp.next = conn->next;
1143 DLIST_REMOVE(cm_conns, conn);
1148 /* Flush failed connection cache */
1150 flush_negative_conn_cache();