a81114169c7c68a46d86902c5be8d10c8ca7d966
[samba.git] / source3 / smbd / groupname.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Groupname handling
5    Copyright (C) Jeremy Allison 1998.
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 /* 
23  * UNIX gid and Local or Domain SID resolution.  This module resolves
24  * only those entries in the map files, it is *NOT* responsible for
25  * resolving UNIX groups not listed: that is an entirely different
26  * matter, altogether...
27  */
28
29 /*
30  *
31  *
32
33  format of the file is:
34
35  unixname       NT Group name
36  unixname       Domain Admins (well-known Domain Group)
37  unixname       DOMAIN_NAME\NT Group name
38  unixname       OTHER_DOMAIN_NAME\NT Group name
39  unixname       DOMAIN_NAME\Domain Admins (well-known Domain Group)
40  ....
41
42  if the DOMAIN_NAME\ component is left off, then your own domain is assumed.
43
44  *
45  *
46  */
47
48
49 #include "includes.h"
50 extern int DEBUGLEVEL;
51
52 extern fstring global_sam_name;
53
54 /* we can map either local aliases or domain groups */
55 typedef enum 
56 {
57         GROUP_LOCAL,
58         GROUP_DOMAIN
59
60 } GROUP_TYPE;
61
62 /**************************************************************************
63  Groupname map functionality. The code loads a groupname map file and
64  (currently) loads it into a linked list. This is slow and memory
65  hungry, but can be changed into a more efficient storage format
66  if the demands on it become excessive.
67 ***************************************************************************/
68
69 typedef struct group_name_info
70 {
71    char *nt_name;
72    char *nt_domain;
73    char *unix_name;
74
75    DOM_SID sid;
76    gid_t  unix_gid;
77
78 } GROUP_NAME_INFO;
79
80 typedef struct name_map
81 {
82         ubi_slNode next;
83         GROUP_NAME_INFO grp;
84
85 } name_map_entry;
86
87 static ubi_slList groupname_map_list;
88 static ubi_slList aliasname_map_list;
89
90 static void delete_name_entry(name_map_entry *gmep)
91 {
92         if (gmep->grp.nt_name)
93         {
94                 free(gmep->grp.nt_name);
95         }
96         if (gmep->grp.nt_domain)
97         {
98                 free(gmep->grp.nt_domain);
99         }
100         if (gmep->grp.unix_name)
101         {
102                 free(gmep->grp.unix_name);
103         }
104         free((char*)gmep);
105 }
106
107 /**************************************************************************
108  Delete all the entries in the name map list.
109 ***************************************************************************/
110
111 static void delete_map_list(ubi_slList *map_list)
112 {
113         name_map_entry *gmep;
114
115         while ((gmep = (name_map_entry *)ubi_slRemHead(map_list )) != NULL)
116         {
117                 delete_name_entry(gmep);
118         }
119 }
120
121
122 /**************************************************************************
123  makes a group sid out of a domain sid and a _unix_ gid.
124 ***************************************************************************/
125 static BOOL make_mydomain_sid(GROUP_NAME_INFO *grp, GROUP_TYPE type)
126 {
127         uint32 tmp_rid;
128         DOM_SID sid;
129         uint8  tmp_type;
130
131         DEBUG(10,("make_mydomain_sid\n"));
132
133         if (!map_domain_name_to_sid(&grp->sid, &(grp->nt_domain)))
134         {
135                 DEBUG(0,("make_mydomain_sid: unknown domain %s\n",
136                           grp->nt_domain));
137                 return False;
138         }
139
140         if (strequal(grp->nt_domain, global_sam_name) &&
141             lookup_builtin_grp_name(grp->nt_name, &sid, &tmp_type) == 0x0)
142         {
143                 sid_copy(&grp->sid, &sid);
144                 return True;
145         }
146         else
147         {
148                 BOOL ret;
149                 fstring sid_str;
150                 if (type == GROUP_DOMAIN)
151                 {
152                         tmp_rid = pwdb_gid_to_group_rid(grp->unix_gid);
153                 }
154                 else
155                 {
156                         tmp_rid = pwdb_gid_to_alias_rid(grp->unix_gid);
157                 }
158                 ret = sid_append_rid(&(grp->sid), tmp_rid);
159                 sid_to_string(sid_str, &grp->sid);
160                 DEBUG(10,("nt name %s gid %d mapped to %s\n",
161                            grp->nt_name, grp->unix_gid, sid_str));
162                 return ret;
163         }
164 }
165
166 /**************************************************************************
167  makes a group sid out of an nt domain, nt group name or a unix group name.
168 ***************************************************************************/
169 static BOOL unix_name_to_group_info(GROUP_NAME_INFO *grp, GROUP_TYPE type)
170 {
171         struct group *gptr = NULL;
172
173         /*
174          * Attempt to get the unix gid_t for this name.
175          */
176
177         DEBUG(5,("unix_name_to_group_info: unix_name:%s\n", grp->unix_name));
178
179         gptr = (struct group *)getgrnam(grp->unix_name);
180         if (gptr == NULL)
181         {
182                 DEBUG(0,("unix_name_to_group_info: getgrnam for group %s\
183 failed. Error was %s.\n", grp->unix_name, strerror(errno) ));
184                 return False;
185         }
186
187         grp->unix_gid = (gid_t)gptr->gr_gid;
188
189         DEBUG(5,("unix_name_to_group_info: unix gid:%d\n", grp->unix_gid));
190
191         /*
192          * Now map the name to an NT SID+RID.
193          */
194
195         if (grp->nt_domain != NULL && !strequal(grp->nt_domain, global_sam_name))
196         {
197                 /* Must add client-call lookup code here, to 
198                  * resolve remote domain's sid and the group's rid,
199                  * in that domain.
200                  *
201                  * NOTE: it is _incorrect_ to put code here that assumes
202                  * that we can call pwdb_gid_to_group_rid() or _alias_rid():
203                  * it is a totally different domain for which we are *NOT*
204                  * responsible.
205                  * for foriegn domains for which we are *NOT* the PDC, all
206                  * we can be responsible for is the unix * gid_t to which
207                  * the foriegn SID+rid maps to, on this _local_ machine.  
208                  */
209
210                 if (!map_domain_name_to_sid(&grp->sid, &(grp->nt_domain)))
211                 {
212                         DEBUG(0,("unix_name_to_group_info: no known sid for %s\n",
213                                   grp->nt_domain));
214                         return False;
215                 }
216         }
217
218         return make_mydomain_sid(grp, type);
219 }
220
221 static BOOL make_name_entry(name_map_entry **new_ep,
222                 char *nt_domain, char *nt_group, char *unix_group,
223                 GROUP_TYPE type)
224 {
225         /*
226          * Create the list entry and add it onto the list.
227          */
228
229         DEBUG(5,("make_name_entry:%s,%s,%s\n", nt_domain, nt_group, unix_group));
230
231         (*new_ep) = (name_map_entry *)malloc(sizeof(name_map_entry));
232         if ((*new_ep) == NULL)
233         {
234                 DEBUG(0,("make_name_entry: malloc fail for name_map_entry.\n"));
235                 return False;
236         } 
237
238         ZERO_STRUCTP(*new_ep);
239
240         (*new_ep)->grp.nt_name   = strdup(nt_group  );
241         (*new_ep)->grp.nt_domain = strdup(nt_domain );
242         (*new_ep)->grp.unix_name = strdup(unix_group);
243
244         if ((*new_ep)->grp.nt_name   == NULL ||
245             (*new_ep)->grp.unix_name == NULL)
246         {
247                 DEBUG(0,("make_name_entry: malloc fail for names in name_map_entry.\n"));
248                 delete_name_entry((*new_ep));
249                 return False;
250         }
251
252         /*
253          * look up the group names, make the Group-SID and unix gid
254          */
255  
256         if (!unix_name_to_group_info(&(*new_ep)->grp, type))
257         {
258                 delete_name_entry((*new_ep));
259                 return False;
260         }
261
262         return True;
263 }
264
265 /**************************************************************************
266  Load a name map file. Sets last accessed timestamp.
267 ***************************************************************************/
268 static ubi_slList *load_name_map(GROUP_TYPE type)
269 {
270         static time_t groupmap_file_last_modified = (time_t)0;
271         static time_t aliasmap_file_last_modified = (time_t)0;
272         static BOOL initialised_group = False;
273         static BOOL initialised_alias = False;
274         char *groupname_map_file = lp_groupname_map();
275         char *aliasname_map_file = lp_aliasname_map();
276
277         SMB_STRUCT_STAT st;
278         FILE *fp;
279         char *s;
280         pstring buf;
281         name_map_entry *new_ep;
282
283         time_t *file_last_modified;
284         int    *initialised;
285         char   *map_file;
286         ubi_slList *map_list;
287
288         if (type == GROUP_DOMAIN)
289         {
290                 file_last_modified = &groupmap_file_last_modified;
291                 initialised        = &initialised_group;
292                 map_file           = groupname_map_file;
293                 map_list           = &groupname_map_list;
294         }
295         else
296         {
297                 file_last_modified = &aliasmap_file_last_modified;
298                 initialised        = &initialised_alias;
299                 map_file           = aliasname_map_file;
300                 map_list           = &aliasname_map_list;
301         }
302
303         if (!(*initialised))
304         {
305                 DEBUG(10,("initialising group map %s\n", map_file));
306                 ubi_slInitList(map_list);
307                 (*initialised) = True;
308         }
309
310         if (!*map_file)
311         {
312                 return map_list;
313         }
314
315         if (sys_stat(map_file, &st) != 0)
316         {
317                 DEBUG(0, ("load_name_map: Unable to stat file %s. Error was %s\n",
318                            map_file, strerror(errno) ));
319                 return map_list;
320         }
321
322         /*
323          * Check if file has changed.
324          */
325         if (st.st_mtime <= (*file_last_modified))
326         {
327                 return map_list;
328         }
329
330         (*file_last_modified) = st.st_mtime;
331
332         /*
333          * Load the file.
334          */
335
336         fp = fopen(map_file,"r");
337         if (!fp)
338         {
339                 DEBUG(0,("load_name_map: can't open name map %s. Error was %s\n",
340                           map_file, strerror(errno)));
341                 return map_list;
342         }
343
344         /*
345          * Throw away any previous list.
346          */
347         delete_map_list(map_list);
348
349         DEBUG(4,("load_name_map: Scanning name map %s\n",map_file));
350
351         while ((s = fgets_slash(buf, sizeof(buf), fp)) != NULL)
352         {
353                 pstring unixname;
354                 pstring nt_name;
355                 fstring nt_domain;
356                 fstring nt_group;
357                 char *p;
358
359                 DEBUG(10,("Read line |%s|\n", s));
360
361                 memset(nt_name, 0, sizeof(nt_name));
362
363                 if (!*s || strchr("#;",*s))
364                         continue;
365
366                 if (!next_token(&s,unixname, "\t\n\r=", sizeof(unixname)))
367                         continue;
368
369                 if (!next_token(&s,nt_name, "\t\n\r=", sizeof(nt_name)))
370                         continue;
371
372                 trim_string(unixname, " ", " ");
373                 trim_string(nt_name, " ", " ");
374
375                 if (!*nt_name)
376                         continue;
377
378                 if (!*unixname)
379                         continue;
380
381                 DEBUG(5,("unixname = %s, ntname = %s.\n",
382                           unixname, nt_name));
383
384                 p = strchr(nt_name, '\\');
385
386                 if (p == NULL)
387                 {
388                         memset(nt_domain, 0, sizeof(nt_domain));
389                         fstrcpy(nt_group, nt_name);
390                 }
391                 else
392                 {
393                         *p = 0;
394                         p++;
395                         fstrcpy(nt_domain, nt_name);
396                         fstrcpy(nt_group , p);
397                 }
398
399                 if (make_name_entry(&new_ep, nt_domain, nt_name, unixname, type))
400                 {
401                         ubi_slAddTail(map_list, (ubi_slNode *)new_ep);
402                 }
403         }
404
405         DEBUG(10,("load_name_map: Added %ld entries to name map.\n",
406                    ubi_slCount(map_list)));
407
408         fclose(fp);
409
410         return map_list;
411 }
412
413 /***********************************************************
414  Lookup by SID
415 ************************************************************/
416 static BOOL map_sid(GROUP_TYPE type,
417                 DOM_SID *psid, gid_t *gid, char *ntname, char *ntdomain)
418 {
419         name_map_entry *gmep;
420         ubi_slList *map_list;
421
422         /*
423          * Initialise and load if not already loaded.
424          */
425         map_list = load_name_map(type);
426
427         for (gmep = (name_map_entry *)ubi_slFirst(map_list);
428              gmep != NULL;
429              gmep = (name_map_entry *)ubi_slNext(gmep ))
430         {
431                 if (sid_equal(&gmep->grp.sid, psid))
432                 {
433                         if (gid != NULL)
434                         {
435                                 *gid = gmep->grp.unix_gid;
436                         }
437                         if (ntname != NULL)
438                         {
439                                 fstrcpy(ntname, gmep->grp.nt_name);
440                         }
441                         if (ntdomain != NULL)
442                         {
443                                 fstrcpy(ntdomain, gmep->grp.nt_domain);
444                         }
445                         DEBUG(7,("map_sid: Mapping unix group %s to nt group %s.\n",
446                                gmep->grp.unix_name, gmep->grp.nt_name ));
447                         return True;
448                 }
449         }
450
451         return False;
452 }
453
454 /***********************************************************
455  Lookup nt name.
456 ************************************************************/
457 static BOOL map_ntname(GROUP_TYPE type,
458                 char *ntname, char *ntdomain, DOM_SID *psid,
459                 char *unixname, gid_t *gid)
460 {
461         name_map_entry *gmep;
462         ubi_slList *map_list;
463
464         /*
465          * Initialise and load if not already loaded.
466          */
467         map_list = load_name_map(type);
468
469         for (gmep = (name_map_entry *)ubi_slFirst(map_list);
470              gmep != NULL;
471              gmep = (name_map_entry *)ubi_slNext(gmep ))
472         {
473                 if (strequal(gmep->grp.nt_name  , ntname) &&
474                     strequal(gmep->grp.nt_domain, ntdomain))
475                 {
476                         if (psid != NULL)
477                         {
478                                 sid_copy(psid, &gmep->grp.sid);
479                         }
480                         if (gid != NULL)
481                         {
482                                 *gid = gmep->grp.unix_gid;
483                         }
484                         if (unixname != NULL)
485                         {
486                                 fstrcpy(unixname, gmep->grp.unix_name);
487                         }
488                         DEBUG(7,("map_ntname: Mapping unix group %s to nt group %s.\n",
489                                gmep->grp.unix_name, gmep->grp.nt_name ));
490                         return True;
491                 }
492         }
493
494         return False;
495 }
496
497 /***********************************************************
498  Lookup unix name.
499 ************************************************************/
500 static BOOL map_unixname(GROUP_TYPE type,
501                 char *unixname, DOM_SID *psid, char *ntname, char *ntdomain)
502 {
503         name_map_entry *gmep;
504         ubi_slList *map_list;
505
506         /*
507          * Initialise and load if not already loaded.
508          */
509         map_list = load_name_map(type);
510
511         for (gmep = (name_map_entry *)ubi_slFirst(map_list);
512              gmep != NULL;
513              gmep = (name_map_entry *)ubi_slNext(gmep ))
514         {
515                 if (strequal(gmep->grp.unix_name, unixname))
516                 {
517                         if (psid != NULL)
518                         {
519                                 sid_copy(psid, &gmep->grp.sid);
520                         }
521                         if (ntname != NULL)
522                         {
523                                 fstrcpy(ntname, gmep->grp.nt_name);
524                         }
525                         if (ntdomain != NULL)
526                         {
527                                 fstrcpy(ntdomain, gmep->grp.nt_domain);
528                         }
529                         DEBUG(7,("map_unixname: Mapping unix group %s to nt group %s.\n",
530                                gmep->grp.unix_name, gmep->grp.nt_name ));
531                         return True;
532                 }
533         }
534
535         return False;
536 }
537
538 /***********************************************************
539  Lookup by gid_t.
540 ************************************************************/
541 static BOOL map_gid(GROUP_TYPE type, 
542                 gid_t gid, DOM_SID *psid, char *ntname, char *ntdomain)
543 {
544         name_map_entry *gmep;
545         ubi_slList *map_list;
546
547         /*
548          * Initialise and load if not already loaded.
549          */
550         map_list = load_name_map(type);
551
552         for (gmep = (name_map_entry *)ubi_slFirst(map_list);
553              gmep != NULL;
554              gmep = (name_map_entry *)ubi_slNext(gmep ))
555         {
556                 fstring sid_str;
557                 sid_to_string(sid_str, &gmep->grp.sid);
558                 DEBUG(10,("map_gid: enum entry unix group %s %d nt %s %s\n",
559                                gmep->grp.unix_name, gmep->grp.unix_gid, gmep->grp.nt_name, sid_str));
560                 if (gmep->grp.unix_gid == gid)
561                 {
562                         if (psid != NULL)
563                         {
564                                 sid_copy(psid, &gmep->grp.sid);
565                         }
566                         if (ntname != NULL)
567                         {
568                                 fstrcpy(ntname, gmep->grp.nt_name);
569                         }
570                         if (ntdomain != NULL)
571                         {
572                                 fstrcpy(ntdomain, gmep->grp.nt_domain);
573                         }
574                         DEBUG(7,("map_gid: Mapping unix group %s to nt group %s.\n",
575                                gmep->grp.unix_name, gmep->grp.nt_name ));
576                         return True;
577                 }
578         }
579
580         return False;
581 }
582
583 /***********************************************************
584  *
585  * Call four functions to resolve unix group ids and either
586  * local group SIDs or domain group SIDs listed in the local group
587  * or domain group map files.
588  *
589  * Note that it is *NOT* the responsibility of these functions to
590  * resolve entries that are not in the map files.
591  *
592  * Any SID can be in the map files (i.e from any Domain).
593  *
594  ***********************************************************/
595
596 /***********************************************************
597  Lookup a Group entry by sid.
598 ************************************************************/
599 BOOL map_group_sid(DOM_SID *psid, gid_t *gid, char *group_name, char *nt_domain)
600 {
601         return map_sid(GROUP_DOMAIN, psid, gid, group_name, nt_domain);
602 }
603
604 /***********************************************************
605  Lookup an Alias SID entry by name.
606 ************************************************************/
607 BOOL map_alias_sid(DOM_SID *psid, gid_t *gid, char *alias_name, char *nt_domain)
608 {
609         return map_sid(GROUP_LOCAL, psid, gid, alias_name, nt_domain);
610 }
611
612 /***********************************************************
613  Lookup a UNIX Group entry by name.
614 ************************************************************/
615 BOOL map_unix_group_name(char *group_name, DOM_SID *psid, char *ntgroup_name, char *nt_domain)
616 {
617         return map_unixname(GROUP_DOMAIN, group_name, psid, ntgroup_name, nt_domain);
618 }
619
620 /***********************************************************
621  Lookup a UNIX Alias entry by name.
622 ************************************************************/
623 BOOL map_unix_alias_name(char *alias_name, DOM_SID *psid, char *ntalias_name, char *nt_domain)
624 {
625         return map_unixname(GROUP_LOCAL, alias_name, psid, ntalias_name, nt_domain);
626 }
627
628 /***********************************************************
629  Lookup a Group entry
630 ************************************************************/
631 BOOL map_nt_group_name(char *ntgroup_name, char *nt_domain, DOM_SID *psid, char *group_name, gid_t *gid)
632 {
633         return map_ntname(GROUP_DOMAIN, ntgroup_name, nt_domain, psid, group_name, gid);
634 }
635
636 /***********************************************************
637  Lookup an Alias name entry 
638 ************************************************************/
639 BOOL map_nt_alias_name(char *ntalias_name, char *nt_domain, DOM_SID *psid, char *alias_name, gid_t *gid)
640 {
641         return map_ntname(GROUP_LOCAL, ntalias_name, nt_domain, psid, alias_name, gid);
642 }
643
644 /***********************************************************
645  Lookup an Alias SID entry by gid_t.
646 ************************************************************/
647 BOOL map_alias_gid(gid_t gid, DOM_SID *psid, char *nt_als_name, char *nt_domain)
648 {
649         return map_gid(GROUP_LOCAL, gid, psid, nt_als_name, nt_domain);
650 }
651
652 /***********************************************************
653  Lookup a Group SID entry by gid_t.
654 ************************************************************/
655 BOOL map_group_gid( gid_t gid, DOM_SID *psid, char *nt_grp_name, char *nt_domain)
656 {
657         return map_gid(GROUP_DOMAIN, gid, psid, nt_grp_name, nt_domain);
658 }
659