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