idmap: Use dom_sid_str_buf
[amitay/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         ZERO_STRUCTP(state->sid);
207         state->ret = false;
208
209         if ((blob.length == 0) || (blob.data[blob.length-1] != 0)) {
210                 /*
211                  * Not a string, can't be a valid mapping
212                  */
213                 return;
214         }
215
216         value = (char *)blob.data;
217
218         if (value[0] != '-') {
219                 state->ret = string_to_sid(state->sid, value);
220         }
221         if (state->ret) {
222                 *state->expired = gencache_timeout_expired(timeout);
223         }
224 }
225
226 /**
227  * Find a uid2sid mapping
228  * @param[in] uid               the uid to map
229  * @param[out] sid              where to put the result
230  * @param[out] expired          is the cache entry expired?
231  * @retval Was anything in the cache at all?
232  *
233  * If "is_null_sid(sid)", this was a negative mapping.
234  */
235
236 bool idmap_cache_find_uid2sid(uid_t uid, struct dom_sid *sid, bool *expired)
237 {
238         fstring key;
239         struct idmap_cache_xid2sid_state state;
240
241         fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)uid);
242
243         state.sid = sid;
244         state.expired = expired;
245         state.ret = false;
246
247         gencache_parse(key, idmap_cache_xid2sid_parser, &state);
248         return state.ret;
249 }
250
251 /**
252  * Find a gid2sid mapping
253  * @param[in] gid               the gid to map
254  * @param[out] sid              where to put the result
255  * @param[out] expired          is the cache entry expired?
256  * @retval Was anything in the cache at all?
257  *
258  * If "is_null_sid(sid)", this was a negative mapping.
259  */
260
261 bool idmap_cache_find_gid2sid(gid_t gid, struct dom_sid *sid, bool *expired)
262 {
263         fstring key;
264         struct idmap_cache_xid2sid_state state;
265
266         fstr_sprintf(key, "IDMAP/GID2SID/%d", (int)gid);
267
268         state.sid = sid;
269         state.expired = expired;
270         state.ret = false;
271
272         gencache_parse(key, idmap_cache_xid2sid_parser, &state);
273         return state.ret;
274 }
275
276 /**
277  * Store a mapping in the idmap cache
278  * @param[in] sid               the sid to map
279  * @param[in] unix_id           the unix_id to map
280  *
281  * If both parameters are valid values, then a positive mapping in both
282  * directions is stored. If "is_null_sid(sid)" is true, then this will be a
283  * negative mapping of xid, we want to cache that for this xid we could not
284  * find anything. Likewise if "xid==-1", then we want to cache that we did not
285  * find a mapping for the sid passed here.
286  */
287
288 void idmap_cache_set_sid2unixid(const struct dom_sid *sid, struct unixid *unix_id)
289 {
290         time_t now = time(NULL);
291         time_t timeout;
292         fstring key, value;
293
294         if (!is_null_sid(sid)) {
295                 struct dom_sid_buf sidstr;
296                 fstr_sprintf(key, "IDMAP/SID2XID/%s",
297                              dom_sid_str_buf(sid, &sidstr));
298                 switch (unix_id->type) {
299                 case ID_TYPE_UID:
300                         fstr_sprintf(value, "%d:U", (int)unix_id->id);
301                         break;
302                 case ID_TYPE_GID:
303                         fstr_sprintf(value, "%d:G", (int)unix_id->id);
304                         break;
305                 case ID_TYPE_BOTH:
306                         fstr_sprintf(value, "%d:B", (int)unix_id->id);
307                         break;
308                 case ID_TYPE_NOT_SPECIFIED:
309                         fstr_sprintf(value, "%d:N", (int)unix_id->id);
310                         break;
311                 default:
312                         return;
313                 }
314                 timeout = (unix_id->id == -1)
315                         ? lp_idmap_negative_cache_time()
316                         : lp_idmap_cache_time();
317                 gencache_set(key, value, now + timeout);
318         }
319         if (unix_id->id != -1) {
320                 if (is_null_sid(sid)) {
321                         /* negative xid mapping */
322                         fstrcpy(value, "-");
323                         timeout = lp_idmap_negative_cache_time();
324                 }
325                 else {
326                         sid_to_fstring(value, sid);
327                         timeout = lp_idmap_cache_time();
328                 }
329                 switch (unix_id->type) {
330                 case ID_TYPE_BOTH:
331                         fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)unix_id->id);
332                         gencache_set(key, value, now + timeout);
333                         fstr_sprintf(key, "IDMAP/GID2SID/%d", (int)unix_id->id);
334                         gencache_set(key, value, now + timeout);
335                         return;
336
337                 case ID_TYPE_UID:
338                         fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)unix_id->id);
339                         break;
340
341                 case ID_TYPE_GID:
342                         fstr_sprintf(key, "IDMAP/GID2SID/%d", (int)unix_id->id);
343                         break;
344
345                 default:
346                         return;
347                 }
348                 gencache_set(key, value, now + timeout);
349         }
350 }
351
352 static char* key_xid2sid_str(TALLOC_CTX* mem_ctx, char t, const char* id) {
353         return talloc_asprintf(mem_ctx, "IDMAP/%cID2SID/%s", t, id);
354 }
355
356 static char* key_xid2sid(TALLOC_CTX* mem_ctx, char t, int id) {
357         char str[32];
358         snprintf(str, sizeof(str), "%d", id);
359         return key_xid2sid_str(mem_ctx, t, str);
360 }
361
362 static char* key_sid2xid_str(TALLOC_CTX* mem_ctx, const char* id) {
363         return talloc_asprintf(mem_ctx, "IDMAP/SID2XID/%s", id);
364 }
365
366 static bool idmap_cache_del_xid(char t, int xid)
367 {
368         TALLOC_CTX* mem_ctx = talloc_stackframe();
369         const char* key = key_xid2sid(mem_ctx, t, xid);
370         char* sid_str = NULL;
371         time_t timeout;
372         bool ret = true;
373
374         if (!gencache_get(key, mem_ctx, &sid_str, &timeout)) {
375                 DEBUG(3, ("no entry: %s\n", key));
376                 ret = false;
377                 goto done;
378         }
379
380         if (sid_str[0] != '-') {
381                 const char* sid_key = key_sid2xid_str(mem_ctx, sid_str);
382                 if (!gencache_del(sid_key)) {
383                         DEBUG(2, ("failed to delete: %s\n", sid_key));
384                         ret = false;
385                 } else {
386                         DEBUG(5, ("delete: %s\n", sid_key));
387                 }
388
389         }
390
391         if (!gencache_del(key)) {
392                 DEBUG(1, ("failed to delete: %s\n", key));
393                 ret = false;
394         } else {
395                 DEBUG(5, ("delete: %s\n", key));
396         }
397
398 done:
399         talloc_free(mem_ctx);
400         return ret;
401 }
402
403 bool idmap_cache_del_uid(uid_t uid) {
404         return idmap_cache_del_xid('U', uid);
405 }
406
407 bool idmap_cache_del_gid(gid_t gid) {
408         return idmap_cache_del_xid('G', gid);
409 }
410
411 bool idmap_cache_del_sid(const struct dom_sid *sid)
412 {
413         TALLOC_CTX* mem_ctx = talloc_stackframe();
414         bool ret = true;
415         bool expired;
416         struct unixid id;
417         const char *sid_key;
418
419         if (!idmap_cache_find_sid2unixid(sid, &id, &expired)) {
420                 ret = false;
421                 goto done;
422         }
423
424         if (id.id != -1) {
425                 switch (id.type) {
426                 case ID_TYPE_BOTH:
427                         idmap_cache_del_xid('U', id.id);
428                         idmap_cache_del_xid('G', id.id);
429                         break;
430                 case ID_TYPE_UID:
431                         idmap_cache_del_xid('U', id.id);
432                         break;
433                 case ID_TYPE_GID:
434                         idmap_cache_del_xid('G', id.id);
435                         break;
436                 default:
437                         break;
438                 }
439         }
440
441         sid_key = key_sid2xid_str(mem_ctx, dom_sid_string(mem_ctx, sid));
442         if (sid_key == NULL) {
443                 return false;
444         }
445         /* If the mapping was symmetric, then this should fail */
446         gencache_del(sid_key);
447 done:
448         talloc_free(mem_ctx);
449         return ret;
450 }