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