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