fixes (asprintf) from 2.2
[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.
42
43    The TNG design is quite good but I disagree with some aspects of the
44    implementation. -tpot
45
46  */
47
48 /*
49    TODO:
50
51      - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
52        moved down into another function.
53
54      - There needs to be a utility function in libsmb/namequery.c that does
55        cm_get_dc_name() 
56
57      - Take care when destroying cli_structs as they can be shared between
58        various sam handles.
59
60  */
61
62 #include "winbindd.h"
63
64 /* Global list of connections.  Initially a DLIST but can become a hash
65    table or whatever later. */
66
67 struct winbindd_cm_conn {
68         struct winbindd_cm_conn *prev, *next;
69         fstring domain;
70         fstring controller;
71         fstring pipe_name;
72         struct cli_state *cli;
73         POLICY_HND pol;
74 };
75
76 static struct winbindd_cm_conn *cm_conns = NULL;
77
78 /* Get a domain controller name.  Cache positive and negative lookups so we
79    don't go to the network too often when something is badly broken. */
80
81 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
82
83 struct get_dc_name_cache {
84         fstring domain_name;
85         fstring srv_name;
86         time_t lookup_time;
87         struct get_dc_name_cache *prev, *next;
88 };
89
90 static BOOL cm_get_dc_name(char *domain, fstring srv_name)
91 {
92         static struct get_dc_name_cache *get_dc_name_cache;
93         struct get_dc_name_cache *dcc;
94         struct in_addr *ip_list, dc_ip;
95         int count, i;
96
97         /* Check the cache for previous lookups */
98
99         for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
100
101                 if (!strequal(domain, dcc->domain_name))
102                         continue; /* Not our domain */
103
104                 if ((time(NULL) - dcc->lookup_time) > 
105                     GET_DC_NAME_CACHE_TIMEOUT) {
106
107                         /* Cache entry has expired, delete it */
108
109                         DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
110
111                         DLIST_REMOVE(get_dc_name_cache, dcc);
112                         SAFE_FREE(dcc);
113
114                         break;
115                 }
116
117                 /* Return a positive or negative lookup for this domain */
118
119                 if (dcc->srv_name[0]) {
120                         DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
121                         fstrcpy(srv_name, dcc->srv_name);
122                         return True;
123                 } else {
124                         DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
125                         return False;
126                 }
127         }
128
129         /* Add cache entry for this lookup. */
130
131         DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
132
133         if (!(dcc = (struct get_dc_name_cache *) 
134               malloc(sizeof(struct get_dc_name_cache))))
135                 return False;
136
137         ZERO_STRUCTP(dcc);
138
139         fstrcpy(dcc->domain_name, domain);
140         dcc->lookup_time = time(NULL);
141
142         DLIST_ADD(get_dc_name_cache, dcc);
143
144         /* Lookup domain controller name */
145                 
146         if (!get_dc_list(False, domain, &ip_list, &count)) {
147                 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
148                 return False;
149         }
150                 
151         /* Firstly choose a PDC/BDC who has the same network address as any
152            of our interfaces. */
153         
154         for (i = 0; i < count; i++) {
155                 if(is_local_net(ip_list[i]))
156                         goto got_ip;
157         }
158
159         if (count == 0) {
160                 DEBUG(3, ("No domain controllers for domain %s\n", domain));
161                 return False;
162         }
163         
164         i = (sys_random() % count);
165         
166  got_ip:
167         dc_ip = ip_list[i];
168         SAFE_FREE(ip_list);
169                 
170         /* We really should be doing a GETDC call here rather than a node
171            status lookup. */
172
173         if (!name_status_find(domain, 0x1c, 0x20, dc_ip, srv_name)) {
174                 DEBUG(3, ("Error looking up DC name for %s in domain %s\n", inet_ntoa(dc_ip), domain));
175                 return False;
176         }
177
178         /* We have a name so make the cache entry positive now */
179
180         fstrcpy(dcc->srv_name, srv_name);
181
182         return True;
183 }
184
185 /* Choose between anonymous or authenticated connections.  We need to use
186    an authenticated connection if DCs have the RestrictAnonymous registry
187    entry set > 0, or the "Additional restrictions for anonymous
188    connections" set in the win2k Local Security Policy. */
189
190 void cm_init_creds(struct ntuser_creds *creds)
191 {
192         char *username, *password;
193
194         ZERO_STRUCTP(creds);
195
196         creds->pwd.null_pwd = True; /* anonymoose */
197
198         username = secrets_fetch(SECRETS_AUTH_USER, NULL);
199         password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
200
201         if (username && *username) {
202                 pwd_set_cleartext(&creds->pwd, password);
203
204                 fstrcpy(creds->user_name, username);
205                 fstrcpy(creds->domain, lp_workgroup());
206
207                 DEBUG(3, ("IPC$ connections done %s\\%s\n", creds->domain,
208                           creds->user_name));
209         } else 
210                 DEBUG(3, ("IPC$ connections done anonymously\n"));
211 }
212
213 /* Open a new smb pipe connection to a DC on a given domain.  Cache
214    negative creation attempts so we don't try and connect to broken
215    machines too often. */
216
217 #define OPEN_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
218
219 struct open_connection_cache {
220         fstring domain_name;
221         fstring controller;
222         time_t lookup_time;
223         struct open_connection_cache *prev, *next;
224 };
225
226 static BOOL cm_open_connection(char *domain, char *pipe_name,
227                                struct winbindd_cm_conn *new_conn)
228 {
229         static struct open_connection_cache *open_connection_cache;
230         struct open_connection_cache *occ;
231         struct nmb_name calling, called;
232         extern pstring global_myname;
233         fstring dest_host;
234         struct in_addr dest_ip;
235         BOOL result = False;
236         struct ntuser_creds creds;
237
238         fstrcpy(new_conn->domain, domain);
239         fstrcpy(new_conn->pipe_name, pipe_name);
240         
241         /* Look for a domain controller for this domain.  Negative results
242                 are cached so don't bother applying the caching for this
243                 function just yet.  */
244
245         if (!cm_get_dc_name(domain, new_conn->controller))
246                 goto done;
247
248         /* Return false if we have tried to look up this domain and netbios
249                 name before and failed. */
250
251         for (occ = open_connection_cache; occ; occ = occ->next) {
252                 
253                 if (!(strequal(domain, occ->domain_name) &&
254                       strequal(new_conn->controller, occ->controller)))
255                         continue; /* Not our domain */
256
257                 if ((time(NULL) - occ->lookup_time) > 
258                     OPEN_CONNECTION_CACHE_TIMEOUT) {
259
260                         /* Cache entry has expired, delete it */
261
262                         DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
263
264                         DLIST_REMOVE(open_connection_cache, occ);
265                         free(occ);
266
267                         break;
268                 }
269
270                 /* The timeout hasn't expired yet so return false */
271
272                 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
273
274                 goto done;
275         }
276
277         /* Initialise SMB connection */
278
279         if (!(new_conn->cli = cli_initialise(NULL)))
280                 goto done;
281
282         if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
283                 goto done;
284
285         make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 0x20);
286         make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
287
288         cm_init_creds(&creds);
289
290         cli_init_creds(new_conn->cli, &creds);
291
292         if (!cli_establish_connection(new_conn->cli, new_conn->controller, 
293                                       &dest_ip, &calling, &called, "IPC$", 
294                                       "IPC", False, True))
295                 goto done;
296
297         if (!cli_nt_session_open (new_conn->cli, pipe_name))
298                 goto done;
299
300         result = True;
301
302  done:
303
304         /* Create negative lookup cache entry for this domain and controller */
305
306         if (!result) {
307                 if (!(occ = (struct open_connection_cache *)
308                       malloc(sizeof(struct open_connection_cache))))
309                         return False;
310
311                 ZERO_STRUCTP(occ);
312
313                 fstrcpy(occ->domain_name, domain);
314                 fstrcpy(occ->controller, new_conn->controller);
315                 occ->lookup_time = time(NULL);
316                 
317                 DLIST_ADD(open_connection_cache, occ);
318         }
319
320         if (!result && new_conn->cli)
321                 cli_shutdown(new_conn->cli);
322
323         return result;
324 }
325
326 /* Return true if a connection is still alive */
327
328 static BOOL connection_ok(struct winbindd_cm_conn *conn)
329 {
330         if (!conn->cli->initialised)
331                 return False;
332
333         if (conn->cli->fd == -1)
334                 return False;
335         
336         return True;
337 }
338
339 /* Return a LSA policy handle on a domain */
340
341 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
342 {
343         struct winbindd_cm_conn *conn;
344         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
345         NTSTATUS result;
346         static CLI_POLICY_HND hnd;
347
348         /* Look for existing connections */
349
350         for (conn = cm_conns; conn; conn = conn->next) {
351                 if (strequal(conn->domain, domain) && 
352                     strequal(conn->pipe_name, PIPE_LSARPC)) {
353
354                         if (!connection_ok(conn)) {
355                                 DLIST_REMOVE(cm_conns, conn);
356                                 return NULL;
357                         }
358
359                         goto ok;
360                 }
361         }
362
363         /* Create a new one */
364
365         if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
366                 return NULL;
367
368         ZERO_STRUCTP(conn);
369
370         if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
371                 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
372                 return NULL;
373         }
374
375         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
376                                      des_access, &conn->pol);
377
378         if (!NT_STATUS_IS_OK(result))
379                 return NULL;
380
381         /* Add to list */
382
383         DLIST_ADD(cm_conns, conn);
384
385  ok:
386         hnd.pol = conn->pol;
387         hnd.cli = conn->cli;
388
389         return &hnd;
390 }
391
392 /* Return a SAM policy handle on a domain */
393
394 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
395
396         struct winbindd_cm_conn *conn;
397         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
398         NTSTATUS result;
399         static CLI_POLICY_HND hnd;
400
401         /* Look for existing connections */
402
403         for (conn = cm_conns; conn; conn = conn->next) {
404                 if (strequal(conn->domain, domain) && strequal(conn->pipe_name, PIPE_SAMR)) {
405
406                         if (!connection_ok(conn)) {
407                                 DLIST_REMOVE(cm_conns, conn);
408                                 return NULL;
409                         }
410
411                         goto ok;
412                 }
413         }
414
415         /* Create a new one */
416
417         if (!(conn = (struct winbindd_cm_conn *) 
418               malloc(sizeof(struct winbindd_cm_conn))))
419                 return NULL;
420
421         ZERO_STRUCTP(conn);
422
423         if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
424                 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
425                 return NULL;
426         }
427
428         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
429                                   des_access, &conn->pol);
430
431         if (!NT_STATUS_IS_OK(result))
432                 return NULL;
433
434         /* Add to list */
435
436         DLIST_ADD(cm_conns, conn);
437
438  ok:
439         hnd.pol = conn->pol;
440         hnd.cli = conn->cli;
441
442         return &hnd;        
443 }
444
445 #if 0
446
447 /* Return a SAM domain policy handle on a domain */
448
449 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
450 {
451         struct winbindd_cm_conn *conn, *basic_conn = NULL;
452         static CLI_POLICY_HND hnd;
453         NTSTATUS result;
454         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
455
456         /* Look for existing connections */
457
458         for (conn = cm_conns; conn; conn = conn->next) {
459                 if (strequal(conn->domain, domain) &&
460                     strequal(conn->pipe_name, PIPE_SAMR) &&
461                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
462
463                         if (!connection_ok(conn)) {
464                                 DLIST_REMOVE(cm_conns, conn);
465                                 return NULL;
466                         }
467
468                         goto ok;
469                 }
470         }
471
472         /* Create a basic handle to open a domain handle from */
473
474         if (!cm_get_sam_handle(domain))
475                 return False;
476
477         for (conn = cm_conns; conn; conn = conn->next) {
478                 if (strequal(conn->domain, domain) &&
479                     strequal(conn->pipe_name, PIPE_SAMR) &&
480                     conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
481                         basic_conn = conn;
482         }
483         
484         if (!(conn = (struct winbindd_cm_conn *)
485               malloc(sizeof(struct winbindd_cm_conn))))
486                 return NULL;
487         
488         ZERO_STRUCTP(conn);
489
490         fstrcpy(conn->domain, basic_conn->domain);
491         fstrcpy(conn->controller, basic_conn->controller);
492         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
493
494         conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
495         conn->cli = basic_conn->cli;
496
497         result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
498                                       &basic_conn->pol, des_access, 
499                                       domain_sid, &conn->pol);
500
501         if (!NT_STATUS_IS_OK(result))
502                 return NULL;
503
504         /* Add to list */
505
506         DLIST_ADD(cm_conns, conn);
507
508  ok:
509         hnd.pol = conn->pol;
510         hnd.cli = conn->cli;
511
512         return &hnd;
513 }
514
515 /* Return a SAM policy handle on a domain user */
516
517 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
518                                        uint32 user_rid)
519 {
520         struct winbindd_cm_conn *conn, *basic_conn = NULL;
521         static CLI_POLICY_HND hnd;
522         NTSTATUS result;
523         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
524
525         /* Look for existing connections */
526
527         for (conn = cm_conns; conn; conn = conn->next) {
528                 if (strequal(conn->domain, domain) &&
529                     strequal(conn->pipe_name, PIPE_SAMR) &&
530                     conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
531                     conn->pipe_data.samr.rid == user_rid) {
532
533                         if (!connection_ok(conn)) {
534                                 DLIST_REMOVE(cm_conns, conn);
535                                 return NULL;
536                         }
537                 
538                         goto ok;
539                 }
540         }
541
542         /* Create a domain handle to open a user handle from */
543
544         if (!cm_get_sam_dom_handle(domain, domain_sid))
545                 return NULL;
546
547         for (conn = cm_conns; conn; conn = conn->next) {
548                 if (strequal(conn->domain, domain) &&
549                     strequal(conn->pipe_name, PIPE_SAMR) &&
550                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
551                         basic_conn = conn;
552         }
553         
554         if (!basic_conn) {
555                 DEBUG(0, ("No domain sam handle was created!\n"));
556                 return NULL;
557         }
558
559         if (!(conn = (struct winbindd_cm_conn *)
560               malloc(sizeof(struct winbindd_cm_conn))))
561                 return NULL;
562         
563         ZERO_STRUCTP(conn);
564
565         fstrcpy(conn->domain, basic_conn->domain);
566         fstrcpy(conn->controller, basic_conn->controller);
567         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
568         
569         conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
570         conn->cli = basic_conn->cli;
571         conn->pipe_data.samr.rid = user_rid;
572
573         result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
574                                     &basic_conn->pol, des_access, user_rid,
575                                     &conn->pol);
576
577         if (!NT_STATUS_IS_OK(result))
578                 return NULL;
579
580         /* Add to list */
581
582         DLIST_ADD(cm_conns, conn);
583
584  ok:
585         hnd.pol = conn->pol;
586         hnd.cli = conn->cli;
587
588         return &hnd;
589 }
590
591 /* Return a SAM policy handle on a domain group */
592
593 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
594                                         uint32 group_rid)
595 {
596         struct winbindd_cm_conn *conn, *basic_conn = NULL;
597         static CLI_POLICY_HND hnd;
598         NTSTATUS result;
599         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
600
601         /* Look for existing connections */
602
603         for (conn = cm_conns; conn; conn = conn->next) {
604                 if (strequal(conn->domain, domain) &&
605                     strequal(conn->pipe_name, PIPE_SAMR) &&
606                     conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
607                     conn->pipe_data.samr.rid == group_rid) {
608
609                         if (!connection_ok(conn)) {
610                                 DLIST_REMOVE(cm_conns, conn);
611                                 return NULL;
612                         }
613                 
614                         goto ok;
615                 }
616         }
617
618         /* Create a domain handle to open a user handle from */
619
620         if (!cm_get_sam_dom_handle(domain, domain_sid))
621                 return NULL;
622
623         for (conn = cm_conns; conn; conn = conn->next) {
624                 if (strequal(conn->domain, domain) &&
625                     strequal(conn->pipe_name, PIPE_SAMR) &&
626                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
627                         basic_conn = conn;
628         }
629         
630         if (!basic_conn) {
631                 DEBUG(0, ("No domain sam handle was created!\n"));
632                 return NULL;
633         }
634
635         if (!(conn = (struct winbindd_cm_conn *)
636               malloc(sizeof(struct winbindd_cm_conn))))
637                 return NULL;
638         
639         ZERO_STRUCTP(conn);
640
641         fstrcpy(conn->domain, basic_conn->domain);
642         fstrcpy(conn->controller, basic_conn->controller);
643         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
644         
645         conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
646         conn->cli = basic_conn->cli;
647         conn->pipe_data.samr.rid = group_rid;
648
649         result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
650                                     &basic_conn->pol, des_access, group_rid,
651                                     &conn->pol);
652
653         if (!NT_STATUS_IS_OK(result))
654                 return NULL;
655
656         /* Add to list */
657
658         DLIST_ADD(cm_conns, conn);
659
660  ok:
661         hnd.pol = conn->pol;
662         hnd.cli = conn->cli;
663
664         return &hnd;
665 }
666
667 #endif
668
669 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
670    netlogon pipe as no handle is returned. */
671
672 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
673                              struct cli_state **cli)
674 {
675         struct winbindd_cm_conn conn;
676         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
677
678         /* Open an initial conection */
679
680         ZERO_STRUCT(conn);
681
682         if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
683                 DEBUG(3, ("Could not open a connection to %s\n", domain));
684                 return result;
685         }
686
687         result = new_cli_nt_setup_creds(conn.cli, trust_passwd);
688
689         if (!NT_STATUS_IS_OK(result)) {
690                 DEBUG(0, ("error connecting to domain password server: %s\n",
691                         get_nt_error_msg(result)));
692                         cli_shutdown(conn.cli);
693                         return result;
694         }
695
696         if (cli)
697                 *cli = conn.cli;
698
699         return result;
700 }
701
702 /* Dump the current connection status */
703
704 static void dump_conn_list(void)
705 {
706         struct winbindd_cm_conn *con;
707
708         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
709
710         for(con = cm_conns; con; con = con->next) {
711                 char *msg;
712
713                 /* Display pipe info */
714                 
715                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
716                         DEBUG(0, ("Error: not enough memory!\n"));
717                 } else {
718                         DEBUG(0, ("%s\n", msg));
719                         SAFE_FREE(msg);
720                 }
721         }
722 }
723
724 void winbindd_cm_status(void)
725 {
726         /* List open connections */
727
728         DEBUG(0, ("winbindd connection manager status:\n"));
729
730         if (cm_conns)
731                 dump_conn_list();
732         else
733                 DEBUG(0, ("\tNo active connections\n"));
734 }