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