xref: /freebsd/contrib/bsnmp/snmpd/trans_lsock.c (revision f81cdf24ba5436367377f7c8e8f51f6df2a75ca7)
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/stat.h>
36 #include <sys/ucred.h>
37 #include <sys/un.h>
38 
39 #include <errno.h>
40 #include <stddef.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <unistd.h>
46 
47 #include "snmpmod.h"
48 #include "snmpd.h"
49 #include "trans_lsock.h"
50 #include "tree.h"
51 #include "oid.h"
52 
53 static const struct asn_oid
54 	oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
55 
56 static int lsock_start(void);
57 static int lsock_stop(int);
58 static void lsock_close_port(struct tport *);
59 static int lsock_init_port(struct tport *);
60 static ssize_t lsock_send(struct tport *, const u_char *, size_t,
61     const struct sockaddr *, size_t);
62 static ssize_t lsock_recv(struct tport *, struct port_input *);
63 
64 /* exported */
65 const struct transport_def lsock_trans = {
66 	"lsock",
67 	OIDX_begemotSnmpdTransLsock,
68 	lsock_start,
69 	lsock_stop,
70 	lsock_close_port,
71 	lsock_init_port,
72 	lsock_send,
73 	lsock_recv,
74 	NULL
75 };
76 static struct transport *my_trans;
77 
78 static int
79 lsock_remove(struct tport *tp, intptr_t arg __unused)
80 {
81 	struct lsock_port *port = (struct lsock_port *)tp;
82 
83 	(void)remove(port->name);
84 
85 	return (-1);
86 }
87 
88 static int
89 lsock_stop(int force)
90 {
91 
92 	if (my_trans != NULL) {
93 		if (!force && trans_first_port(my_trans) != NULL)
94 			return (SNMP_ERR_GENERR);
95 		trans_iter_port(my_trans, lsock_remove, 0);
96 		return (trans_unregister(my_trans));
97 	}
98 	return (SNMP_ERR_NOERROR);
99 }
100 
101 static int
102 lsock_start(void)
103 {
104 	return (trans_register(&lsock_trans, &my_trans));
105 }
106 
107 /*
108  * Open a local port. If this is a datagram socket create also the
109  * one and only peer.
110  */
111 static int
112 lsock_open_port(u_char *name, size_t namelen, struct lsock_port **pp,
113     int type)
114 {
115 	struct lsock_port *port;
116 	struct lsock_peer *peer = NULL;
117 	int is_stream, need_cred;
118 	size_t u;
119 	int err;
120 	struct sockaddr_un sa;
121 
122 	if (namelen == 0 || namelen + 1 > sizeof(sa.sun_path))
123 		return (SNMP_ERR_BADVALUE);
124 
125 	switch (type) {
126 	  case LOCP_DGRAM_UNPRIV:
127 		is_stream = 0;
128 		need_cred = 0;
129 		break;
130 
131 	  case LOCP_DGRAM_PRIV:
132 		is_stream = 0;
133 		need_cred = 1;
134 		break;
135 
136 	  case LOCP_STREAM_UNPRIV:
137 		is_stream = 1;
138 		need_cred = 0;
139 		break;
140 
141 	  case LOCP_STREAM_PRIV:
142 		is_stream = 1;
143 		need_cred = 1;
144 		break;
145 
146 	  default:
147 		return (SNMP_ERR_BADVALUE);
148 	}
149 
150 	if ((port = calloc(1, sizeof(*port))) == NULL)
151 		return (SNMP_ERR_GENERR);
152 
153 	if (!is_stream) {
154 		if ((peer = calloc(1, sizeof(*peer))) == NULL) {
155 			free(port);
156 			return (SNMP_ERR_GENERR);
157 		}
158 	}
159 	if ((port->name = malloc(namelen + 1)) == NULL) {
160 		free(port);
161 		if (!is_stream)
162 			free(peer);
163 		return (SNMP_ERR_GENERR);
164 	}
165 	strncpy(port->name, name, namelen);
166 	port->name[namelen] = '\0';
167 
168 	port->type = type;
169 	port->str_sock = -1;
170 	LIST_INIT(&port->peers);
171 
172 	port->tport.index.len = namelen + 1;
173 	port->tport.index.subs[0] = namelen;
174 	for (u = 0; u < namelen; u++)
175 		port->tport.index.subs[u + 1] = name[u];
176 
177 	if (peer != NULL) {
178 		LIST_INSERT_HEAD(&port->peers, peer, link);
179 
180 		peer->port = port;
181 
182 		peer->input.fd = -1;
183 		peer->input.id = NULL;
184 		peer->input.stream = is_stream;
185 		peer->input.cred = need_cred;
186 		peer->input.peer = (struct sockaddr *)&peer->peer;
187 	}
188 
189 	trans_insert_port(my_trans, &port->tport);
190 
191 	if (community != COMM_INITIALIZE &&
192 	    (err = lsock_init_port(&port->tport)) != SNMP_ERR_NOERROR) {
193 		lsock_close_port(&port->tport);
194 		return (err);
195 	}
196 
197 	*pp = port;
198 
199 	return (SNMP_ERR_NOERROR);
200 }
201 
202 /*
203  * Close a local domain peer
204  */
205 static void
206 lsock_peer_close(struct lsock_peer *peer)
207 {
208 
209 	LIST_REMOVE(peer, link);
210 	snmpd_input_close(&peer->input);
211 	free(peer);
212 }
213 
214 /*
215  * Close a local port
216  */
217 static void
218 lsock_close_port(struct tport *tp)
219 {
220 	struct lsock_port *port = (struct lsock_port *)tp;
221 	struct lsock_peer *peer;
222 
223 	if (port->str_id != NULL)
224 		fd_deselect(port->str_id);
225 	if (port->str_sock >= 0)
226 		(void)close(port->str_sock);
227 	(void)remove(port->name);
228 
229 	trans_remove_port(tp);
230 
231 	while ((peer = LIST_FIRST(&port->peers)) != NULL)
232 		lsock_peer_close(peer);
233 
234 	free(port->name);
235 	free(port);
236 }
237 
238 /*
239  * Input on a local socket (either datagram or stream)
240  */
241 static void
242 lsock_input(int fd __unused, void *udata)
243 {
244 	struct lsock_peer *peer = udata;
245 	struct lsock_port *p = peer->port;
246 
247 	peer->input.peerlen = sizeof(peer->peer);
248 	if (snmpd_input(&peer->input, &p->tport) == -1 && peer->input.stream)
249 		/* framing or other input error */
250 		lsock_peer_close(peer);
251 }
252 
253 /*
254  * A UNIX domain listening socket is ready. This means we have a peer
255  * that we need to accept
256  */
257 static void
258 lsock_listen_input(int fd, void *udata)
259 {
260 	struct lsock_port *p = udata;
261 	struct lsock_peer *peer;
262 
263 	if ((peer = calloc(1, sizeof(*peer))) == NULL) {
264 		syslog(LOG_WARNING, "%s: peer malloc failed", p->name);
265 		(void)close(accept(fd, NULL, NULL));
266 		return;
267 	}
268 
269 	peer->port = p;
270 
271 	peer->input.stream = 1;
272 	peer->input.cred = (p->type == LOCP_DGRAM_PRIV ||
273 	    p->type == LOCP_STREAM_PRIV);
274 	peer->input.peerlen = sizeof(peer->peer);
275 	peer->input.peer = (struct sockaddr *)&peer->peer;
276 
277 	peer->input.fd = accept(fd, peer->input.peer, &peer->input.peerlen);
278 	if (peer->input.fd == -1) {
279 		syslog(LOG_WARNING, "%s: accept failed: %m", p->name);
280 		free(peer);
281 		return;
282 	}
283 
284 	if ((peer->input.id = fd_select(peer->input.fd, lsock_input,
285 	    peer, NULL)) == NULL) {
286 		close(peer->input.fd);
287 		free(peer);
288 		return;
289 	}
290 
291 	LIST_INSERT_HEAD(&p->peers, peer, link);
292 }
293 
294 /*
295  * Create a local socket
296  */
297 static int
298 lsock_init_port(struct tport *tp)
299 {
300 	struct lsock_port *p = (struct lsock_port *)tp;
301 	struct sockaddr_un sa;
302 
303 	if (p->type == LOCP_STREAM_PRIV || p->type == LOCP_STREAM_UNPRIV) {
304 		if ((p->str_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
305 			syslog(LOG_ERR, "creating local socket: %m");
306 			return (SNMP_ERR_RES_UNAVAIL);
307 		}
308 
309 		strlcpy(sa.sun_path, p->name, sizeof(sa.sun_path));
310 		sa.sun_family = AF_LOCAL;
311 		sa.sun_len = SUN_LEN(&sa);
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 		const int on = 1;
347 
348 		peer = LIST_FIRST(&p->peers);
349 
350 		if ((peer->input.fd = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0) {
351 			syslog(LOG_ERR, "creating local socket: %m");
352 			return (SNMP_ERR_RES_UNAVAIL);
353 		}
354 
355 		if (setsockopt(peer->input.fd, 0, LOCAL_CREDS, &on,
356 		    sizeof(on)) == -1) {
357 			syslog(LOG_ERR, "setsockopt(LOCAL_CREDS): %m");
358 			close(peer->input.fd);
359 			peer->input.fd = -1;
360 			return (SNMP_ERR_GENERR);
361 		}
362 
363 		strlcpy(sa.sun_path, p->name, sizeof(sa.sun_path));
364 		sa.sun_family = AF_LOCAL;
365 		sa.sun_len = SUN_LEN(&sa);
366 
367 		(void)remove(p->name);
368 
369 		if (bind(peer->input.fd, (struct sockaddr *)&sa, sizeof(sa))) {
370 			if (errno == EADDRNOTAVAIL) {
371 				close(peer->input.fd);
372 				peer->input.fd = -1;
373 				return (SNMP_ERR_INCONS_NAME);
374 			}
375 			syslog(LOG_ERR, "bind: %s %m", p->name);
376 			close(peer->input.fd);
377 			peer->input.fd = -1;
378 			return (SNMP_ERR_GENERR);
379 		}
380 		if (chmod(p->name, 0666) == -1)
381 			syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
382 
383 		peer->input.id = fd_select(peer->input.fd, lsock_input,
384 		    peer, NULL);
385 		if (peer->input.id == NULL) {
386 			(void)remove(p->name);
387 			close(peer->input.fd);
388 			peer->input.fd = -1;
389 			return (SNMP_ERR_GENERR);
390 		}
391 	}
392 	return (SNMP_ERR_NOERROR);
393 }
394 
395 /*
396  * Send something
397  */
398 static ssize_t
399 lsock_send(struct tport *tp, const u_char *buf, size_t len,
400     const struct sockaddr *addr, size_t addrlen)
401 {
402 	struct lsock_port *p = (struct lsock_port *)tp;
403 	struct lsock_peer *peer;
404 
405 	if (p->type == LOCP_DGRAM_PRIV || p->type == LOCP_DGRAM_UNPRIV) {
406 		peer = LIST_FIRST(&p->peers);
407 
408 	} else {
409 		/* search for the peer */
410 		LIST_FOREACH(peer, &p->peers, link)
411 			if (peer->input.peerlen == addrlen &&
412 			    memcmp(peer->input.peer, addr, addrlen) == 0)
413 				break;
414 		if (peer == NULL) {
415 			errno = ENOTCONN;
416 			return (-1);
417 		}
418 	}
419 
420 	return (sendto(peer->input.fd, buf, len, 0, addr, addrlen));
421 }
422 
423 static void
424 check_priv_stream(struct port_input *pi)
425 {
426 	struct xucred ucred;
427 	socklen_t ucredlen;
428 
429 	/* obtain the accept time credentials */
430 	ucredlen = sizeof(ucred);
431 
432 	if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 &&
433 	    ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION)
434 		pi->priv = (ucred.cr_uid == 0);
435 	else
436 		pi->priv = 0;
437 }
438 
439 /*
440  * Receive something
441  */
442 static ssize_t
443 lsock_recv(struct tport *tp __unused, struct port_input *pi)
444 {
445 	struct msghdr msg;
446 	struct iovec iov[1];
447 	ssize_t len;
448 
449 	msg.msg_control = NULL;
450 	msg.msg_controllen = 0;
451 
452 	if (pi->buf == NULL) {
453 		/* no buffer yet - allocate one */
454 		if ((pi->buf = buf_alloc(0)) == NULL) {
455 			/* ups - could not get buffer. Return an error
456 			 * the caller must close the transport. */
457 			return (-1);
458 		}
459 		pi->buflen = buf_size(0);
460 		pi->consumed = 0;
461 		pi->length = 0;
462 	}
463 
464 	/* try to get a message */
465 	msg.msg_name = pi->peer;
466 	msg.msg_namelen = pi->peerlen;
467 	msg.msg_iov = iov;
468 	msg.msg_iovlen = 1;
469 	msg.msg_control = NULL;
470 	msg.msg_controllen = 0;
471 	msg.msg_flags = 0;
472 
473 	iov[0].iov_base = pi->buf + pi->length;
474 	iov[0].iov_len = pi->buflen - pi->length;
475 
476 	len = recvmsg(pi->fd, &msg, 0);
477 
478 	if (len == -1 || len == 0)
479 		/* receive error */
480 		return (-1);
481 
482 	pi->length += len;
483 
484 	if (pi->cred)
485 		check_priv_stream(pi);
486 
487 	return (0);
488 }
489 
490 /*
491  * Dependency to create a lsock port
492  */
493 struct lsock_dep {
494 	struct snmp_dependency dep;
495 
496 	/* index (path name) */
497 	u_char *path;
498 	size_t pathlen;
499 
500 	/* the port */
501 	struct lsock_port *port;
502 
503 	/* which of the fields are set */
504 	u_int set;
505 
506 	/* type of the port */
507 	int type;
508 
509 	/* status */
510 	int status;
511 };
512 #define	LD_TYPE		0x01
513 #define	LD_STATUS	0x02
514 #define	LD_CREATE	0x04	/* rollback create */
515 #define	LD_DELETE	0x08	/* rollback delete */
516 
517 /*
518  * dependency handler for lsock ports
519  */
520 static int
521 lsock_func(struct snmp_context *ctx, struct snmp_dependency *dep,
522     enum snmp_depop op)
523 {
524 	struct lsock_dep *ld = (struct lsock_dep *)(void *)dep;
525 	int err = SNMP_ERR_NOERROR;
526 
527 	switch (op) {
528 
529 	  case SNMP_DEPOP_COMMIT:
530 		if (!(ld->set & LD_STATUS))
531 			err = SNMP_ERR_BADVALUE;
532 		else if (ld->port == NULL) {
533 			if (!ld->status)
534 				err = SNMP_ERR_BADVALUE;
535 
536 			else {
537 				/* create */
538 				err = lsock_open_port(ld->path, ld->pathlen,
539 				    &ld->port, ld->type);
540 				if (err == SNMP_ERR_NOERROR)
541 					ld->set |= LD_CREATE;
542 			}
543 		} else if (!ld->status) {
544 			/* delete - hard to roll back so defer to finalizer */
545 			ld->set |= LD_DELETE;
546 		} else
547 			/* modify - read-only */
548 			err = SNMP_ERR_READONLY;
549 
550 		return (err);
551 
552 	  case SNMP_DEPOP_ROLLBACK:
553 		if (ld->set & LD_CREATE) {
554 			/* was create */
555 			lsock_close_port(&ld->port->tport);
556 		}
557 		return (SNMP_ERR_NOERROR);
558 
559 	  case SNMP_DEPOP_FINISH:
560 		if ((ld->set & LD_DELETE) && ctx->code == SNMP_RET_OK)
561 			lsock_close_port(&ld->port->tport);
562 		free(ld->path);
563 		return (SNMP_ERR_NOERROR);
564 	}
565 	abort();
566 }
567 
568 /*
569  * Local port table
570  */
571 int
572 op_lsock_port(struct snmp_context *ctx, struct snmp_value *value,
573     u_int sub, u_int iidx, enum snmp_op op)
574 {
575 	asn_subid_t which = value->var.subs[sub-1];
576 	struct lsock_port *p;
577 	u_char *name;
578 	size_t namelen;
579 	struct lsock_dep *ld;
580 	struct asn_oid didx;
581 
582 	switch (op) {
583 
584 	  case SNMP_OP_GETNEXT:
585 		if ((p = (struct lsock_port *)trans_next_port(my_trans,
586 		    &value->var, sub)) == NULL)
587 			return (SNMP_ERR_NOSUCHNAME);
588 		index_append(&value->var, sub, &p->tport.index);
589 		break;
590 
591 	  case SNMP_OP_GET:
592 		if ((p = (struct lsock_port *)trans_find_port(my_trans,
593 		    &value->var, sub)) == NULL)
594 			return (SNMP_ERR_NOSUCHNAME);
595 		break;
596 
597 	  case SNMP_OP_SET:
598 		p = (struct lsock_port *)trans_find_port(my_trans,
599 		    &value->var, sub);
600 
601 		if (index_decode(&value->var, sub, iidx, &name, &namelen))
602 			return (SNMP_ERR_NO_CREATION);
603 
604 		asn_slice_oid(&didx, &value->var, sub, value->var.len);
605 		if ((ld = (struct lsock_dep *)(void *)snmp_dep_lookup(ctx,
606 		    &oid_begemotSnmpdLocalPortTable, &didx, sizeof(*ld),
607 		    lsock_func)) == NULL) {
608 			free(name);
609 			return (SNMP_ERR_GENERR);
610 		}
611 
612 		if (ld->path == NULL) {
613 			ld->path = name;
614 			ld->pathlen = namelen;
615 		} else {
616 			free(name);
617 		}
618 		ld->port = p;
619 
620 		switch (which) {
621 
622 		  case LEAF_begemotSnmpdLocalPortStatus:
623 			if (ld->set & LD_STATUS)
624 				return (SNMP_ERR_INCONS_VALUE);
625 			if (!TRUTH_OK(value->v.integer))
626 				return (SNMP_ERR_WRONG_VALUE);
627 
628 			ld->status = TRUTH_GET(value->v.integer);
629 			ld->set |= LD_STATUS;
630 			break;
631 
632 		  case LEAF_begemotSnmpdLocalPortType:
633 			if (ld->set & LD_TYPE)
634 				return (SNMP_ERR_INCONS_VALUE);
635 			if (value->v.integer < 1 || value->v.integer > 4)
636 				return (SNMP_ERR_WRONG_VALUE);
637 
638 			ld->type = value->v.integer;
639 			ld->set |= LD_TYPE;
640 			break;
641 		}
642 		return (SNMP_ERR_NOERROR);
643 
644 	  case SNMP_OP_ROLLBACK:
645 	  case SNMP_OP_COMMIT:
646 		return (SNMP_ERR_NOERROR);
647 
648 	  default:
649 		abort();
650 	}
651 
652 	/*
653 	 * Come here to fetch the value
654 	 */
655 	switch (which) {
656 
657 	  case LEAF_begemotSnmpdLocalPortStatus:
658 		value->v.integer = 1;
659 		break;
660 
661 	  case LEAF_begemotSnmpdLocalPortType:
662 		value->v.integer = p->type;
663 		break;
664 
665 	  default:
666 		abort();
667 	}
668 
669 	return (SNMP_ERR_NOERROR);
670 }
671