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};
75static struct transport *my_trans;
76
77static int
78lsock_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
87static int
88lsock_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
100static int
101lsock_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 */
110static int
111lsock_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 */
204static void
205lsock_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 */
216static void
217lsock_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 */
240static void
241lsock_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 */
256static void
257lsock_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 */
296static int
297lsock_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 */
397static ssize_t
398lsock_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
422static void
423check_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 */
441static ssize_t
442lsock_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 */
492struct 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 */
519static int
520lsock_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 */
570int
571op_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