1 /*	$OpenBSD$	*/
2 
3 /*
4  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "includes.h"
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/uio.h>
24 
25 #include <event.h>
26 #include <fcntl.h>
27 #include <imsg.h>
28 #include <pwd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include <smtpd-api.h>
35 
36 static int (*handler_update)(void);
37 static int (*handler_check)(int, struct dict *, const char *);
38 static int (*handler_lookup)(int, struct dict *, const char *, char *, size_t);
39 static int (*handler_fetch)(int, struct dict *, char *, size_t);
40 
41 static int		 quit;
42 static struct imsgbuf	 ibuf;
43 static struct imsg	 imsg;
44 static size_t		 rlen;
45 static char		*rdata;
46 static struct ibuf	*buf;
47 static char		*name;
48 
49 #if 0
50 static char		*rootpath;
51 static char		*user = SMTPD_USER;
52 #endif
53 
54 static void
table_msg_get(void * dst,size_t len)55 table_msg_get(void *dst, size_t len)
56 {
57 	if (len > rlen) {
58 		log_warnx("warn: table-proc: bad msg len");
59 		fatalx("table-proc: exiting");
60 	}
61 
62 	if (len == 0)
63 		return;
64 
65 	if (dst)
66 		memmove(dst, rdata, len);
67 
68 	rlen -= len;
69 	rdata += len;
70 }
71 
72 static void
table_msg_end(void)73 table_msg_end(void)
74 {
75 	if (rlen) {
76 		log_warnx("warn: table-proc: bogus data");
77 		fatalx("table-proc: exiting");
78 	}
79 	imsg_free(&imsg);
80 }
81 
82 static void
table_msg_add(const void * data,size_t len)83 table_msg_add(const void *data, size_t len)
84 {
85 	if (buf == NULL)
86 		buf = imsg_create(&ibuf, PROC_TABLE_OK, 0, 0, 1024);
87 	if (buf == NULL) {
88 		log_warnx("warn: table-api: imsg_create failed");
89 		fatalx("table-api: exiting");
90 	}
91 	if (imsg_add(buf, data, len) == -1) {
92 		log_warnx("warn: table-api: imsg_add failed");
93 		fatalx("table-api: exiting");
94 	}
95 }
96 
97 static void
table_msg_close(void)98 table_msg_close(void)
99 {
100 	imsg_close(&ibuf, buf);
101 	buf = NULL;
102 }
103 
104 static int
table_read_params(struct dict * params)105 table_read_params(struct dict *params)
106 {
107 	size_t	count;
108 	char	*key;
109 	char	*value;
110 
111 	dict_init(params);
112 
113 	table_msg_get(&count, sizeof(count));
114 
115 	for (;count; count--) {
116 		key = rdata;
117 		table_msg_get(NULL, strlen(key) + 1);
118 		value = rdata;
119 		table_msg_get(NULL, strlen(value) + 1);
120 		dict_set(params, key, value);
121 	}
122 
123 	return (0);
124 }
125 
126 static void
table_clear_params(struct dict * params)127 table_clear_params(struct dict *params)
128 {
129 	while (dict_poproot(params, NULL))
130 		;
131 }
132 
133 static void
table_msg_dispatch(void)134 table_msg_dispatch(void)
135 {
136 	struct table_open_params op;
137 	struct dict	 params;
138 	char		 res[4096];
139 	int		 type, r;
140 
141 	switch (imsg.hdr.type) {
142 	case PROC_TABLE_OPEN:
143 		table_msg_get(&op, sizeof op);
144 		table_msg_end();
145 
146 		if (op.version != PROC_TABLE_API_VERSION) {
147 			log_warnx("warn: table-api: bad API version");
148 			fatalx("table-api: terminating");
149 		}
150 		if ((name = strdup(op.name)) == NULL) {
151 			log_warn("warn: table-api");
152 			fatalx("table-api: terminating");
153 		}
154 
155 		imsg_compose(&ibuf, PROC_TABLE_OK, 0, 0, -1, NULL, 0);
156 		break;
157 
158 	case PROC_TABLE_UPDATE:
159 		table_msg_end();
160 
161 		if (handler_update)
162 			r = handler_update();
163 		else
164 			r = 1;
165 
166 		imsg_compose(&ibuf, PROC_TABLE_OK, 0, 0, -1, &r, sizeof(r));
167 		break;
168 
169 	case PROC_TABLE_CLOSE:
170 		quit = 1;
171 		break;
172 
173 	case PROC_TABLE_CHECK:
174 		table_msg_get(&type, sizeof(type));
175 		table_read_params(&params);
176 		if (rlen == 0) {
177 			log_warnx("warn: table-api: no key");
178 			fatalx("table-api: exiting");
179 		}
180 		if (rdata[rlen - 1] != '\0') {
181 			log_warnx("warn: table-api: key not NUL-terminated");
182 			fatalx("table-api: exiting");
183 		}
184 
185 		if (handler_check)
186 			r = handler_check(type, &params, rdata);
187 		else
188 			r = -1;
189 		table_clear_params(&params);
190 		table_msg_get(NULL, rlen);
191 		table_msg_end();
192 
193 		table_msg_add(&r, sizeof(r));
194 		table_msg_close();
195 		break;
196 
197 	case PROC_TABLE_LOOKUP:
198 		table_msg_get(&type, sizeof(type));
199 		table_read_params(&params);
200 		if (rlen == 0) {
201 			log_warnx("warn: table-api: no key");
202 			fatalx("table-api: exiting");
203 		}
204 		if (rdata[rlen - 1] != '\0') {
205 			log_warnx("warn: table-api: key not NUL-terminated");
206 			fatalx("table-api: exiting");
207 		}
208 
209 		if (handler_lookup)
210 			r = handler_lookup(type, &params, rdata, res, sizeof(res));
211 		else
212 			r = -1;
213 		table_clear_params(&params);
214 		table_msg_get(NULL, rlen);
215 		table_msg_end();
216 
217 		table_msg_add(&r, sizeof(r));
218 		if (r == 1)
219 			table_msg_add(res, strlen(res) + 1);
220 		table_msg_close();
221 		break;
222 
223 
224 	case PROC_TABLE_FETCH:
225 		table_msg_get(&type, sizeof(type));
226 		table_read_params(&params);
227 		if (handler_fetch)
228 			r = handler_fetch(type, &params, res, sizeof(res));
229 		else
230 			r = -1;
231 		table_clear_params(&params);
232 		table_msg_end();
233 
234 		table_msg_add(&r, sizeof(r));
235 		if (r == 1)
236 			table_msg_add(res, strlen(res) + 1);
237 		table_msg_close();
238 		break;
239 
240 	default:
241 		log_warnx("warn: table-api: bad message %d", imsg.hdr.type);
242 		fatalx("table-api: exiting");
243 	}
244 }
245 
246 void
table_api_on_update(int (* cb)(void))247 table_api_on_update(int(*cb)(void))
248 {
249 	handler_update = cb;
250 }
251 
252 void
table_api_on_check(int (* cb)(int,struct dict *,const char *))253 table_api_on_check(int(*cb)(int, struct dict *, const char *))
254 {
255 	handler_check = cb;
256 }
257 
258 void
table_api_on_lookup(int (* cb)(int,struct dict *,const char *,char *,size_t))259 table_api_on_lookup(int(*cb)(int, struct dict  *, const char *, char *, size_t))
260 {
261 	handler_lookup = cb;
262 }
263 
264 void
table_api_on_fetch(int (* cb)(int,struct dict *,char *,size_t))265 table_api_on_fetch(int(*cb)(int, struct dict *, char *, size_t))
266 {
267 	handler_fetch = cb;
268 }
269 
270 const char *
table_api_get_name(void)271 table_api_get_name(void)
272 {
273 	return name;
274 }
275 
276 int
table_api_dispatch(void)277 table_api_dispatch(void)
278 {
279 #if 0
280 	struct passwd	*pw;
281 #endif
282 	ssize_t		 n;
283 
284 #if 0
285 	pw = getpwnam(user);
286 	if (pw == NULL) {
287 		log_warn("table-api: getpwnam");
288 		fatalx("table-api: exiting");
289 	}
290 
291 	if (rootpath) {
292 		if (chroot(rootpath) == -1) {
293 			log_warn("table-api: chroot");
294 			fatalx("table-api: exiting");
295 		}
296 		if (chdir("/") == -1) {
297 			log_warn("table-api: chdir");
298 			fatalx("table-api: exiting");
299 		}
300 	}
301 
302 	if (setgroups(1, &pw->pw_gid) ||
303 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
304 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
305 		log_warn("table-api: cannot drop privileges");
306 		fatalx("table-api: exiting");
307 	}
308 #endif
309 
310 	imsg_init(&ibuf, 0);
311 
312 	while (1) {
313 		n = imsg_get(&ibuf, &imsg);
314 		if (n == -1) {
315 			log_warn("warn: table-api: imsg_get");
316 			break;
317 		}
318 
319 		if (n) {
320 			rdata = imsg.data;
321 			rlen = imsg.hdr.len - IMSG_HEADER_SIZE;
322 			table_msg_dispatch();
323 			if (quit)
324 				break;
325 			imsg_flush(&ibuf);
326 			continue;
327 		}
328 
329 		n = imsg_read(&ibuf);
330 		if (n == -1) {
331 			log_warn("warn: table-api: imsg_read");
332 			break;
333 		}
334 		if (n == 0) {
335 			log_warnx("warn: table-api: pipe closed");
336 			break;
337 		}
338 	}
339 
340 	return (1);
341 }
342