fix connecting to a BDC when the PDC is down but in WINS and no bcast
[Samba/bb.git] / source3 / nsswitch / winbindd_cm.c
blob8c11bd337295158a15c5b0203e79c5b3f448c160
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 - There needs to be a utility function in libsmb/namequery.c that does
55 cm_get_dc_name()
57 - Take care when destroying cli_structs as they can be shared between
58 various sam handles.
62 #include "winbindd.h"
64 #undef DBGC_CLASS
65 #define DBGC_CLASS DBGC_WINBIND
67 /* Global list of connections. Initially a DLIST but can become a hash
68 table or whatever later. */
70 struct winbindd_cm_conn {
71 struct winbindd_cm_conn *prev, *next;
72 fstring domain;
73 fstring controller;
74 fstring pipe_name;
75 struct cli_state *cli;
76 POLICY_HND pol;
79 static struct winbindd_cm_conn *cm_conns = NULL;
81 /* Get a domain controller name. Cache positive and negative lookups so we
82 don't go to the network too often when something is badly broken. */
84 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
86 struct get_dc_name_cache {
87 fstring domain_name;
88 fstring srv_name;
89 time_t lookup_time;
90 struct get_dc_name_cache *prev, *next;
95 find the DC for a domain using methods appropriate for a ADS domain
97 static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
99 ADS_STRUCT *ads;
100 const char *realm = domain;
102 if (strcasecmp(realm, lp_workgroup()) == 0) {
103 realm = lp_realm();
106 ads = ads_init(realm, domain, NULL);
107 if (!ads) {
108 return False;
111 /* we don't need to bind, just connect */
112 ads->auth.no_bind = 1;
114 DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
116 #ifdef HAVE_ADS
117 /* a full ads_connect() is actually overkill, as we don't srictly need
118 to do the SASL auth in order to get the info we need, but libads
119 doesn't offer a better way right now */
120 ads_connect(ads);
121 #endif
123 if (!ads->config.realm) {
124 return False;
127 fstrcpy(srv_name, ads->config.ldap_server_name);
128 strupper(srv_name);
129 *dc_ip = ads->ldap_ip;
130 ads_destroy(&ads);
132 DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
133 srv_name, inet_ntoa(*dc_ip)));
135 return True;
139 find the DC for a domain using methods appropriate for a RPC domain
141 static BOOL cm_rpc_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
143 struct in_addr *ip_list = NULL;
144 int count, i;
146 /* Lookup domain controller name. Try the real PDC first to avoid
147 SAM sync delays */
148 if (get_dc_list(True, domain, &ip_list, &count) &&
149 name_status_find(domain, 0x1c, 0x20, ip_list[0], srv_name)) {
150 *dc_ip = ip_list[0];
151 SAFE_FREE(ip_list);
152 return True;
155 if (!get_dc_list(True, domain, &ip_list, &count)) {
156 if (!get_dc_list(False, domain, &ip_list, &count)) {
157 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
158 return False;
162 /* Pick a nice close server */
163 /* Look for DC on local net */
164 for (i = 0; i < count; i++) {
165 if (!is_local_net(ip_list[i]))
166 continue;
168 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
169 *dc_ip = ip_list[i];
170 SAFE_FREE(ip_list);
171 return True;
173 zero_ip(&ip_list[i]);
177 * Secondly try and contact a random PDC/BDC.
180 i = (sys_random() % count);
182 if (!is_zero_ip(ip_list[i]) &&
183 name_status_find(domain, 0x1c, 0x20,
184 ip_list[i], srv_name)) {
185 *dc_ip = ip_list[i];
186 SAFE_FREE(ip_list);
187 return True;
189 zero_ip(&ip_list[i]); /* Tried and failed. */
191 /* Finally return first DC that we can contact using a node
192 status */
193 for (i = 0; i < count; i++) {
194 if (is_zero_ip(ip_list[i]))
195 continue;
197 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
198 *dc_ip = ip_list[i];
199 SAFE_FREE(ip_list);
200 return True;
204 SAFE_FREE(ip_list);
206 return False;
210 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
212 static struct get_dc_name_cache *get_dc_name_cache;
213 struct get_dc_name_cache *dcc;
214 struct in_addr dc_ip;
215 BOOL ret;
217 /* Check the cache for previous lookups */
219 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
221 if (!strequal(domain, dcc->domain_name))
222 continue; /* Not our domain */
224 if ((time(NULL) - dcc->lookup_time) >
225 GET_DC_NAME_CACHE_TIMEOUT) {
227 /* Cache entry has expired, delete it */
229 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
231 DLIST_REMOVE(get_dc_name_cache, dcc);
232 SAFE_FREE(dcc);
234 break;
237 /* Return a positive or negative lookup for this domain */
239 if (dcc->srv_name[0]) {
240 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
241 fstrcpy(srv_name, dcc->srv_name);
242 return True;
243 } else {
244 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
245 return False;
249 /* Add cache entry for this lookup. */
251 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
253 if (!(dcc = (struct get_dc_name_cache *)
254 malloc(sizeof(struct get_dc_name_cache))))
255 return False;
257 ZERO_STRUCTP(dcc);
259 fstrcpy(dcc->domain_name, domain);
260 dcc->lookup_time = time(NULL);
262 DLIST_ADD(get_dc_name_cache, dcc);
264 zero_ip(&dc_ip);
266 ret = False;
267 if (lp_security() == SEC_ADS) {
268 ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
270 if (!ret) {
271 /* fall back on rpc methods if the ADS methods fail */
272 ret = cm_rpc_find_dc(domain, &dc_ip, srv_name);
275 if (!ret) {
276 return False;
279 /* We have a name so make the cache entry positive now */
280 fstrcpy(dcc->srv_name, srv_name);
282 DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
283 inet_ntoa(dc_ip), domain));
285 *ip_out = dc_ip;
287 return True;
290 /* Choose between anonymous or authenticated connections. We need to use
291 an authenticated connection if DCs have the RestrictAnonymous registry
292 entry set > 0, or the "Additional restrictions for anonymous
293 connections" set in the win2k Local Security Policy.
295 Caller to free() result in domain, username, password
298 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
300 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
301 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
302 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
304 if (*username && **username) {
305 if (!*domain || !**domain) {
306 *domain = smb_xstrdup(lp_workgroup());
309 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
310 } else {
311 DEBUG(3, ("IPC$ connections done anonymously\n"));
312 *username = smb_xstrdup("");
313 *domain = smb_xstrdup("");
314 *password = smb_xstrdup("");
318 /* Open a new smb pipe connection to a DC on a given domain. Cache
319 negative creation attempts so we don't try and connect to broken
320 machines too often. */
322 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
324 struct failed_connection_cache {
325 fstring domain_name;
326 fstring controller;
327 time_t lookup_time;
328 NTSTATUS nt_status;
329 struct failed_connection_cache *prev, *next;
332 static struct failed_connection_cache *failed_connection_cache;
334 /* Add an entry to the failed conneciton cache */
336 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn,
337 NTSTATUS result)
339 struct failed_connection_cache *fcc;
341 SMB_ASSERT(!NT_STATUS_IS_OK(result));
343 /* Check we already aren't in the cache */
345 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
346 if (strequal(fcc->domain_name, new_conn->domain)) {
347 DEBUG(10, ("domain %s already tried and failed\n",
348 fcc->domain_name));
349 return;
353 /* Create negative lookup cache entry for this domain and controller */
355 if (!(fcc = (struct failed_connection_cache *)
356 malloc(sizeof(struct failed_connection_cache)))) {
357 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
358 return;
361 ZERO_STRUCTP(fcc);
363 fstrcpy(fcc->domain_name, new_conn->domain);
364 fstrcpy(fcc->controller, new_conn->controller);
365 fcc->lookup_time = time(NULL);
366 fcc->nt_status = result;
368 DLIST_ADD(failed_connection_cache, fcc);
371 /* Open a connction to the remote server, cache failures for 30 seconds */
373 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
374 struct winbindd_cm_conn *new_conn)
376 struct failed_connection_cache *fcc;
377 extern pstring global_myname;
378 NTSTATUS result;
379 char *ipc_username, *ipc_domain, *ipc_password;
380 struct in_addr dc_ip;
382 ZERO_STRUCT(dc_ip);
384 fstrcpy(new_conn->domain, domain);
385 fstrcpy(new_conn->pipe_name, pipe_name);
387 /* Look for a domain controller for this domain. Negative results
388 are cached so don't bother applying the caching for this
389 function just yet. */
391 if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
392 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
393 add_failed_connection_entry(new_conn, result);
394 return result;
397 /* Return false if we have tried to look up this domain and netbios
398 name before and failed. */
400 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
402 if (!(strequal(domain, fcc->domain_name) &&
403 strequal(new_conn->controller, fcc->controller)))
404 continue; /* Not our domain */
406 if ((time(NULL) - fcc->lookup_time) >
407 FAILED_CONNECTION_CACHE_TIMEOUT) {
409 /* Cache entry has expired, delete it */
411 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
413 DLIST_REMOVE(failed_connection_cache, fcc);
414 free(fcc);
416 break;
419 /* The timeout hasn't expired yet so return false */
421 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
423 result = fcc->nt_status;
424 SMB_ASSERT(!NT_STATUS_IS_OK(result));
425 return result;
428 /* Initialise SMB connection */
430 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
432 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
433 new_conn->controller, global_myname, ipc_domain, ipc_username));
435 result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller,
436 &dc_ip, 0, "IPC$",
437 "IPC", ipc_username, ipc_domain,
438 ipc_password, 0);
440 SAFE_FREE(ipc_username);
441 SAFE_FREE(ipc_domain);
442 SAFE_FREE(ipc_password);
444 if (!NT_STATUS_IS_OK(result)) {
445 add_failed_connection_entry(new_conn, result);
446 return result;
449 if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
450 result = NT_STATUS_PIPE_NOT_AVAILABLE;
451 add_failed_connection_entry(new_conn, result);
452 cli_shutdown(new_conn->cli);
453 return result;
456 return NT_STATUS_OK;
459 /* Return true if a connection is still alive */
461 static BOOL connection_ok(struct winbindd_cm_conn *conn)
463 if (!conn) {
464 smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
465 return False;
468 if (!conn->cli) {
469 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
470 conn->controller, conn->domain, conn->pipe_name));
471 smb_panic("connection_ok: conn->cli was null!");
472 return False;
475 if (!conn->cli->initialised) {
476 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
477 conn->controller, conn->domain, conn->pipe_name));
478 smb_panic("connection_ok: conn->cli->initialised is False!");
479 return False;
482 if (conn->cli->fd == -1) {
483 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
484 conn->controller, conn->domain, conn->pipe_name));
485 return False;
488 return True;
491 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
493 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out)
495 struct winbindd_cm_conn *conn, conn_temp;
496 NTSTATUS result;
498 for (conn = cm_conns; conn; conn = conn->next) {
499 if (strequal(conn->domain, domain) &&
500 strequal(conn->pipe_name, pipe_name)) {
501 if (!connection_ok(conn)) {
502 if (conn->cli) {
503 cli_shutdown(conn->cli);
505 ZERO_STRUCT(conn_temp);
506 conn_temp.next = conn->next;
507 DLIST_REMOVE(cm_conns, conn);
508 SAFE_FREE(conn);
509 conn = &conn_temp; /* Just to keep the loop moving */
510 } else {
511 break;
516 if (!conn) {
517 if (!(conn = malloc(sizeof(*conn))))
518 return NT_STATUS_NO_MEMORY;
520 ZERO_STRUCTP(conn);
522 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
523 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
524 domain, pipe_name, nt_errstr(result)));
525 SAFE_FREE(conn);
526 return result;
528 DLIST_ADD(cm_conns, conn);
531 *conn_out = conn;
532 return NT_STATUS_OK;
535 /* Return a LSA policy handle on a domain */
537 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
539 struct winbindd_cm_conn *conn;
540 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
541 NTSTATUS result;
542 static CLI_POLICY_HND hnd;
544 /* Look for existing connections */
546 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
547 return NULL;
550 /* This *shitty* code needs scrapping ! JRA */
551 if (policy_handle_is_valid(&conn->pol)) {
552 hnd.pol = conn->pol;
553 hnd.cli = conn->cli;
554 return &hnd;
557 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
558 des_access, &conn->pol);
560 if (!NT_STATUS_IS_OK(result)) {
561 /* Hit the cache code again. This cleans out the old connection and gets a new one */
562 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
563 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
564 return NULL;
567 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
568 des_access, &conn->pol);
571 if (!NT_STATUS_IS_OK(result)) {
572 cli_shutdown(conn->cli);
573 DLIST_REMOVE(cm_conns, conn);
574 SAFE_FREE(conn);
575 return NULL;
579 hnd.pol = conn->pol;
580 hnd.cli = conn->cli;
582 return &hnd;
585 /* Return a SAM policy handle on a domain */
587 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
589 struct winbindd_cm_conn *conn;
590 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
591 NTSTATUS result;
592 static CLI_POLICY_HND hnd;
594 /* Look for existing connections */
596 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
597 return NULL;
600 /* This *shitty* code needs scrapping ! JRA */
601 if (policy_handle_is_valid(&conn->pol)) {
602 hnd.pol = conn->pol;
603 hnd.cli = conn->cli;
604 return &hnd;
606 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
607 des_access, &conn->pol);
609 if (!NT_STATUS_IS_OK(result)) {
610 /* Hit the cache code again. This cleans out the old connection and gets a new one */
611 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
612 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
613 return NULL;
616 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
617 des_access, &conn->pol);
620 if (!NT_STATUS_IS_OK(result)) {
621 cli_shutdown(conn->cli);
622 DLIST_REMOVE(cm_conns, conn);
623 SAFE_FREE(conn);
624 return NULL;
628 hnd.pol = conn->pol;
629 hnd.cli = conn->cli;
631 return &hnd;
634 #if 0 /* This code now *well* out of date */
636 /* Return a SAM domain policy handle on a domain */
638 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
640 struct winbindd_cm_conn *conn, *basic_conn = NULL;
641 static CLI_POLICY_HND hnd;
642 NTSTATUS result;
643 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
645 /* Look for existing connections */
647 for (conn = cm_conns; conn; conn = conn->next) {
648 if (strequal(conn->domain, domain) &&
649 strequal(conn->pipe_name, PIPE_SAMR) &&
650 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
652 if (!connection_ok(conn)) {
653 /* Shutdown cli? Free conn? Allow retry of DC? */
654 DLIST_REMOVE(cm_conns, conn);
655 return NULL;
658 goto ok;
662 /* Create a basic handle to open a domain handle from */
664 if (!cm_get_sam_handle(domain))
665 return False;
667 for (conn = cm_conns; conn; conn = conn->next) {
668 if (strequal(conn->domain, domain) &&
669 strequal(conn->pipe_name, PIPE_SAMR) &&
670 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
671 basic_conn = conn;
674 if (!(conn = (struct winbindd_cm_conn *)
675 malloc(sizeof(struct winbindd_cm_conn))))
676 return NULL;
678 ZERO_STRUCTP(conn);
680 fstrcpy(conn->domain, basic_conn->domain);
681 fstrcpy(conn->controller, basic_conn->controller);
682 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
684 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
685 conn->cli = basic_conn->cli;
687 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
688 &basic_conn->pol, des_access,
689 domain_sid, &conn->pol);
691 if (!NT_STATUS_IS_OK(result))
692 return NULL;
694 /* Add to list */
696 DLIST_ADD(cm_conns, conn);
699 hnd.pol = conn->pol;
700 hnd.cli = conn->cli;
702 return &hnd;
705 /* Return a SAM policy handle on a domain user */
707 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
708 uint32 user_rid)
710 struct winbindd_cm_conn *conn, *basic_conn = NULL;
711 static CLI_POLICY_HND hnd;
712 NTSTATUS result;
713 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
715 /* Look for existing connections */
717 for (conn = cm_conns; conn; conn = conn->next) {
718 if (strequal(conn->domain, domain) &&
719 strequal(conn->pipe_name, PIPE_SAMR) &&
720 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
721 conn->pipe_data.samr.rid == user_rid) {
723 if (!connection_ok(conn)) {
724 /* Shutdown cli? Free conn? Allow retry of DC? */
725 DLIST_REMOVE(cm_conns, conn);
726 return NULL;
729 goto ok;
733 /* Create a domain handle to open a user handle from */
735 if (!cm_get_sam_dom_handle(domain, domain_sid))
736 return NULL;
738 for (conn = cm_conns; conn; conn = conn->next) {
739 if (strequal(conn->domain, domain) &&
740 strequal(conn->pipe_name, PIPE_SAMR) &&
741 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
742 basic_conn = conn;
745 if (!basic_conn) {
746 DEBUG(0, ("No domain sam handle was created!\n"));
747 return NULL;
750 if (!(conn = (struct winbindd_cm_conn *)
751 malloc(sizeof(struct winbindd_cm_conn))))
752 return NULL;
754 ZERO_STRUCTP(conn);
756 fstrcpy(conn->domain, basic_conn->domain);
757 fstrcpy(conn->controller, basic_conn->controller);
758 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
760 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
761 conn->cli = basic_conn->cli;
762 conn->pipe_data.samr.rid = user_rid;
764 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
765 &basic_conn->pol, des_access, user_rid,
766 &conn->pol);
768 if (!NT_STATUS_IS_OK(result))
769 return NULL;
771 /* Add to list */
773 DLIST_ADD(cm_conns, conn);
776 hnd.pol = conn->pol;
777 hnd.cli = conn->cli;
779 return &hnd;
782 /* Return a SAM policy handle on a domain group */
784 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
785 uint32 group_rid)
787 struct winbindd_cm_conn *conn, *basic_conn = NULL;
788 static CLI_POLICY_HND hnd;
789 NTSTATUS result;
790 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
792 /* Look for existing connections */
794 for (conn = cm_conns; conn; conn = conn->next) {
795 if (strequal(conn->domain, domain) &&
796 strequal(conn->pipe_name, PIPE_SAMR) &&
797 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
798 conn->pipe_data.samr.rid == group_rid) {
800 if (!connection_ok(conn)) {
801 /* Shutdown cli? Free conn? Allow retry of DC? */
802 DLIST_REMOVE(cm_conns, conn);
803 return NULL;
806 goto ok;
810 /* Create a domain handle to open a user handle from */
812 if (!cm_get_sam_dom_handle(domain, domain_sid))
813 return NULL;
815 for (conn = cm_conns; conn; conn = conn->next) {
816 if (strequal(conn->domain, domain) &&
817 strequal(conn->pipe_name, PIPE_SAMR) &&
818 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
819 basic_conn = conn;
822 if (!basic_conn) {
823 DEBUG(0, ("No domain sam handle was created!\n"));
824 return NULL;
827 if (!(conn = (struct winbindd_cm_conn *)
828 malloc(sizeof(struct winbindd_cm_conn))))
829 return NULL;
831 ZERO_STRUCTP(conn);
833 fstrcpy(conn->domain, basic_conn->domain);
834 fstrcpy(conn->controller, basic_conn->controller);
835 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
837 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
838 conn->cli = basic_conn->cli;
839 conn->pipe_data.samr.rid = group_rid;
841 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
842 &basic_conn->pol, des_access, group_rid,
843 &conn->pol);
845 if (!NT_STATUS_IS_OK(result))
846 return NULL;
848 /* Add to list */
850 DLIST_ADD(cm_conns, conn);
853 hnd.pol = conn->pol;
854 hnd.cli = conn->cli;
856 return &hnd;
859 #endif
861 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
862 netlogon pipe as no handle is returned. */
864 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
865 struct cli_state **cli)
867 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
868 struct winbindd_cm_conn *conn;
870 if (!cli) {
871 return NT_STATUS_INVALID_PARAMETER;
874 /* Open an initial conection */
876 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
877 return result;
880 result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd);
882 if (!NT_STATUS_IS_OK(result)) {
883 DEBUG(0, ("error connecting to domain password server: %s\n",
884 nt_errstr(result)));
886 /* Hit the cache code again. This cleans out the old connection and gets a new one */
887 if (conn->cli->fd == -1) {
888 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
889 return result;
892 /* Try again */
893 result = cli_nt_setup_creds(
894 conn->cli, get_sec_chan(),trust_passwd);
897 if (!NT_STATUS_IS_OK(result)) {
898 cli_shutdown(conn->cli);
899 DLIST_REMOVE(cm_conns, conn);
900 SAFE_FREE(conn);
901 return result;
905 *cli = conn->cli;
907 return result;
910 /* Dump the current connection status */
912 static void dump_conn_list(void)
914 struct winbindd_cm_conn *con;
916 DEBUG(0, ("\tDomain Controller Pipe\n"));
918 for(con = cm_conns; con; con = con->next) {
919 char *msg;
921 /* Display pipe info */
923 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
924 DEBUG(0, ("Error: not enough memory!\n"));
925 } else {
926 DEBUG(0, ("%s\n", msg));
927 SAFE_FREE(msg);
932 void winbindd_cm_status(void)
934 /* List open connections */
936 DEBUG(0, ("winbindd connection manager status:\n"));
938 if (cm_conns)
939 dump_conn_list();
940 else
941 DEBUG(0, ("\tNo active connections\n"));