Fix immediate bug where the idmap can't tell the difference between an entry
[ira/wip.git] / source3 / nsswitch / winbindd_cm.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon connection manager
5
6    Copyright (C) Tim Potter 2001
7    Copyright (C) Andrew Bartlett 2002
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      - Take care when destroying cli_structs as they can be shared between
55        various sam handles.
56
57  */
58
59 #include "winbindd.h"
60
61 #undef DBGC_CLASS
62 #define DBGC_CLASS DBGC_WINBIND
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         size_t mutex_ref_count;
73         struct cli_state *cli;
74         POLICY_HND pol;
75 };
76
77 static struct winbindd_cm_conn *cm_conns = NULL;
78
79 /*
80   find the DC for a domain using methods appropriate for a ADS domain
81 */
82 static BOOL ads_dc_name(const char *domain, struct in_addr *dc_ip, fstring srv_name)
83 {
84         ADS_STRUCT *ads;
85         const char *realm = domain;
86
87         if (strcasecmp(realm, lp_workgroup()) == 0)
88                 realm = lp_realm();
89
90         ads = ads_init(realm, domain, NULL);
91         if (!ads)
92                 return False;
93
94         /* we don't need to bind, just connect */
95         ads->auth.flags |= ADS_AUTH_NO_BIND;
96
97         DEBUG(4,("ads_dc_name: domain=%s\n", domain));
98
99 #ifdef HAVE_ADS
100         /* a full ads_connect() is actually overkill, as we don't srictly need
101            to do the SASL auth in order to get the info we need, but libads
102            doesn't offer a better way right now */
103         ads_connect(ads);
104 #endif
105
106         if (!ads->config.realm)
107                 return False;
108
109         fstrcpy(srv_name, ads->config.ldap_server_name);
110         strupper(srv_name);
111         *dc_ip = ads->ldap_ip;
112         ads_destroy(&ads);
113         
114         DEBUG(4,("ads_dc_name: using server='%s' IP=%s\n",
115                  srv_name, inet_ntoa(*dc_ip)));
116         
117         return True;
118 }
119
120 /**********************************************************************
121  wrapper around ads and rpc methods of finds DC's
122 **********************************************************************/
123
124 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, 
125                            struct in_addr *ip_out)
126 {
127         struct in_addr dc_ip;
128         BOOL ret;
129
130         zero_ip(&dc_ip);
131
132         ret = False;
133         if (lp_security() == SEC_ADS)
134                 ret = ads_dc_name(domain, &dc_ip, srv_name);
135
136         if (!ret) {
137                 /* fall back on rpc methods if the ADS methods fail */
138                 ret = rpc_dc_name(domain, srv_name, &dc_ip);
139         }
140
141         *ip_out = dc_ip;
142
143         return ret;
144 }
145
146 /* Choose between anonymous or authenticated connections.  We need to use
147    an authenticated connection if DCs have the RestrictAnonymous registry
148    entry set > 0, or the "Additional restrictions for anonymous
149    connections" set in the win2k Local Security Policy. 
150    
151    Caller to free() result in domain, username, password
152 */
153
154 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
155 {
156         *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
157         *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
158         *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
159         
160         if (*username && **username) {
161
162                 if (!*domain || !**domain)
163                         *domain = smb_xstrdup(lp_workgroup());
164                 
165                 if (!*password || !**password)
166                         *password = smb_xstrdup("");
167
168                 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", 
169                           *domain, *username));
170
171         } else {
172                 DEBUG(3, ("IPC$ connections done anonymously\n"));
173                 *username = smb_xstrdup("");
174                 *domain = smb_xstrdup("");
175                 *password = smb_xstrdup("");
176         }
177 }
178
179 /* Open a connction to the remote server, cache failures for 30 seconds */
180
181 static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
182                                struct winbindd_cm_conn *new_conn)
183 {
184         NTSTATUS result;
185         char *ipc_username, *ipc_domain, *ipc_password;
186         struct in_addr dc_ip;
187         int i;
188         BOOL retry = True;
189
190         ZERO_STRUCT(dc_ip);
191
192         fstrcpy(new_conn->domain, domain);
193         fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
194         
195         /* connection failure cache has been moved inside of rpc_dc_name
196            so we can deal with half dead DC's   --jerry */
197
198         if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
199                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
200                 add_failed_connection_entry(domain, "", result);
201                 return result;
202         }
203                 
204         /* Initialise SMB connection */
205
206         cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
207
208         DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
209               new_conn->controller, global_myname(), ipc_domain, ipc_username));
210
211         for (i = 0; retry && (i < 3); i++) {
212                 BOOL got_mutex;
213                 if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
214                         DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
215                         result = NT_STATUS_POSSIBLE_DEADLOCK;
216                         continue;
217                 }
218                 
219                 result = cli_full_connection(&new_conn->cli, global_myname(), new_conn->controller, 
220                                              &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain, 
221                                              ipc_password, CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, &retry);
222                 
223                 secrets_named_mutex_release(new_conn->controller);
224
225                 if (NT_STATUS_IS_OK(result))
226                         break;
227         }
228
229         SAFE_FREE(ipc_username);
230         SAFE_FREE(ipc_domain);
231         SAFE_FREE(ipc_password);
232
233         if (!NT_STATUS_IS_OK(result)) {
234                 add_failed_connection_entry(domain, new_conn->controller, result);
235                 return result;
236         }
237         
238         if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
239                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
240                 /* 
241                  * only cache a failure if we are not trying to open the 
242                  * **win2k** specific lsarpc UUID.  This could be an NT PDC 
243                  * and therefore a failure is normal.  This should probably
244                  * be abstracted to a check for 2k specific pipes and wondering
245                  * if the PDC is an NT4 box.   but since there is only one 2k 
246                  * specific UUID right now, i'm not going to bother.  --jerry
247                  */
248                 if ( !is_win2k_pipe(pipe_index) )
249                         add_failed_connection_entry(domain, new_conn->controller, result);
250                 cli_shutdown(new_conn->cli);
251                 return result;
252         }
253
254         return NT_STATUS_OK;
255 }
256
257 /* Return true if a connection is still alive */
258
259 static BOOL connection_ok(struct winbindd_cm_conn *conn)
260 {
261         if (!conn) {
262                 smb_panic("Invalid parameter passed to connection_ok():  conn was NULL!\n");
263                 return False;
264         }
265
266         if (!conn->cli) {
267                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
268                           conn->controller, conn->domain, conn->pipe_name));
269                 return False;
270         }
271
272         if (!conn->cli->initialised) {
273                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
274                           conn->controller, conn->domain, conn->pipe_name));
275                 return False;
276         }
277
278         if (conn->cli->fd == -1) {
279                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
280                           conn->controller, conn->domain, conn->pipe_name));
281                 return False;
282         }
283         
284         return True;
285 }
286
287 /* Search the cache for a connection. If there is a broken one,
288    shut it down properly and return NULL. */
289
290 static void find_cm_connection(const char *domain, const char *pipe_name,
291                                struct winbindd_cm_conn **conn_out) 
292 {
293         struct winbindd_cm_conn *conn;
294
295         for (conn = cm_conns; conn; ) {
296                 if (strequal(conn->domain, domain) && 
297                     strequal(conn->pipe_name, pipe_name)) {
298                         if (!connection_ok(conn)) {
299                                 /* Dead connection - remove it. */
300                                 struct winbindd_cm_conn *conn_temp = conn->next;
301                                 if (conn->cli)
302                                         cli_shutdown(conn->cli);
303                                 DLIST_REMOVE(cm_conns, conn);
304                                 SAFE_FREE(conn);
305                                 conn = conn_temp;  /* Keep the loop moving */
306                                 continue;
307                         } else {
308                                 break;
309                         }
310                 }
311                 conn = conn->next;
312         }
313
314         *conn_out = conn;
315 }
316
317 /* Initialize a new connection up to the RPC BIND. */
318
319 static NTSTATUS new_cm_connection(const char *domain, const char *pipe_name,
320                                   struct winbindd_cm_conn **conn_out)
321 {
322         struct winbindd_cm_conn *conn;
323         NTSTATUS result;
324
325         if (!(conn = malloc(sizeof(*conn))))
326                 return NT_STATUS_NO_MEMORY;
327                 
328         ZERO_STRUCTP(conn);
329                 
330         if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
331                 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
332                           domain, pipe_name, nt_errstr(result)));
333                 SAFE_FREE(conn);
334                 return result;
335         }
336         DLIST_ADD(cm_conns, conn);
337
338         *conn_out = conn;
339         return NT_STATUS_OK;
340 }
341
342 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
343
344 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
345                                           struct winbindd_cm_conn **conn_out)
346 {
347         find_cm_connection(domain, pipe_name, conn_out);
348
349         if (*conn_out != NULL)
350                 return NT_STATUS_OK;
351
352         return new_cm_connection(domain, pipe_name, conn_out);
353 }
354
355 /**********************************************************************************
356 **********************************************************************************/
357
358 BOOL cm_check_for_native_mode_win2k( const char *domain )
359 {
360         NTSTATUS                result;
361         struct winbindd_cm_conn conn;
362         DS_DOMINFO_CTR          ctr;
363         BOOL                    ret = False;
364         
365         ZERO_STRUCT( conn );
366         ZERO_STRUCT( ctr );
367         
368         
369         if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
370                 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n", 
371                           domain, nt_errstr(result)));
372                 return False;
373         }
374         
375         if ( conn.cli ) {
376                 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
377                                 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
378                         ret = False;
379                         goto done;
380                 }
381         }
382                                 
383         if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) 
384                         && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
385                 ret = True;
386
387 done:
388
389 #if 0
390         /*
391          * I don't think we need to shutdown here ? JRA.
392          */
393         if ( conn.cli )
394                 cli_shutdown( conn.cli );
395 #endif
396         
397         return ret;
398 }
399
400
401
402 /* Return a LSA policy handle on a domain */
403
404 NTSTATUS cm_get_lsa_handle(const char *domain, CLI_POLICY_HND **return_hnd)
405 {
406         struct winbindd_cm_conn *conn;
407         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
408         NTSTATUS result;
409         static CLI_POLICY_HND hnd;
410
411         /* Look for existing connections */
412
413         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
414                 return result;
415
416         /* This *shitty* code needs scrapping ! JRA */
417         
418         if (policy_handle_is_valid(&conn->pol)) {
419                 hnd.pol = conn->pol;
420                 hnd.cli = conn->cli;
421                 *return_hnd = &hnd;
422
423                 return NT_STATUS_OK;
424         }
425         
426         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
427                                      des_access, &conn->pol);
428
429         if (!NT_STATUS_IS_OK(result)) {
430                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
431                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
432                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
433                                 return result;
434
435                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
436                                                      des_access, &conn->pol);
437                 }
438
439                 if (!NT_STATUS_IS_OK(result)) {
440                         cli_shutdown(conn->cli);
441                         DLIST_REMOVE(cm_conns, conn);
442                         SAFE_FREE(conn);
443                         return result;
444                 }
445         }       
446
447         hnd.pol = conn->pol;
448         hnd.cli = conn->cli;
449
450         *return_hnd = &hnd;
451
452         return NT_STATUS_OK;
453 }
454
455 /* Return a SAM policy handle on a domain */
456
457 NTSTATUS cm_get_sam_handle(char *domain, CLI_POLICY_HND **return_hnd)
458
459         struct winbindd_cm_conn *conn;
460         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
461         NTSTATUS result;
462         static CLI_POLICY_HND hnd;
463
464         /* Look for existing connections */
465
466         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
467                 return result;
468         
469         /* This *shitty* code needs scrapping ! JRA */
470         
471         if (policy_handle_is_valid(&conn->pol)) {
472                 hnd.pol = conn->pol;
473                 hnd.cli = conn->cli;
474                 
475                 *return_hnd = &hnd;
476
477                 return NT_STATUS_OK;
478         }
479         
480         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
481                                   des_access, &conn->pol);
482
483         if (!NT_STATUS_IS_OK(result)) {
484                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
485                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
486                 
487                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
488                                 return result;
489
490                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
491                                                   des_access, &conn->pol);
492                 }
493
494                 if (!NT_STATUS_IS_OK(result)) {
495                 
496                         cli_shutdown(conn->cli);
497                         DLIST_REMOVE(cm_conns, conn);
498                         SAFE_FREE(conn);
499                         
500                         return result;
501                 }
502         }       
503
504         hnd.pol = conn->pol;
505         hnd.cli = conn->cli;
506
507         *return_hnd = &hnd;
508
509         return NT_STATUS_OK;
510 }
511
512 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
513    netlogon pipe as no handle is returned. */
514
515 NTSTATUS cm_get_netlogon_cli(const char *domain, 
516                              const unsigned char *trust_passwd, 
517                              uint32 sec_channel_type,
518                              BOOL fresh,
519                              struct cli_state **cli)
520 {
521         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
522         struct winbindd_cm_conn *conn;
523         fstring lock_name;
524         BOOL got_mutex;
525
526         if (!cli)
527                 return NT_STATUS_INVALID_PARAMETER;
528
529         /* Open an initial conection - keep the mutex. */
530
531         find_cm_connection(domain, PIPE_NETLOGON, &conn);
532
533         if ( fresh && (conn != NULL) ) {
534                 cli_shutdown(conn->cli);
535                 conn->cli = NULL;
536
537                 conn = NULL;
538
539                 /* purge connection from cache */
540                 find_cm_connection(domain, PIPE_NETLOGON, &conn);
541                 if (conn != NULL) {
542                         DEBUG(0,("Could not purge connection\n"));
543                         return NT_STATUS_UNSUCCESSFUL;
544                 }
545         }
546
547         if (conn != NULL) {
548                 *cli = conn->cli;
549                 return NT_STATUS_OK;
550         }
551
552         result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
553
554         if (!NT_STATUS_IS_OK(result))
555                 return result;
556         
557         snprintf(lock_name, sizeof(lock_name), "NETLOGON\\%s", conn->controller);
558
559         if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
560                 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
561         }
562                         
563         result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
564         
565         if (got_mutex)
566                 secrets_named_mutex_release(lock_name);
567                                 
568         if (!NT_STATUS_IS_OK(result)) {
569                 cli_shutdown(conn->cli);
570                 DLIST_REMOVE(cm_conns, conn);
571                 SAFE_FREE(conn);
572                 return result;
573         }
574
575         *cli = conn->cli;
576
577         return result;
578 }
579
580 /* Dump the current connection status */
581
582 static void dump_conn_list(void)
583 {
584         struct winbindd_cm_conn *con;
585
586         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
587
588         for(con = cm_conns; con; con = con->next) {
589                 char *msg;
590
591                 /* Display pipe info */
592                 
593                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
594                         DEBUG(0, ("Error: not enough memory!\n"));
595                 } else {
596                         DEBUG(0, ("%s\n", msg));
597                         SAFE_FREE(msg);
598                 }
599         }
600 }
601
602 void winbindd_cm_status(void)
603 {
604         /* List open connections */
605
606         DEBUG(0, ("winbindd connection manager status:\n"));
607
608         if (cm_conns)
609                 dump_conn_list();
610         else
611                 DEBUG(0, ("\tNo active connections\n"));
612 }
613
614 /* Close all cached connections */
615
616 void winbindd_cm_flush(void)
617 {
618         struct winbindd_cm_conn *conn, tmp;
619
620         /* Flush connection cache */
621
622         for (conn = cm_conns; conn; conn = conn->next) {
623
624                 if (!connection_ok(conn))
625                         continue;
626
627                 DEBUG(10, ("Closing connection to %s on %s\n",
628                         conn->pipe_name, conn->controller));
629
630                 if (conn->cli)
631                         cli_shutdown(conn->cli);
632
633                 tmp.next = conn->next;
634
635                 DLIST_REMOVE(cm_conns, conn);
636                 SAFE_FREE(conn);
637                 conn = &tmp;
638         }
639
640         /* Flush failed connection cache */
641
642         flush_negative_conn_cache();
643 }