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