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