move to SAFE_FREE()
[tprouty/samba.git] / source / nsswitch / winbindd_util.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4
5    Winbind daemon for ntdom nss module
6
7    Copyright (C) Tim Potter 2000
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 #include "winbindd.h"
25 #include "sids.h"
26
27 static void winbindd_kill_connections(struct winbindd_domain *domain);
28
29 /* Debug connection state */
30
31 void debug_conn_state(void)
32 {
33         struct winbindd_domain *domain;
34
35         DEBUG(3, ("server: dc=%s, pwdb_init=%d, lsa_hnd=%d\n", 
36                   server_state.controller,
37                   server_state.pwdb_initialised,
38                   server_state.lsa_handle_open));
39
40         for (domain = domain_list; domain; domain = domain->next) {
41                 DEBUG(3, ("%s: dc=%s, got_sid=%d, sam_hnd=%d sam_dom_hnd=%d\n",
42                           domain->name, domain->controller,
43                           domain->got_domain_info, domain->sam_handle_open,
44                           domain->sam_dom_handle_open));
45         }
46 }
47
48 /* Add a trusted domain to our list of domains */
49
50 static struct winbindd_domain *add_trusted_domain(char *domain_name)
51 {
52     struct winbindd_domain *domain, *tmp;
53
54     for (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
55             if (strcmp(domain_name, tmp->name) == 0) {
56                     DEBUG(3, ("domain %s already in trusted list\n",
57                               domain_name));
58                     return tmp;
59             }
60     }
61
62     DEBUG(1, ("adding trusted domain %s\n", domain_name));
63
64     /* Create new domain entry */
65
66     if ((domain = (struct winbindd_domain *)malloc(sizeof(*domain))) == NULL) {
67         return NULL;
68     }
69
70     /* Fill in fields */
71
72     ZERO_STRUCTP(domain);
73
74     if (domain_name) {
75         fstrcpy(domain->name, domain_name);
76     }
77
78     /* Link to domain list */
79
80     DLIST_ADD(domain_list, domain);
81
82     return domain;
83 }
84
85 /* Look up global info for the winbind daemon */
86
87 static BOOL get_trusted_domains(void)
88 {
89         uint32 enum_ctx = 0;
90         uint32 num_doms = 0;
91         char **domains = NULL;
92         DOM_SID *sids = NULL;
93         BOOL result;
94         int i;
95         
96         DEBUG(1, ("getting trusted domain list\n"));
97
98         /* Add our workgroup - keep handle to look up trusted domains */
99         if (!add_trusted_domain(lp_workgroup())) {
100                 DEBUG(0, ("could not add record for domain %s\n", 
101                           lp_workgroup()));
102                 return False;
103         }
104         
105         /* Enumerate list of trusted domains */ 
106         result = wb_lsa_enum_trust_dom(&server_state.lsa_handle, &enum_ctx,
107                                        &num_doms, &domains, &sids);
108         
109         if (!result || !domains) return False;
110         
111         /* Add each domain to the trusted domain list */
112         for(i = 0; i < num_doms; i++) {
113                 if (!add_trusted_domain(domains[i])) {
114                         DEBUG(0, ("could not add record for domain %s\n", 
115                                   domains[i]));
116                         result = False;
117                 }
118         }
119         
120         return True;
121 }
122
123 /* Open sam and sam domain handles */
124
125 static BOOL open_sam_handles(struct winbindd_domain *domain)
126 {
127         /* Get domain info (sid and controller name) */
128
129         if (!domain->got_domain_info) {
130                 domain->got_domain_info = get_domain_info(domain);
131                 if (!domain->got_domain_info) return False;
132         }
133
134         /* Shut down existing sam handles */
135
136         if (domain->sam_dom_handle_open) {
137                 wb_samr_close(&domain->sam_dom_handle);
138                 domain->sam_dom_handle_open = False;
139         }
140
141         if (domain->sam_handle_open) {
142                 wb_samr_close(&domain->sam_handle);
143                 domain->sam_handle_open = False;
144         }
145
146         /* Open sam handle */
147
148         domain->sam_handle_open = 
149                 wb_samr_connect(domain->controller, 
150                                 SEC_RIGHTS_MAXIMUM_ALLOWED, 
151                                 &domain->sam_handle);
152
153         if (!domain->sam_handle_open) return False;
154
155         /* Open sam domain handle */
156
157         domain->sam_dom_handle_open =
158                 wb_samr_open_domain(&domain->sam_handle, 
159                                     SEC_RIGHTS_MAXIMUM_ALLOWED, 
160                                     &domain->sid, 
161                                     &domain->sam_dom_handle);
162
163         if (!domain->sam_dom_handle_open) return False;
164         
165         return True;
166 }
167
168 static BOOL rpc_hnd_ok(CLI_POLICY_HND *hnd)
169 {
170         return hnd->cli->fd != -1;
171 }
172
173 /* Return true if the SAM domain handles are open and responding.  */
174
175 BOOL domain_handles_open(struct winbindd_domain *domain)
176 {
177         time_t t;
178         BOOL result;
179
180         /* Check we haven't checked too recently */
181
182         t = time(NULL);
183
184         if ((t - domain->last_check) < WINBINDD_ESTABLISH_LOOP) {
185                 return domain->sam_handle_open &&
186                         domain->sam_dom_handle_open;
187         }
188         
189         DEBUG(3, ("checking domain handles for domain %s\n", domain->name));
190         debug_conn_state();
191
192         domain->last_check = t;
193
194         /* Open sam handles if they are marked as closed */
195
196         if (!domain->sam_handle_open || !domain->sam_dom_handle_open) {
197         reopen:
198                 DEBUG(3, ("opening sam handles\n"));
199                 return open_sam_handles(domain);
200         }
201
202         /* Check sam handles are ok - the domain controller may have failed
203            and we need to move to a BDC. */
204
205         if (!rpc_hnd_ok(&domain->sam_handle) || 
206             !rpc_hnd_ok(&domain->sam_dom_handle)) {
207
208                 /* We want to close the current connection but attempt
209                    to open a new set, possibly to a new dc.  If this
210                    doesn't work then return False as we have no dc
211                    to talk to. */
212
213                 DEBUG(3, ("sam handles not responding\n"));
214
215                 winbindd_kill_connections(domain);
216                 goto reopen;
217         }
218
219         result = domain->sam_handle_open && domain->sam_dom_handle_open;
220
221         return result;
222 }
223
224 /* Shut down connections to all domain controllers */
225
226 static void winbindd_kill_connections(struct winbindd_domain *domain)
227 {
228         /* Kill all connections */
229
230         if (!domain) {
231                 struct winbindd_domain *tmp;
232
233                 for (tmp = domain_list; tmp; tmp = tmp->next) {
234                         winbindd_kill_connections(domain);
235                 }
236
237                 return;
238         }
239
240         /* Log a level 0 message - this is probably a domain controller
241            failure */
242
243         if (!domain->controller[0])
244                 return;
245
246         DEBUG(0, ("killing connections to domain %s with controller %s\n", 
247                   domain->name, domain->controller));
248
249         debug_conn_state();
250
251         /* Close LSA connections if we are killing connections to the dc
252            that has them open. */
253
254         if (strequal(server_state.controller, domain->controller)) {
255                 server_state.pwdb_initialised = False;
256                 server_state.lsa_handle_open = False;
257                 wb_lsa_close(&server_state.lsa_handle);
258         }
259         
260         /* Close domain sam handles but don't free them as this
261            severely traumatises the getent state.  The connections
262            will be reopened later. */
263
264         if (domain->sam_dom_handle_open) {
265                 wb_samr_close(&domain->sam_dom_handle);
266                 domain->sam_dom_handle_open = False;
267         }
268         
269         if (domain->sam_handle_open) {
270                 wb_samr_close(&domain->sam_handle);
271                 domain->sam_handle_open = False;
272         }
273
274         /* Re-lookup domain info which includes domain controller name */
275         
276         domain->got_domain_info = False;
277 }
278
279 /* Kill connections to all servers */
280
281 void winbindd_kill_all_connections(void)
282 {
283         struct winbindd_domain *domain;
284
285         /* Iterate over domain list */
286
287         domain = domain_list;
288
289         while (domain) {
290                 struct winbindd_domain *next;
291
292                 /* Kill conections */
293
294                 winbindd_kill_connections(domain);
295
296                 /* Remove domain from list */
297
298                 next = domain->next;
299                 DLIST_REMOVE(domain_list, domain);
300                 SAFE_FREE(domain);
301
302                 domain = next;
303         }
304 }
305
306 static BOOL get_any_dc_name(char *domain, fstring srv_name)
307 {
308         struct in_addr *ip_list, dc_ip;
309         extern pstring global_myname;
310         int count, i;
311
312         /* Lookup domain controller name */
313                 
314         if (!get_dc_list(False, domain, &ip_list, &count))
315                 return False;
316                 
317         /* Firstly choose a PDC/BDC who has the same network address as any
318            of our interfaces. */
319         
320         for (i = 0; i < count; i++) {
321                 if(!is_local_net(ip_list[i]))
322                         goto got_ip;
323         }
324         
325         i = (sys_random() % count);
326         
327  got_ip:
328         dc_ip = ip_list[i];
329         SAFE_FREE(ip_list);
330                 
331         if (!lookup_pdc_name(global_myname, domain, &dc_ip, srv_name))
332                 return False;
333
334         return True;
335 }
336
337 /* Attempt to connect to all domain controllers we know about */
338
339 void establish_connections(BOOL force_reestablish) 
340 {
341         static time_t lastt;
342         time_t t;
343
344         /* Check we haven't checked too recently */
345
346         t = time(NULL);
347         if ((t - lastt < WINBINDD_ESTABLISH_LOOP) && !force_reestablish) {
348                 return;
349         }
350         lastt = t;
351
352         DEBUG(3, ("establishing connections\n"));
353         debug_conn_state();
354
355         /* Maybe the connection died - if so then close up and restart */
356
357         if (server_state.pwdb_initialised &&
358             server_state.lsa_handle_open &&
359             !rpc_hnd_ok(&server_state.lsa_handle)) {
360                 winbindd_kill_connections(NULL);
361         }
362
363         if (!server_state.pwdb_initialised) {
364
365                 /* Lookup domain controller name */
366
367                 if (!get_any_dc_name(lp_workgroup(), 
368                                      server_state.controller)) {
369                         DEBUG(3, ("could not find any domain controllers "
370                                   "for domain %s\n", lp_workgroup()));
371                         return;
372                 }
373
374                 /* Initialise password database and sids */
375                 
376                 /* server_state.pwdb_initialised = pwdb_initialise(False); */
377                 server_state.pwdb_initialised = True;
378
379                 if (!server_state.pwdb_initialised) {
380                         DEBUG(3, ("could not initialise pwdb\n"));
381                         return;
382                 }
383         }
384
385         /* Open lsa handle if it isn't already open */
386         
387         if (!server_state.lsa_handle_open) {
388                 
389                 server_state.lsa_handle_open =
390                         wb_lsa_open_policy(server_state.controller, 
391                                            False, SEC_RIGHTS_MAXIMUM_ALLOWED,
392                                            &server_state.lsa_handle);
393
394                 if (!server_state.lsa_handle_open) {
395                         DEBUG(0, ("error opening lsa handle on dc %s\n",
396                                   server_state.controller));
397                         return;
398                 }
399
400                 /* Now we can talk to the server we can get some info */
401
402                 get_trusted_domains();
403         }
404
405         debug_conn_state();
406 }
407
408 /* Connect to a domain controller using get_any_dc_name() to discover 
409    the domain name and sid */
410
411 BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain)
412 {
413     fstring level5_dom;
414     BOOL res;
415     uint32 enum_ctx = 0;
416     uint32 num_doms = 0;
417     char **domains = NULL;
418     DOM_SID *sids = NULL;
419
420     if (domain == NULL) {
421         return False;
422     }
423
424     DEBUG(1, ("looking up sid for domain %s\n", domain_name));
425
426     /* Get controller name for domain */
427
428     if (!get_any_dc_name(domain_name, domain->controller)) {
429             DEBUG(0, ("Could not resolve domain controller for domain %s\n",
430                       domain_name));
431             return False;
432     }
433
434     /* Do a level 5 query info policy if we are looking up our own SID */
435
436     if (strequal(domain_name, lp_workgroup())) {
437             return wb_lsa_query_info_pol(&server_state.lsa_handle, 0x05, 
438                                          level5_dom, &domain->sid);
439     } 
440
441     /* Use lsaenumdomains to get sid for this domain */
442     
443     res = wb_lsa_enum_trust_dom(&server_state.lsa_handle, &enum_ctx,
444                                 &num_doms, &domains, &sids);
445     
446     /* Look for domain name */
447     
448     if (res && domains && sids) {
449             int found = False;
450             int i;
451             
452             for(i = 0; i < num_doms; i++) {
453                     if (strequal(domain_name, domains[i])) {
454                             sid_copy(&domain->sid, &sids[i]);
455                             found = True;
456                             break;
457                     }
458             }
459             
460             res = found;
461     }
462     
463     return res;
464 }
465
466 /* Lookup domain controller and sid for a domain */
467
468 BOOL get_domain_info(struct winbindd_domain *domain)
469 {
470     fstring sid_str;
471
472     DEBUG(1, ("Getting domain info for domain %s\n", domain->name));
473
474     /* Lookup domain sid */        
475
476     if (!lookup_domain_sid(domain->name, domain)) {
477             DEBUG(0, ("could not find sid for domain %s\n", domain->name));
478
479             /* Could be a DC failure - shut down connections to this domain */
480
481             winbindd_kill_connections(domain);
482
483             return False;
484     }
485     
486     /* Lookup OK */
487
488     domain->got_domain_info = 1;
489
490     sid_to_string(sid_str, &domain->sid);
491     DEBUG(1, ("found sid %s for domain %s\n", sid_str, domain->name));
492
493     return True;
494 }        
495
496 /* Lookup a sid in a domain from a name */
497
498 BOOL winbindd_lookup_sid_by_name(char *name, DOM_SID *sid,
499                                  enum SID_NAME_USE *type)
500 {
501     int num_sids = 0, num_names = 1;
502     DOM_SID *sids = NULL;
503     uint32 *types = NULL;
504     BOOL res;
505
506     /* Don't bother with machine accounts */
507
508     if (name[strlen(name) - 1] == '$') {
509         return False;
510     }
511
512     /* Lookup name */
513
514     res = wb_lsa_lookup_names(&server_state.lsa_handle, num_names, 
515                               (char **)&name, &sids, &types, &num_sids);
516
517     /* Return rid and type if lookup successful */
518
519     if (res) {
520
521         /* Return sid */
522
523         if ((sid != NULL) && (sids != NULL)) {
524             sid_copy(sid, &sids[0]);
525         }
526
527         /* Return name type */
528
529         if ((type != NULL) && (types != NULL)) {
530             *type = types[0];
531         }
532     }
533     
534     return res;
535 }
536
537 /* Lookup a name in a domain from a sid */
538
539 BOOL winbindd_lookup_name_by_sid(DOM_SID *sid, fstring name,
540                                  enum SID_NAME_USE *type)
541 {
542     int num_sids = 1, num_names = 0;
543     uint32 *types = NULL;
544     char **names;
545     BOOL res;
546
547     /* Lookup name */
548
549     res = wb_lsa_lookup_sids(&server_state.lsa_handle, num_sids, sid, 
550                              &names, &types, &num_names);
551
552     /* Return name and type if successful */
553
554     if (res) {
555
556         /* Return name */
557
558         if ((names != NULL) && (name != NULL)) {
559             fstrcpy(name, names[0]);
560         }
561
562         /* Return name type */
563
564         if ((type != NULL) && (types != NULL)) {
565             *type = types[0];
566         }
567     }
568
569     return res;
570 }
571
572 /* Lookup user information from a rid */
573
574 BOOL winbindd_lookup_userinfo(struct winbindd_domain *domain,
575                               uint32 user_rid, SAM_USERINFO_CTR **user_info)
576 {
577         return wb_get_samr_query_userinfo(&domain->sam_dom_handle, 0x15, 
578                                           user_rid, user_info);
579 }                                   
580
581 /* Lookup groups a user is a member of.  I wish Unix had a call like this! */
582
583 BOOL winbindd_lookup_usergroups(struct winbindd_domain *domain,
584                                 uint32 user_rid, uint32 *num_groups,
585                                 DOM_GID **user_groups)
586 {
587         POLICY_HND user_pol;
588         BOOL result;
589
590         if (!wb_samr_open_user(&domain->sam_dom_handle, 
591                                SEC_RIGHTS_MAXIMUM_ALLOWED,
592                                user_rid, &user_pol)) {
593                 return False;
594         }
595
596         if (!NT_STATUS_IS_OK(cli_samr_query_usergroups(domain->sam_dom_handle.cli,
597                                                        domain->sam_dom_handle.mem_ctx,
598                                                        &user_pol, num_groups, user_groups))) {
599                 result = False;
600                 goto done;
601         }
602
603         result = True;
604
605 done:
606         cli_samr_close(domain->sam_dom_handle.cli,
607                        domain->sam_dom_handle.mem_ctx, &user_pol);
608
609         return True;
610 }
611
612 /* Lookup group membership given a rid */
613
614 BOOL winbindd_lookup_groupmem(struct winbindd_domain *domain,
615                               uint32 group_rid, uint32 *num_names, 
616                               uint32 **rid_mem, char ***names, 
617                               uint32 **name_types)
618 {
619         return wb_sam_query_groupmem(&domain->sam_dom_handle, group_rid, 
620                                      num_names, rid_mem, names, name_types);
621 }
622
623 /* Globals for domain list stuff */
624
625 struct winbindd_domain *domain_list = NULL;
626
627 /* Given a domain name, return the struct winbindd domain info for it 
628    if it is actually working. */
629
630 struct winbindd_domain *find_domain_from_name(char *domain_name)
631 {
632         struct winbindd_domain *tmp;
633
634         /* Search through list */
635
636         for (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
637                 if (strcmp(domain_name, tmp->name) == 0) {
638
639                         if (!tmp->got_domain_info) {
640                                 get_domain_info(tmp);
641                         }
642
643                         return tmp->got_domain_info ? tmp : NULL;
644                 }
645         }
646
647         /* Not found */
648
649         return NULL;
650 }
651
652 /* Given a domain name, return the struct winbindd domain info for it */
653
654 struct winbindd_domain *find_domain_from_sid(DOM_SID *sid)
655 {
656         struct winbindd_domain *tmp;
657
658         /* Search through list */
659         for (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
660                 if (sid_equal(sid, &tmp->sid)) {
661                         if (!tmp->got_domain_info) return NULL;
662                         return tmp;
663                 }
664         }
665
666         /* Not found */
667         return NULL;
668 }
669
670 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
671
672 void free_getent_state(struct getent_state *state)
673 {
674     struct getent_state *temp;
675
676     /* Iterate over state list */
677
678     temp = state;
679
680     while(temp != NULL) {
681         struct getent_state *next;
682
683         /* Free sam entries then list entry */
684
685         SAFE_FREE(state->sam_entries);
686         DLIST_REMOVE(state, state);
687         next = temp->next;
688
689         SAFE_FREE(temp);
690         temp = next;
691     }
692 }
693
694 /* Parse list of arguments to winbind uid or winbind gid parameters */
695
696 static BOOL parse_id_list(char *paramstr, BOOL is_user)
697 {
698     uid_t id_low, id_high = 0;
699
700     /* Give a nicer error message if no parameters specified */
701
702     if (strequal(paramstr, "")) {
703         DEBUG(0, ("winbind %s parameter missing\n", is_user ? "uid" : "gid"));
704         return False;
705     }
706     
707     /* Parse entry */
708
709     if (sscanf(paramstr, "%u-%u", &id_low, &id_high) != 2) {
710         DEBUG(0, ("winbind %s parameter invalid\n", 
711                   is_user ? "uid" : "gid"));
712         return False;
713     }
714     
715     /* Store id info */
716     
717     if (is_user) {
718         server_state.uid_low = id_low;
719         server_state.uid_high = id_high;
720     } else {
721         server_state.gid_low = id_low;
722         server_state.gid_high = id_high;
723     }
724
725     return True;
726 }
727
728 /* Initialise trusted domain info */
729
730 BOOL winbindd_param_init(void)
731 {
732     /* Parse winbind uid and winbind_gid parameters */
733
734     if (!(parse_id_list(lp_winbind_uid(), True) &&
735           parse_id_list(lp_winbind_gid(), False))) {
736         return False;
737     }
738
739     /* Check for reversed uid and gid ranges */
740         
741     if (server_state.uid_low > server_state.uid_high) {
742         DEBUG(0, ("uid range invalid\n"));
743         return False;
744     }
745     
746     if (server_state.gid_low > server_state.gid_high) {
747         DEBUG(0, ("gid range invalid\n"));
748         return False;
749     }
750     
751     return True;
752 }
753
754 /* find the sequence number for a domain */
755
756 uint32 domain_sequence_number(char *domain_name)
757 {
758         struct winbindd_domain *domain;
759         SAM_UNK_CTR ctr;
760
761         domain = find_domain_from_name(domain_name);
762         if (!domain) return DOM_SEQUENCE_NONE;
763
764         if (!wb_samr_query_dom_info(&domain->sam_dom_handle, 2, &ctr)) {
765
766                 /* If this fails, something bad has gone wrong */
767
768                 winbindd_kill_connections(domain);
769
770                 DEBUG(2,("domain sequence query failed\n"));
771                 return DOM_SEQUENCE_NONE;
772         }
773
774         DEBUG(4,("got domain sequence number for %s of %u\n", 
775                  domain_name, (unsigned)ctr.info.inf2.seq_num));
776         
777         return ctr.info.inf2.seq_num;
778 }
779
780 /* Query display info for a domain.  This returns enough information plus a
781    bit extra to give an overview of domain users for the User Manager
782    application. */
783
784 NTSTATUS winbindd_query_dispinfo(struct winbindd_domain *domain,
785                                  uint32 *start_ndx, uint16 info_level, 
786                                  uint32 *num_entries, SAM_DISPINFO_CTR *ctr)
787 {
788         return wb_samr_query_dispinfo(&domain->sam_dom_handle, start_ndx,
789                                       info_level, num_entries, ctr);
790 }
791
792 /* Check if a domain is present in a comma-separated list of domains */
793
794 BOOL check_domain_env(char *domain_env, char *domain)
795 {
796         fstring name;
797         char *tmp = domain_env;
798
799         while(next_token(&tmp, name, ",", sizeof(fstring))) {
800                 if (strequal(name, domain)) {
801                         return True;
802                 }
803         }
804
805         return False;
806 }
807
808
809 /* Parse a string of the form DOMAIN/user into a domain and a user */
810
811 void parse_domain_user(char *domuser, fstring domain, fstring user)
812 {
813         char *p;
814         char *sep = lp_winbind_separator();
815         if (!sep) sep = "\\";
816         p = strchr(domuser,*sep);
817         if (!p) p = strchr(domuser,'\\');
818         if (!p) {
819                 fstrcpy(domain,"");
820                 fstrcpy(user, domuser);
821                 return;
822         }
823         
824         fstrcpy(user, p+1);
825         fstrcpy(domain, domuser);
826         domain[PTR_DIFF(p, domuser)] = 0;
827         strupper(domain);
828 }