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