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