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