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