xref: /freebsd/contrib/openbsm/bin/auditdistd/proto.c (revision e6bfd18d21b225af6a0ed67ceeaf1293b7b9eba5)
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/queue.h>
32 #include <sys/socket.h>
33 
34 #include <errno.h>
35 #include <stdint.h>
36 #include <string.h>
37 #include <strings.h>
38 
39 #include "pjdlog.h"
40 #include "proto.h"
41 #include "proto_impl.h"
42 
43 #define	PROTO_CONN_MAGIC	0x907041c
44 struct proto_conn {
45 	int		 pc_magic;
46 	struct proto	*pc_proto;
47 	void		*pc_ctx;
48 	int		 pc_side;
49 #define	PROTO_SIDE_CLIENT		0
50 #define	PROTO_SIDE_SERVER_LISTEN	1
51 #define	PROTO_SIDE_SERVER_WORK		2
52 };
53 
54 static TAILQ_HEAD(, proto) protos = TAILQ_HEAD_INITIALIZER(protos);
55 
56 void
57 proto_register(struct proto *proto, bool isdefault)
58 {
59 	static bool seen_default = false;
60 
61 	if (!isdefault)
62 		TAILQ_INSERT_HEAD(&protos, proto, prt_next);
63 	else {
64 		PJDLOG_ASSERT(!seen_default);
65 		seen_default = true;
66 		TAILQ_INSERT_TAIL(&protos, proto, prt_next);
67 	}
68 }
69 
70 static struct proto_conn *
71 proto_alloc(struct proto *proto, int side)
72 {
73 	struct proto_conn *conn;
74 
75 	PJDLOG_ASSERT(proto != NULL);
76 	PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT ||
77 	    side == PROTO_SIDE_SERVER_LISTEN ||
78 	    side == PROTO_SIDE_SERVER_WORK);
79 
80 	conn = malloc(sizeof(*conn));
81 	if (conn != NULL) {
82 		conn->pc_proto = proto;
83 		conn->pc_side = side;
84 		conn->pc_magic = PROTO_CONN_MAGIC;
85 	}
86 	return (conn);
87 }
88 
89 static void
90 proto_free(struct proto_conn *conn)
91 {
92 
93 	PJDLOG_ASSERT(conn != NULL);
94 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
95 	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT ||
96 	    conn->pc_side == PROTO_SIDE_SERVER_LISTEN ||
97 	    conn->pc_side == PROTO_SIDE_SERVER_WORK);
98 	PJDLOG_ASSERT(conn->pc_proto != NULL);
99 
100 	bzero(conn, sizeof(*conn));
101 	free(conn);
102 }
103 
104 static int
105 proto_common_setup(const char *srcaddr, const char *dstaddr, int timeout,
106     int side, struct proto_conn **connp)
107 {
108 	struct proto *proto;
109 	struct proto_conn *conn;
110 	void *ctx;
111 	int ret;
112 
113 	PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT ||
114 	    side == PROTO_SIDE_SERVER_LISTEN);
115 
116 	TAILQ_FOREACH(proto, &protos, prt_next) {
117 		if (side == PROTO_SIDE_CLIENT) {
118 			if (proto->prt_connect == NULL) {
119 				ret = -1;
120 			} else {
121 				ret = proto->prt_connect(srcaddr, dstaddr,
122 				    timeout, &ctx);
123 			}
124 		} else /* if (side == PROTO_SIDE_SERVER_LISTEN) */ {
125 			if (proto->prt_server == NULL)
126 				ret = -1;
127 			else
128 				ret = proto->prt_server(dstaddr, &ctx);
129 		}
130 		/*
131 		 * ret == 0  - success
132 		 * ret == -1 - dstaddr is not for this protocol
133 		 * ret > 0   - right protocol, but an error occured
134 		 */
135 		if (ret >= 0)
136 			break;
137 	}
138 	if (proto == NULL) {
139 		/* Unrecognized address. */
140 		errno = EINVAL;
141 		return (-1);
142 	}
143 	if (ret > 0) {
144 		/* An error occured. */
145 		errno = ret;
146 		return (-1);
147 	}
148 	conn = proto_alloc(proto, side);
149 	if (conn == NULL) {
150 		if (proto->prt_close != NULL)
151 			proto->prt_close(ctx);
152 		errno = ENOMEM;
153 		return (-1);
154 	}
155 	conn->pc_ctx = ctx;
156 	*connp = conn;
157 
158 	return (0);
159 }
160 
161 int
162 proto_connect(const char *srcaddr, const char *dstaddr, int timeout,
163     struct proto_conn **connp)
164 {
165 
166 	PJDLOG_ASSERT(srcaddr == NULL || srcaddr[0] != '\0');
167 	PJDLOG_ASSERT(dstaddr != NULL);
168 	PJDLOG_ASSERT(timeout >= -1);
169 
170 	return (proto_common_setup(srcaddr, dstaddr, timeout,
171 	    PROTO_SIDE_CLIENT, connp));
172 }
173 
174 int
175 proto_connect_wait(struct proto_conn *conn, int timeout)
176 {
177 	int error;
178 
179 	PJDLOG_ASSERT(conn != NULL);
180 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
181 	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT);
182 	PJDLOG_ASSERT(conn->pc_proto != NULL);
183 	PJDLOG_ASSERT(conn->pc_proto->prt_connect_wait != NULL);
184 	PJDLOG_ASSERT(timeout >= 0);
185 
186 	error = conn->pc_proto->prt_connect_wait(conn->pc_ctx, timeout);
187 	if (error != 0) {
188 		errno = error;
189 		return (-1);
190 	}
191 
192 	return (0);
193 }
194 
195 int
196 proto_server(const char *addr, struct proto_conn **connp)
197 {
198 
199 	PJDLOG_ASSERT(addr != NULL);
200 
201 	return (proto_common_setup(NULL, addr, -1, PROTO_SIDE_SERVER_LISTEN,
202 	    connp));
203 }
204 
205 int
206 proto_accept(struct proto_conn *conn, struct proto_conn **newconnp)
207 {
208 	struct proto_conn *newconn;
209 	int error;
210 
211 	PJDLOG_ASSERT(conn != NULL);
212 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
213 	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_SERVER_LISTEN);
214 	PJDLOG_ASSERT(conn->pc_proto != NULL);
215 	PJDLOG_ASSERT(conn->pc_proto->prt_accept != NULL);
216 
217 	newconn = proto_alloc(conn->pc_proto, PROTO_SIDE_SERVER_WORK);
218 	if (newconn == NULL)
219 		return (-1);
220 
221 	error = conn->pc_proto->prt_accept(conn->pc_ctx, &newconn->pc_ctx);
222 	if (error != 0) {
223 		proto_free(newconn);
224 		errno = error;
225 		return (-1);
226 	}
227 
228 	*newconnp = newconn;
229 
230 	return (0);
231 }
232 
233 int
234 proto_send(const struct proto_conn *conn, const void *data, size_t size)
235 {
236 	int error;
237 
238 	PJDLOG_ASSERT(conn != NULL);
239 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
240 	PJDLOG_ASSERT(conn->pc_proto != NULL);
241 	PJDLOG_ASSERT(conn->pc_proto->prt_send != NULL);
242 
243 	error = conn->pc_proto->prt_send(conn->pc_ctx, data, size, -1);
244 	if (error != 0) {
245 		errno = error;
246 		return (-1);
247 	}
248 	return (0);
249 }
250 
251 int
252 proto_recv(const struct proto_conn *conn, void *data, size_t size)
253 {
254 	int error;
255 
256 	PJDLOG_ASSERT(conn != NULL);
257 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
258 	PJDLOG_ASSERT(conn->pc_proto != NULL);
259 	PJDLOG_ASSERT(conn->pc_proto->prt_recv != NULL);
260 
261 	error = conn->pc_proto->prt_recv(conn->pc_ctx, data, size, NULL);
262 	if (error != 0) {
263 		errno = error;
264 		return (-1);
265 	}
266 	return (0);
267 }
268 
269 int
270 proto_connection_send(const struct proto_conn *conn, struct proto_conn *mconn)
271 {
272 	const char *protoname;
273 	int error, fd;
274 
275 	PJDLOG_ASSERT(conn != NULL);
276 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
277 	PJDLOG_ASSERT(conn->pc_proto != NULL);
278 	PJDLOG_ASSERT(conn->pc_proto->prt_send != NULL);
279 	PJDLOG_ASSERT(mconn != NULL);
280 	PJDLOG_ASSERT(mconn->pc_magic == PROTO_CONN_MAGIC);
281 	PJDLOG_ASSERT(mconn->pc_proto != NULL);
282 	fd = proto_descriptor(mconn);
283 	PJDLOG_ASSERT(fd >= 0);
284 	protoname = mconn->pc_proto->prt_name;
285 	PJDLOG_ASSERT(protoname != NULL);
286 
287 	error = conn->pc_proto->prt_send(conn->pc_ctx,
288 	    (const unsigned char *)protoname, strlen(protoname) + 1, fd);
289 	proto_close(mconn);
290 	if (error != 0) {
291 		errno = error;
292 		return (-1);
293 	}
294 	return (0);
295 }
296 
297 int
298 proto_wrap(const char *protoname, bool client, int fd,
299     struct proto_conn **newconnp)
300 {
301 	struct proto *proto;
302 	struct proto_conn *newconn;
303 	int error;
304 
305 	TAILQ_FOREACH(proto, &protos, prt_next) {
306 		if (strcmp(proto->prt_name, protoname) == 0)
307 			break;
308 	}
309 	if (proto == NULL) {
310 		errno = EINVAL;
311 		return (-1);
312 	}
313 
314 	newconn = proto_alloc(proto,
315 	    client ? PROTO_SIDE_CLIENT : PROTO_SIDE_SERVER_WORK);
316 	if (newconn == NULL)
317 		return (-1);
318 	PJDLOG_ASSERT(newconn->pc_proto->prt_wrap != NULL);
319 	error = newconn->pc_proto->prt_wrap(fd, client, &newconn->pc_ctx);
320 	if (error != 0) {
321 		proto_free(newconn);
322 		errno = error;
323 		return (-1);
324 	}
325 
326 	*newconnp = newconn;
327 
328 	return (0);
329 }
330 
331 int
332 proto_connection_recv(const struct proto_conn *conn, bool client,
333     struct proto_conn **newconnp)
334 {
335 	char protoname[128];
336 	int error, fd;
337 
338 	PJDLOG_ASSERT(conn != NULL);
339 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
340 	PJDLOG_ASSERT(conn->pc_proto != NULL);
341 	PJDLOG_ASSERT(conn->pc_proto->prt_recv != NULL);
342 	PJDLOG_ASSERT(newconnp != NULL);
343 
344 	bzero(protoname, sizeof(protoname));
345 
346 	error = conn->pc_proto->prt_recv(conn->pc_ctx,
347 	    (unsigned char *)protoname, sizeof(protoname) - 1, &fd);
348 	if (error != 0) {
349 		errno = error;
350 		return (-1);
351 	}
352 
353 	PJDLOG_ASSERT(fd >= 0);
354 
355 	return (proto_wrap(protoname, client, fd, newconnp));
356 }
357 
358 int
359 proto_descriptor(const struct proto_conn *conn)
360 {
361 
362 	PJDLOG_ASSERT(conn != NULL);
363 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
364 	PJDLOG_ASSERT(conn->pc_proto != NULL);
365 	PJDLOG_ASSERT(conn->pc_proto->prt_descriptor != NULL);
366 
367 	return (conn->pc_proto->prt_descriptor(conn->pc_ctx));
368 }
369 
370 bool
371 proto_address_match(const struct proto_conn *conn, const char *addr)
372 {
373 
374 	PJDLOG_ASSERT(conn != NULL);
375 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
376 	PJDLOG_ASSERT(conn->pc_proto != NULL);
377 	PJDLOG_ASSERT(conn->pc_proto->prt_address_match != NULL);
378 
379 	return (conn->pc_proto->prt_address_match(conn->pc_ctx, addr));
380 }
381 
382 void
383 proto_local_address(const struct proto_conn *conn, char *addr, size_t size)
384 {
385 
386 	PJDLOG_ASSERT(conn != NULL);
387 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
388 	PJDLOG_ASSERT(conn->pc_proto != NULL);
389 	PJDLOG_ASSERT(conn->pc_proto->prt_local_address != NULL);
390 
391 	conn->pc_proto->prt_local_address(conn->pc_ctx, addr, size);
392 }
393 
394 void
395 proto_remote_address(const struct proto_conn *conn, char *addr, size_t size)
396 {
397 
398 	PJDLOG_ASSERT(conn != NULL);
399 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
400 	PJDLOG_ASSERT(conn->pc_proto != NULL);
401 	PJDLOG_ASSERT(conn->pc_proto->prt_remote_address != NULL);
402 
403 	conn->pc_proto->prt_remote_address(conn->pc_ctx, addr, size);
404 }
405 
406 int
407 proto_timeout(const struct proto_conn *conn, int timeout)
408 {
409 	struct timeval tv;
410 	int fd;
411 
412 	PJDLOG_ASSERT(conn != NULL);
413 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
414 	PJDLOG_ASSERT(conn->pc_proto != NULL);
415 
416 	fd = proto_descriptor(conn);
417 	if (fd < 0)
418 		return (-1);
419 
420 	tv.tv_sec = timeout;
421 	tv.tv_usec = 0;
422 	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
423 		return (-1);
424 	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
425 		return (-1);
426 
427 	return (0);
428 }
429 
430 void
431 proto_close(struct proto_conn *conn)
432 {
433 
434 	PJDLOG_ASSERT(conn != NULL);
435 	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
436 	PJDLOG_ASSERT(conn->pc_proto != NULL);
437 	PJDLOG_ASSERT(conn->pc_proto->prt_close != NULL);
438 
439 	conn->pc_proto->prt_close(conn->pc_ctx);
440 	proto_free(conn);
441 }
442 
443 int
444 proto_exec(int argc, char *argv[])
445 {
446 	struct proto *proto;
447 	int error;
448 
449 	if (argc == 0) {
450 		errno = EINVAL;
451 		return (-1);
452 	}
453 	TAILQ_FOREACH(proto, &protos, prt_next) {
454 		if (strcmp(proto->prt_name, argv[0]) == 0)
455 			break;
456 	}
457 	if (proto == NULL) {
458 		errno = EINVAL;
459 		return (-1);
460 	}
461 	if (proto->prt_exec == NULL) {
462 		errno = EOPNOTSUPP;
463 		return (-1);
464 	}
465 	error = proto->prt_exec(argc, argv);
466 	if (error != 0) {
467 		errno = error;
468 		return (-1);
469 	}
470 	/* NOTREACHED */
471 	return (0);
472 }
473 
474 struct proto_nvpair {
475 	char	*pnv_name;
476 	char	*pnv_value;
477 	TAILQ_ENTRY(proto_nvpair) pnv_next;
478 };
479 
480 static TAILQ_HEAD(, proto_nvpair) proto_nvpairs =
481     TAILQ_HEAD_INITIALIZER(proto_nvpairs);
482 
483 int
484 proto_set(const char *name, const char *value)
485 {
486 	struct proto_nvpair *pnv;
487 
488 	TAILQ_FOREACH(pnv, &proto_nvpairs, pnv_next) {
489 		if (strcmp(pnv->pnv_name, name) == 0)
490 			break;
491 	}
492 	if (pnv != NULL) {
493 		TAILQ_REMOVE(&proto_nvpairs, pnv, pnv_next);
494 		free(pnv->pnv_value);
495 	} else {
496 		pnv = malloc(sizeof(*pnv));
497 		if (pnv == NULL)
498 			return (-1);
499 		pnv->pnv_name = strdup(name);
500 		if (pnv->pnv_name == NULL) {
501 			free(pnv);
502 			return (-1);
503 		}
504 	}
505 	pnv->pnv_value = strdup(value);
506 	if (pnv->pnv_value == NULL) {
507 		free(pnv->pnv_name);
508 		free(pnv);
509 		return (-1);
510 	}
511 	TAILQ_INSERT_TAIL(&proto_nvpairs, pnv, pnv_next);
512 	return (0);
513 }
514 
515 const char *
516 proto_get(const char *name)
517 {
518 	struct proto_nvpair *pnv;
519 
520 	TAILQ_FOREACH(pnv, &proto_nvpairs, pnv_next) {
521 		if (strcmp(pnv->pnv_name, name) == 0)
522 			break;
523 	}
524 	if (pnv != NULL)
525 		return (pnv->pnv_value);
526 	return (NULL);
527 }
528