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