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