This is the start of a bit of a rewrite of winbindd's connection handling.
[ira/wip.git] / source3 / nsswitch / winbindd_cm.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4
5    Winbind daemon connection manager
6
7    Copyright (C) Tim Potter 2001
8    
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.
13    
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.
18    
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.
22 */
23
24 /*
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:
28   
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
35   
36    We can also throw away the CLI_POLICY_HND stuff as all this information
37    will be stored within this module.
38   
39    Why not have connection management as part of the rpc layer like tng?
40    Good question.  This code may morph into libsmb/rpc_cache.c or something
41    like that but at the moment it's simply staying as part of winbind.  I
42    think the TNG architecture of forcing every user of the rpc layer to use
43    the connection caching system is a bad idea.  It should be an optional
44    method of using the routines.
45
46    The TNG design is quite good but I disagree with some aspects of the
47    implementation. -tpot
48
49  */
50
51 /*
52    TODO:
53
54      - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
55        moved down into another function.
56
57      - There needs to be a utility function in libsmb/namequery.c that does
58        get_any_dc_name() 
59
60  */
61
62 #include "winbindd.h"
63
64 /* We store lists of connections here */
65
66 struct winbindd_cm_conn {
67         struct winbindd_cm_conn *prev, *next;
68         fstring domain;
69         fstring controller;
70         fstring pipe_name;
71         struct cli_state cli;
72         POLICY_HND pol;
73 };
74
75 /* Global list of connections.  Initially a DLIST but can become a hash
76    table or whatever later. */
77
78 struct winbindd_cm_conn *cm_conns = NULL;
79
80 /* Get a domain controller name */
81
82 BOOL cm_get_dc_name(char *domain, fstring srv_name)
83 {
84         struct in_addr *ip_list, dc_ip;
85         extern pstring global_myname;
86         int count, i;
87
88         /* Lookup domain controller name */
89                 
90         if (!get_dc_list(False, domain, &ip_list, &count))
91                 return False;
92                 
93         /* Firstly choose a PDC/BDC who has the same network address as any
94            of our interfaces. */
95         
96         for (i = 0; i < count; i++) {
97                 if(!is_local_net(ip_list[i]))
98                         goto got_ip;
99         }
100         
101         i = (sys_random() % count);
102         
103  got_ip:
104         dc_ip = ip_list[i];
105         SAFE_FREE(ip_list);
106                 
107         if (!lookup_pdc_name(global_myname, domain, &dc_ip, srv_name))
108                 return False;
109
110         return True;
111 }
112
113 /* Open a new smb pipe connection to a DC on a given domain */
114
115 static BOOL cm_open_connection(char *domain, char *pipe_name,
116                                struct winbindd_cm_conn *new_conn)
117 {
118         struct nmb_name calling, called;
119         extern pstring global_myname;
120         fstring dest_host;
121         struct in_addr dest_ip;
122         BOOL result = False;
123         struct ntuser_creds creds;
124
125         ZERO_STRUCT(new_conn->cli);
126
127         fstrcpy(new_conn->domain, domain);
128         fstrcpy(new_conn->pipe_name, pipe_name);
129         
130         /* Look for a domain controller for this domain */
131
132         if (!cm_get_dc_name(lp_workgroup(), new_conn->controller))
133                 goto done;
134
135         /* Initialise SMB connection */
136
137         if (!cli_initialise(&new_conn->cli))
138                 goto done;
139
140         if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
141                 goto done;
142
143         make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 
144                       0x20);
145         make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
146
147         ZERO_STRUCT(creds);
148         creds.pwd.null_pwd = 1;
149
150         cli_init_creds(&new_conn->cli, &creds);
151
152         if (!cli_establish_connection(&new_conn->cli, new_conn->controller, 
153                                       &dest_ip, &calling, &called, "IPC$", 
154                                       "IPC", False, True))
155                 goto done;
156
157         if (!cli_nt_session_open (&new_conn->cli, pipe_name))
158                 goto done;
159
160         result = True;
161
162  done:
163         if (!result)
164                 cli_shutdown(&new_conn->cli);
165
166         return result;
167 }
168
169 /* Return a LSA policy handle on a domain */
170
171 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
172 {
173         struct winbindd_cm_conn *conn;
174         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
175         NTSTATUS result;
176         static CLI_POLICY_HND hnd;
177
178         /* Look for existing connections */
179
180         for (conn = cm_conns; conn; conn = conn->next) {
181                 if (strequal(conn->domain, domain) &&
182                     strequal(conn->pipe_name, PIPE_LSARPC))
183                         goto ok;
184         }
185
186         /* Create a new one */
187
188         if (!(conn = (struct winbindd_cm_conn *)
189               malloc(sizeof(struct winbindd_cm_conn))))
190                 return NULL;
191
192         if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
193                 DEBUG(3, ("Could not connect to a dc for domain %s\n",
194                           domain));
195                 return NULL;
196         }
197
198         result = cli_lsa_open_policy(&conn->cli, conn->cli.mem_ctx, False, 
199                                      des_access, &conn->pol);
200
201         if (!NT_STATUS_IS_OK(result))
202                 return NULL;
203
204         /* Add to list */
205
206         DLIST_ADD(cm_conns, conn);
207
208  ok:
209         hnd.pol = conn->pol;
210         hnd.cli = &conn->cli;
211
212         return &hnd;
213 }
214
215 /* Return a SAM policy handle on a domain */
216
217 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
218
219         DEBUG(0, ("get_sam_handle(): not implemented\n"));
220         return NULL;
221 }
222
223 /* Return a SAM domain policy handle on a domain */
224
225 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain)
226 {
227         DEBUG(0, ("get_sam_dom_handle(): not implemented\n"));
228         return NULL;
229 }
230
231 /* Return a SAM policy handle on a domain user */
232
233 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, char *user)
234 {
235         DEBUG(0, ("get_sam_user_handle(): not implemented\n"));
236         return NULL;
237 }
238
239 /* Return a SAM policy handle on a domain group */
240
241 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, char *group)
242 {
243         DEBUG(0, ("get_sam_group_handle(): not implemented\n"));
244         return NULL;
245 }