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
53static const struct asn_oid
54	oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
55
56static int lsock_start(void);
57static int lsock_stop(int);
58static void lsock_close_port(struct tport *);
59static int lsock_init_port(struct tport *);
60static ssize_t lsock_send(struct tport *, const u_char *, size_t,
61    const struct sockaddr *, size_t);
62static ssize_t lsock_recv(struct tport *, struct port_input *);
63
64/* exported */
65const 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};
76static struct transport *my_trans;
77
78static int
79lsock_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
88static int
89lsock_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
101static int
102lsock_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 */
111static int
112lsock_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 */
205static void
206lsock_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 */
217static void
218lsock_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 */
241static void
242lsock_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 */
257static void
258lsock_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 */
297static int
298lsock_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 */
398static ssize_t
399lsock_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
423static void
424check_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 */
442static ssize_t
443lsock_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 */
493struct 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 */
520static int
521lsock_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 */
571int
572op_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