xref: /freebsd/contrib/bsnmp/snmpd/trans_lsock.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
1 /*
2  * Copyright (c) 2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
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 AUTHOR 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 AUTHOR 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  * $Begemot: bsnmp/snmpd/trans_lsock.c,v 1.6 2005/02/25 11:50:25 brandt_h Exp $
30  *
31  * Local domain socket transport
32  */
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/un.h>
36 #include <sys/stat.h>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stddef.h>
41 #include <syslog.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <unistd.h>
45 
46 #include "snmpmod.h"
47 #include "snmpd.h"
48 #include "trans_lsock.h"
49 #include "tree.h"
50 #include "oid.h"
51 
52 static const struct asn_oid
53 	oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
54 
55 static int lsock_start(void);
56 static int lsock_stop(int);
57 static void lsock_close_port(struct tport *);
58 static int lsock_init_port(struct tport *);
59 static ssize_t lsock_send(struct tport *, const u_char *, size_t,
60     const struct sockaddr *, size_t);
61 
62 /* exported */
63 const struct transport_def lsock_trans = {
64 	"lsock",
65 	OIDX_begemotSnmpdTransLsock,
66 	lsock_start,
67 	lsock_stop,
68 	lsock_close_port,
69 	lsock_init_port,
70 	lsock_send
71 };
72 static struct transport *my_trans;
73 
74 static int
75 lsock_remove(struct tport *tp, intptr_t arg __unused)
76 {
77 	struct lsock_port *port = (struct lsock_port *)tp;
78 
79 	(void)remove(port->name);
80 
81 	return (-1);
82 }
83 
84 static int
85 lsock_stop(int force)
86 {
87 
88 	if (my_trans != NULL) {
89 		if (!force && trans_first_port(my_trans) != NULL)
90 			return (SNMP_ERR_GENERR);
91 		trans_iter_port(my_trans, lsock_remove, 0);
92 		return (trans_unregister(my_trans));
93 	}
94 	return (SNMP_ERR_NOERROR);
95 }
96 
97 static int
98 lsock_start(void)
99 {
100 	return (trans_register(&lsock_trans, &my_trans));
101 }
102 
103 /*
104  * Open a local port. If this is a datagram socket create also the
105  * one and only peer.
106  */
107 static int
108 lsock_open_port(u_char *name, size_t namelen, struct lsock_port **pp,
109     int type)
110 {
111 	struct lsock_port *port;
112 	struct lsock_peer *peer = NULL;
113 	int is_stream, need_cred;
114 	size_t u;
115 	int err;
116 	struct sockaddr_un sa;
117 
118 	if (namelen == 0 || namelen + 1 > sizeof(sa.sun_path))
119 		return (SNMP_ERR_BADVALUE);
120 
121 	switch (type) {
122 	  case LOCP_DGRAM_UNPRIV:
123 		is_stream = 0;
124 		need_cred = 0;
125 		break;
126 
127 	  case LOCP_DGRAM_PRIV:
128 		is_stream = 0;
129 		need_cred = 1;
130 		break;
131 
132 	  case LOCP_STREAM_UNPRIV:
133 		is_stream = 1;
134 		need_cred = 0;
135 		break;
136 
137 	  case LOCP_STREAM_PRIV:
138 		is_stream = 1;
139 		need_cred = 1;
140 		break;
141 
142 	  default:
143 		return (SNMP_ERR_BADVALUE);
144 	}
145 
146 	if ((port = malloc(sizeof(*port))) == NULL)
147 		return (SNMP_ERR_GENERR);
148 
149 	memset(port, 0, sizeof(*port));
150 	if (!is_stream) {
151 		if ((peer = malloc(sizeof(*peer))) == NULL) {
152 			free(port);
153 			return (SNMP_ERR_GENERR);
154 		}
155 		memset(peer, 0, sizeof(*peer));
156 	}
157 	if ((port->name = malloc(namelen + 1)) == NULL) {
158 		free(port);
159 		if (!is_stream)
160 			free(peer);
161 		return (SNMP_ERR_GENERR);
162 	}
163 	strncpy(port->name, name, namelen);
164 	port->name[namelen] = '\0';
165 
166 	port->type = type;
167 	port->str_sock = -1;
168 	LIST_INIT(&port->peers);
169 
170 	port->tport.index.len = namelen + 1;
171 	port->tport.index.subs[0] = namelen;
172 	for (u = 0; u < namelen; u++)
173 		port->tport.index.subs[u + 1] = name[u];
174 
175 	if (peer != NULL) {
176 		LIST_INSERT_HEAD(&port->peers, peer, link);
177 
178 		peer->port = port;
179 
180 		peer->input.fd = -1;
181 		peer->input.id = NULL;
182 		peer->input.stream = is_stream;
183 		peer->input.cred = need_cred;
184 		peer->input.peer = (struct sockaddr *)&peer->peer;
185 	}
186 
187 	trans_insert_port(my_trans, &port->tport);
188 
189 	if (community != COMM_INITIALIZE &&
190 	    (err = lsock_init_port(&port->tport)) != SNMP_ERR_NOERROR) {
191 		lsock_close_port(&port->tport);
192 		return (err);
193 	}
194 
195 	*pp = port;
196 
197 	return (SNMP_ERR_NOERROR);
198 }
199 
200 /*
201  * Close a local domain peer
202  */
203 static void
204 lsock_peer_close(struct lsock_peer *peer)
205 {
206 
207 	LIST_REMOVE(peer, link);
208 	snmpd_input_close(&peer->input);
209 	free(peer);
210 }
211 
212 /*
213  * Close a local port
214  */
215 static void
216 lsock_close_port(struct tport *tp)
217 {
218 	struct lsock_port *port = (struct lsock_port *)tp;
219 	struct lsock_peer *peer;
220 
221 	if (port->str_id != NULL)
222 		fd_deselect(port->str_id);
223 	if (port->str_sock >= 0)
224 		(void)close(port->str_sock);
225 	(void)remove(port->name);
226 
227 	trans_remove_port(tp);
228 
229 	while ((peer = LIST_FIRST(&port->peers)) != NULL)
230 		lsock_peer_close(peer);
231 
232 	free(port->name);
233 	free(port);
234 }
235 
236 /*
237  * Input on a local socket (either datagram or stream)
238  */
239 static void
240 lsock_input(int fd __unused, void *udata)
241 {
242 	struct lsock_peer *peer = udata;
243 	struct lsock_port *p = peer->port;
244 
245 	peer->input.peerlen = sizeof(peer->peer);
246 	if (snmpd_input(&peer->input, &p->tport) == -1 && peer->input.stream)
247 		/* framing or other input error */
248 		lsock_peer_close(peer);
249 }
250 
251 /*
252  * A UNIX domain listening socket is ready. This means we have a peer
253  * that we need to accept
254  */
255 static void
256 lsock_listen_input(int fd, void *udata)
257 {
258 	struct lsock_port *p = udata;
259 	struct lsock_peer *peer;
260 
261 	if ((peer = malloc(sizeof(*peer))) == NULL) {
262 		syslog(LOG_WARNING, "%s: peer malloc failed", p->name);
263 		(void)close(accept(fd, NULL, NULL));
264 		return;
265 	}
266 	memset(peer, 0, sizeof(*peer));
267 
268 	peer->port = p;
269 
270 	peer->input.stream = 1;
271 	peer->input.cred = (p->type == LOCP_DGRAM_PRIV ||
272 	    p->type == LOCP_STREAM_PRIV);
273 	peer->input.peerlen = sizeof(peer->peer);
274 	peer->input.peer = (struct sockaddr *)&peer->peer;
275 
276 	peer->input.fd = accept(fd, peer->input.peer, &peer->input.peerlen);
277 	if (peer->input.fd == -1) {
278 		syslog(LOG_WARNING, "%s: accept failed: %m", p->name);
279 		free(peer);
280 		return;
281 	}
282 
283 	if ((peer->input.id = fd_select(peer->input.fd, lsock_input,
284 	    peer, NULL)) == NULL) {
285 		close(peer->input.fd);
286 		free(peer);
287 		return;
288 	}
289 
290 	LIST_INSERT_HEAD(&p->peers, peer, link);
291 }
292 
293 /*
294  * Create a local socket
295  */
296 static int
297 lsock_init_port(struct tport *tp)
298 {
299 	struct lsock_port *p = (struct lsock_port *)tp;
300 	struct sockaddr_un sa;
301 
302 	if (p->type == LOCP_STREAM_PRIV || p->type == LOCP_STREAM_UNPRIV) {
303 		if ((p->str_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
304 			syslog(LOG_ERR, "creating local socket: %m");
305 			return (SNMP_ERR_RES_UNAVAIL);
306 		}
307 
308 		strcpy(sa.sun_path, p->name);
309 		sa.sun_family = AF_LOCAL;
310 		sa.sun_len = strlen(p->name) +
311 		    offsetof(struct sockaddr_un, sun_path);
312 
313 		(void)remove(p->name);
314 
315 		if (bind(p->str_sock, (struct sockaddr *)&sa, sizeof(sa))) {
316 			if (errno == EADDRNOTAVAIL) {
317 				close(p->str_sock);
318 				p->str_sock = -1;
319 				return (SNMP_ERR_INCONS_NAME);
320 			}
321 			syslog(LOG_ERR, "bind: %s %m", p->name);
322 			close(p->str_sock);
323 			p->str_sock = -1;
324 			return (SNMP_ERR_GENERR);
325 		}
326 		if (chmod(p->name, 0666) == -1)
327 			syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
328 
329 		if (listen(p->str_sock, 10) == -1) {
330 			syslog(LOG_ERR, "listen: %s %m", p->name);
331 			(void)remove(p->name);
332 			close(p->str_sock);
333 			p->str_sock = -1;
334 			return (SNMP_ERR_GENERR);
335 		}
336 
337 		p->str_id = fd_select(p->str_sock, lsock_listen_input, p, NULL);
338 		if (p->str_id == NULL) {
339 			(void)remove(p->name);
340 			close(p->str_sock);
341 			p->str_sock = -1;
342 			return (SNMP_ERR_GENERR);
343 		}
344 	} else {
345 		struct lsock_peer *peer;
346 
347 		peer = LIST_FIRST(&p->peers);
348 
349 		if ((peer->input.fd = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0) {
350 			syslog(LOG_ERR, "creating local socket: %m");
351 			return (SNMP_ERR_RES_UNAVAIL);
352 		}
353 
354 		strcpy(sa.sun_path, p->name);
355 		sa.sun_family = AF_LOCAL;
356 		sa.sun_len = strlen(p->name) +
357 		    offsetof(struct sockaddr_un, sun_path);
358 
359 		(void)remove(p->name);
360 
361 		if (bind(peer->input.fd, (struct sockaddr *)&sa, sizeof(sa))) {
362 			if (errno == EADDRNOTAVAIL) {
363 				close(peer->input.fd);
364 				peer->input.fd = -1;
365 				return (SNMP_ERR_INCONS_NAME);
366 			}
367 			syslog(LOG_ERR, "bind: %s %m", p->name);
368 			close(peer->input.fd);
369 			peer->input.fd = -1;
370 			return (SNMP_ERR_GENERR);
371 		}
372 		if (chmod(p->name, 0666) == -1)
373 			syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
374 
375 		peer->input.id = fd_select(peer->input.fd, lsock_input,
376 		    peer, NULL);
377 		if (peer->input.id == NULL) {
378 			(void)remove(p->name);
379 			close(peer->input.fd);
380 			peer->input.fd = -1;
381 			return (SNMP_ERR_GENERR);
382 		}
383 	}
384 	return (SNMP_ERR_NOERROR);
385 }
386 
387 /*
388  * Send something
389  */
390 static ssize_t
391 lsock_send(struct tport *tp, const u_char *buf, size_t len,
392     const struct sockaddr *addr, size_t addrlen)
393 {
394 	struct lsock_port *p = (struct lsock_port *)tp;
395 	struct lsock_peer *peer;
396 
397 	if (p->type == LOCP_DGRAM_PRIV || p->type == LOCP_DGRAM_UNPRIV) {
398 		peer = LIST_FIRST(&p->peers);
399 
400 	} else {
401 		/* search for the peer */
402 		LIST_FOREACH(peer, &p->peers, link)
403 			if (peer->input.peerlen == addrlen &&
404 			    memcmp(peer->input.peer, addr, addrlen) == 0)
405 				break;
406 		if (peer == NULL) {
407 			errno = ENOTCONN;
408 			return (-1);
409 		}
410 	}
411 
412 	return (sendto(peer->input.fd, buf, len, 0, addr, addrlen));
413 }
414 
415 /*
416  * Dependency to create a lsock port
417  */
418 struct lsock_dep {
419 	struct snmp_dependency dep;
420 
421 	/* index (path name) */
422 	u_char *path;
423 	size_t pathlen;
424 
425 	/* the port */
426 	struct lsock_port *port;
427 
428 	/* which of the fields are set */
429 	u_int set;
430 
431 	/* type of the port */
432 	int type;
433 
434 	/* status */
435 	int status;
436 };
437 #define	LD_TYPE		0x01
438 #define	LD_STATUS	0x02
439 #define	LD_CREATE	0x04	/* rollback create */
440 #define	LD_DELETE	0x08	/* rollback delete */
441 
442 /*
443  * dependency handler for lsock ports
444  */
445 static int
446 lsock_func(struct snmp_context *ctx, struct snmp_dependency *dep,
447     enum snmp_depop op)
448 {
449 	struct lsock_dep *ld = (struct lsock_dep *)(void *)dep;
450 	int err = SNMP_ERR_NOERROR;
451 
452 	switch (op) {
453 
454 	  case SNMP_DEPOP_COMMIT:
455 		if (!(ld->set & LD_STATUS))
456 			err = SNMP_ERR_BADVALUE;
457 		else if (ld->port == NULL) {
458 			if (!ld->status)
459 				err = SNMP_ERR_BADVALUE;
460 
461 			else {
462 				/* create */
463 				err = lsock_open_port(ld->path, ld->pathlen,
464 				    &ld->port, ld->type);
465 				if (err == SNMP_ERR_NOERROR)
466 					ld->set |= LD_CREATE;
467 			}
468 		} else if (!ld->status) {
469 			/* delete - hard to roll back so defer to finalizer */
470 			ld->set |= LD_DELETE;
471 		} else
472 			/* modify - read-only */
473 			err = SNMP_ERR_READONLY;
474 
475 		return (err);
476 
477 	  case SNMP_DEPOP_ROLLBACK:
478 		if (ld->set & LD_CREATE) {
479 			/* was create */
480 			lsock_close_port(&ld->port->tport);
481 		}
482 		return (SNMP_ERR_NOERROR);
483 
484 	  case SNMP_DEPOP_FINISH:
485 		if ((ld->set & LD_DELETE) && ctx->code == SNMP_RET_OK)
486 			lsock_close_port(&ld->port->tport);
487 		free(ld->path);
488 		return (SNMP_ERR_NOERROR);
489 	}
490 	abort();
491 }
492 
493 /*
494  * Local port table
495  */
496 int
497 op_lsock_port(struct snmp_context *ctx, struct snmp_value *value,
498     u_int sub, u_int iidx, enum snmp_op op)
499 {
500 	asn_subid_t which = value->var.subs[sub-1];
501 	struct lsock_port *p;
502 	u_char *name;
503 	size_t namelen;
504 	struct lsock_dep *ld;
505 	struct asn_oid didx;
506 
507 	switch (op) {
508 
509 	  case SNMP_OP_GETNEXT:
510 		if ((p = (struct lsock_port *)trans_next_port(my_trans,
511 		    &value->var, sub)) == NULL)
512 			return (SNMP_ERR_NOSUCHNAME);
513 		index_append(&value->var, sub, &p->tport.index);
514 		break;
515 
516 	  case SNMP_OP_GET:
517 		if ((p = (struct lsock_port *)trans_find_port(my_trans,
518 		    &value->var, sub)) == NULL)
519 			return (SNMP_ERR_NOSUCHNAME);
520 		break;
521 
522 	  case SNMP_OP_SET:
523 		p = (struct lsock_port *)trans_find_port(my_trans,
524 		    &value->var, sub);
525 
526 		if (index_decode(&value->var, sub, iidx, &name, &namelen))
527 			return (SNMP_ERR_NO_CREATION);
528 
529 		asn_slice_oid(&didx, &value->var, sub, value->var.len);
530 		if ((ld = (struct lsock_dep *)(void *)snmp_dep_lookup(ctx,
531 		    &oid_begemotSnmpdLocalPortTable, &didx, sizeof(*ld),
532 		    lsock_func)) == NULL) {
533 			free(name);
534 			return (SNMP_ERR_GENERR);
535 		}
536 
537 		if (ld->path == NULL) {
538 			ld->path = name;
539 			ld->pathlen = namelen;
540 		} else {
541 			free(name);
542 		}
543 		ld->port = p;
544 
545 		switch (which) {
546 
547 		  case LEAF_begemotSnmpdLocalPortStatus:
548 			if (ld->set & LD_STATUS)
549 				return (SNMP_ERR_INCONS_VALUE);
550 			if (!TRUTH_OK(value->v.integer))
551 				return (SNMP_ERR_WRONG_VALUE);
552 
553 			ld->status = TRUTH_GET(value->v.integer);
554 			ld->set |= LD_STATUS;
555 			break;
556 
557 		  case LEAF_begemotSnmpdLocalPortType:
558 			if (ld->set & LD_TYPE)
559 				return (SNMP_ERR_INCONS_VALUE);
560 			if (value->v.integer < 1 || value->v.integer > 4)
561 				return (SNMP_ERR_WRONG_VALUE);
562 
563 			ld->type = value->v.integer;
564 			ld->set |= LD_TYPE;
565 			break;
566 		}
567 		return (SNMP_ERR_NOERROR);
568 
569 	  case SNMP_OP_ROLLBACK:
570 	  case SNMP_OP_COMMIT:
571 		return (SNMP_ERR_NOERROR);
572 
573 	  default:
574 		abort();
575 	}
576 
577 	/*
578 	 * Come here to fetch the value
579 	 */
580 	switch (which) {
581 
582 	  case LEAF_begemotSnmpdLocalPortStatus:
583 		value->v.integer = 1;
584 		break;
585 
586 	  case LEAF_begemotSnmpdLocalPortType:
587 		value->v.integer = p->type;
588 		break;
589 
590 	  default:
591 		abort();
592 	}
593 
594 	return (SNMP_ERR_NOERROR);
595 }
596