more merging
[kai/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
26 BOOL domain_handles_open(struct winbindd_domain *domain)
27 {
28         return domain->sam_handle_open &&
29                 domain->sam_dom_handle_open;
30 }
31
32 static BOOL resolve_dc_name(char *domain_name, fstring domain_controller)
33 {
34         struct in_addr ip;
35         extern pstring global_myname;
36         
37         /* if its our primary domain and password server is not '*' then use the
38            password server parameter */
39         if (strcmp(domain_name,lp_workgroup()) == 0 && 
40             strcmp(lp_passwordserver(),"*") != 0) {
41                 fstrcpy(domain_controller, lp_passwordserver());
42                 return True;
43         }
44
45         if (!resolve_name(domain_name, &ip, 0x1B)) return False;
46
47         return lookup_pdc_name(global_myname, domain_name, &ip, domain_controller);
48 }
49
50
51 static struct winbindd_domain *add_trusted_domain(char *domain_name)
52 {
53     struct winbindd_domain *domain;
54
55     DEBUG(1, ("adding trusted domain %s\n", domain_name));
56
57     /* Create new domain entry */
58
59     if ((domain = (struct winbindd_domain *)malloc(sizeof(*domain))) == NULL) {
60         return NULL;
61     }
62
63     /* Fill in fields */
64
65     ZERO_STRUCTP(domain);
66
67     if (domain_name) {
68         fstrcpy(domain->name, domain_name);
69     }
70
71     /* Link to domain list */
72
73     DLIST_ADD(domain_list, domain);
74
75     return domain;
76 }
77
78 /* Look up global info for the winbind daemon */
79 static BOOL get_trusted_domains(void)
80 {
81         uint32 enum_ctx = 0;
82         uint32 num_doms = 0;
83         char **domains = NULL;
84         DOM_SID **sids = NULL;
85         BOOL result;
86         int i;
87         
88         DEBUG(1, ("getting trusted domain list\n"));
89
90         /* Add our workgroup - keep handle to look up trusted domains */
91         if (!add_trusted_domain(lp_workgroup())) {
92                 DEBUG(0, ("could not add record for domain %s\n", lp_workgroup()));
93                 return False;
94         }
95         
96         /* Enumerate list of trusted domains */ 
97         result = lsa_enum_trust_dom(&server_state.lsa_handle, &enum_ctx,
98                                     &num_doms, &domains, &sids);
99         
100         if (!result || !domains) return False;
101         
102         /* Add each domain to the trusted domain list */
103         for(i = 0; i < num_doms; i++) {
104                 if (!add_trusted_domain(domains[i])) {
105                         DEBUG(0, ("could not add record for domain %s\n", domains[i]));
106                         result = False;
107                 }
108         }
109         
110         /* Free memory */       
111         free_char_array(num_doms, domains);
112         free_sid_array(num_doms, sids);
113         
114         return True;
115 }
116
117
118 /* Open sam and sam domain handles to a domain and cache the results */
119 static BOOL open_sam_handles(struct winbindd_domain *domain)
120 {
121         /* Get domain info */
122         if (!domain->got_domain_info) {
123                 domain->got_domain_info = get_domain_info(domain);
124                 if (!domain->got_domain_info) return False;
125         }
126
127         /* Open sam handle if it isn't already open */
128         if (!domain->sam_handle_open) {
129                 domain->sam_handle_open = 
130                         samr_connect(domain->controller, SEC_RIGHTS_MAXIMUM_ALLOWED, 
131                                      &domain->sam_handle);
132                 if (!domain->sam_handle_open) return False;
133         }
134
135         /* Open sam domain handle if it isn't already open */
136         if (!domain->sam_dom_handle_open) {
137                 domain->sam_dom_handle_open =
138                         samr_open_domain(&domain->sam_handle, 
139                                          SEC_RIGHTS_MAXIMUM_ALLOWED, &domain->sid, 
140                                          &domain->sam_dom_handle);
141                 if (!domain->sam_dom_handle_open) return False;
142         }
143         
144         return True;
145 }
146
147 static void winbindd_kill_connections(void)
148 {
149         struct winbindd_domain *domain;
150
151         DEBUG(1,("killing winbindd connections\n"));
152
153         server_state.pwdb_initialised = False;
154         server_state.lsa_handle_open = False;
155         lsa_close(&server_state.lsa_handle);
156         
157         for (domain=domain_list; domain; domain=domain->next) {
158                 if (domain->sam_dom_handle_open) {
159                         samr_close(&domain->sam_dom_handle);
160                         domain->sam_dom_handle_open = False;
161                 }
162                 if (domain->sam_handle_open) {
163                         samr_close(&domain->sam_handle);
164                         domain->sam_handle_open = False;
165                 }
166                 DLIST_REMOVE(domain_list, domain);
167                 free(domain);
168         }
169 }
170
171 void establish_connections(void) 
172 {
173         struct winbindd_domain *domain;
174         static time_t lastt;
175         time_t t;
176
177         t = time(NULL);
178         if (t - lastt < WINBINDD_ESTABLISH_LOOP) return;
179         lastt = t;
180
181         /* maybe the connection died - if so then close up and restart */
182         if (server_state.pwdb_initialised &&
183             server_state.lsa_handle_open &&
184             !rpc_hnd_ok(&server_state.lsa_handle)) {
185                 winbindd_kill_connections();
186         }
187
188         if (!server_state.pwdb_initialised) {
189                 fstrcpy(server_state.controller, lp_passwordserver());
190                 if (strcmp(server_state.controller,"*") == 0) {
191                         if (!resolve_dc_name(lp_workgroup(), server_state.controller)) {
192                                 return;
193                         }
194                 }
195
196                 server_state.pwdb_initialised = pwdb_initialise(False);
197                 if (!server_state.pwdb_initialised) return;
198         }
199
200         /* Open lsa handle if it isn't already open */
201         if (!server_state.lsa_handle_open) {
202                 server_state.lsa_handle_open =
203                         lsa_open_policy(server_state.controller, &server_state.lsa_handle, 
204                                         False, SEC_RIGHTS_MAXIMUM_ALLOWED);
205                 if (!server_state.lsa_handle_open) return;
206
207                 /* now we can talk to the server we can get some info */
208                 get_trusted_domains();
209         }
210
211         for (domain=domain_list; domain; domain=domain->next) {
212                 if (!domain_handles_open(domain)) {
213                         open_sam_handles(domain);
214                 }
215         }
216 }
217
218
219 /* Connect to a domain controller using get_any_dc_name() to discover 
220    the domain name and sid */
221 BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain)
222 {
223     fstring level5_dom;
224     BOOL res;
225     uint32 enum_ctx = 0;
226     uint32 num_doms = 0;
227     char **domains = NULL;
228     DOM_SID **sids = NULL;
229
230     if (domain == NULL) {
231         return False;
232     }
233
234     DEBUG(1, ("looking up sid for domain %s\n", domain_name));
235
236     /* Get controller name for domain */
237     if (!resolve_dc_name(domain_name, domain->controller)) {
238             return False;
239     }
240
241     if (strequal(domain->controller, server_state.controller)) {
242             /* Do a level 5 query info policy */
243             return lsa_query_info_pol(&server_state.lsa_handle, 0x05, 
244                                       level5_dom, &domain->sid);
245     } 
246
247     /* Use lsaenumdomains to get sid for this domain */
248     
249     res = lsa_enum_trust_dom(&server_state.lsa_handle, &enum_ctx,
250                              &num_doms, &domains, &sids);
251     
252     /* Look for domain name */
253     
254     if (res && domains && sids) {
255             int found = False;
256             int i;
257             
258             for(i = 0; i < num_doms; i++) {
259                     if (strequal(domain_name, domains[i])) {
260                             sid_copy(&domain->sid, sids[i]);
261                             found = True;
262                             break;
263                     }
264             }
265             
266             res = found;
267     }
268     
269     /* Free memory */
270     
271     free_char_array(num_doms, domains);
272     free_sid_array(num_doms, sids);
273
274     return res;
275 }
276
277
278 /* Lookup domain controller and sid for a domain */
279
280 BOOL get_domain_info(struct winbindd_domain *domain)
281 {
282     fstring sid_str;
283
284     DEBUG(1, ("Getting domain info for domain %s\n", domain->name));
285
286     /* Lookup domain sid */        
287     if (!lookup_domain_sid(domain->name, domain)) {
288             DEBUG(0, ("could not find sid for domain %s\n", domain->name));
289             return False;
290     }
291     
292     /* Lookup OK */
293
294     domain->got_domain_info = 1;
295
296     sid_to_string(sid_str, &domain->sid);
297     DEBUG(1, ("found sid %s for domain %s\n", sid_str, domain->name));
298
299     return True;
300 }        
301
302 /* Lookup a sid in a domain from a name */
303
304 BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain,
305                                  char *name, DOM_SID *sid,
306                                  enum SID_NAME_USE *type)
307 {
308     int num_sids = 0, num_names = 1;
309     DOM_SID *sids = NULL;
310     uint32 *types = NULL;
311     BOOL res;
312
313     /* Don't bother with machine accounts */
314
315     if (name[strlen(name) - 1] == '$') {
316         return False;
317     }
318
319     /* Lookup name */
320
321     res = lsa_lookup_names(&server_state.lsa_handle, num_names, (char **)&name,
322                            &sids, &types, &num_sids);
323
324     /* Return rid and type if lookup successful */
325
326     if (res) {
327
328         /* Return sid */
329
330         if ((sid != NULL) && (sids != NULL)) {
331             sid_copy(sid, &sids[0]);
332         }
333
334         /* Return name type */
335
336         if ((type != NULL) && (types != NULL)) {
337             *type = types[0];
338         }
339     }
340     
341     /* Free memory */
342
343     if (types != NULL) free(types);
344     if (sids != NULL) free(sids);
345
346     return res;
347 }
348
349 /* Lookup a name in a domain from a sid */
350
351 BOOL winbindd_lookup_name_by_sid(struct winbindd_domain *domain,
352                                  DOM_SID *sid, char *name,
353                                  enum SID_NAME_USE *type)
354 {
355     int num_sids = 1, num_names = 0;
356     uint32 *types = NULL;
357     char **names;
358     BOOL res;
359
360     /* Lookup name */
361     res = lsa_lookup_sids(&server_state.lsa_handle, num_sids, &sid, &names, 
362                           &types, &num_names);
363
364     /* Return name and type if successful */
365
366     if (res) {
367
368         /* Return name */
369
370         if ((names != NULL) && (name != NULL)) {
371             fstrcpy(name, names[0]);
372         }
373
374         /* Return name type */
375
376         if ((type != NULL) && (types != NULL)) {
377             *type = types[0];
378         }
379     }
380
381     /* Free memory */
382
383     safe_free(types);
384     free_char_array(num_names, names);
385
386     return res;
387 }
388
389 /* Lookup user information from a rid */
390
391 BOOL winbindd_lookup_userinfo(struct winbindd_domain *domain,
392                               uint32 user_rid, SAM_USERINFO_CTR *user_info)
393 {
394         if (!domain_handles_open(domain)) return False;
395
396         return get_samr_query_userinfo(&domain->sam_dom_handle, 0x15, user_rid, user_info);
397 }                                   
398
399 /* Lookup group information from a rid */
400
401 BOOL winbindd_lookup_groupinfo(struct winbindd_domain *domain,
402                               uint32 group_rid, GROUP_INFO_CTR *info)
403 {
404         if (!domain_handles_open(domain)) return False;
405
406         return get_samr_query_groupinfo(&domain->sam_dom_handle, 1, group_rid, info);
407 }
408
409 /* Lookup group membership given a rid */
410
411 BOOL winbindd_lookup_groupmem(struct winbindd_domain *domain,
412                               uint32 group_rid, uint32 *num_names, 
413                               uint32 **rid_mem, char ***names, 
414                               enum SID_NAME_USE **name_types)
415 {
416         if (!domain_handles_open(domain)) return False;
417     
418         return sam_query_groupmem(&domain->sam_dom_handle, group_rid, num_names, 
419                                   rid_mem, names, name_types);
420 }
421
422 /* Lookup alias membership given a rid */
423
424 int winbindd_lookup_aliasmem(struct winbindd_domain *domain,
425                              uint32 alias_rid, uint32 *num_names, 
426                              DOM_SID ***sids, char ***names, 
427                              enum SID_NAME_USE **name_types)
428 {
429     /* Open sam handles */
430     if (!domain_handles_open(domain)) return False;
431
432     return sam_query_aliasmem(domain->controller, 
433                               &domain->sam_dom_handle, alias_rid, num_names, 
434                               sids, names, name_types);
435 }
436
437 /* Globals for domain list stuff */
438
439 struct winbindd_domain *domain_list = NULL;
440
441 /* Given a domain name, return the struct winbindd domain info for it */
442
443 struct winbindd_domain *find_domain_from_name(char *domain_name)
444 {
445         struct winbindd_domain *tmp;
446
447         /* Search through list */
448         for (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
449                 if (strcmp(domain_name, tmp->name) == 0) {
450                         if (!tmp->got_domain_info) return NULL;
451                         return tmp;
452                 }
453         }
454
455         /* Not found */
456         return NULL;
457 }
458
459 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
460
461 void free_getent_state(struct getent_state *state)
462 {
463     struct getent_state *temp;
464
465     /* Iterate over state list */
466
467     temp = state;
468
469     while(temp != NULL) {
470         struct getent_state *next;
471
472         /* Free sam entries then list entry */
473
474         safe_free(state->sam_entries);
475         DLIST_REMOVE(state, state);
476         next = temp->next;
477
478         free(temp);
479         temp = next;
480     }
481 }
482
483 /* Parse list of arguments to winbind uid or winbind gid parameters */
484
485 static BOOL parse_id_list(char *paramstr, BOOL is_user)
486 {
487     uid_t id_low, id_high = 0;
488
489     /* Parse entry */
490
491     if (sscanf(paramstr, "%u-%u", &id_low, &id_high) != 2) {
492         DEBUG(0, ("winbid %s parameter invalid\n", 
493                   is_user ? "uid" : "gid"));
494         return False;
495     }
496     
497     /* Store id info */
498     
499     if (is_user) {
500         server_state.uid_low = id_low;
501         server_state.uid_high = id_high;
502     } else {
503         server_state.gid_low = id_low;
504         server_state.gid_high = id_high;
505     }
506
507     return True;
508 }
509
510 /* Initialise trusted domain info */
511
512 BOOL winbindd_param_init(void)
513 {
514     /* Parse winbind uid and winbind_gid parameters */
515
516     if (!(parse_id_list(lp_winbind_uid(), True) &&
517           parse_id_list(lp_winbind_gid(), False))) {
518         return False;
519     }
520
521     /* Check for reversed uid and gid ranges */
522         
523     if (server_state.uid_low > server_state.uid_high) {
524         DEBUG(0, ("uid range invalid\n"));
525         return False;
526     }
527     
528     if (server_state.gid_low > server_state.gid_high) {
529         DEBUG(0, ("gid range for invalid\n"));
530         return False;
531     }
532     
533     return True;
534 }
535
536 /* Convert a enum winbindd_cmd to a string */
537
538 char *winbindd_cmd_to_string(enum winbindd_cmd cmd)
539 {
540     char *result;
541
542     switch (cmd) {
543
544     case WINBINDD_GETPWNAM_FROM_USER:
545         result = "getpwnam from user";
546         break;
547             
548     case WINBINDD_GETPWNAM_FROM_UID:
549         result = "getpwnam from uid";
550         break;
551
552     case WINBINDD_GETGRNAM_FROM_GROUP:
553         result = "getgrnam from group";
554         break;
555
556     case WINBINDD_GETGRNAM_FROM_GID:
557         result = "getgrnam from gid";
558         break;
559
560     case WINBINDD_SETPWENT:
561         result = "setpwent";
562         break;
563
564     case WINBINDD_ENDPWENT:
565         result = "endpwent";
566         break;
567
568     case WINBINDD_GETPWENT:
569         result = "getpwent";
570         break;
571
572     case WINBINDD_SETGRENT:
573         result = "setgrent";
574         break;
575
576     case WINBINDD_ENDGRENT:
577         result = "endgrent"; 
578         break;
579
580     case WINBINDD_GETGRENT:
581         result = "getgrent";
582         break;
583
584     case WINBINDD_PAM_AUTH:
585         result = "pam_auth";
586         break;
587
588     default:
589         result = "invalid command";
590         break;
591     }
592
593     return result;
594 };
595
596
597 /* parse a string of the form DOMAIN/user into a domain and a user */
598 void parse_domain_user(char *domuser, fstring domain, fstring user)
599 {
600         char *p;
601         p = strchr(domuser,'/');
602         if (!p) p = strchr(domuser,'\\');
603         if (!p) {
604                 fstrcpy(domain,"");
605                 fstrcpy(user, domuser);
606                 return;
607         }
608         
609         fstrcpy(user, p+1);
610         fstrcpy(domain, domuser);
611         domain[PTR_DIFF(p, domuser)] = 0;
612 }
613
614 /* find the sequence number for a domain */
615 uint32 domain_sequence_number(char *domain_name)
616 {
617         struct winbindd_domain *domain;
618         SAM_UNK_CTR ctr;
619
620         domain = find_domain_from_name(domain_name);
621         if (!domain) return DOM_SEQUENCE_NONE;
622
623         if (!samr_query_dom_info(&domain->sam_dom_handle, 2, &ctr)) {
624                 DEBUG(2,("domain sequence query failed\n"));
625                 return DOM_SEQUENCE_NONE;
626         }
627
628         DEBUG(4,("got domain sequence number for %s of %u\n", 
629                  domain_name, (unsigned)ctr.info.inf2.seq_num));
630         
631         return ctr.info.inf2.seq_num;
632 }