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