1 /*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2015 Varnish Software AS
4 * All rights reserved.
5 *
6 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7 *
8 * SPDX-License-Identifier: BSD-2-Clause
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * VCL compiler stuff
32 */
33
34 #include "config.h"
35
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <sys/stat.h>
42
43 #include "mgt/mgt.h"
44 #include "mgt/mgt_vcl.h"
45 #include "common/heritage.h"
46 #include "storage/storage.h"
47
48 #include "libvcc.h"
49 #include "vcli_serve.h"
50 #include "vfil.h"
51 #include "vsub.h"
52 #include "vtim.h"
53
54 struct vcc_priv {
55 unsigned magic;
56 #define VCC_PRIV_MAGIC 0x70080cb8
57 const char *vclsrc;
58 const char *vclsrcfile;
59 struct vsb *dir;
60 struct vsb *csrcfile;
61 struct vsb *libfile;
62 struct vsb *symfile;
63 };
64
65 char *mgt_cc_cmd;
66 const char *mgt_vcl_path;
67 const char *mgt_vmod_path;
68 #define MGT_VCC(t, n, cc) t mgt_vcc_ ## n;
69 #include <tbl/mgt_vcc.h>
70
71 #define VGC_SRC "vgc.c"
72 #define VGC_LIB "vgc.so"
73 #define VGC_SYM "vgc.sym"
74
75 /*--------------------------------------------------------------------*/
76
77 void
mgt_DumpBuiltin(void)78 mgt_DumpBuiltin(void)
79 {
80 printf("%s\n", builtin_vcl);
81 }
82
83 /*--------------------------------------------------------------------
84 * Invoke system VCC compiler in a sub-process
85 */
86
v_matchproto_(vsub_func_f)87 static void v_noreturn_ v_matchproto_(vsub_func_f)
88 run_vcc(void *priv)
89 {
90 struct vsb *sb = NULL;
91 struct vclprog *vpg;
92 struct vcc_priv *vp;
93 struct vcc *vcc;
94 struct stevedore *stv;
95 int i;
96
97 VJ_subproc(JAIL_SUBPROC_VCC);
98 CAST_OBJ_NOTNULL(vp, priv, VCC_PRIV_MAGIC);
99
100 AZ(chdir(VSB_data(vp->dir)));
101
102 vcc = VCC_New();
103 AN(vcc);
104 VCC_Builtin_VCL(vcc, builtin_vcl);
105 VCC_VCL_path(vcc, mgt_vcl_path);
106 VCC_VMOD_path(vcc, mgt_vmod_path);
107
108 #define MGT_VCC(type, name, camelcase) \
109 VCC_ ## camelcase (vcc, mgt_vcc_ ## name);
110 #include "tbl/mgt_vcc.h"
111
112 STV_Foreach(stv)
113 VCC_Predef(vcc, "VCL_STEVEDORE", stv->ident);
114 VTAILQ_FOREACH(vpg, &vclhead, list)
115 if (mcf_is_label(vpg))
116 VCC_Predef(vcc, "VCL_VCL", vpg->name);
117 i = VCC_Compile(vcc, &sb, vp->vclsrc, vp->vclsrcfile,
118 VGC_SRC, VGC_SYM);
119 if (VSB_len(sb))
120 printf("%s", VSB_data(sb));
121 VSB_destroy(&sb);
122 exit(i == 0 ? 0 : 2);
123 }
124
125 /*--------------------------------------------------------------------
126 * Invoke system C compiler in a sub-process
127 */
128
v_matchproto_(vsub_func_f)129 static void v_matchproto_(vsub_func_f)
130 run_cc(void *priv)
131 {
132 struct vcc_priv *vp;
133 struct vsb *sb;
134 int pct;
135 char *p;
136
137 VJ_subproc(JAIL_SUBPROC_CC);
138 CAST_OBJ_NOTNULL(vp, priv, VCC_PRIV_MAGIC);
139
140 AZ(chdir(VSB_data(vp->dir)));
141
142 sb = VSB_new_auto();
143 AN(sb);
144 for (p = mgt_cc_cmd, pct = 0; *p; ++p) {
145 if (pct) {
146 switch (*p) {
147 case 's':
148 VSB_cat(sb, VGC_SRC);
149 break;
150 case 'o':
151 VSB_cat(sb, VGC_LIB);
152 break;
153 case '%':
154 VSB_putc(sb, '%');
155 break;
156 default:
157 VSB_putc(sb, '%');
158 VSB_putc(sb, *p);
159 break;
160 }
161 pct = 0;
162 } else if (*p == '%') {
163 pct = 1;
164 } else {
165 VSB_putc(sb, *p);
166 }
167 }
168 if (pct)
169 VSB_putc(sb, '%');
170 AZ(VSB_finish(sb));
171
172 (void)umask(027);
173 (void)execl("/bin/sh", "/bin/sh", "-c", VSB_data(sb), (char*)0);
174 VSB_destroy(&sb); // For flexelint
175 }
176
177 /*--------------------------------------------------------------------
178 * Attempt to open compiled VCL in a sub-process
179 */
180
v_matchproto_(vsub_func_f)181 static void v_noreturn_ v_matchproto_(vsub_func_f)
182 run_dlopen(void *priv)
183 {
184 struct vcc_priv *vp;
185
186 VJ_subproc(JAIL_SUBPROC_VCLLOAD);
187 CAST_OBJ_NOTNULL(vp, priv, VCC_PRIV_MAGIC);
188 if (VCL_TestLoad(VSB_data(vp->libfile)))
189 exit(1);
190 exit(0);
191 }
192
193 /*--------------------------------------------------------------------
194 * Touch a filename and make it available to privsep-privs
195 */
196
197 static int
mgt_vcc_touchfile(const char * fn,struct vsb * sb)198 mgt_vcc_touchfile(const char *fn, struct vsb *sb)
199 {
200 int i;
201
202 i = open(fn, O_WRONLY|O_CREAT|O_TRUNC, 0640);
203 if (i < 0) {
204 VSB_printf(sb, "Failed to create %s: %s", fn, vstrerror(errno));
205 return (2);
206 }
207 if (fchown(i, mgt_param.uid, mgt_param.gid) != 0)
208 if (geteuid() == 0)
209 VSB_printf(sb, "Failed to change owner on %s: %s\n",
210 fn, vstrerror(errno));
211 closefd(&i);
212 return (0);
213 }
214
215 /*--------------------------------------------------------------------
216 * Compile a VCL program, return shared object, errors in sb.
217 */
218
219 static unsigned
mgt_vcc_compile(struct vcc_priv * vp,struct vsb * sb,int C_flag)220 mgt_vcc_compile(struct vcc_priv *vp, struct vsb *sb, int C_flag)
221 {
222 char *csrc;
223 unsigned subs;
224
225 AN(sb);
226 VSB_clear(sb);
227 if (mgt_vcc_touchfile(VSB_data(vp->csrcfile), sb))
228 return (2);
229 if (mgt_vcc_touchfile(VSB_data(vp->libfile), sb))
230 return (2);
231
232 VJ_master(JAIL_MASTER_SYSTEM);
233 subs = VSUB_run(sb, run_vcc, vp, "VCC-compiler", -1);
234 VJ_master(JAIL_MASTER_LOW);
235 if (subs)
236 return (subs);
237
238 if (C_flag) {
239 csrc = VFIL_readfile(NULL, VSB_data(vp->csrcfile), NULL);
240 AN(csrc);
241 VSB_cat(sb, csrc);
242 free(csrc);
243
244 VSB_cat(sb, "/* EXTERNAL SYMBOL TABLE\n");
245 csrc = VFIL_readfile(NULL, VSB_data(vp->symfile), NULL);
246 AN(csrc);
247 VSB_cat(sb, csrc);
248 VSB_cat(sb, "*/\n");
249 free(csrc);
250 }
251
252 VJ_master(JAIL_MASTER_SYSTEM);
253 subs = VSUB_run(sb, run_cc, vp, "C-compiler", 10);
254 VJ_master(JAIL_MASTER_LOW);
255 if (subs)
256 return (subs);
257
258 VJ_master(JAIL_MASTER_SYSTEM);
259 subs = VSUB_run(sb, run_dlopen, vp, "dlopen", 10);
260 VJ_master(JAIL_MASTER_LOW);
261 return (subs);
262 }
263
264 /*--------------------------------------------------------------------*/
265
266 static void
mgt_vcc_init_vp(struct vcc_priv * vp)267 mgt_vcc_init_vp(struct vcc_priv *vp)
268 {
269 INIT_OBJ(vp, VCC_PRIV_MAGIC);
270 vp->csrcfile = VSB_new_auto();
271 AN(vp->csrcfile);
272 vp->libfile = VSB_new_auto();
273 AN(vp->libfile);
274 vp->symfile = VSB_new_auto();
275 AN(vp->symfile);
276 vp->dir = VSB_new_auto();
277 AN(vp->dir);
278 }
279
280 static void
mgt_vcc_fini_vp(struct vcc_priv * vp,int leave_lib)281 mgt_vcc_fini_vp(struct vcc_priv *vp, int leave_lib)
282 {
283 if (!MGT_DO_DEBUG(DBG_VCL_KEEP)) {
284 VJ_unlink(VSB_data(vp->csrcfile));
285 VJ_unlink(VSB_data(vp->symfile));
286 if (!leave_lib) {
287 VJ_unlink(VSB_data(vp->libfile));
288 VJ_rmdir(VSB_data(vp->dir));
289 }
290 }
291 VSB_destroy(&vp->csrcfile);
292 VSB_destroy(&vp->libfile);
293 VSB_destroy(&vp->symfile);
294 VSB_destroy(&vp->dir);
295 }
296
297 char *
mgt_VccCompile(struct cli * cli,struct vclprog * vcl,const char * vclname,const char * vclsrc,const char * vclsrcfile,int C_flag)298 mgt_VccCompile(struct cli *cli, struct vclprog *vcl, const char *vclname,
299 const char *vclsrc, const char *vclsrcfile, int C_flag)
300 {
301 struct vcc_priv vp[1];
302 struct vsb *sb;
303 unsigned status;
304 char *p;
305
306 AN(cli);
307
308 sb = VSB_new_auto();
309 AN(sb);
310
311 mgt_vcc_init_vp(vp);
312 vp->vclsrc = vclsrc;
313 vp->vclsrcfile = vclsrcfile;
314
315 /*
316 * The subdirectory must have a unique name to 100% certain evade
317 * the refcounting semantics of dlopen(3).
318 *
319 * Bad implementations of dlopen(3) think the shlib you are opening
320 * is the same, if the filename is the same as one already opened.
321 *
322 * Sensible implementations do a stat(2) and requires st_ino and
323 * st_dev to also match.
324 *
325 * A correct implementation would run on filesystems which tickle
326 * st_gen, and also insist that be the identical, before declaring
327 * a match.
328 *
329 * Since no correct implementations are known to exist, we are subject
330 * to really interesting races if you do something like:
331 *
332 * (running on 'boot' vcl)
333 * vcl.load foo /foo.vcl
334 * vcl.use foo
335 * few/slow requests
336 * vcl.use boot
337 * vcl.discard foo
338 * vcl.load foo /foo.vcl // dlopen(3) says "same-same"
339 * vcl.use foo
340 *
341 * Because discard of the first 'foo' lingers on non-zero reference
342 * count, and when it finally runs, it trashes the second 'foo' because
343 * dlopen(3) decided they were really the same thing.
344 *
345 * The Best way to reproduce this is to have regexps in the VCL.
346 */
347
348 VSB_printf(vp->dir, "vcl_%s.%.6f", vclname, VTIM_real());
349 AZ(VSB_finish(vp->dir));
350
351 VSB_printf(vp->csrcfile, "%s/%s", VSB_data(vp->dir), VGC_SRC);
352 AZ(VSB_finish(vp->csrcfile));
353
354 VSB_printf(vp->libfile, "%s/%s", VSB_data(vp->dir), VGC_LIB);
355 AZ(VSB_finish(vp->libfile));
356
357 VSB_printf(vp->symfile, "%s/%s", VSB_data(vp->dir), VGC_SYM);
358 AZ(VSB_finish(vp->symfile));
359
360 if (VJ_make_subdir(VSB_data(vp->dir), "VCL", cli->sb)) {
361 mgt_vcc_fini_vp(vp, 0);
362 VSB_destroy(&sb);
363 VCLI_Out(cli, "VCL compilation failed");
364 VCLI_SetResult(cli, CLIS_PARAM);
365 return (NULL);
366 }
367
368 status = mgt_vcc_compile(vp, sb, C_flag);
369 AZ(VSB_finish(sb));
370 if (VSB_len(sb) > 0)
371 VCLI_Out(cli, "%s", VSB_data(sb));
372 VSB_destroy(&sb);
373
374 if (status || C_flag) {
375 mgt_vcc_fini_vp(vp, 0);
376 if (status) {
377 VCLI_Out(cli, "VCL compilation failed");
378 VCLI_SetResult(cli, CLIS_PARAM);
379 }
380 return (NULL);
381 }
382
383 p = VFIL_readfile(NULL, VSB_data(vp->symfile), NULL);
384 AN(p);
385 mgt_vcl_symtab(vcl, p);
386
387 VCLI_Out(cli, "VCL compiled.\n");
388
389 REPLACE(p, VSB_data(vp->libfile));
390 mgt_vcc_fini_vp(vp, 1);
391 return (p);
392 }
393