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