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