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