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
lsock_remove(struct tport * tp,intptr_t arg __unused)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
lsock_stop(int force)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
lsock_start(void)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
lsock_open_port(u_char * name,size_t namelen,struct lsock_port ** pp,int type)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
lsock_peer_close(struct lsock_peer * peer)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
lsock_close_port(struct tport * tp)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
lsock_input(int fd __unused,void * udata)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
lsock_listen_input(int fd,void * udata)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
lsock_init_port(struct tport * tp)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
lsock_send(struct tport * tp,const u_char * buf,size_t len,const struct sockaddr * addr,size_t addrlen)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
lsock_func(struct snmp_context * ctx,struct snmp_dependency * dep,enum snmp_depop op)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
op_lsock_port(struct snmp_context * ctx,struct snmp_value * value,u_int sub,u_int iidx,enum snmp_op op)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