vfs_shadow_copy2: check crossmountpoints against snapdirseverywhere
[obnox/samba/samba-obnox.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
39 #undef DBGC_CLASS
40 #define DBGC_CLASS DBGC_IDMAP
41
42 struct idmap_script_context {
43         const char *script; /* script to provide idmaps */
44 };
45
46 /*
47   run a script to perform a mapping
48
49   The script should accept the following command lines:
50
51       SIDTOID S-1-xxxx -> XID:<id> | ERR:<str>
52       SIDTOID S-1-xxxx -> UID:<id> | ERR:<str>
53       SIDTOID S-1-xxxx -> GID:<id> | ERR:<str>
54       IDTOSID XID xxxx -> SID:<sid> | ERR:<str>
55       IDTOSID UID xxxx -> SID:<sid> | ERR:<str>
56       IDTOSID GID xxxx -> SID:<sid> | ERR:<str>
57
58   where XID means both a UID and a GID. This is the case for ID_TYPE_BOTH.
59
60   TODO: Needs more validation ... like that we got a UID when we asked for one.
61  */
62 static NTSTATUS idmap_script_script(struct idmap_script_context *ctx,
63                                     struct id_map *map, const char *fmt, ...)
64 {
65         va_list ap;
66         char *cmd, **lines;
67         int numlines = 0;
68         unsigned long v;
69
70         cmd = talloc_asprintf(ctx, "%s ", ctx->script);
71         if (!cmd) {
72                 DEBUG(10, ("Unable to allocate memory for the script command!\n"));
73                 return NT_STATUS_NO_MEMORY;
74         }
75
76         va_start(ap, fmt);
77         cmd = talloc_vasprintf_append(cmd, fmt, ap);
78         va_end(ap);
79         if (!cmd) {
80                 DEBUG(10, ("Unable to allocate memory for the script command!\n"));
81                 return NT_STATUS_NO_MEMORY;
82         }
83
84         lines = file_lines_pload(cmd, &numlines);
85         talloc_free(cmd);
86         if (!lines) {
87                 return NT_STATUS_NONE_MAPPED;
88         }
89
90         DEBUG(10,("idmap script gave %d lines, first: %s\n", numlines,
91                 lines[0]));
92
93         if (sscanf(lines[0], "XID:%lu", &v) == 1) {
94                 map->xid.id   = v;
95                 map->xid.type = ID_TYPE_BOTH;
96         } else if (sscanf(lines[0], "UID:%lu", &v) == 1) {
97                 map->xid.id   = v;
98                 map->xid.type = ID_TYPE_UID;
99         } else if (sscanf(lines[0], "GID:%lu", &v) == 1) {
100                 map->xid.id   = v;
101                 map->xid.type = ID_TYPE_GID;
102         } else if (strncmp(lines[0], "SID:S-", 6) == 0) {
103                 if (!string_to_sid(map->sid, &lines[0][4])) {
104                         DEBUG(0,("Bad SID in '%s' from idmap script %s\n",
105                                  lines[0], ctx->script));
106                         talloc_free(lines);
107                         return NT_STATUS_NONE_MAPPED;
108                 }
109         } else {
110                 DEBUG(0,("Bad reply '%s' from idmap script %s\n",
111                          lines[0], ctx->script));
112                 talloc_free(lines);
113                 return NT_STATUS_NONE_MAPPED;
114         }
115
116         talloc_free(lines);
117         return NT_STATUS_OK;
118 }
119
120 /*
121   Single id to sid lookup function.
122 */
123 static NTSTATUS idmap_script_id_to_sid(struct idmap_domain *dom,
124                                        struct id_map *map)
125 {
126         NTSTATUS ret;
127         char *keystr;
128         char *sidstr;
129         struct idmap_script_context *ctx = dom->private_data;
130
131         if (!dom || !map) {
132                 return NT_STATUS_INVALID_PARAMETER;
133         }
134
135         /* apply filters before checking */
136         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
137                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
138                                 map->xid.id, dom->low_id, dom->high_id));
139                 return NT_STATUS_NONE_MAPPED;
140         }
141
142         switch (map->xid.type) {
143
144         case ID_TYPE_UID:
145                 keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
146                 break;
147
148         case ID_TYPE_GID:
149                 keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
150                 break;
151
152         case ID_TYPE_BOTH:
153                 keystr = talloc_asprintf(ctx, "XID %lu", (unsigned long)map->xid.id);
154                 break;
155
156         default:
157                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
158                 return NT_STATUS_INVALID_PARAMETER;
159         }
160
161         if (keystr == NULL) {
162                 DEBUG(0, ("Out of memory!\n"));
163                 ret = NT_STATUS_NO_MEMORY;
164                 goto done;
165         }
166
167         DEBUG(10,("Running script to fetch mapping %s\n", keystr));
168
169         ret = idmap_script_script(ctx, map, "IDTOSID %s", keystr);
170         if (!NT_STATUS_IS_OK(ret)) {
171                 goto done;
172         }
173
174         sidstr = sid_string_talloc(keystr, map->sid);
175         if (!sidstr) {
176                 ret = NT_STATUS_NO_MEMORY;
177                 goto done;
178         }
179
180         DEBUG(10,("Found id %s:%d -> %s\n", keystr, map->xid.id,
181                   (const char *)sidstr));
182         ret = NT_STATUS_OK;
183
184 done:
185         talloc_free(keystr);
186         return ret;
187 }
188
189 /*
190  Single sid to id lookup function.
191 */
192 static NTSTATUS idmap_script_sid_to_id(struct idmap_domain *dom,
193                                        struct id_map *map)
194 {
195         NTSTATUS ret;
196         char *keystr;
197         struct idmap_script_context *ctx = dom->private_data;
198         TALLOC_CTX *tmp_ctx = talloc_stackframe();
199
200         keystr = sid_string_talloc(tmp_ctx, map->sid);
201         if (keystr == NULL) {
202                 DEBUG(0, ("Out of memory!\n"));
203                 ret = NT_STATUS_NO_MEMORY;
204                 goto done;
205         }
206
207         DEBUG(10,("Fetching record %s\n", keystr));
208
209         if (ctx->script == NULL) {
210                 ret = NT_STATUS_NONE_MAPPED;
211                 goto done;
212         }
213
214         ret = idmap_script_script(ctx, map, "SIDTOID %s", keystr);
215         if (!NT_STATUS_IS_OK(ret)) {
216                 goto done;
217         }
218
219         /* apply filters before returning result */
220         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
221                 DEBUG(5, ("Script returned id (%u) out of range (%u - %u)."
222                           " Filtered!\n",
223                           map->xid.id, dom->low_id, dom->high_id));
224                 ret = NT_STATUS_NONE_MAPPED;
225                 goto done;
226         }
227
228 done:
229         talloc_free(tmp_ctx);
230         return ret;
231 }
232
233 static NTSTATUS idmap_script_unixids_to_sids(struct idmap_domain *dom,
234                                       struct id_map **ids)
235 {
236         NTSTATUS ret;
237         int i, num_mapped = 0;
238
239         DEBUG(10, ("%s called ...\n", __func__));
240         /* Init status to avoid surprise ... */
241         for (i = 0; ids[i]; i++) {
242                 ids[i]->status = ID_UNKNOWN;
243         }
244
245         for (i = 0; ids[i]; i++) {
246                 ret = idmap_script_id_to_sid(dom, ids[i]);
247                 if (!NT_STATUS_IS_OK(ret)) {
248                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
249                                 ids[i]->status = ID_UNMAPPED;
250                                 continue;
251                         }
252
253                         /*
254                          * We cannot keep going if it is other than mapping
255                          * failed.
256                          */
257                         goto done;
258                 }
259
260                 ids[i]->status = ID_MAPPED;
261                 num_mapped++;
262         }
263
264         ret = NT_STATUS_OK;
265
266 done:
267         if (NT_STATUS_IS_OK(ret)) {
268                 if (i == 0 || num_mapped == 0) {
269                         ret = NT_STATUS_NONE_MAPPED;
270                 }
271                 else if (num_mapped < i) {
272                         ret = STATUS_SOME_UNMAPPED;
273                 } else {
274                         DEBUG(10, ("Returning NT_STATUS_OK\n"));
275                         ret = NT_STATUS_OK;
276                 }
277         }
278
279         return ret;
280 }
281
282 static NTSTATUS idmap_script_sids_to_unixids(struct idmap_domain *dom,
283                                       struct id_map **ids)
284 {
285         NTSTATUS ret;
286         int i, num_mapped = 0;
287
288         DEBUG(10, ("%s called ...\n", __func__));
289         /* Init status to avoid surprise ... */
290         for (i = 0; ids[i]; i++) {
291                 ids[i]->status = ID_UNKNOWN;
292         }
293
294         for (i = 0; ids[i]; i++) {
295                 ret = idmap_script_sid_to_id(dom, ids[i]);
296                 if (!NT_STATUS_IS_OK(ret)) {
297                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
298                                 ids[i]->status = ID_UNMAPPED;
299                                 continue;
300                         }
301
302                         /*
303                          * We cannot keep going if it is other than mapping
304                          * failed.
305                          */
306                         goto done;
307                 }
308
309                 ids[i]->status = ID_MAPPED;
310                 num_mapped++;
311         }
312
313         ret = NT_STATUS_OK;
314
315 done:
316         if (NT_STATUS_IS_OK(ret)) {
317                 if (i == 0 || num_mapped == 0) {
318                         ret = NT_STATUS_NONE_MAPPED;
319                 }
320                 else if (num_mapped < i) {
321                         ret = STATUS_SOME_UNMAPPED;
322                 } else {
323                         DEBUG(10, ("Returning NT_STATUS_OK\n"));
324                         ret = NT_STATUS_OK;
325                 }
326         }
327
328         return ret;
329 }
330
331 /*
332  *   Initialise idmap_script database.
333  */
334 static NTSTATUS idmap_script_db_init(struct idmap_domain *dom)
335 {
336         NTSTATUS ret;
337         struct idmap_script_context *ctx;
338         char *config_option = NULL;
339         const char * idmap_script = NULL;
340
341         DEBUG(10, ("%s called ...\n", __func__));
342
343         ctx = talloc_zero(dom, struct idmap_script_context);
344         if (!ctx) {
345                 DEBUG(0, ("Out of memory!\n"));
346                 ret = NT_STATUS_NO_MEMORY;
347                 goto failed;
348         }
349
350         config_option = talloc_asprintf(ctx, "idmap config %s", dom->name);
351         if (config_option == NULL) {
352                 DEBUG(0, ("Out of memory!\n"));
353                 ret = NT_STATUS_NO_MEMORY;
354                 goto failed;
355         }
356         ctx->script = lp_parm_const_string(-1, config_option, "script", NULL);
357         talloc_free(config_option);
358
359         /* Do we even need to handle this? */
360         idmap_script = lp_parm_const_string(-1, "idmap", "script", NULL);
361         if (idmap_script != NULL) {
362                 DEBUG(0, ("Warning: 'idmap:script' is deprecated. "
363                           " Please use 'idmap config * : script' instead!\n"));
364         }
365
366         if (strequal(dom->name, "*") && ctx->script == NULL) {
367                 /* fall back to idmap:script for backwards compatibility */
368                 ctx->script = idmap_script;
369         }
370
371         if (ctx->script) {
372                 DEBUG(1, ("using idmap script '%s'\n", ctx->script));
373         }
374
375         dom->private_data = ctx;
376         dom->read_only = true; /* We do not allocate!*/
377
378         return NT_STATUS_OK;
379
380 failed:
381         talloc_free(ctx);
382         return ret;
383 }
384
385 static struct idmap_methods db_methods = {
386         .init            = idmap_script_db_init,
387         .unixids_to_sids = idmap_script_unixids_to_sids,
388         .sids_to_unixids = idmap_script_sids_to_unixids,
389 };
390
391 static_decl_idmap;
392 NTSTATUS idmap_script_init(void)
393 {
394         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "script", &db_methods);
395 }