s3:idmap_cache: add common exit point to idmap_cache_find_sid2unixid()
[kai/samba.git] / source3 / lib / idmap_cache.c
1 /*
2    Unix SMB/CIFS implementation.
3    ID Mapping Cache
4
5    Copyright (C) Volker Lendecke        2008
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 3 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, see <http://www.gnu.org/licenses/>.*/
19
20 #include "includes.h"
21 #include "idmap_cache.h"
22 #include "../libcli/security/security.h"
23 #include "../librpc/gen_ndr/idmap.h"
24
25 /**
26  * Find a sid2xid mapping
27  * @param[in] sid               the sid to map
28  * @param[out] id               where to put the result
29  * @param[out] expired          is the cache entry expired?
30  * @retval Was anything in the cache at all?
31  *
32  * If id->id == -1 this was a negative mapping.
33  */
34
35 bool idmap_cache_find_sid2unixid(const struct dom_sid *sid, struct unixid *id,
36                                  bool *expired)
37 {
38         fstring sidstr;
39         char *key;
40         char *value = NULL;
41         char *endptr;
42         time_t timeout;
43         bool ret;
44         struct unixid tmp_id;
45
46         key = talloc_asprintf(talloc_tos(), "IDMAP/SID2XID/%s",
47                               sid_to_fstring(sidstr, sid));
48         if (key == NULL) {
49                 return false;
50         }
51         ret = gencache_get(key, &value, &timeout);
52         if (!ret) {
53                 goto done;
54         }
55
56         DEBUG(10, ("Parsing value for key [%s]: value=[%s]\n", key, value));
57
58         tmp_id.id = strtol(value, &endptr, 10);
59         DEBUG(10, ("Parsing value for key [%s]: id=[%llu], endptr=[%s]\n",
60                    key, (unsigned long long)tmp_id.id, endptr));
61
62         ret = (*endptr == ':');
63         if (ret) {
64                 switch (endptr[1]) {
65                 case 'U':
66                         tmp_id.type = ID_TYPE_UID;
67                         break;
68
69                 case 'G':
70                         tmp_id.type = ID_TYPE_GID;
71                         break;
72
73                 case 'B':
74                         tmp_id.type = ID_TYPE_BOTH;
75                         break;
76
77                 case '\0':
78                         DEBUG(0, ("FAILED to parse value for key [%s] "
79                                   "(id=[%llu], endptr=[%s]): "
80                                   "no type character after colon\n",
81                                   key, (unsigned long long)tmp_id.id, endptr));
82                         ret = false;
83                         goto done;
84                 default:
85                         DEBUG(0, ("FAILED to parse value for key [%s] "
86                                   "(id=[%llu], endptr=[%s]): "
87                                   "illegal type character '%c'\n",
88                                   key, (unsigned long long)tmp_id.id, endptr,
89                                   endptr[1]));
90                         ret = false;
91                         goto done;
92                 }
93                 if (endptr[2] != '\0') {
94                         DEBUG(0, ("FAILED to parse value for key [%s] "
95                                   "(id=[%llu], endptr=[%s]): "
96                                   "more than 1 type character after colon\n",
97                                   key, (unsigned long long)tmp_id.id, endptr));
98                         ret = false;
99                         goto done;
100                 }
101
102                 *id = tmp_id;
103                 *expired = (timeout <= time(NULL));
104         } else {
105                 DEBUG(0, ("FAILED to parse value for key [%s] (value=[%s]): "
106                           "colon missing after id=[%llu]\n",
107                           key, value, (unsigned long long)tmp_id.id));
108         }
109
110 done:
111         TALLOC_FREE(key);
112         SAFE_FREE(value);
113         return ret;
114 }
115
116 /**
117  * Find a sid2uid mapping
118  * @param[in] sid               the sid to map
119  * @param[out] puid             where to put the result
120  * @param[out] expired          is the cache entry expired?
121  * @retval Was anything in the cache at all?
122  *
123  * If *puid == -1 this was a negative mapping.
124  */
125
126 bool idmap_cache_find_sid2uid(const struct dom_sid *sid, uid_t *puid,
127                               bool *expired)
128 {
129         bool ret;
130         struct unixid id;
131         ret = idmap_cache_find_sid2unixid(sid, &id, expired);
132         if (!ret) {
133                 return false;
134         }
135
136         if (id.type == ID_TYPE_BOTH || id.type == ID_TYPE_UID) {
137                 *puid = id.id;
138         } else {
139                 *puid = -1;
140         }
141         return true;
142 }
143
144 /**
145  * Find a sid2gid mapping
146  * @param[in] sid               the sid to map
147  * @param[out] pgid             where to put the result
148  * @param[out] expired          is the cache entry expired?
149  * @retval Was anything in the cache at all?
150  *
151  * If *pgid == -1 this was a negative mapping.
152  */
153
154 bool idmap_cache_find_sid2gid(const struct dom_sid *sid, gid_t *pgid,
155                               bool *expired)
156 {
157         bool ret;
158         struct unixid id;
159         ret = idmap_cache_find_sid2unixid(sid, &id, expired);
160         if (!ret) {
161                 return false;
162         }
163
164         if (id.type == ID_TYPE_BOTH || id.type == ID_TYPE_GID) {
165                 *pgid = id.id;
166         } else {
167                 *pgid = -1;
168         }
169         return true;
170 }
171
172 /**
173  * Find a uid2sid mapping
174  * @param[in] uid               the uid to map
175  * @param[out] sid              where to put the result
176  * @param[out] expired          is the cache entry expired?
177  * @retval Was anything in the cache at all?
178  *
179  * If "is_null_sid(sid)", this was a negative mapping.
180  */
181
182 bool idmap_cache_find_uid2sid(uid_t uid, struct dom_sid *sid, bool *expired)
183 {
184         char *key;
185         char *value;
186         time_t timeout;
187         bool ret = true;
188
189         key = talloc_asprintf(talloc_tos(), "IDMAP/UID2SID/%d", (int)uid);
190         if (key == NULL) {
191                 return false;
192         }
193         ret = gencache_get(key, &value, &timeout);
194         TALLOC_FREE(key);
195         if (!ret) {
196                 return false;
197         }
198         ZERO_STRUCTP(sid);
199         if (value[0] != '-') {
200                 ret = string_to_sid(sid, value);
201         }
202         SAFE_FREE(value);
203         if (ret) {
204                 *expired = (timeout <= time(NULL));
205         }
206         return ret;
207 }
208
209 /**
210  * Find a gid2sid mapping
211  * @param[in] gid               the gid to map
212  * @param[out] sid              where to put the result
213  * @param[out] expired          is the cache entry expired?
214  * @retval Was anything in the cache at all?
215  *
216  * If "is_null_sid(sid)", this was a negative mapping.
217  */
218
219 bool idmap_cache_find_gid2sid(gid_t gid, struct dom_sid *sid, bool *expired)
220 {
221         char *key;
222         char *value;
223         time_t timeout;
224         bool ret = true;
225
226         key = talloc_asprintf(talloc_tos(), "IDMAP/GID2SID/%d", (int)gid);
227         if (key == NULL) {
228                 return false;
229         }
230         ret = gencache_get(key, &value, &timeout);
231         TALLOC_FREE(key);
232         if (!ret) {
233                 return false;
234         }
235         ZERO_STRUCTP(sid);
236         if (value[0] != '-') {
237                 ret = string_to_sid(sid, value);
238         }
239         SAFE_FREE(value);
240         if (ret) {
241                 *expired = (timeout <= time(NULL));
242         }
243         return ret;
244 }
245
246 /**
247  * Store a mapping in the idmap cache
248  * @param[in] sid               the sid to map
249  * @param[in] gid               the gid to map
250  *
251  * If both parameters are valid values, then a positive mapping in both
252  * directions is stored. If "is_null_sid(sid)" is true, then this will be a
253  * negative mapping of gid, we want to cache that for this gid we could not
254  * find anything. Likewise if "gid==-1", then we want to cache that we did not
255  * find a mapping for the sid passed here.
256  */
257
258 void idmap_cache_set_sid2unixid(const struct dom_sid *sid, struct unixid *unix_id)
259 {
260         time_t now = time(NULL);
261         time_t timeout;
262         fstring sidstr, key, value;
263
264         if (!is_null_sid(sid)) {
265                 fstr_sprintf(key, "IDMAP/SID2XID/%s",
266                              sid_to_fstring(sidstr, sid));
267                 switch (unix_id->type) {
268                 case ID_TYPE_UID:
269                         fstr_sprintf(value, "%d:U", (int)unix_id->id);
270                         break;
271                 case ID_TYPE_GID:
272                         fstr_sprintf(value, "%d:G", (int)unix_id->id);
273                         break;
274                 case ID_TYPE_BOTH:
275                         fstr_sprintf(value, "%d:B", (int)unix_id->id);
276                         break;
277                 default:
278                         return;
279                 }
280                 timeout = (unix_id->id == -1)
281                         ? lp_idmap_negative_cache_time()
282                         : lp_idmap_cache_time();
283                 gencache_set(key, value, now + timeout);
284         }
285         if (unix_id->id != -1) {
286                 if (is_null_sid(sid)) {
287                         /* negative gid mapping */
288                         fstrcpy(value, "-");
289                         timeout = lp_idmap_negative_cache_time();
290                 }
291                 else {
292                         sid_to_fstring(value, sid);
293                         timeout = lp_idmap_cache_time();
294                 }
295                 switch (unix_id->type) {
296                 case ID_TYPE_BOTH:
297                         fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)unix_id->id);
298                         gencache_set(key, value, now + timeout);
299                         fstr_sprintf(key, "IDMAP/GID2SID/%d", (int)unix_id->id);
300                         gencache_set(key, value, now + timeout);
301                         return;
302
303                 case ID_TYPE_UID:
304                         fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)unix_id->id);
305                         break;
306
307                 case ID_TYPE_GID:
308                         fstr_sprintf(key, "IDMAP/GID2SID/%d", (int)unix_id->id);
309                         break;
310
311                 default:
312                         return;
313                 }
314                 gencache_set(key, value, now + timeout);
315         }
316 }
317
318 /**
319  * Store a mapping in the idmap cache
320  * @param[in] sid               the sid to map
321  * @param[in] uid               the uid to map
322  *
323  * If both parameters are valid values, then a positive mapping in both
324  * directions is stored. If "is_null_sid(sid)" is true, then this will be a
325  * negative mapping of uid, we want to cache that for this uid we could not
326  * find anything. Likewise if "uid==-1", then we want to cache that we did not
327  * find a mapping for the sid passed here.
328  */
329
330 void idmap_cache_set_sid2uid(const struct dom_sid *sid, uid_t uid)
331 {
332         struct unixid id;
333         id.type = ID_TYPE_UID;
334         id.id = uid;
335
336         if (uid == -1) {
337                 uid_t tmp_gid;
338                 bool expired;
339                 /* If we were asked to invalidate this SID -> UID
340                  * mapping, it was because we found out that this was
341                  * not a UID at all.  Do not overwrite a valid GID or
342                  * BOTH mapping */
343                 if (idmap_cache_find_sid2gid(sid, &tmp_gid, &expired)) {
344                         if (!expired) {
345                                 return;
346                         }
347                 }
348         }
349
350         idmap_cache_set_sid2unixid(sid, &id);
351         return;
352 }
353
354 /**
355  * Store a mapping in the idmap cache
356  * @param[in] sid               the sid to map
357  * @param[in] gid               the gid to map
358  *
359  * If both parameters are valid values, then a positive mapping in both
360  * directions is stored. If "is_null_sid(sid)" is true, then this will be a
361  * negative mapping of gid, we want to cache that for this gid we could not
362  * find anything. Likewise if "gid==-1", then we want to cache that we did not
363  * find a mapping for the sid passed here.
364  */
365
366 void idmap_cache_set_sid2gid(const struct dom_sid *sid, gid_t gid)
367 {
368         struct unixid id;
369         id.type = ID_TYPE_GID;
370         id.id = gid;
371
372         if (gid == -1) {
373                 uid_t tmp_uid;
374                 bool expired;
375                 /* If we were asked to invalidate this SID -> GID
376                  * mapping, it was because we found out that this was
377                  * not a GID at all.  Do not overwrite a valid UID or
378                  * BOTH mapping */
379                 if (idmap_cache_find_sid2uid(sid, &tmp_uid, &expired)) {
380                         if (!expired) {
381                                 return;
382                         }
383                 }
384         }
385
386         idmap_cache_set_sid2unixid(sid, &id);
387         return;
388 }
389
390 static char* key_xid2sid_str(TALLOC_CTX* mem_ctx, char t, const char* id) {
391         return talloc_asprintf(mem_ctx, "IDMAP/%cID2SID/%s", t, id);
392 }
393
394 static char* key_xid2sid(TALLOC_CTX* mem_ctx, char t, int id) {
395         char str[32];
396         snprintf(str, sizeof(str), "%d", id);
397         return key_xid2sid_str(mem_ctx, t, str);
398 }
399
400 static char* key_sid2xid_str(TALLOC_CTX* mem_ctx, const char* id) {
401         return talloc_asprintf(mem_ctx, "IDMAP/SID2XID/%s", id);
402 }
403
404 static bool idmap_cache_del_xid(char t, int xid)
405 {
406         TALLOC_CTX* mem_ctx = talloc_stackframe();
407         const char* key = key_xid2sid(mem_ctx, t, xid);
408         char* sid_str = NULL;
409         time_t timeout;
410         bool ret = true;
411
412         if (!gencache_get(key, &sid_str, &timeout)) {
413                 DEBUG(3, ("no entry: %s\n", key));
414                 ret = false;
415                 goto done;
416         }
417
418         if (sid_str[0] != '-') {
419                 const char* sid_key = key_sid2xid_str(mem_ctx, sid_str);
420                 if (!gencache_del(sid_key)) {
421                         DEBUG(2, ("failed to delete: %s\n", sid_key));
422                         ret = false;
423                 } else {
424                         DEBUG(5, ("delete: %s\n", sid_key));
425                 }
426
427         }
428
429         if (!gencache_del(key)) {
430                 DEBUG(1, ("failed to delete: %s\n", key));
431                 ret = false;
432         } else {
433                 DEBUG(5, ("delete: %s\n", key));
434         }
435
436 done:
437         talloc_free(mem_ctx);
438         return ret;
439 }
440
441 bool idmap_cache_del_uid(uid_t uid) {
442         return idmap_cache_del_xid('U', uid);
443 }
444
445 bool idmap_cache_del_gid(gid_t gid) {
446         return idmap_cache_del_xid('G', gid);
447 }
448
449 bool idmap_cache_del_sid(const struct dom_sid *sid)
450 {
451         TALLOC_CTX* mem_ctx = talloc_stackframe();
452         bool ret = true;
453         bool expired;
454         struct unixid id;
455         const char *sid_key;
456
457         if (!idmap_cache_find_sid2unixid(sid, &id, &expired)) {
458                 ret = false;
459                 goto done;
460         }
461
462         if (id.id != -1) {
463                 switch (id.type) {
464                 case ID_TYPE_BOTH:
465                         idmap_cache_del_xid('U', id.id);
466                         idmap_cache_del_xid('G', id.id);
467                         break;
468                 case ID_TYPE_UID:
469                         idmap_cache_del_xid('U', id.id);
470                         break;
471                 case ID_TYPE_GID:
472                         idmap_cache_del_xid('G', id.id);
473                         break;
474                 default:
475                         break;
476                 }
477         }
478
479         sid_key = key_sid2xid_str(mem_ctx, dom_sid_string(mem_ctx, sid));
480         if (sid_key == NULL) {
481                 return false;
482         }
483         /* If the mapping was symmetric, then this should fail */
484         gencache_del(sid_key);
485 done:
486         talloc_free(mem_ctx);
487         return ret;
488 }