Initial import
[samba] / source / passdb / lookup_sid.c
1 /* 
2    Unix SMB/CIFS implementation.
3    uid/user handling
4    Copyright (C) Andrew Tridgell         1992-1998
5    Copyright (C) Gerald (Jerry) Carter   2003
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 #include "includes.h"
23
24 /*****************************************************************
25  *THE CANONICAL* convert name to SID function.
26  Tries local lookup first - for local domains - then uses winbind.
27 *****************************************************************/  
28
29 BOOL lookup_name(const char *domain, const char *name, DOM_SID *psid, enum SID_NAME_USE *name_type)
30 {
31         fstring sid;
32         BOOL local_lookup = False;
33         
34         *name_type = SID_NAME_UNKNOWN;
35
36         /* If we are looking up a domain user, make sure it is
37            for the local machine only */
38         
39         if (strequal(domain, get_global_sam_name())) {
40                 if (local_lookup_name(name, psid, name_type)) {
41                         DEBUG(10,
42                               ("lookup_name: (local) [%s]\\[%s] -> SID %s (type %s: %u)\n",
43                                domain, name, sid_to_string(sid,psid),
44                                sid_type_lookup(*name_type), (unsigned int)*name_type));
45                         return True;
46                 }
47         } else {
48                 /* Remote */
49                 if (winbind_lookup_name(domain, name, psid, name_type)) {
50                         
51                         DEBUG(10,("lookup_name (winbindd): [%s]\\[%s] -> SID %s (type %u)\n",
52                                   domain, name, sid_to_string(sid, psid), 
53                                   (unsigned int)*name_type));
54                         return True;
55                 }
56         }
57         
58         DEBUG(10, ("lookup_name: %s lookup for [%s]\\[%s] failed\n", 
59                    local_lookup ? "local" : "winbind", domain, name));
60
61         return False;
62 }
63
64 /*****************************************************************
65  *THE CANONICAL* convert SID to name function.
66  Tries local lookup first - for local sids, then tries winbind.
67 *****************************************************************/  
68
69 BOOL lookup_sid(const DOM_SID *sid, fstring dom_name, fstring name,
70                 enum SID_NAME_USE *name_type)
71 {
72         if (!name_type)
73                 return False;
74
75         *name_type = SID_NAME_UNKNOWN;
76
77         /* Check if this is our own sid.  This should perhaps be done by
78            winbind?  For the moment handle it here. */
79
80         if (sid_check_is_domain(sid)) {
81                 fstrcpy(dom_name, get_global_sam_name());
82                 fstrcpy(name, "");
83                 *name_type = SID_NAME_DOMAIN;
84                 return True;
85         }
86
87         if (sid_check_is_in_our_domain(sid)) {
88                 uint32 rid;
89                 SMB_ASSERT(sid_peek_rid(sid, &rid));
90
91                 /* For our own domain passdb is responsible */
92                 fstrcpy(dom_name, get_global_sam_name());
93                 return lookup_global_sam_rid(rid, name, name_type);
94         }
95
96         if (sid_check_is_builtin(sid)) {
97
98                 /* Got through map_domain_sid_to_name here so that the mapping
99                  * of S-1-5-32 to the name "BUILTIN" in as few places as
100                  * possible. We might add i18n... */
101                 SMB_ASSERT(map_domain_sid_to_name(sid, dom_name));
102
103                 /* Yes, W2k3 returns "BUILTIN" both as domain and name here */
104                 fstrcpy(name, dom_name); 
105
106                 *name_type = SID_NAME_DOMAIN;
107                 return True;
108         }
109
110         if (sid_check_is_in_builtin(sid)) {
111                 uint32 rid;
112
113                 SMB_ASSERT(sid_peek_rid(sid, &rid));
114
115                 /* Got through map_domain_sid_to_name here so that the mapping
116                  * of S-1-5-32 to the name "BUILTIN" in as few places as
117                  * possible. We might add i18n... */
118                 SMB_ASSERT(map_domain_sid_to_name(&global_sid_Builtin,
119                                                   dom_name));
120
121                 /* There's only aliases in S-1-5-32 */
122                 *name_type = SID_NAME_ALIAS;
123
124                 return lookup_builtin_rid(rid, name);
125         }
126
127         if (winbind_lookup_sid(sid, dom_name, name, name_type)) {
128                 return True;
129         }
130
131         DEBUG(10,("lookup_sid: winbind lookup for SID %s failed - trying "
132                   "special SIDs.\n", sid_string_static(sid)));
133
134         {
135                 const char *dom, *obj_name;
136                 
137                 if (lookup_special_sid(sid, &dom, &obj_name, name_type)) {
138                         DEBUG(10, ("found %s\\%s\n", dom, obj_name));
139                         fstrcpy(dom_name, dom);
140                         fstrcpy(name, obj_name);
141                         return True;
142                 }
143         }
144
145         DEBUG(10, ("lookup_sid failed\n"));
146
147         return False;
148 }
149
150 /*****************************************************************
151  Id mapping cache.  This is to avoid Winbind mappings already
152  seen by smbd to be queried too frequently, keeping winbindd
153  busy, and blocking smbd while winbindd is busy with other
154  stuff. Written by Michael Steffens <michael.steffens@hp.com>,
155  modified to use linked lists by jra.
156 *****************************************************************/  
157
158 #define MAX_UID_SID_CACHE_SIZE 100
159 #define TURNOVER_UID_SID_CACHE_SIZE 10
160 #define MAX_GID_SID_CACHE_SIZE 100
161 #define TURNOVER_GID_SID_CACHE_SIZE 10
162
163 static size_t n_uid_sid_cache = 0;
164 static size_t n_gid_sid_cache = 0;
165
166 static struct uid_sid_cache {
167         struct uid_sid_cache *next, *prev;
168         uid_t uid;
169         DOM_SID sid;
170         enum SID_NAME_USE sidtype;
171 } *uid_sid_cache_head;
172
173 static struct gid_sid_cache {
174         struct gid_sid_cache *next, *prev;
175         gid_t gid;
176         DOM_SID sid;
177         enum SID_NAME_USE sidtype;
178 } *gid_sid_cache_head;
179
180 /*****************************************************************
181   Find a SID given a uid.
182 *****************************************************************/  
183
184 static BOOL fetch_sid_from_uid_cache(DOM_SID *psid, uid_t uid)
185 {
186         struct uid_sid_cache *pc;
187
188         for (pc = uid_sid_cache_head; pc; pc = pc->next) {
189                 if (pc->uid == uid) {
190                         fstring sid;
191                         *psid = pc->sid;
192                         DEBUG(3,("fetch sid from uid cache %u -> %s\n",
193                                 (unsigned int)uid, sid_to_string(sid, psid)));
194                         DLIST_PROMOTE(uid_sid_cache_head, pc);
195                         return True;
196                 }
197         }
198         return False;
199 }
200
201 /*****************************************************************
202   Find a uid given a SID.
203 *****************************************************************/  
204
205 static BOOL fetch_uid_from_cache( uid_t *puid, const DOM_SID *psid )
206 {
207         struct uid_sid_cache *pc;
208
209         for (pc = uid_sid_cache_head; pc; pc = pc->next) {
210                 if (sid_compare(&pc->sid, psid) == 0) {
211                         fstring sid;
212                         *puid = pc->uid;
213                         DEBUG(3,("fetch uid from cache %u -> %s\n",
214                                 (unsigned int)*puid, sid_to_string(sid, psid)));
215                         DLIST_PROMOTE(uid_sid_cache_head, pc);
216                         return True;
217                 }
218         }
219         return False;
220 }
221
222 /*****************************************************************
223  Store uid to SID mapping in cache.
224 *****************************************************************/  
225
226 static void store_uid_sid_cache(const DOM_SID *psid, uid_t uid)
227 {
228         struct uid_sid_cache *pc;
229
230         if (n_uid_sid_cache >= MAX_UID_SID_CACHE_SIZE && n_uid_sid_cache > TURNOVER_UID_SID_CACHE_SIZE) {
231                 /* Delete the last TURNOVER_UID_SID_CACHE_SIZE entries. */
232                 struct uid_sid_cache *pc_next;
233                 size_t i;
234
235                 for (i = 0, pc = uid_sid_cache_head; i < (n_uid_sid_cache - TURNOVER_UID_SID_CACHE_SIZE); i++, pc = pc->next)
236                         ;
237                 for(; pc; pc = pc_next) {
238                         pc_next = pc->next;
239                         DLIST_REMOVE(uid_sid_cache_head,pc);
240                         SAFE_FREE(pc);
241                         n_uid_sid_cache--;
242                 }
243         }
244
245         pc = SMB_MALLOC_P(struct uid_sid_cache);
246         if (!pc)
247                 return;
248         pc->uid = uid;
249         sid_copy(&pc->sid, psid);
250         DLIST_ADD(uid_sid_cache_head, pc);
251         n_uid_sid_cache++;
252 }
253
254 /*****************************************************************
255   Find a SID given a gid.
256 *****************************************************************/  
257
258 static BOOL fetch_sid_from_gid_cache(DOM_SID *psid, gid_t gid)
259 {
260         struct gid_sid_cache *pc;
261
262         for (pc = gid_sid_cache_head; pc; pc = pc->next) {
263                 if (pc->gid == gid) {
264                         fstring sid;
265                         *psid = pc->sid;
266                         DEBUG(3,("fetch sid from gid cache %u -> %s\n",
267                                 (unsigned int)gid, sid_to_string(sid, psid)));
268                         DLIST_PROMOTE(gid_sid_cache_head, pc);
269                         return True;
270                 }
271         }
272         return False;
273 }
274
275 /*****************************************************************
276   Find a gid given a SID.
277 *****************************************************************/  
278
279 static BOOL fetch_gid_from_cache(gid_t *pgid, const DOM_SID *psid)
280 {
281         struct gid_sid_cache *pc;
282
283         for (pc = gid_sid_cache_head; pc; pc = pc->next) {
284                 if (sid_compare(&pc->sid, psid) == 0) {
285                         fstring sid;
286                         *pgid = pc->gid;
287                         DEBUG(3,("fetch gid from cache %u -> %s\n",
288                                  (unsigned int)*pgid, sid_to_string(sid, psid)));
289                         DLIST_PROMOTE(gid_sid_cache_head, pc);
290                         return True;
291                 }
292         }
293         return False;
294 }
295
296 /*****************************************************************
297  Store gid to SID mapping in cache.
298 *****************************************************************/  
299
300 static void store_gid_sid_cache(const DOM_SID *psid, gid_t gid)
301 {
302         struct gid_sid_cache *pc;
303
304         if (n_gid_sid_cache >= MAX_GID_SID_CACHE_SIZE && n_gid_sid_cache > TURNOVER_GID_SID_CACHE_SIZE) {
305                 /* Delete the last TURNOVER_GID_SID_CACHE_SIZE entries. */
306                 struct gid_sid_cache *pc_next;
307                 size_t i;
308
309                 for (i = 0, pc = gid_sid_cache_head; i < (n_gid_sid_cache - TURNOVER_GID_SID_CACHE_SIZE); i++, pc = pc->next)
310                         ;
311                 for(; pc; pc = pc_next) {
312                         pc_next = pc->next;
313                         DLIST_REMOVE(gid_sid_cache_head,pc);
314                         SAFE_FREE(pc);
315                         n_gid_sid_cache--;
316                 }
317         }
318
319         pc = SMB_MALLOC_P(struct gid_sid_cache);
320         if (!pc)
321                 return;
322         pc->gid = gid;
323         sid_copy(&pc->sid, psid);
324         DLIST_ADD(gid_sid_cache_head, pc);
325         n_gid_sid_cache++;
326 }
327
328 /*****************************************************************
329  *THE CANONICAL* convert uid_t to SID function.
330 *****************************************************************/  
331
332 NTSTATUS uid_to_sid(DOM_SID *psid, uid_t uid)
333 {
334         fstring sid;
335         uid_t low, high;
336
337         ZERO_STRUCTP(psid);
338
339         if (fetch_sid_from_uid_cache(psid, uid))
340                 return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
341
342         /* DC's never use winbindd to resolve users outside the 
343            defined idmap range */
344
345         if ( lp_server_role()==ROLE_DOMAIN_MEMBER 
346                 || (lp_idmap_uid(&low, &high) && uid >= low && uid <= high) ) 
347         {
348                 if (winbind_uid_to_sid(psid, uid)) {
349
350                         DEBUG(10,("uid_to_sid: winbindd %u -> %s\n",
351                                 (unsigned int)uid, sid_to_string(sid, psid)));
352
353                         if (psid)
354                                 store_uid_sid_cache(psid, uid);
355                         return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
356                 }
357         }
358
359         if (!local_uid_to_sid(psid, uid)) {
360                 DEBUG(10,("uid_to_sid: local %u failed to map to sid\n", (unsigned int)uid ));
361                 return NT_STATUS_UNSUCCESSFUL;
362         }
363         
364         DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid, sid_to_string(sid, psid)));
365
366         store_uid_sid_cache(psid, uid);
367         return NT_STATUS_OK;
368 }
369
370 /*****************************************************************
371  *THE CANONICAL* convert gid_t to SID function.
372 *****************************************************************/  
373
374 NTSTATUS gid_to_sid(DOM_SID *psid, gid_t gid)
375 {
376         fstring sid;
377         gid_t low, high;
378
379         ZERO_STRUCTP(psid);
380
381         if (fetch_sid_from_gid_cache(psid, gid))
382                 return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
383
384         /* DC's never use winbindd to resolve groups outside the
385            defined idmap range */
386
387         if ( lp_server_role()==ROLE_DOMAIN_MEMBER
388                 || (lp_idmap_gid(&low, &high) && gid >= low && gid <= high) )
389         {
390                 if (winbind_gid_to_sid(psid, gid)) {
391
392                         DEBUG(10,("gid_to_sid: winbindd %u -> %s\n",
393                                 (unsigned int)gid, sid_to_string(sid, psid)));
394                         
395                         if (psid)
396                                 store_gid_sid_cache(psid, gid);
397                         return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
398                 }
399         }
400
401         if (!local_gid_to_sid(psid, gid)) {
402                 DEBUG(10,("gid_to_sid: local %u failed to map to sid\n", (unsigned int)gid ));
403                 return NT_STATUS_UNSUCCESSFUL;
404         }
405         
406         DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid, sid_to_string(sid, psid)));
407
408         store_gid_sid_cache(psid, gid);
409         return NT_STATUS_OK;
410 }
411
412 /*****************************************************************
413  *THE CANONICAL* convert SID to uid function.
414 *****************************************************************/  
415
416 NTSTATUS sid_to_uid(const DOM_SID *psid, uid_t *puid)
417 {
418         fstring dom_name, name, sid_str;
419         enum SID_NAME_USE name_type;
420
421         if (fetch_uid_from_cache(puid, psid))
422                 return NT_STATUS_OK;
423
424         /* if this is our SID then go straight to a local lookup */
425         
426         if ( sid_compare_domain(get_global_sam_sid(), psid) == 0 ) {
427                 DEBUG(10,("sid_to_uid: my domain (%s) - trying local.\n",
428                         sid_string_static(psid) ));
429                 
430                 if ( local_sid_to_uid(puid, psid, &name_type) )
431                         goto success;
432                         
433                 DEBUG(10,("sid_to_uid: local lookup failed\n"));
434                 
435                 return NT_STATUS_UNSUCCESSFUL;
436         }
437         
438         /* If it is not our local domain, only hope is winbindd */
439
440         if ( !winbind_lookup_sid(psid, dom_name, name, &name_type) ) {
441                 DEBUG(10,("sid_to_uid: winbind lookup for non-local sid %s failed\n",
442                         sid_string_static(psid) ));
443                         
444                 return NT_STATUS_UNSUCCESSFUL;
445         }
446
447         /* If winbindd does know the SID, ensure this is a user */
448
449         if (name_type != SID_NAME_USER) {
450                 DEBUG(10,("sid_to_uid: winbind lookup succeeded but SID is not a user (%u)\n",
451                         (unsigned int)name_type ));
452                 return NT_STATUS_INVALID_PARAMETER;
453         }
454
455         /* get the uid.  Has to work or else we are dead in the water */
456
457         if ( !winbind_sid_to_uid(puid, psid) ) {
458                 DEBUG(10,("sid_to_uid: winbind failed to allocate a new uid for sid %s\n",
459                         sid_to_string(sid_str, psid) ));
460                 return NT_STATUS_UNSUCCESSFUL;
461         }
462
463 success:
464         DEBUG(10,("sid_to_uid: %s -> %u\n", sid_to_string(sid_str, psid),
465                 (unsigned int)*puid ));
466
467         store_uid_sid_cache(psid, *puid);
468         
469         return NT_STATUS_OK;
470 }
471 /*****************************************************************
472  *THE CANONICAL* convert SID to gid function.
473  Group mapping is used for gids that maps to Wellknown SIDs
474 *****************************************************************/  
475
476 NTSTATUS sid_to_gid(const DOM_SID *psid, gid_t *pgid)
477 {
478         fstring dom_name, name, sid_str;
479         enum SID_NAME_USE name_type;
480
481         if (fetch_gid_from_cache(pgid, psid))
482                 return NT_STATUS_OK;
483
484         /*
485          * First we must look up the name and decide if this is a group sid.
486          * Group mapping can deal with foreign SIDs
487          */
488
489         if ( local_sid_to_gid(pgid, psid, &name_type) )
490                 goto success;
491         
492         if (!winbind_lookup_sid(psid, dom_name, name, &name_type)) {
493                 DEBUG(10,("sid_to_gid: no one knows the SID %s (tried local, then winbind)\n", sid_to_string(sid_str, psid)));
494                 
495                 return NT_STATUS_UNSUCCESSFUL;
496         }
497
498         /* winbindd knows it; Ensure this is a group sid */
499
500         if ((name_type != SID_NAME_DOM_GRP) && (name_type != SID_NAME_ALIAS) 
501                 && (name_type != SID_NAME_WKN_GRP)) 
502         {
503                 DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is not a known group (%u)\n",
504                         (unsigned int)name_type ));
505
506                 /* winbindd is running and knows about this SID.  Just the wrong type.
507                    Don't fallback to a local lookup here */
508                    
509                 return NT_STATUS_INVALID_PARAMETER;
510         }
511         
512         /* winbindd knows it and it is a type of group; sid_to_gid must succeed
513            or we are dead in the water */
514
515         if ( !winbind_sid_to_gid(pgid, psid) ) {
516                 DEBUG(10,("sid_to_gid: winbind failed to allocate a new gid for sid %s\n",
517                         sid_to_string(sid_str, psid) ));
518                 return NT_STATUS_UNSUCCESSFUL;
519         }
520
521 success:
522         DEBUG(10,("sid_to_gid: %s -> %u\n", sid_to_string(sid_str, psid),
523                 (unsigned int)*pgid ));
524
525         store_gid_sid_cache(psid, *pgid);
526         
527         return NT_STATUS_OK;
528 }
529