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