Line data Source code
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 1043771 : bool idmap_cache_find_sid2unixid(const struct dom_sid *sid, struct unixid *id,
38 : bool *expired)
39 : {
40 2323 : struct dom_sid_buf sidstr;
41 2323 : char *key;
42 1043771 : char *value = NULL;
43 2323 : char *endptr;
44 2323 : time_t timeout;
45 2323 : bool ret;
46 2323 : struct unixid tmp_id;
47 :
48 1043771 : key = talloc_asprintf(talloc_tos(), "IDMAP/SID2XID/%s",
49 : dom_sid_str_buf(sid, &sidstr));
50 1043771 : if (key == NULL) {
51 0 : return false;
52 : }
53 1043771 : ret = gencache_get(key, talloc_tos(), &value, &timeout);
54 1043771 : if (!ret) {
55 38935 : goto done;
56 : }
57 :
58 1004836 : DEBUG(10, ("Parsing value for key [%s]: value=[%s]\n", key, value));
59 :
60 1004836 : if (value[0] == '\0') {
61 0 : DEBUG(0, ("Failed to parse value for key [%s]: "
62 : "value is empty\n", key));
63 0 : ret = false;
64 0 : goto done;
65 : }
66 :
67 1004836 : tmp_id.id = strtol(value, &endptr, 10);
68 :
69 1004836 : if ((value == endptr) && (tmp_id.id == 0)) {
70 0 : DEBUG(0, ("Failed to parse value for key [%s]: value[%s] does "
71 : "not start with a number\n", key, value));
72 0 : ret = false;
73 0 : goto done;
74 : }
75 :
76 1004836 : DEBUG(10, ("Parsing value for key [%s]: id=[%llu], endptr=[%s]\n",
77 : key, (unsigned long long)tmp_id.id, endptr));
78 :
79 1004836 : ret = (*endptr == ':');
80 1004836 : if (ret) {
81 1004836 : switch (endptr[1]) {
82 338204 : case 'U':
83 338204 : tmp_id.type = ID_TYPE_UID;
84 338204 : break;
85 :
86 476020 : case 'G':
87 476020 : tmp_id.type = ID_TYPE_GID;
88 476020 : break;
89 :
90 189712 : case 'B':
91 189712 : tmp_id.type = ID_TYPE_BOTH;
92 189712 : break;
93 :
94 767 : case 'N':
95 767 : tmp_id.type = ID_TYPE_NOT_SPECIFIED;
96 767 : break;
97 :
98 0 : case '\0':
99 0 : 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 0 : ret = false;
104 0 : goto done;
105 0 : default:
106 0 : 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 0 : ret = false;
112 0 : goto done;
113 : }
114 1004836 : if (endptr[2] != '\0') {
115 0 : 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 0 : ret = false;
120 0 : goto done;
121 : }
122 :
123 1004836 : *id = tmp_id;
124 1004836 : *expired = (timeout <= time(NULL));
125 : } else {
126 0 : 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 1043771 : done:
132 1043771 : TALLOC_FREE(key);
133 1043771 : TALLOC_FREE(value);
134 1041448 : 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 177360 : bool idmap_cache_find_sid2uid(const struct dom_sid *sid, uid_t *puid,
148 : bool *expired)
149 : {
150 459 : bool ret;
151 459 : struct unixid id;
152 177360 : ret = idmap_cache_find_sid2unixid(sid, &id, expired);
153 177360 : if (!ret) {
154 2423 : return false;
155 : }
156 :
157 174929 : if (id.type == ID_TYPE_BOTH || id.type == ID_TYPE_UID) {
158 174332 : *puid = id.id;
159 : } else {
160 597 : *puid = -1;
161 : }
162 174478 : 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 151957 : bool idmap_cache_find_sid2gid(const struct dom_sid *sid, gid_t *pgid,
176 : bool *expired)
177 : {
178 474 : bool ret;
179 474 : struct unixid id;
180 151957 : ret = idmap_cache_find_sid2unixid(sid, &id, expired);
181 151957 : if (!ret) {
182 1299 : return false;
183 : }
184 :
185 150638 : if (id.type == ID_TYPE_BOTH || id.type == ID_TYPE_GID) {
186 150622 : *pgid = id.id;
187 : } else {
188 16 : *pgid = -1;
189 : }
190 150184 : return true;
191 : }
192 :
193 : struct idmap_cache_xid2sid_state {
194 : struct dom_sid *sid;
195 : bool *expired;
196 : bool ret;
197 : };
198 :
199 1515107 : static void idmap_cache_xid2sid_parser(const struct gencache_timeout *timeout,
200 : DATA_BLOB blob,
201 : void *private_data)
202 : {
203 1515107 : struct idmap_cache_xid2sid_state *state =
204 : (struct idmap_cache_xid2sid_state *)private_data;
205 6442 : char *value;
206 :
207 1515107 : if ((blob.length == 0) || (blob.data[blob.length-1] != 0)) {
208 : /*
209 : * Not a string, can't be a valid mapping
210 : */
211 0 : state->ret = false;
212 0 : return;
213 : }
214 :
215 1515107 : value = (char *)blob.data;
216 :
217 1515107 : if ((value[0] == '-') && (value[1] == '\0')) {
218 : /*
219 : * Return NULL SID, see comment to uid2sid
220 : */
221 172273 : *state->sid = (struct dom_sid) {0};
222 172273 : state->ret = true;
223 : } else {
224 1342834 : state->ret = string_to_sid(state->sid, value);
225 : }
226 1515107 : if (state->ret) {
227 1515107 : *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 1700003 : bool idmap_cache_find_xid2sid(
241 : const struct unixid *id, struct dom_sid *sid, bool *expired)
242 : {
243 1700003 : struct idmap_cache_xid2sid_state state = {
244 : .sid = sid, .expired = expired
245 : };
246 6456 : fstring key;
247 6456 : char c;
248 :
249 1700003 : switch (id->type) {
250 720152 : case ID_TYPE_UID:
251 720152 : c = 'U';
252 720152 : break;
253 976890 : case ID_TYPE_GID:
254 976890 : c = 'G';
255 976890 : break;
256 0 : default:
257 0 : return false;
258 : }
259 :
260 1700003 : fstr_sprintf(key, "IDMAP/%cID2SID/%d", c, (int)id->id);
261 :
262 1700003 : gencache_parse(key, idmap_cache_xid2sid_parser, &state);
263 1700003 : 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 3540 : void idmap_cache_set_sid2unixid(const struct dom_sid *sid, struct unixid *unix_id)
280 : {
281 3540 : time_t now = time(NULL);
282 53 : time_t timeout;
283 53 : fstring key, value;
284 :
285 3540 : if (!is_null_sid(sid)) {
286 52 : struct dom_sid_buf sidstr;
287 3186 : fstr_sprintf(key, "IDMAP/SID2XID/%s",
288 : dom_sid_str_buf(sid, &sidstr));
289 3186 : switch (unix_id->type) {
290 1067 : case ID_TYPE_UID:
291 1067 : fstr_sprintf(value, "%d:U", (int)unix_id->id);
292 1067 : break;
293 1669 : case ID_TYPE_GID:
294 1669 : fstr_sprintf(value, "%d:G", (int)unix_id->id);
295 1669 : break;
296 340 : case ID_TYPE_BOTH:
297 340 : fstr_sprintf(value, "%d:B", (int)unix_id->id);
298 340 : break;
299 110 : case ID_TYPE_NOT_SPECIFIED:
300 110 : fstr_sprintf(value, "%d:N", (int)unix_id->id);
301 110 : break;
302 0 : default:
303 0 : return;
304 : }
305 6372 : timeout = (unix_id->id == -1)
306 110 : ? lp_idmap_negative_cache_time()
307 3296 : : lp_idmap_cache_time();
308 3186 : gencache_set(key, value, now + timeout);
309 : }
310 3540 : if (unix_id->id != -1) {
311 3430 : if (is_null_sid(sid)) {
312 : /* negative xid mapping */
313 354 : fstrcpy(value, "-");
314 354 : timeout = lp_idmap_negative_cache_time();
315 : }
316 : else {
317 3076 : sid_to_fstring(value, sid);
318 3076 : timeout = lp_idmap_cache_time();
319 : }
320 3430 : switch (unix_id->type) {
321 340 : case ID_TYPE_BOTH:
322 340 : fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)unix_id->id);
323 340 : gencache_set(key, value, now + timeout);
324 340 : fstr_sprintf(key, "IDMAP/GID2SID/%d", (int)unix_id->id);
325 340 : gencache_set(key, value, now + timeout);
326 340 : return;
327 :
328 1147 : case ID_TYPE_UID:
329 1147 : fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)unix_id->id);
330 1147 : break;
331 :
332 1943 : case ID_TYPE_GID:
333 1943 : fstr_sprintf(key, "IDMAP/GID2SID/%d", (int)unix_id->id);
334 1943 : break;
335 :
336 0 : default:
337 0 : return;
338 : }
339 3090 : gencache_set(key, value, now + timeout);
340 : }
341 : }
342 :
343 14 : static char* key_xid2sid_str(TALLOC_CTX* mem_ctx, char t, const char* id) {
344 14 : return talloc_asprintf(mem_ctx, "IDMAP/%cID2SID/%s", t, id);
345 : }
346 :
347 14 : static char* key_xid2sid(TALLOC_CTX* mem_ctx, char t, int id) {
348 14 : char str[32];
349 14 : snprintf(str, sizeof(str), "%d", id);
350 14 : return key_xid2sid_str(mem_ctx, t, str);
351 : }
352 :
353 24 : static char* key_sid2xid_str(TALLOC_CTX* mem_ctx, const char* id) {
354 24 : return talloc_asprintf(mem_ctx, "IDMAP/SID2XID/%s", id);
355 : }
356 :
357 14 : static bool idmap_cache_del_xid(char t, int xid)
358 : {
359 14 : TALLOC_CTX* mem_ctx = talloc_stackframe();
360 14 : const char* key = key_xid2sid(mem_ctx, t, xid);
361 14 : char* sid_str = NULL;
362 14 : time_t timeout;
363 14 : bool ret = true;
364 :
365 14 : if (!gencache_get(key, mem_ctx, &sid_str, &timeout)) {
366 0 : DEBUG(3, ("no entry: %s\n", key));
367 0 : ret = false;
368 0 : goto done;
369 : }
370 :
371 14 : if (sid_str[0] != '-') {
372 14 : const char* sid_key = key_sid2xid_str(mem_ctx, sid_str);
373 14 : if (!gencache_del(sid_key)) {
374 4 : DEBUG(2, ("failed to delete: %s\n", sid_key));
375 0 : ret = false;
376 : } else {
377 10 : DEBUG(5, ("delete: %s\n", sid_key));
378 : }
379 :
380 : }
381 :
382 14 : if (!gencache_del(key)) {
383 0 : DEBUG(1, ("failed to delete: %s\n", key));
384 0 : ret = false;
385 : } else {
386 14 : DEBUG(5, ("delete: %s\n", key));
387 : }
388 :
389 14 : done:
390 14 : talloc_free(mem_ctx);
391 14 : return ret;
392 : }
393 :
394 0 : bool idmap_cache_del_uid(uid_t uid) {
395 0 : return idmap_cache_del_xid('U', uid);
396 : }
397 :
398 0 : bool idmap_cache_del_gid(gid_t gid) {
399 0 : return idmap_cache_del_xid('G', gid);
400 : }
401 :
402 10 : bool idmap_cache_del_sid(const struct dom_sid *sid)
403 : {
404 10 : TALLOC_CTX* mem_ctx = talloc_stackframe();
405 10 : bool ret = true;
406 10 : bool expired;
407 10 : struct unixid id;
408 10 : struct dom_sid_buf sidbuf;
409 10 : const char *sid_key;
410 :
411 10 : if (!idmap_cache_find_sid2unixid(sid, &id, &expired)) {
412 0 : ret = false;
413 0 : goto done;
414 : }
415 :
416 10 : if (id.id != -1) {
417 10 : switch (id.type) {
418 4 : case ID_TYPE_BOTH:
419 4 : idmap_cache_del_xid('U', id.id);
420 4 : idmap_cache_del_xid('G', id.id);
421 4 : break;
422 3 : case ID_TYPE_UID:
423 3 : idmap_cache_del_xid('U', id.id);
424 3 : break;
425 3 : case ID_TYPE_GID:
426 3 : idmap_cache_del_xid('G', id.id);
427 3 : break;
428 0 : default:
429 0 : break;
430 : }
431 : }
432 :
433 10 : sid_key = key_sid2xid_str(mem_ctx, dom_sid_str_buf(sid, &sidbuf));
434 10 : if (sid_key == NULL) {
435 0 : return false;
436 : }
437 : /* If the mapping was symmetric, then this should fail */
438 10 : gencache_del(sid_key);
439 10 : done:
440 10 : talloc_free(mem_ctx);
441 10 : return ret;
442 : }
|