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