Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : idmap script backend, used for Samba setups where you need to map SIDs to
5 : specific UIDs/GIDs.
6 :
7 : Copyright (C) Richard Sharpe 2014.
8 :
9 : This is heavily based upon idmap_tdb2.c, which is:
10 :
11 : Copyright (C) Tim Potter 2000
12 : Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
13 : Copyright (C) Jeremy Allison 2006
14 : Copyright (C) Simo Sorce 2003-2006
15 : Copyright (C) Michael Adam 2009-2010
16 :
17 : This program is free software; you can redistribute it and/or modify
18 : it under the terms of the GNU General Public License as published by
19 : the Free Software Foundation; either version 2 of the License, or
20 : (at your option) any later version.
21 :
22 : This program is distributed in the hope that it will be useful,
23 : but WITHOUT ANY WARRANTY; without even the implied warranty of
24 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 : GNU General Public License for more details.
26 :
27 : You should have received a copy of the GNU General Public License
28 : along with this program; if not, write to the Free Software
29 : Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 : */
31 :
32 : #include "includes.h"
33 : #include "system/filesys.h"
34 : #include "winbindd.h"
35 : #include "idmap.h"
36 : #include "idmap_rw.h"
37 : #include "../libcli/security/dom_sid.h"
38 : #include "lib/util_file.h"
39 : #include "lib/util/tevent_unix.h"
40 :
41 : #undef DBGC_CLASS
42 : #define DBGC_CLASS DBGC_IDMAP
43 :
44 : struct idmap_script_context {
45 : const char *script; /* script to provide idmaps */
46 : };
47 :
48 : /*
49 : run a script to perform a mapping
50 :
51 : The script should accept the following command lines:
52 :
53 : SIDTOID S-1-xxxx -> XID:<id> | ERR:<str>
54 : SIDTOID S-1-xxxx -> UID:<id> | ERR:<str>
55 : SIDTOID S-1-xxxx -> GID:<id> | ERR:<str>
56 : IDTOSID XID xxxx -> SID:<sid> | ERR:<str>
57 : IDTOSID UID xxxx -> SID:<sid> | ERR:<str>
58 : IDTOSID GID xxxx -> SID:<sid> | ERR:<str>
59 :
60 : where XID means both a UID and a GID. This is the case for ID_TYPE_BOTH.
61 :
62 : TODO: Needs more validation ... like that we got a UID when we asked for one.
63 : */
64 :
65 : struct idmap_script_xid2sid_state {
66 : char **argl;
67 : size_t idx;
68 : uint8_t *out;
69 : };
70 :
71 : static void idmap_script_xid2sid_done(struct tevent_req *subreq);
72 :
73 0 : static struct tevent_req *idmap_script_xid2sid_send(
74 : TALLOC_CTX *mem_ctx, struct tevent_context *ev,
75 : struct unixid xid, const char *script, size_t idx)
76 : {
77 : struct tevent_req *req, *subreq;
78 : struct idmap_script_xid2sid_state *state;
79 : char key;
80 :
81 0 : req = tevent_req_create(mem_ctx, &state,
82 : struct idmap_script_xid2sid_state);
83 0 : if (req == NULL) {
84 0 : return NULL;
85 : }
86 0 : state->idx = idx;
87 :
88 0 : switch (xid.type) {
89 0 : case ID_TYPE_UID:
90 0 : key = 'U';
91 0 : break;
92 0 : case ID_TYPE_GID:
93 0 : key = 'G';
94 0 : break;
95 0 : case ID_TYPE_BOTH:
96 0 : key = 'X';
97 0 : break;
98 0 : default:
99 0 : DBG_WARNING("INVALID unix ID type: 0x02%x\n", xid.type);
100 0 : tevent_req_error(req, EINVAL);
101 0 : return tevent_req_post(req, ev);
102 : }
103 :
104 0 : state->argl = str_list_make_empty(state);
105 0 : str_list_add_printf(&state->argl, "%s", script);
106 0 : str_list_add_printf(&state->argl, "IDTOSID");
107 0 : str_list_add_printf(&state->argl, "%cID", key);
108 0 : str_list_add_printf(&state->argl, "%lu", (unsigned long)xid.id);
109 0 : if (tevent_req_nomem(state->argl, req)) {
110 0 : return tevent_req_post(req, ev);
111 : }
112 :
113 0 : subreq = file_ploadv_send(state, ev, state->argl, 1024);
114 0 : if (tevent_req_nomem(subreq, req)) {
115 0 : return tevent_req_post(req, ev);
116 : }
117 0 : tevent_req_set_callback(subreq, idmap_script_xid2sid_done, req);
118 0 : return req;
119 : }
120 :
121 0 : static void idmap_script_xid2sid_done(struct tevent_req *subreq)
122 : {
123 0 : struct tevent_req *req = tevent_req_callback_data(
124 : subreq, struct tevent_req);
125 0 : struct idmap_script_xid2sid_state *state = tevent_req_data(
126 : req, struct idmap_script_xid2sid_state);
127 : int ret;
128 :
129 0 : ret = file_ploadv_recv(subreq, state, &state->out);
130 0 : TALLOC_FREE(subreq);
131 0 : if (tevent_req_error(req, ret)) {
132 0 : return;
133 : }
134 0 : tevent_req_done(req);
135 : }
136 :
137 0 : static int idmap_script_xid2sid_recv(struct tevent_req *req, size_t *idx,
138 : enum id_mapping *status,
139 : struct dom_sid *sid)
140 : {
141 0 : struct idmap_script_xid2sid_state *state = tevent_req_data(
142 : req, struct idmap_script_xid2sid_state);
143 0 : char *out = (char *)state->out;
144 0 : size_t out_size = talloc_get_size(out);
145 : int err;
146 :
147 0 : if (tevent_req_is_unix_error(req, &err)) {
148 0 : return err;
149 : }
150 :
151 0 : if (out_size == 0) {
152 0 : goto unmapped;
153 : }
154 0 : if (state->out[out_size-1] != '\0') {
155 0 : goto unmapped;
156 : }
157 :
158 0 : *idx = state->idx;
159 :
160 0 : if ((strncmp(out, "SID:S-", 6) != 0) ||
161 0 : !dom_sid_parse(out+4, sid)) {
162 0 : DBG_WARNING("Bad sid from script: %s\n", out);
163 0 : goto unmapped;
164 : }
165 :
166 0 : *status = ID_MAPPED;
167 0 : return 0;
168 :
169 0 : unmapped:
170 0 : *sid = (struct dom_sid) {0};
171 0 : *status = ID_UNMAPPED;
172 0 : return 0;
173 : }
174 :
175 : struct idmap_script_xids2sids_state {
176 : struct id_map **ids;
177 : size_t num_ids;
178 : size_t num_done;
179 : };
180 :
181 : static void idmap_script_xids2sids_done(struct tevent_req *subreq);
182 :
183 0 : static struct tevent_req *idmap_script_xids2sids_send(
184 : TALLOC_CTX *mem_ctx, struct tevent_context *ev,
185 : struct id_map **ids, size_t num_ids, const char *script)
186 : {
187 : struct tevent_req *req;
188 : struct idmap_script_xids2sids_state *state;
189 : size_t i;
190 :
191 0 : req = tevent_req_create(mem_ctx, &state,
192 : struct idmap_script_xids2sids_state);
193 0 : if (req == NULL) {
194 0 : return NULL;
195 : }
196 0 : state->ids = ids;
197 0 : state->num_ids = num_ids;
198 :
199 0 : if (state->num_ids == 0) {
200 0 : tevent_req_done(req);
201 0 : return tevent_req_post(req, ev);
202 : }
203 :
204 0 : for (i=0; i<num_ids; i++) {
205 : struct tevent_req *subreq;
206 :
207 0 : subreq = idmap_script_xid2sid_send(
208 0 : state, ev, ids[i]->xid, script, i);
209 0 : if (tevent_req_nomem(subreq, req)) {
210 0 : return tevent_req_post(req, ev);
211 : }
212 0 : tevent_req_set_callback(subreq, idmap_script_xids2sids_done,
213 : req);
214 : }
215 :
216 0 : return req;
217 : }
218 :
219 0 : static void idmap_script_xids2sids_done(struct tevent_req *subreq)
220 : {
221 0 : struct tevent_req *req = tevent_req_callback_data(
222 : subreq, struct tevent_req);
223 0 : struct idmap_script_xids2sids_state *state = tevent_req_data(
224 : req, struct idmap_script_xids2sids_state);
225 0 : size_t idx = 0;
226 0 : enum id_mapping status = ID_UNKNOWN;
227 0 : struct dom_sid sid = {0};
228 : int ret;
229 :
230 0 : ret = idmap_script_xid2sid_recv(subreq, &idx, &status, &sid);
231 0 : TALLOC_FREE(subreq);
232 0 : if (tevent_req_error(req, ret)) {
233 0 : return;
234 : }
235 :
236 0 : if (idx >= state->num_ids) {
237 0 : tevent_req_error(req, EINVAL);
238 0 : return;
239 : }
240 :
241 0 : state->ids[idx]->status = status;
242 :
243 0 : state->ids[idx]->sid = dom_sid_dup(state->ids, &sid);
244 0 : if (tevent_req_nomem(state->ids[idx]->sid, req)) {
245 0 : return;
246 : }
247 :
248 0 : state->num_done += 1;
249 :
250 0 : if (state->num_done >= state->num_ids) {
251 0 : tevent_req_done(req);
252 : }
253 : }
254 :
255 0 : static int idmap_script_xids2sids_recv(struct tevent_req *req)
256 : {
257 0 : return tevent_req_simple_recv_unix(req);
258 : }
259 :
260 0 : static int idmap_script_xids2sids(struct id_map **ids, size_t num_ids,
261 : const char *script)
262 : {
263 0 : TALLOC_CTX *frame = talloc_stackframe();
264 : struct tevent_context *ev;
265 : struct tevent_req *req;
266 0 : int ret = ENOMEM;
267 :
268 0 : ev = samba_tevent_context_init(frame);
269 0 : if (ev == NULL) {
270 0 : goto fail;
271 : }
272 0 : req = idmap_script_xids2sids_send(frame, ev, ids, num_ids, script);
273 0 : if (req == NULL) {
274 0 : goto fail;
275 : }
276 0 : if (!tevent_req_poll(req, ev)) {
277 0 : ret = errno;
278 0 : goto fail;
279 : }
280 0 : ret = idmap_script_xids2sids_recv(req);
281 0 : fail:
282 0 : TALLOC_FREE(frame);
283 0 : return ret;
284 : }
285 :
286 0 : static NTSTATUS idmap_script_unixids_to_sids(struct idmap_domain *dom,
287 : struct id_map **ids)
288 : {
289 0 : struct idmap_script_context *ctx = talloc_get_type_abort(
290 : dom->private_data, struct idmap_script_context);
291 : int ret;
292 : size_t i, num_ids, num_mapped;
293 :
294 0 : DEBUG(10, ("%s called ...\n", __func__));
295 : /* Init status to avoid surprise ... */
296 0 : for (i = 0; ids[i]; i++) {
297 0 : ids[i]->status = ID_UNKNOWN;
298 : }
299 0 : num_ids = i;
300 :
301 0 : ret = idmap_script_xids2sids(ids, num_ids, ctx->script);
302 0 : if (ret != 0) {
303 0 : DBG_DEBUG("idmap_script_xids2sids returned %s\n",
304 : strerror(ret));
305 0 : return map_nt_error_from_unix(ret);
306 : }
307 :
308 0 : num_mapped = 0;
309 :
310 0 : for (i = 0; ids[i]; i++) {
311 0 : if (ids[i]->status == ID_MAPPED) {
312 0 : num_mapped += 1;
313 : }
314 : }
315 :
316 0 : if (num_mapped == 0) {
317 0 : return NT_STATUS_NONE_MAPPED;
318 : }
319 0 : if (num_mapped < num_ids) {
320 0 : return STATUS_SOME_UNMAPPED;
321 : }
322 0 : return NT_STATUS_OK;
323 : }
324 :
325 : struct idmap_script_sid2xid_state {
326 : char **argl;
327 : size_t idx;
328 : uint8_t *out;
329 : };
330 :
331 : static void idmap_script_sid2xid_done(struct tevent_req *subreq);
332 :
333 0 : static struct tevent_req *idmap_script_sid2xid_send(
334 : TALLOC_CTX *mem_ctx, struct tevent_context *ev,
335 : const struct dom_sid *sid, const char *script, size_t idx)
336 : {
337 : struct tevent_req *req, *subreq;
338 : struct idmap_script_sid2xid_state *state;
339 : struct dom_sid_buf sidbuf;
340 :
341 0 : req = tevent_req_create(mem_ctx, &state,
342 : struct idmap_script_sid2xid_state);
343 0 : if (req == NULL) {
344 0 : return NULL;
345 : }
346 0 : state->idx = idx;
347 :
348 0 : state->argl = str_list_make_empty(state);
349 0 : str_list_add_printf(&state->argl, "%s", script);
350 0 : str_list_add_printf(&state->argl, "SIDTOID");
351 0 : str_list_add_printf(
352 0 : &state->argl, "%s", dom_sid_str_buf(sid, &sidbuf));
353 0 : if (tevent_req_nomem(state->argl, req)) {
354 0 : return tevent_req_post(req, ev);
355 : }
356 :
357 0 : subreq = file_ploadv_send(state, ev, state->argl, 1024);
358 0 : if (tevent_req_nomem(subreq, req)) {
359 0 : return tevent_req_post(req, ev);
360 : }
361 0 : tevent_req_set_callback(subreq, idmap_script_sid2xid_done, req);
362 0 : return req;
363 : }
364 :
365 0 : static void idmap_script_sid2xid_done(struct tevent_req *subreq)
366 : {
367 0 : struct tevent_req *req = tevent_req_callback_data(
368 : subreq, struct tevent_req);
369 0 : struct idmap_script_sid2xid_state *state = tevent_req_data(
370 : req, struct idmap_script_sid2xid_state);
371 : int ret;
372 :
373 0 : ret = file_ploadv_recv(subreq, state, &state->out);
374 0 : TALLOC_FREE(subreq);
375 0 : if (tevent_req_error(req, ret)) {
376 0 : return;
377 : }
378 0 : tevent_req_done(req);
379 : }
380 :
381 0 : static int idmap_script_sid2xid_recv(struct tevent_req *req,
382 : size_t *idx, enum id_mapping *status,
383 : struct unixid *xid)
384 : {
385 0 : struct idmap_script_sid2xid_state *state = tevent_req_data(
386 : req, struct idmap_script_sid2xid_state);
387 0 : char *out = (char *)state->out;
388 0 : size_t out_size = talloc_get_size(out);
389 : unsigned long v;
390 : int err;
391 :
392 0 : if (tevent_req_is_unix_error(req, &err)) {
393 0 : return err;
394 : }
395 :
396 0 : if (out_size == 0) {
397 0 : goto unmapped;
398 : }
399 0 : if (state->out[out_size-1] != '\0') {
400 0 : goto unmapped;
401 : }
402 :
403 0 : *idx = state->idx;
404 :
405 0 : if (sscanf(out, "XID:%lu\n", &v) == 1) {
406 0 : *xid = (struct unixid) { .id = v, .type = ID_TYPE_BOTH };
407 0 : } else if (sscanf(out, "UID:%lu\n", &v) == 1) {
408 0 : *xid = (struct unixid) { .id = v, .type = ID_TYPE_UID };
409 0 : } else if (sscanf(out, "GID:%lu\n", &v) == 1) {
410 0 : *xid = (struct unixid) { .id = v, .type = ID_TYPE_GID };
411 : } else {
412 0 : goto unmapped;
413 : }
414 :
415 0 : *status = ID_MAPPED;
416 0 : return 0;
417 :
418 0 : unmapped:
419 0 : *xid = (struct unixid) { .id = UINT32_MAX,
420 : .type = ID_TYPE_NOT_SPECIFIED };
421 0 : *status = ID_UNMAPPED;
422 0 : return 0;
423 : }
424 :
425 : struct idmap_script_sids2xids_state {
426 : struct id_map **ids;
427 : size_t num_ids;
428 : size_t num_done;
429 : };
430 :
431 : static void idmap_script_sids2xids_done(struct tevent_req *subreq);
432 :
433 0 : static struct tevent_req *idmap_script_sids2xids_send(
434 : TALLOC_CTX *mem_ctx, struct tevent_context *ev,
435 : struct id_map **ids, size_t num_ids, const char *script)
436 : {
437 : struct tevent_req *req;
438 : struct idmap_script_sids2xids_state *state;
439 : size_t i;
440 :
441 0 : req = tevent_req_create(mem_ctx, &state,
442 : struct idmap_script_sids2xids_state);
443 0 : if (req == NULL) {
444 0 : return NULL;
445 : }
446 0 : state->ids = ids;
447 0 : state->num_ids = num_ids;
448 :
449 0 : if (state->num_ids == 0) {
450 0 : tevent_req_done(req);
451 0 : return tevent_req_post(req, ev);
452 : }
453 :
454 0 : for (i=0; i<num_ids; i++) {
455 : struct tevent_req *subreq;
456 :
457 0 : subreq = idmap_script_sid2xid_send(
458 0 : state, ev, ids[i]->sid, script, i);
459 0 : if (tevent_req_nomem(subreq, req)) {
460 0 : return tevent_req_post(req, ev);
461 : }
462 0 : tevent_req_set_callback(subreq, idmap_script_sids2xids_done,
463 : req);
464 : }
465 :
466 0 : return req;
467 : }
468 :
469 0 : static void idmap_script_sids2xids_done(struct tevent_req *subreq)
470 : {
471 0 : struct tevent_req *req = tevent_req_callback_data(
472 : subreq, struct tevent_req);
473 0 : struct idmap_script_sids2xids_state *state = tevent_req_data(
474 : req, struct idmap_script_sids2xids_state);
475 0 : size_t idx = 0;
476 0 : enum id_mapping status = ID_UNKNOWN;
477 0 : struct unixid xid = { .id = UINT32_MAX,
478 : .type = ID_TYPE_NOT_SPECIFIED };
479 : int ret;
480 :
481 0 : ret = idmap_script_sid2xid_recv(subreq, &idx, &status, &xid);
482 0 : TALLOC_FREE(subreq);
483 0 : if (tevent_req_error(req, ret)) {
484 0 : return;
485 : }
486 :
487 0 : if (idx >= state->num_ids) {
488 0 : tevent_req_error(req, EINVAL);
489 0 : return;
490 : }
491 :
492 0 : state->ids[idx]->status = status;
493 0 : state->ids[idx]->xid = xid;
494 :
495 0 : state->num_done += 1;
496 :
497 0 : if (state->num_done >= state->num_ids) {
498 0 : tevent_req_done(req);
499 : }
500 : }
501 :
502 0 : static int idmap_script_sids2xids_recv(struct tevent_req *req)
503 : {
504 0 : return tevent_req_simple_recv_unix(req);
505 : }
506 :
507 0 : static int idmap_script_sids2xids(struct id_map **ids, size_t num_ids,
508 : const char *script)
509 : {
510 0 : TALLOC_CTX *frame = talloc_stackframe();
511 : struct tevent_context *ev;
512 : struct tevent_req *req;
513 0 : int ret = ENOMEM;
514 :
515 0 : ev = samba_tevent_context_init(frame);
516 0 : if (ev == NULL) {
517 0 : goto fail;
518 : }
519 0 : req = idmap_script_sids2xids_send(frame, ev, ids, num_ids, script);
520 0 : if (req == NULL) {
521 0 : goto fail;
522 : }
523 0 : if (!tevent_req_poll(req, ev)) {
524 0 : ret = errno;
525 0 : goto fail;
526 : }
527 0 : ret = idmap_script_sids2xids_recv(req);
528 0 : fail:
529 0 : TALLOC_FREE(frame);
530 0 : return ret;
531 : }
532 :
533 0 : static NTSTATUS idmap_script_sids_to_unixids(struct idmap_domain *dom,
534 : struct id_map **ids)
535 : {
536 0 : struct idmap_script_context *ctx = talloc_get_type_abort(
537 : dom->private_data, struct idmap_script_context);
538 : int ret;
539 : size_t i, num_ids, num_mapped;
540 :
541 0 : DEBUG(10, ("%s called ...\n", __func__));
542 : /* Init status to avoid surprise ... */
543 0 : for (i = 0; ids[i]; i++) {
544 0 : ids[i]->status = ID_UNKNOWN;
545 : }
546 0 : num_ids = i;
547 :
548 0 : ret = idmap_script_sids2xids(ids, num_ids, ctx->script);
549 0 : if (ret != 0) {
550 0 : DBG_DEBUG("idmap_script_sids2xids returned %s\n",
551 : strerror(ret));
552 0 : return map_nt_error_from_unix(ret);
553 : }
554 :
555 0 : num_mapped = 0;
556 :
557 0 : for (i=0; i<num_ids; i++) {
558 0 : struct id_map *map = ids[i];
559 :
560 0 : if ((map->status == ID_MAPPED) &&
561 0 : !idmap_unix_id_is_in_range(map->xid.id, dom)) {
562 0 : DBG_INFO("Script returned id (%u) out of range "
563 : "(%u - %u). Filtered!\n",
564 : map->xid.id, dom->low_id, dom->high_id);
565 0 : map->status = ID_UNMAPPED;
566 : }
567 :
568 0 : if (map->status == ID_MAPPED) {
569 0 : num_mapped += 1;
570 : }
571 : }
572 :
573 0 : if (num_mapped == 0) {
574 0 : return NT_STATUS_NONE_MAPPED;
575 : }
576 0 : if (num_mapped < num_ids) {
577 0 : return STATUS_SOME_UNMAPPED;
578 : }
579 0 : return NT_STATUS_OK;
580 : }
581 :
582 : /*
583 : * Initialise idmap_script database.
584 : */
585 0 : static NTSTATUS idmap_script_db_init(struct idmap_domain *dom)
586 : {
587 : NTSTATUS ret;
588 : struct idmap_script_context *ctx;
589 0 : const char * idmap_script = NULL;
590 0 : const char *ctx_script = NULL;
591 :
592 0 : DEBUG(10, ("%s called ...\n", __func__));
593 :
594 0 : ctx = talloc_zero(dom, struct idmap_script_context);
595 0 : if (!ctx) {
596 0 : DEBUG(0, ("Out of memory!\n"));
597 0 : ret = NT_STATUS_NO_MEMORY;
598 0 : goto failed;
599 : }
600 :
601 0 : ctx_script = idmap_config_const_string(dom->name, "script", NULL);
602 :
603 : /* Do we even need to handle this? */
604 0 : idmap_script = lp_parm_const_string(-1, "idmap", "script", NULL);
605 0 : if (idmap_script != NULL) {
606 0 : DEBUG(0, ("Warning: 'idmap:script' is deprecated. "
607 : " Please use 'idmap config * : script' instead!\n"));
608 : }
609 :
610 0 : if (strequal(dom->name, "*") && ctx_script == NULL) {
611 : /* fall back to idmap:script for backwards compatibility */
612 0 : ctx_script = idmap_script;
613 : }
614 :
615 0 : if (ctx_script) {
616 0 : DEBUG(1, ("using idmap script '%s'\n", ctx->script));
617 : /*
618 : * We must ensure this memory is owned by ctx.
619 : * The ctx_script const pointer is a pointer into
620 : * the config file data and may become invalid
621 : * on config file reload. BUG: 13956
622 : */
623 0 : ctx->script = talloc_strdup(ctx, ctx_script);
624 0 : if (ctx->script == NULL) {
625 0 : ret = NT_STATUS_NO_MEMORY;
626 0 : goto failed;
627 : }
628 : }
629 :
630 0 : dom->private_data = ctx;
631 0 : dom->read_only = true; /* We do not allocate!*/
632 :
633 0 : return NT_STATUS_OK;
634 :
635 0 : failed:
636 0 : talloc_free(ctx);
637 0 : return ret;
638 : }
639 :
640 : static const struct idmap_methods db_methods = {
641 : .init = idmap_script_db_init,
642 : .unixids_to_sids = idmap_script_unixids_to_sids,
643 : .sids_to_unixids = idmap_script_sids_to_unixids,
644 : };
645 :
646 : static_decl_idmap;
647 0 : NTSTATUS idmap_script_init(TALLOC_CTX *ctx)
648 : {
649 0 : return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "script", &db_methods);
650 : }
|