xref: /freebsd/usr.sbin/syslogd/syslogd_cap_config.c (revision 61a29eca550b80d179934a7198c41ad4d255f81c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 The FreeBSD Foundation
5  *
6  * This software was developed by Jake Freeland <jfree@FreeBSD.org>
7  * under sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 
34 #include <assert.h>
35 #include <err.h>
36 #include <libcasper.h>
37 #include <netdb.h>
38 #include <string.h>
39 
40 #include <casper/cap_net.h>
41 
42 #include "syslogd_cap.h"
43 
44 /*
45  * Convert the given prop_filter structure into an nvlist.
46  * Return a heap allocated pointer to the resulting nvlist.
47  */
48 nvlist_t *
49 prop_filter_to_nvlist(const struct prop_filter *pfilter)
50 {
51 	nvlist_t *nvl_prop_filter = nvlist_create(0);
52 
53 	nvlist_add_number(nvl_prop_filter, "prop_type", pfilter->prop_type);
54 	nvlist_add_number(nvl_prop_filter, "cmp_type", pfilter->cmp_type);
55 	nvlist_add_number(nvl_prop_filter, "cmp_flags", pfilter->cmp_flags);
56 	nvlist_add_string(nvl_prop_filter, "pflt_strval", pfilter->pflt_strval);
57 	/*
58 	 * Do not bother adding pflt_re. It will be recompiled
59 	 * using pflt_strval later, if applicable.
60 	 */
61 
62 	return (nvl_prop_filter);
63 }
64 
65 /*
66  * Convert the given nvlist into a prop_filter structure.
67  * Return a heap allocated pointer to the resulting prop_filter.
68  */
69 struct prop_filter *
70 nvlist_to_prop_filter(const nvlist_t *nvl_prop_filter)
71 {
72 	struct prop_filter *pfilter;
73 
74 	pfilter = calloc(1, sizeof(*pfilter));
75 	if (pfilter == NULL)
76 		err(1, "calloc");
77 	pfilter->prop_type = nvlist_get_number(nvl_prop_filter, "prop_type");
78 	pfilter->cmp_type = nvlist_get_number(nvl_prop_filter, "cmp_type");
79 	pfilter->cmp_flags = nvlist_get_number(nvl_prop_filter, "cmp_flags");
80 	pfilter->pflt_strval = strdup(nvlist_get_string(nvl_prop_filter,
81 	    "pflt_strval"));
82 	if (pfilter->cmp_type == FILT_CMP_REGEX) {
83 		int re_flags = REG_NOSUB;
84 		pfilter->pflt_re = calloc(1, sizeof(*pfilter->pflt_re));
85 		if (pfilter->pflt_re == NULL)
86 			errx(1, "RE calloc() error");
87 		if ((pfilter->cmp_flags & FILT_FLAG_EXTENDED) != 0)
88 			re_flags |= REG_EXTENDED;
89 		if ((pfilter->cmp_flags & FILT_FLAG_ICASE) != 0)
90 			re_flags |= REG_ICASE;
91 		if (regcomp(pfilter->pflt_re, pfilter->pflt_strval,
92 		    re_flags) != 0)
93 			errx(1, "RE compilation error");
94 	}
95 
96 	return (pfilter);
97 }
98 
99 /*
100  * Convert the given struct filed into an nvl_filed nvlist.
101  * Return a heap allocated pointer to the resulting nvlist.
102  */
103 nvlist_t *
104 filed_to_nvlist(const struct filed *filed)
105 {
106 	nvlist_t *nvl_filed = nvlist_create(0);
107 	enum f_type f_type = filed->f_type;
108 	size_t i, sz;
109 
110 	nvlist_add_number(nvl_filed, "f_type", f_type);
111 	nvlist_add_string(nvl_filed, "f_host", filed->f_host);
112 	nvlist_add_string(nvl_filed, "f_program", filed->f_program);
113 	if (filed->f_prop_filter != NULL) {
114 		nvlist_add_nvlist(nvl_filed, "f_prop_filter",
115 		    prop_filter_to_nvlist(filed->f_prop_filter));
116 	}
117 	sz = nitems(filed->f_pmask);
118 	for (i = 0; i < sz; ++i) {
119 		nvlist_append_number_array(nvl_filed, "f_pmask",
120 		    filed->f_pmask[i]);
121 	}
122 	sz = nitems(filed->f_pcmp);
123 	for (i = 0; i < sz; ++i) {
124 		nvlist_append_number_array(nvl_filed, "f_pcmp",
125 		    filed->f_pcmp[i]);
126 	}
127 
128 	if (filed->f_file >= 0)
129 		nvlist_add_descriptor(nvl_filed, "f_file", filed->f_file);
130 	nvlist_add_number(nvl_filed, "f_flags", filed->f_flags);
131 	if (f_type == F_WALL || f_type == F_USERS) {
132 		sz = nitems(filed->f_uname);
133 		for (i = 0; i < sz; ++i) {
134 			nvlist_append_string_array(nvl_filed, "f_uname",
135 			    filed->f_uname[i]);
136 		}
137 	} else if (f_type == F_FILE || f_type == F_CONSOLE || f_type == F_TTY) {
138 		nvlist_add_string(nvl_filed, "f_fname", filed->f_fname);
139 	} else if (f_type == F_FORW) {
140 		struct addrinfo *ai = filed->f_addr, *cur;
141 		nvlist_t *nvl_addrinfo;
142 
143 		nvlist_add_string(nvl_filed, "f_hname", filed->f_hname);
144 		if (filed->f_addr != NULL) {
145 			for (cur = ai; cur != NULL; cur = cur->ai_next) {
146 				nvl_addrinfo = addrinfo_pack(cur);
147 				nvlist_append_nvlist_array(nvl_filed,
148 				    "f_addr", nvl_addrinfo);
149 				nvlist_destroy(nvl_addrinfo);
150 			}
151 		}
152 	} else if (filed->f_type == F_PIPE) {
153 		nvlist_add_string(nvl_filed, "f_pname", filed->f_pname);
154 		if (filed->f_procdesc >= 0) {
155 			nvlist_add_descriptor(nvl_filed, "f_procdesc",
156 			    filed->f_procdesc);
157 		}
158 	}
159 
160 	/*
161 	 * Book-keeping fields are not transferred.
162 	 */
163 
164 	return (nvl_filed);
165 }
166 
167 /*
168  * Convert the given nvl_filed nvlist into a struct filed.
169  * Return a heap allocated pointer to the resulting struct
170  * filed.
171  */
172 struct filed *
173 nvlist_to_filed(const nvlist_t *nvl_filed)
174 {
175 	struct filed *filed;
176 	enum f_type f_type;
177 	const uint64_t *narr;
178 	size_t i, sz;
179 
180 	filed = calloc(1, sizeof(*filed));
181 	if (filed == NULL)
182 		err(1, "calloc");
183 
184 	f_type = filed->f_type = nvlist_get_number(nvl_filed, "f_type");
185 	(void)strlcpy(filed->f_host, nvlist_get_string(nvl_filed, "f_host"),
186 	    sizeof(filed->f_host));
187 	(void)strlcpy(filed->f_program, nvlist_get_string(nvl_filed,
188 	    "f_program"), sizeof(filed->f_program));
189 	if (nvlist_exists_nvlist(nvl_filed, "f_prop_filter")) {
190 		filed->f_prop_filter = nvlist_to_prop_filter(
191 		    nvlist_get_nvlist(nvl_filed, "f_prop_filter"));
192 	}
193 	narr = nvlist_get_number_array(nvl_filed, "f_pmask", &sz);
194 	assert(sz == nitems(filed->f_pmask));
195 	for (i = 0; i < sz; ++i)
196 		filed->f_pmask[i] = narr[i];
197 	narr = nvlist_get_number_array(nvl_filed, "f_pcmp", &sz);
198 	assert(sz == nitems(filed->f_pcmp));
199 	for (i = 0; i < sz; ++i)
200 		filed->f_pcmp[i] = narr[i];
201 
202 	if (nvlist_exists_descriptor(nvl_filed, "f_file"))
203 		filed->f_file = dup(nvlist_get_descriptor(nvl_filed, "f_file"));
204 	else
205 		filed->f_file = -1;
206 	filed->f_flags = nvlist_get_number(nvl_filed, "f_flags");
207 	if (f_type == F_WALL || f_type == F_USERS) {
208 		const char * const *f_uname;
209 
210 		f_uname = nvlist_get_string_array(nvl_filed, "f_uname", &sz);
211 		assert(sz == nitems(filed->f_uname));
212 		for (i = 0; i < sz; ++i) {
213 			(void)strlcpy(filed->f_uname[i], f_uname[i],
214 			    sizeof(filed->f_uname[i]));
215 		}
216 	} else if (f_type == F_FILE || f_type == F_CONSOLE || f_type == F_TTY) {
217 		(void)strlcpy(filed->f_fname, nvlist_get_string(nvl_filed,
218 		    "f_fname"), sizeof(filed->f_fname));
219 	} else if (f_type == F_FORW) {
220 		const nvlist_t * const *f_addr;
221 		struct addrinfo *ai, **next = NULL;
222 
223 		(void)strlcpy(filed->f_hname, nvlist_get_string(nvl_filed,
224 		    "f_hname"), sizeof(filed->f_hname));
225 		f_addr = nvlist_get_nvlist_array(nvl_filed, "f_addr", &sz);
226 		for (i = 0; i < sz; ++i) {
227 			ai = addrinfo_unpack(f_addr[i]);
228 			if (next == NULL)
229 				filed->f_addr = ai;
230 			else
231 				*next = ai;
232 			next = &ai->ai_next;
233 		}
234 	} else if (filed->f_type == F_PIPE) {
235 		(void)strlcpy(filed->f_pname, nvlist_get_string(nvl_filed,
236 		    "f_pname"), sizeof(filed->f_pname));
237 		if (nvlist_exists_descriptor(nvl_filed, "f_procdesc")) {
238 			filed->f_procdesc = dup(nvlist_get_descriptor(nvl_filed,
239 			    "f_procdesc"));
240 		} else {
241 			filed->f_procdesc = -1;
242 		}
243 	}
244 
245 	/*
246 	 * Book-keeping fields are not transferred.
247 	 */
248 
249 	return (filed);
250 }
251 
252 nvlist_t *
253 cap_readconfigfile(cap_channel_t *chan, const char *path)
254 {
255 	nvlist_t *nvl, *nvl_conf;
256 
257 	nvl = nvlist_create(0);
258 	nvlist_add_string(nvl, "cmd", "readconfigfile");
259 	nvlist_add_string(nvl, "path", path);
260 	/* It is possible that our hostname has changed. */
261 	nvlist_add_string(nvl, "LocalHostName", LocalHostName);
262 	nvl = cap_xfer_nvlist(chan, nvl);
263 	if (nvl == NULL) {
264 		logerror("Failed to xfer configuration nvlist");
265 		exit(1);
266 	}
267 	nvl_conf = nvlist_take_nvlist(nvl, "nvl_conf");
268 
269 	nvlist_destroy(nvl);
270 	return (nvl_conf);
271 }
272 
273 /*
274  * Now that we're executing as libcasper, we can obtain the
275  * resources specified in the configuration.
276  */
277 int
278 casper_readconfigfile(nvlist_t *nvlin, nvlist_t *nvlout)
279 {
280 	const nvlist_t * const *filed_list;
281 	nvlist_t *nvl_conf;
282 	size_t n_fileds;
283 	const char *path;
284 
285 	/*
286 	 * Verify that syslogd did not manipulate the
287 	 * configuration file path.
288 	 */
289 	path = nvlist_get_string(nvlin, "path");
290 	if (strcmp(path, ConfFile) != 0)
291 		err(1, "Configuration file mismatch: %s != %s", path, ConfFile);
292 
293 	/* Refresh our copy of LocalHostName, in case it changed. */
294 	strlcpy(LocalHostName, nvlist_get_string(nvlin, "LocalHostName"),
295 	    sizeof(LocalHostName));
296 
297 	nvl_conf = readconfigfile(path);
298 
299 	/* Remove old filed data in case we are reloading. */
300 	while (!SLIST_EMPTY(&cfiled_head)) {
301 		struct cap_filed *cfiled;
302 
303 		cfiled = SLIST_FIRST(&cfiled_head);
304 		SLIST_REMOVE_HEAD(&cfiled_head, next);
305 		free(cfiled);
306 	}
307 	/* Record F_PIPE filed data for use in p_open(). */
308 	if (!nvlist_exists_nvlist_array(nvl_conf, "filed_list"))
309 		return (0);
310 	filed_list = nvlist_get_nvlist_array(nvl_conf, "filed_list", &n_fileds);
311 	for (size_t i = 0; i < n_fileds; ++i) {
312 		if (nvlist_get_number(filed_list[i], "f_type") == F_PIPE) {
313 			struct cap_filed *cfiled;
314 			const char *pipe_cmd;
315 
316 			cfiled = malloc(sizeof(*cfiled));
317 			if (cfiled == NULL)
318 				err(1, "malloc");
319 			cfiled->idx = i;
320 			pipe_cmd = nvlist_get_string(filed_list[i], "f_pname");
321 			strlcpy(cfiled->pipe_cmd, pipe_cmd, sizeof(cfiled->pipe_cmd));
322 			SLIST_INSERT_HEAD(&cfiled_head, cfiled, next);
323 		}
324 	}
325 
326 	nvlist_move_nvlist(nvlout, "nvl_conf", nvl_conf);
327 	return (0);
328 }
329