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