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