sync 3.0 into HEAD for the last time
[vlendec/samba-autobuild/.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(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         uid_t low, high;
303         fstring sid;
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         if (lp_idmap_uid(&low, &high) && uid >= low && uid <= high) {
311                 if (winbind_uid_to_sid(psid, uid)) {
312
313                         DEBUG(10,("uid_to_sid: winbindd %u -> %s\n",
314                                 (unsigned int)uid, sid_to_string(sid, psid)));
315
316                         if (psid)
317                                 store_uid_sid_cache(psid, uid);
318                         return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
319                 }
320         }
321
322         if (!local_uid_to_sid(psid, uid)) {
323                 DEBUG(10,("uid_to_sid: local %u failed to map to sid\n", (unsigned int)uid ));
324                 return NT_STATUS_UNSUCCESSFUL;
325         }
326         
327         DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid, sid_to_string(sid, psid)));
328
329         store_uid_sid_cache(psid, uid);
330         return NT_STATUS_OK;
331 }
332
333 /*****************************************************************
334  *THE CANONICAL* convert gid_t to SID function.
335 *****************************************************************/  
336
337 NTSTATUS gid_to_sid(DOM_SID *psid, gid_t gid)
338 {
339         gid_t low, high;
340         fstring sid;
341
342         ZERO_STRUCTP(psid);
343
344         if (fetch_sid_from_gid_cache(psid, gid))
345                 return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
346
347         if (lp_idmap_gid(&low, &high) && gid >= low && gid <= high) {
348                 if (winbind_gid_to_sid(psid, gid)) {
349
350                         DEBUG(10,("gid_to_sid: winbindd %u -> %s\n",
351                                 (unsigned int)gid, sid_to_string(sid, psid)));
352                         
353                         if (psid)
354                                 store_gid_sid_cache(psid, gid);
355                         return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
356                 }
357         }
358
359         if (!local_gid_to_sid(psid, gid)) {
360                 DEBUG(10,("gid_to_sid: local %u failed to map to sid\n", (unsigned int)gid ));
361                 return NT_STATUS_UNSUCCESSFUL;
362         }
363         
364         DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid, sid_to_string(sid, psid)));
365
366         store_gid_sid_cache(psid, gid);
367         return NT_STATUS_OK;
368 }
369
370 /*****************************************************************
371  *THE CANONICAL* convert SID to uid function.
372 *****************************************************************/  
373
374 NTSTATUS sid_to_uid(const DOM_SID *psid, uid_t *puid)
375 {
376         fstring dom_name, name, sid_str;
377         enum SID_NAME_USE name_type;
378
379         if (fetch_uid_from_cache(puid, psid))
380                 return NT_STATUS_OK;
381
382         /* if this is our SID then go straight to a local lookup */
383         
384         if ( sid_compare_domain(get_global_sam_sid(), psid) == 0 ) {
385                 DEBUG(10,("sid_to_uid: my domain (%s) - trying local.\n",
386                         sid_string_static(psid) ));
387                 
388                 if ( local_sid_to_uid(puid, psid, &name_type) )
389                         goto success;
390                         
391                 DEBUG(10,("sid_to_uid: local lookup failed\n"));
392                 
393                 return NT_STATUS_UNSUCCESSFUL;
394         }
395         
396         /* If it is not our local domain, only hope is winbindd */
397
398         if ( !winbind_lookup_sid(psid, dom_name, name, &name_type) ) {
399                 DEBUG(10,("sid_to_uid: winbind lookup for non-local sid %s failed\n",
400                         sid_string_static(psid) ));
401                         
402                 return NT_STATUS_UNSUCCESSFUL;
403         }
404
405         /* If winbindd does know the SID, ensure this is a user */
406
407         if (name_type != SID_NAME_USER) {
408                 DEBUG(10,("sid_to_uid: winbind lookup succeeded but SID is not a user (%u)\n",
409                         (unsigned int)name_type ));
410                 return NT_STATUS_INVALID_PARAMETER;
411         }
412
413         /* get the uid.  Has to work or else we are dead in the water */
414
415         if ( !winbind_sid_to_uid(puid, psid) ) {
416                 DEBUG(10,("sid_to_uid: winbind failed to allocate a new uid for sid %s\n",
417                         sid_to_string(sid_str, psid) ));
418                 return NT_STATUS_UNSUCCESSFUL;
419         }
420
421 success:
422         DEBUG(10,("sid_to_uid: %s -> %u\n", sid_to_string(sid_str, psid),
423                 (unsigned int)*puid ));
424
425         store_uid_sid_cache(psid, *puid);
426         
427         return NT_STATUS_OK;
428 }
429 /*****************************************************************
430  *THE CANONICAL* convert SID to gid function.
431  Group mapping is used for gids that maps to Wellknown SIDs
432 *****************************************************************/  
433
434 NTSTATUS sid_to_gid(const DOM_SID *psid, gid_t *pgid)
435 {
436         fstring dom_name, name, sid_str;
437         enum SID_NAME_USE name_type;
438
439         if (fetch_gid_from_cache(pgid, psid))
440                 return NT_STATUS_OK;
441
442         /*
443          * First we must look up the name and decide if this is a group sid.
444          * Group mapping can deal with foreign SIDs
445          */
446
447         if (!winbind_lookup_sid(psid, dom_name, name, &name_type)) {
448                 DEBUG(10,("sid_to_gid: winbind lookup for sid %s failed - trying local.\n",
449                         sid_to_string(sid_str, psid) ));
450
451                 if ( local_sid_to_gid(pgid, psid, &name_type) )
452                         goto success;
453                         
454                 DEBUG(10,("sid_to_gid: no one knows this SID\n"));
455                 
456                 return NT_STATUS_UNSUCCESSFUL;
457         }
458
459         /* winbindd knows it; Ensure this is a group sid */
460
461         if ((name_type != SID_NAME_DOM_GRP) && (name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_WKN_GRP)) {
462                 DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is not a known group (%u)\n",
463                         (unsigned int)name_type ));
464
465                 /* winbindd is running and knows about this SID.  Just the wrong type.
466                    Don't fallback to a local lookup here */
467                    
468                 return NT_STATUS_INVALID_PARAMETER;
469         }
470         
471         /* winbindd knows it and it is a type of group; sid_to_gid must succeed
472            or we are dead in the water */
473
474         if ( !winbind_sid_to_gid(pgid, psid) ) {
475                 DEBUG(10,("sid_to_uid: winbind failed to allocate a new gid for sid %s\n",
476                         sid_to_string(sid_str, psid) ));
477                 return NT_STATUS_UNSUCCESSFUL;
478         }
479
480 success:
481         DEBUG(10,("sid_to_gid: %s -> %u\n", sid_to_string(sid_str, psid),
482                 (unsigned int)*pgid ));
483
484         store_gid_sid_cache(psid, *pgid);
485         
486         return NT_STATUS_OK;
487 }
488