Don't force winbind to use non-local DC's.
[tprouty/samba.git] / source / 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    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.  We actually cache policy handles - tng
42    caches connections to pipes.
43
44    The TNG design is quite good but I disagree with some aspects of the
45    implementation. -tpot
46
47  */
48
49 /*
50    TODO:
51
52      - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
53        moved down into another function.
54
55      - There needs to be a utility function in libsmb/namequery.c that does
56        cm_get_dc_name() 
57
58      - When closing down sam handles we need to close down user, group and
59        domain handles.
60
61      - Take care when destroying cli_structs as they can be shared between
62        various sam handles.
63
64  */
65
66 #include "winbindd.h"
67
68 /* We store lists of connections here */
69
70 enum sam_pipe_type {
71         SAM_PIPE_BASIC,         /* A basic handle */
72         SAM_PIPE_DOM,           /* A domain handle */
73         SAM_PIPE_USER,          /* A handle on a user */
74         SAM_PIPE_GROUP          /* A handle on a group */
75 };
76
77 struct winbindd_cm_conn {
78         struct winbindd_cm_conn *prev, *next;
79         fstring domain;
80         fstring controller;
81         fstring pipe_name;
82         struct cli_state *cli;
83         POLICY_HND pol;
84
85         /* Specific pipe stuff - move into a union? */
86
87         enum sam_pipe_type sam_pipe_type; /* Domain, user, group etc  */
88         uint32 user_rid, group_rid;
89 };
90
91 /* Global list of connections.  Initially a DLIST but can become a hash
92    table or whatever later. */
93
94 struct winbindd_cm_conn *cm_conns = NULL;
95
96 /* Get a domain controller name */
97
98 BOOL cm_get_dc_name(char *domain, fstring srv_name)
99 {
100         struct in_addr *ip_list, dc_ip;
101         extern pstring global_myname;
102         int count, i;
103
104         /* Lookup domain controller name */
105                 
106         if (!get_dc_list(False, domain, &ip_list, &count))
107                 return False;
108                 
109         /* Firstly choose a PDC/BDC who has the same network address as any
110            of our interfaces. */
111         
112         for (i = 0; i < count; i++) {
113                 if(is_local_net(ip_list[i]))
114                         goto got_ip;
115         }
116         
117         i = (sys_random() % count);
118         
119  got_ip:
120         dc_ip = ip_list[i];
121         SAFE_FREE(ip_list);
122                 
123         if (!lookup_pdc_name(global_myname, domain, &dc_ip, srv_name))
124                 return False;
125
126         return True;
127 }
128
129 /* Open a new smb pipe connection to a DC on a given domain */
130
131 static BOOL cm_open_connection(char *domain, char *pipe_name,
132                                struct winbindd_cm_conn *new_conn)
133 {
134         struct nmb_name calling, called;
135         extern pstring global_myname;
136         fstring dest_host;
137         struct in_addr dest_ip;
138         BOOL result = False;
139         struct ntuser_creds creds;
140
141         fstrcpy(new_conn->domain, domain);
142         fstrcpy(new_conn->pipe_name, pipe_name);
143         
144         /* Look for a domain controller for this domain */
145
146         if (!cm_get_dc_name(domain, new_conn->controller))
147                 goto done;
148
149         /* Initialise SMB connection */
150
151         if (!(new_conn->cli = cli_initialise(NULL)))
152                 goto done;
153
154         if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
155                 goto done;
156
157         make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 
158                       0x20);
159         make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
160
161         ZERO_STRUCT(creds);
162         creds.pwd.null_pwd = 1;
163
164         cli_init_creds(new_conn->cli, &creds);
165
166         if (!cli_establish_connection(new_conn->cli, new_conn->controller, 
167                                       &dest_ip, &calling, &called, "IPC$", 
168                                       "IPC", False, True))
169                 goto done;
170
171         if (!cli_nt_session_open (new_conn->cli, pipe_name))
172                 goto done;
173
174         result = True;
175
176  done:
177         if (!result && new_conn->cli)
178                 cli_shutdown(new_conn->cli);
179
180         return result;
181 }
182
183 /* Return true if a connection is still alive */
184
185 static BOOL connection_ok(struct winbindd_cm_conn *conn)
186 {
187         if (!conn->cli->initialised)
188                 return False;
189
190         if (!conn->cli->fd == -1)
191                 return False;
192         
193         return True;
194 }
195
196 /* Return a LSA policy handle on a domain */
197
198 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
199 {
200         struct winbindd_cm_conn *conn;
201         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
202         NTSTATUS result;
203         static CLI_POLICY_HND hnd;
204
205         /* Look for existing connections */
206
207         for (conn = cm_conns; conn; conn = conn->next) {
208                 if (strequal(conn->domain, domain) &&
209                     strequal(conn->pipe_name, PIPE_LSARPC)) {
210
211                         if (!connection_ok(conn))
212                                 return NULL;
213
214                         goto ok;
215                 }
216         }
217
218         /* Create a new one */
219
220         if (!(conn = (struct winbindd_cm_conn *)
221               malloc(sizeof(struct winbindd_cm_conn))))
222                 return NULL;
223
224         ZERO_STRUCTP(conn);
225
226         if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
227                 DEBUG(3, ("Could not connect to a dc for domain %s\n",
228                           domain));
229                 return NULL;
230         }
231
232         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
233                                      des_access, &conn->pol);
234
235         if (!NT_STATUS_IS_OK(result))
236                 return NULL;
237
238         /* Add to list */
239
240         DLIST_ADD(cm_conns, conn);
241
242  ok:
243         hnd.pol = conn->pol;
244         hnd.cli = conn->cli;
245
246         return &hnd;
247 }
248
249 /* Return a SAM policy handle on a domain */
250
251 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
252
253         struct winbindd_cm_conn *conn;
254         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
255         NTSTATUS result;
256         static CLI_POLICY_HND hnd;
257
258         /* Look for existing connections */
259
260         for (conn = cm_conns; conn; conn = conn->next) {
261                 if (strequal(conn->domain, domain) &&
262                     strequal(conn->pipe_name, PIPE_SAMR) &&
263                     conn->sam_pipe_type == SAM_PIPE_BASIC) {
264
265                         if (!connection_ok(conn))
266                                 return NULL;
267
268                         goto ok;
269                 }
270         }
271
272         /* Create a new one */
273
274         if (!(conn = (struct winbindd_cm_conn *)
275               malloc(sizeof(struct winbindd_cm_conn))))
276                 return NULL;
277
278         ZERO_STRUCTP(conn);
279
280         if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
281                 DEBUG(3, ("Could not connect to a dc for domain %s\n",
282                           domain));
283                 return NULL;
284         }
285
286         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx, des_access, 
287                                   &conn->pol);
288
289         if (!NT_STATUS_IS_OK(result))
290                 return NULL;
291
292         /* Add to list */
293
294         DLIST_ADD(cm_conns, conn);
295
296  ok:
297         hnd.pol = conn->pol;
298         hnd.cli = conn->cli;
299
300         return &hnd;        
301 }
302
303 /* Return a SAM domain policy handle on a domain */
304
305 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
306 {
307         struct winbindd_cm_conn *conn, *basic_conn = NULL;
308         static CLI_POLICY_HND hnd;
309         NTSTATUS result;
310         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
311
312         /* Look for existing connections */
313
314         for (conn = cm_conns; conn; conn = conn->next) {
315                 if (strequal(conn->domain, domain) &&
316                     strequal(conn->pipe_name, PIPE_SAMR) &&
317                     conn->sam_pipe_type == SAM_PIPE_DOM) {
318
319                         if (!connection_ok(conn))
320                                 return NULL;
321
322                         goto ok;
323                 }
324         }
325
326         /* Create a basic handle to open a domain handle from */
327
328         if (!cm_get_sam_handle(domain))
329                 return False;
330
331         for (conn = cm_conns; conn; conn = conn->next) {
332                 if (strequal(conn->domain, domain) &&
333                     strequal(conn->pipe_name, PIPE_SAMR) &&
334                     conn->sam_pipe_type == SAM_PIPE_BASIC)
335                         basic_conn = conn;
336         }
337         
338         if (!(conn = (struct winbindd_cm_conn *)
339               malloc(sizeof(struct winbindd_cm_conn))))
340                 return NULL;
341         
342         ZERO_STRUCTP(conn);
343
344         fstrcpy(conn->domain, basic_conn->domain);
345         fstrcpy(conn->controller, basic_conn->controller);
346         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
347
348         conn->sam_pipe_type = SAM_PIPE_DOM;
349         conn->cli = basic_conn->cli;
350
351         result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
352                                       &basic_conn->pol, des_access, 
353                                       domain_sid, &conn->pol);
354
355         if (!NT_STATUS_IS_OK(result))
356                 return NULL;
357
358         /* Add to list */
359
360         DLIST_ADD(cm_conns, conn);
361
362  ok:
363         hnd.pol = conn->pol;
364         hnd.cli = conn->cli;
365
366         return &hnd;
367 }
368
369 /* Return a SAM policy handle on a domain user */
370
371 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
372                                        uint32 user_rid)
373 {
374         struct winbindd_cm_conn *conn, *basic_conn = NULL;
375         static CLI_POLICY_HND hnd;
376         NTSTATUS result;
377         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
378
379         /* Look for existing connections */
380
381         for (conn = cm_conns; conn; conn = conn->next) {
382                 if (strequal(conn->domain, domain) &&
383                     strequal(conn->pipe_name, PIPE_SAMR) &&
384                     conn->sam_pipe_type == SAM_PIPE_USER &&
385                     conn->user_rid == user_rid) {
386
387                         if (!connection_ok(conn))
388                                 return NULL;
389                 
390                         goto ok;
391                 }
392         }
393
394         /* Create a domain handle to open a user handle from */
395
396         if (!cm_get_sam_dom_handle(domain, domain_sid))
397                 return NULL;
398
399         for (conn = cm_conns; conn; conn = conn->next) {
400                 if (strequal(conn->domain, domain) &&
401                     strequal(conn->pipe_name, PIPE_SAMR) &&
402                     conn->sam_pipe_type == SAM_PIPE_DOM)
403                         basic_conn = conn;
404         }
405         
406         if (!basic_conn) {
407                 DEBUG(0, ("No domain sam handle was created!\n"));
408                 return NULL;
409         }
410
411         if (!(conn = (struct winbindd_cm_conn *)
412               malloc(sizeof(struct winbindd_cm_conn))))
413                 return NULL;
414         
415         ZERO_STRUCTP(conn);
416
417         fstrcpy(conn->domain, basic_conn->domain);
418         fstrcpy(conn->controller, basic_conn->controller);
419         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
420         
421         conn->sam_pipe_type = SAM_PIPE_USER;
422         conn->cli = basic_conn->cli;
423         conn->user_rid = user_rid;
424
425         result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
426                                     &basic_conn->pol, des_access, user_rid,
427                                     &conn->pol);
428
429         if (!NT_STATUS_IS_OK(result))
430                 return NULL;
431
432         /* Add to list */
433
434         DLIST_ADD(cm_conns, conn);
435
436  ok:
437         hnd.pol = conn->pol;
438         hnd.cli = conn->cli;
439
440         return &hnd;
441 }
442
443 /* Return a SAM policy handle on a domain group */
444
445 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
446                                         uint32 group_rid)
447 {
448         struct winbindd_cm_conn *conn, *basic_conn = NULL;
449         static CLI_POLICY_HND hnd;
450         NTSTATUS result;
451         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
452
453         /* Look for existing connections */
454
455         for (conn = cm_conns; conn; conn = conn->next) {
456                 if (strequal(conn->domain, domain) &&
457                     strequal(conn->pipe_name, PIPE_SAMR) &&
458                     conn->sam_pipe_type == SAM_PIPE_GROUP &&
459                     conn->group_rid == group_rid) {
460
461                         if (!connection_ok(conn))
462                                 return NULL;
463                 
464                         goto ok;
465                 }
466         }
467
468         /* Create a domain handle to open a user handle from */
469
470         if (!cm_get_sam_dom_handle(domain, domain_sid))
471                 return NULL;
472
473         for (conn = cm_conns; conn; conn = conn->next) {
474                 if (strequal(conn->domain, domain) &&
475                     strequal(conn->pipe_name, PIPE_SAMR) &&
476                     conn->sam_pipe_type == SAM_PIPE_DOM)
477                         basic_conn = conn;
478         }
479         
480         if (!basic_conn) {
481                 DEBUG(0, ("No domain sam handle was created!\n"));
482                 return NULL;
483         }
484
485         if (!(conn = (struct winbindd_cm_conn *)
486               malloc(sizeof(struct winbindd_cm_conn))))
487                 return NULL;
488         
489         ZERO_STRUCTP(conn);
490
491         fstrcpy(conn->domain, basic_conn->domain);
492         fstrcpy(conn->controller, basic_conn->controller);
493         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
494         
495         conn->sam_pipe_type = SAM_PIPE_GROUP;
496         conn->cli = basic_conn->cli;
497         conn->group_rid = group_rid;
498
499         result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
500                                     &basic_conn->pol, des_access, group_rid,
501                                     &conn->pol);
502
503         if (!NT_STATUS_IS_OK(result))
504                 return NULL;
505
506         /* Add to list */
507
508         DLIST_ADD(cm_conns, conn);
509
510  ok:
511         hnd.pol = conn->pol;
512         hnd.cli = conn->cli;
513
514         return &hnd;
515 }