s3:winbindd: Fix double close(fd)
[samba.git] / source3 / winbindd / idmap_script.c
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 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         req = tevent_req_create(mem_ctx, &state,
82                                 struct idmap_script_xid2sid_state);
83         if (req == NULL) {
84                 return NULL;
85         }
86         state->idx = idx;
87
88         switch (xid.type) {
89             case ID_TYPE_UID:
90                     key = 'U';
91                     break;
92             case ID_TYPE_GID:
93                     key = 'G';
94                     break;
95             case ID_TYPE_BOTH:
96                     key = 'X';
97                     break;
98             default:
99                     DBG_WARNING("INVALID unix ID type: 0x02%x\n", xid.type);
100                     tevent_req_error(req, EINVAL);
101                     return tevent_req_post(req, ev);
102         }
103
104         state->argl = str_list_make_empty(state);
105         str_list_add_printf(&state->argl, "%s", script);
106         str_list_add_printf(&state->argl, "IDTOSID");
107         str_list_add_printf(&state->argl, "%cID", key);
108         str_list_add_printf(&state->argl, "%lu", (unsigned long)xid.id);
109         if (tevent_req_nomem(state->argl, req)) {
110                 return tevent_req_post(req, ev);
111         }
112
113         subreq = file_ploadv_send(state, ev, state->argl, 1024);
114         if (tevent_req_nomem(subreq, req)) {
115                 return tevent_req_post(req, ev);
116         }
117         tevent_req_set_callback(subreq, idmap_script_xid2sid_done, req);
118         return req;
119 }
120
121 static void idmap_script_xid2sid_done(struct tevent_req *subreq)
122 {
123         struct tevent_req *req = tevent_req_callback_data(
124                 subreq, struct tevent_req);
125         struct idmap_script_xid2sid_state *state = tevent_req_data(
126                 req, struct idmap_script_xid2sid_state);
127         int ret;
128
129         ret = file_ploadv_recv(subreq, state, &state->out);
130         TALLOC_FREE(subreq);
131         if (tevent_req_error(req, ret)) {
132                 return;
133         }
134         tevent_req_done(req);
135 }
136
137 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         struct idmap_script_xid2sid_state *state = tevent_req_data(
142                 req, struct idmap_script_xid2sid_state);
143         char *out = (char *)state->out;
144         size_t out_size = talloc_get_size(out);
145         int err;
146
147         if (tevent_req_is_unix_error(req, &err)) {
148                 return err;
149         }
150
151         if (out_size == 0) {
152                 goto unmapped;
153         }
154         if (state->out[out_size-1] != '\0') {
155                 goto unmapped;
156         }
157
158         *idx = state->idx;
159
160         if ((strncmp(out, "SID:S-", 6) != 0) ||
161             !dom_sid_parse(out+4, sid)) {
162                 DBG_WARNING("Bad sid from script: %s\n", out);
163                 goto unmapped;
164         }
165
166         *status = ID_MAPPED;
167         return 0;
168
169 unmapped:
170         *sid = (struct dom_sid) {0};
171         *status = ID_UNMAPPED;
172         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 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         req = tevent_req_create(mem_ctx, &state,
192                                 struct idmap_script_xids2sids_state);
193         if (req == NULL) {
194                 return NULL;
195         }
196         state->ids = ids;
197         state->num_ids = num_ids;
198
199         if (state->num_ids == 0) {
200                 tevent_req_done(req);
201                 return tevent_req_post(req, ev);
202         }
203
204         for (i=0; i<num_ids; i++) {
205                 struct tevent_req *subreq;
206
207                 subreq = idmap_script_xid2sid_send(
208                         state, ev, ids[i]->xid, script, i);
209                 if (tevent_req_nomem(subreq, req)) {
210                         return tevent_req_post(req, ev);
211                 }
212                 tevent_req_set_callback(subreq, idmap_script_xids2sids_done,
213                                         req);
214         }
215
216         return req;
217 }
218
219 static void idmap_script_xids2sids_done(struct tevent_req *subreq)
220 {
221         struct tevent_req *req = tevent_req_callback_data(
222                 subreq, struct tevent_req);
223         struct idmap_script_xids2sids_state *state = tevent_req_data(
224                 req, struct idmap_script_xids2sids_state);
225         size_t idx = 0;
226         enum id_mapping status = ID_UNKNOWN;
227         struct dom_sid sid = {0};
228         int ret;
229
230         ret = idmap_script_xid2sid_recv(subreq, &idx, &status, &sid);
231         TALLOC_FREE(subreq);
232         if (tevent_req_error(req, ret)) {
233                 return;
234         }
235
236         if (idx >= state->num_ids) {
237                 tevent_req_error(req, EINVAL);
238                 return;
239         }
240
241         state->ids[idx]->status = status;
242
243         state->ids[idx]->sid = dom_sid_dup(state->ids, &sid);
244         if (tevent_req_nomem(state->ids[idx]->sid, req)) {
245                 return;
246         }
247
248         state->num_done += 1;
249
250         if (state->num_done >= state->num_ids) {
251                 tevent_req_done(req);
252         }
253 }
254
255 static int idmap_script_xids2sids_recv(struct tevent_req *req)
256 {
257         return tevent_req_simple_recv_unix(req);
258 }
259
260 static int idmap_script_xids2sids(struct id_map **ids, size_t num_ids,
261                                   const char *script)
262 {
263         TALLOC_CTX *frame = talloc_stackframe();
264         struct tevent_context *ev;
265         struct tevent_req *req;
266         int ret = ENOMEM;
267
268         ev = samba_tevent_context_init(frame);
269         if (ev == NULL) {
270                 goto fail;
271         }
272         req = idmap_script_xids2sids_send(frame, ev, ids, num_ids, script);
273         if (req == NULL) {
274                 goto fail;
275         }
276         if (!tevent_req_poll(req, ev)) {
277                 ret = errno;
278                 goto fail;
279         }
280         ret = idmap_script_xids2sids_recv(req);
281 fail:
282         TALLOC_FREE(frame);
283         return ret;
284 }
285
286 static NTSTATUS idmap_script_unixids_to_sids(struct idmap_domain *dom,
287                                              struct id_map **ids)
288 {
289         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         DEBUG(10, ("%s called ...\n", __func__));
295         /* Init status to avoid surprise ... */
296         for (i = 0; ids[i]; i++) {
297                 ids[i]->status = ID_UNKNOWN;
298         }
299         num_ids = i;
300
301         ret = idmap_script_xids2sids(ids, num_ids, ctx->script);
302         if (ret != 0) {
303                 DBG_DEBUG("idmap_script_xids2sids returned %s\n",
304                           strerror(ret));
305                 return map_nt_error_from_unix(ret);
306         }
307
308         num_mapped = 0;
309
310         for (i = 0; ids[i]; i++) {
311                 if (ids[i]->status == ID_MAPPED) {
312                         num_mapped += 1;
313                 }
314         }
315
316         if (num_mapped == 0) {
317                 return NT_STATUS_NONE_MAPPED;
318         }
319         if (num_mapped < num_ids) {
320                 return STATUS_SOME_UNMAPPED;
321         }
322         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 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         req = tevent_req_create(mem_ctx, &state,
342                                 struct idmap_script_sid2xid_state);
343         if (req == NULL) {
344                 return NULL;
345         }
346         state->idx = idx;
347
348         state->argl = str_list_make_empty(state);
349         str_list_add_printf(&state->argl, "%s", script);
350         str_list_add_printf(&state->argl, "SIDTOID");
351         str_list_add_printf(
352                 &state->argl, "%s", dom_sid_str_buf(sid, &sidbuf));
353         if (tevent_req_nomem(state->argl, req)) {
354                 return tevent_req_post(req, ev);
355         }
356
357         subreq = file_ploadv_send(state, ev, state->argl, 1024);
358         if (tevent_req_nomem(subreq, req)) {
359                 return tevent_req_post(req, ev);
360         }
361         tevent_req_set_callback(subreq, idmap_script_sid2xid_done, req);
362         return req;
363 }
364
365 static void idmap_script_sid2xid_done(struct tevent_req *subreq)
366 {
367         struct tevent_req *req = tevent_req_callback_data(
368                 subreq, struct tevent_req);
369         struct idmap_script_sid2xid_state *state = tevent_req_data(
370                 req, struct idmap_script_sid2xid_state);
371         int ret;
372
373         ret = file_ploadv_recv(subreq, state, &state->out);
374         TALLOC_FREE(subreq);
375         if (tevent_req_error(req, ret)) {
376                 return;
377         }
378         tevent_req_done(req);
379 }
380
381 static int idmap_script_sid2xid_recv(struct tevent_req *req,
382                                      size_t *idx, enum id_mapping *status,
383                                      struct unixid *xid)
384 {
385         struct idmap_script_sid2xid_state *state = tevent_req_data(
386                 req, struct idmap_script_sid2xid_state);
387         char *out = (char *)state->out;
388         size_t out_size = talloc_get_size(out);
389         unsigned long v;
390         int err;
391
392         if (tevent_req_is_unix_error(req, &err)) {
393                 return err;
394         }
395
396         if (out_size == 0) {
397                 goto unmapped;
398         }
399         if (state->out[out_size-1] != '\0') {
400                 goto unmapped;
401         }
402
403         *idx = state->idx;
404
405         if (sscanf(out, "XID:%lu\n", &v) == 1) {
406                 *xid = (struct unixid) { .id = v, .type = ID_TYPE_BOTH };
407         } else if (sscanf(out, "UID:%lu\n", &v) == 1) {
408                 *xid = (struct unixid) { .id = v, .type = ID_TYPE_UID };
409         } else if (sscanf(out, "GID:%lu\n", &v) == 1) {
410                 *xid = (struct unixid) { .id = v, .type = ID_TYPE_GID };
411         } else {
412                 goto unmapped;
413         }
414
415         *status = ID_MAPPED;
416         return 0;
417
418 unmapped:
419         *xid = (struct unixid) { .id = UINT32_MAX,
420                                  .type = ID_TYPE_NOT_SPECIFIED };
421         *status = ID_UNMAPPED;
422         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 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         req = tevent_req_create(mem_ctx, &state,
442                                 struct idmap_script_sids2xids_state);
443         if (req == NULL) {
444                 return NULL;
445         }
446         state->ids = ids;
447         state->num_ids = num_ids;
448
449         if (state->num_ids == 0) {
450                 tevent_req_done(req);
451                 return tevent_req_post(req, ev);
452         }
453
454         for (i=0; i<num_ids; i++) {
455                 struct tevent_req *subreq;
456
457                 subreq = idmap_script_sid2xid_send(
458                         state, ev, ids[i]->sid, script, i);
459                 if (tevent_req_nomem(subreq, req)) {
460                         return tevent_req_post(req, ev);
461                 }
462                 tevent_req_set_callback(subreq, idmap_script_sids2xids_done,
463                                         req);
464         }
465
466         return req;
467 }
468
469 static void idmap_script_sids2xids_done(struct tevent_req *subreq)
470 {
471         struct tevent_req *req = tevent_req_callback_data(
472                 subreq, struct tevent_req);
473         struct idmap_script_sids2xids_state *state = tevent_req_data(
474                 req, struct idmap_script_sids2xids_state);
475         size_t idx = 0;
476         enum id_mapping status = ID_UNKNOWN;
477         struct unixid xid = { .id = UINT32_MAX,
478                               .type = ID_TYPE_NOT_SPECIFIED };
479         int ret;
480
481         ret = idmap_script_sid2xid_recv(subreq, &idx, &status, &xid);
482         TALLOC_FREE(subreq);
483         if (tevent_req_error(req, ret)) {
484                 return;
485         }
486
487         if (idx >= state->num_ids) {
488                 tevent_req_error(req, EINVAL);
489                 return;
490         }
491
492         state->ids[idx]->status = status;
493         state->ids[idx]->xid = xid;
494
495         state->num_done += 1;
496
497         if (state->num_done >= state->num_ids) {
498                 tevent_req_done(req);
499         }
500 }
501
502 static int idmap_script_sids2xids_recv(struct tevent_req *req)
503 {
504         return tevent_req_simple_recv_unix(req);
505 }
506
507 static int idmap_script_sids2xids(struct id_map **ids, size_t num_ids,
508                                   const char *script)
509 {
510         TALLOC_CTX *frame = talloc_stackframe();
511         struct tevent_context *ev;
512         struct tevent_req *req;
513         int ret = ENOMEM;
514
515         ev = samba_tevent_context_init(frame);
516         if (ev == NULL) {
517                 goto fail;
518         }
519         req = idmap_script_sids2xids_send(frame, ev, ids, num_ids, script);
520         if (req == NULL) {
521                 goto fail;
522         }
523         if (!tevent_req_poll(req, ev)) {
524                 ret = errno;
525                 goto fail;
526         }
527         ret = idmap_script_sids2xids_recv(req);
528 fail:
529         TALLOC_FREE(frame);
530         return ret;
531 }
532
533 static NTSTATUS idmap_script_sids_to_unixids(struct idmap_domain *dom,
534                                              struct id_map **ids)
535 {
536         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         DEBUG(10, ("%s called ...\n", __func__));
542         /* Init status to avoid surprise ... */
543         for (i = 0; ids[i]; i++) {
544                 ids[i]->status = ID_UNKNOWN;
545         }
546         num_ids = i;
547
548         ret = idmap_script_sids2xids(ids, num_ids, ctx->script);
549         if (ret != 0) {
550                 DBG_DEBUG("idmap_script_sids2xids returned %s\n",
551                           strerror(ret));
552                 return map_nt_error_from_unix(ret);
553         }
554
555         num_mapped = 0;
556
557         for (i=0; i<num_ids; i++) {
558                 struct id_map *map = ids[i];
559
560                 if ((map->status == ID_MAPPED) &&
561                     !idmap_unix_id_is_in_range(map->xid.id, dom)) {
562                         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                         map->status = ID_UNMAPPED;
566                 }
567
568                 if (map->status == ID_MAPPED) {
569                         num_mapped += 1;
570                 }
571         }
572
573         if (num_mapped == 0) {
574                 return NT_STATUS_NONE_MAPPED;
575         }
576         if (num_mapped < num_ids) {
577                 return STATUS_SOME_UNMAPPED;
578         }
579         return NT_STATUS_OK;
580 }
581
582 /*
583  *   Initialise idmap_script database.
584  */
585 static NTSTATUS idmap_script_db_init(struct idmap_domain *dom)
586 {
587         NTSTATUS ret;
588         struct idmap_script_context *ctx;
589         const char * idmap_script = NULL;
590         const char *ctx_script = NULL;
591
592         DEBUG(10, ("%s called ...\n", __func__));
593
594         ctx = talloc_zero(dom, struct idmap_script_context);
595         if (!ctx) {
596                 DEBUG(0, ("Out of memory!\n"));
597                 ret = NT_STATUS_NO_MEMORY;
598                 goto failed;
599         }
600
601         ctx_script = idmap_config_const_string(dom->name, "script", NULL);
602
603         /* Do we even need to handle this? */
604         idmap_script = lp_parm_const_string(-1, "idmap", "script", NULL);
605         if (idmap_script != NULL) {
606                 DEBUG(0, ("Warning: 'idmap:script' is deprecated. "
607                           " Please use 'idmap config * : script' instead!\n"));
608         }
609
610         if (strequal(dom->name, "*") && ctx_script == NULL) {
611                 /* fall back to idmap:script for backwards compatibility */
612                 ctx_script = idmap_script;
613         }
614
615         if (ctx_script) {
616                 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                 ctx->script = talloc_strdup(ctx, ctx_script);
624                 if (ctx->script == NULL) {
625                         ret = NT_STATUS_NO_MEMORY;
626                         goto failed;
627                 }
628         }
629
630         dom->private_data = ctx;
631         dom->read_only = true; /* We do not allocate!*/
632
633         return NT_STATUS_OK;
634
635 failed:
636         talloc_free(ctx);
637         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 NTSTATUS idmap_script_init(TALLOC_CTX *ctx)
648 {
649         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "script", &db_methods);
650 }