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
53 static const struct asn_oid
54 oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
55
56 static int lsock_start(void);
57 static int lsock_stop(int);
58 static void lsock_close_port(struct tport *);
59 static int lsock_init_port(struct tport *);
60 static ssize_t lsock_send(struct tport *, const u_char *, size_t,
61 struct port_input *);
62 static ssize_t lsock_recv(struct tport *, struct port_input *);
63
64 /* exported */
65 const struct transport_def lsock_trans = {
66 .name = "lsock",
67 .id = OIDX_begemotSnmpdTransLsock,
68 .start = lsock_start,
69 .stop = lsock_stop,
70 .close_port = lsock_close_port,
71 .init_port = lsock_init_port,
72 .send = lsock_send,
73 .recv = lsock_recv,
74 };
75 static struct transport *my_trans;
76
77 static int
lsock_remove(struct tport * tp,intptr_t arg __unused)78 lsock_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
87 static int
lsock_stop(int force)88 lsock_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
100 static int
lsock_start(void)101 lsock_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 */
110 static int
lsock_open_port(u_char * name,size_t namelen,struct lsock_port ** pp,int type)111 lsock_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 */
204 static void
lsock_peer_close(struct lsock_peer * peer)205 lsock_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 */
216 static void
lsock_close_port(struct tport * tp)217 lsock_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 */
240 static void
lsock_input(int fd __unused,void * udata)241 lsock_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 */
256 static void
lsock_listen_input(int fd,void * udata)257 lsock_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 */
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 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 */
397 static ssize_t
lsock_send(struct tport * tp __unused,const u_char * buf,size_t len,struct port_input * pi)398 lsock_send(struct tport *tp __unused, const u_char *buf, size_t len,
399 struct port_input *pi)
400 {
401 return (sendto(pi->fd, buf, len, MSG_NOSIGNAL, pi->peer, pi->peerlen));
402 }
403
404 static void
check_priv_stream(struct port_input * pi)405 check_priv_stream(struct port_input *pi)
406 {
407 struct xucred ucred;
408 socklen_t ucredlen;
409
410 /* obtain the accept time credentials */
411 ucredlen = sizeof(ucred);
412
413 if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 &&
414 ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION)
415 pi->priv = (ucred.cr_uid == 0);
416 else
417 pi->priv = 0;
418 }
419
420 /*
421 * Receive something
422 */
423 static ssize_t
lsock_recv(struct tport * tp __unused,struct port_input * pi)424 lsock_recv(struct tport *tp __unused, struct port_input *pi)
425 {
426 struct msghdr msg;
427 struct iovec iov[1];
428 ssize_t len;
429
430 msg.msg_control = NULL;
431 msg.msg_controllen = 0;
432
433 if (pi->buf == NULL) {
434 /* no buffer yet - allocate one */
435 if ((pi->buf = buf_alloc(0)) == NULL) {
436 /* ups - could not get buffer. Return an error
437 * the caller must close the transport. */
438 return (-1);
439 }
440 pi->buflen = buf_size(0);
441 pi->consumed = 0;
442 pi->length = 0;
443 }
444
445 /* try to get a message */
446 msg.msg_name = pi->peer;
447 msg.msg_namelen = pi->peerlen;
448 msg.msg_iov = iov;
449 msg.msg_iovlen = 1;
450 msg.msg_control = NULL;
451 msg.msg_controllen = 0;
452 msg.msg_flags = 0;
453
454 iov[0].iov_base = pi->buf + pi->length;
455 iov[0].iov_len = pi->buflen - pi->length;
456
457 len = recvmsg(pi->fd, &msg, 0);
458
459 if (len == -1 || len == 0)
460 /* receive error */
461 return (-1);
462
463 pi->length += len;
464
465 if (pi->cred)
466 check_priv_stream(pi);
467
468 return (0);
469 }
470
471 /*
472 * Dependency to create a lsock port
473 */
474 struct lsock_dep {
475 struct snmp_dependency dep;
476
477 /* index (path name) */
478 u_char *path;
479 size_t pathlen;
480
481 /* the port */
482 struct lsock_port *port;
483
484 /* which of the fields are set */
485 u_int set;
486
487 /* type of the port */
488 int type;
489
490 /* status */
491 int status;
492 };
493 #define LD_TYPE 0x01
494 #define LD_STATUS 0x02
495 #define LD_CREATE 0x04 /* rollback create */
496 #define LD_DELETE 0x08 /* rollback delete */
497
498 /*
499 * dependency handler for lsock ports
500 */
501 static int
lsock_func(struct snmp_context * ctx,struct snmp_dependency * dep,enum snmp_depop op)502 lsock_func(struct snmp_context *ctx, struct snmp_dependency *dep,
503 enum snmp_depop op)
504 {
505 struct lsock_dep *ld = (struct lsock_dep *)(void *)dep;
506 int err = SNMP_ERR_NOERROR;
507
508 switch (op) {
509
510 case SNMP_DEPOP_COMMIT:
511 if (!(ld->set & LD_STATUS))
512 err = SNMP_ERR_BADVALUE;
513 else if (ld->port == NULL) {
514 if (!ld->status)
515 err = SNMP_ERR_BADVALUE;
516
517 else {
518 /* create */
519 err = lsock_open_port(ld->path, ld->pathlen,
520 &ld->port, ld->type);
521 if (err == SNMP_ERR_NOERROR)
522 ld->set |= LD_CREATE;
523 }
524 } else if (!ld->status) {
525 /* delete - hard to roll back so defer to finalizer */
526 ld->set |= LD_DELETE;
527 } else
528 /* modify - read-only */
529 err = SNMP_ERR_READONLY;
530
531 return (err);
532
533 case SNMP_DEPOP_ROLLBACK:
534 if (ld->set & LD_CREATE) {
535 /* was create */
536 lsock_close_port(&ld->port->tport);
537 }
538 return (SNMP_ERR_NOERROR);
539
540 case SNMP_DEPOP_FINISH:
541 if ((ld->set & LD_DELETE) && ctx->code == SNMP_RET_OK)
542 lsock_close_port(&ld->port->tport);
543 free(ld->path);
544 return (SNMP_ERR_NOERROR);
545 }
546 abort();
547 }
548
549 /*
550 * Local port table
551 */
552 int
op_lsock_port(struct snmp_context * ctx,struct snmp_value * value,u_int sub,u_int iidx,enum snmp_op op)553 op_lsock_port(struct snmp_context *ctx, struct snmp_value *value,
554 u_int sub, u_int iidx, enum snmp_op op)
555 {
556 asn_subid_t which = value->var.subs[sub-1];
557 struct lsock_port *p;
558 u_char *name;
559 size_t namelen;
560 struct lsock_dep *ld;
561 struct asn_oid didx;
562
563 switch (op) {
564
565 case SNMP_OP_GETNEXT:
566 if ((p = (struct lsock_port *)trans_next_port(my_trans,
567 &value->var, sub)) == NULL)
568 return (SNMP_ERR_NOSUCHNAME);
569 index_append(&value->var, sub, &p->tport.index);
570 break;
571
572 case SNMP_OP_GET:
573 if ((p = (struct lsock_port *)trans_find_port(my_trans,
574 &value->var, sub)) == NULL)
575 return (SNMP_ERR_NOSUCHNAME);
576 break;
577
578 case SNMP_OP_SET:
579 p = (struct lsock_port *)trans_find_port(my_trans,
580 &value->var, sub);
581
582 if (index_decode(&value->var, sub, iidx, &name, &namelen))
583 return (SNMP_ERR_NO_CREATION);
584
585 asn_slice_oid(&didx, &value->var, sub, value->var.len);
586 if ((ld = (struct lsock_dep *)(void *)snmp_dep_lookup(ctx,
587 &oid_begemotSnmpdLocalPortTable, &didx, sizeof(*ld),
588 lsock_func)) == NULL) {
589 free(name);
590 return (SNMP_ERR_GENERR);
591 }
592
593 if (ld->path == NULL) {
594 ld->path = name;
595 ld->pathlen = namelen;
596 } else {
597 free(name);
598 }
599 ld->port = p;
600
601 switch (which) {
602
603 case LEAF_begemotSnmpdLocalPortStatus:
604 if (ld->set & LD_STATUS)
605 return (SNMP_ERR_INCONS_VALUE);
606 if (!TRUTH_OK(value->v.integer))
607 return (SNMP_ERR_WRONG_VALUE);
608
609 ld->status = TRUTH_GET(value->v.integer);
610 ld->set |= LD_STATUS;
611 break;
612
613 case LEAF_begemotSnmpdLocalPortType:
614 if (ld->set & LD_TYPE)
615 return (SNMP_ERR_INCONS_VALUE);
616 if (value->v.integer < 1 || value->v.integer > 4)
617 return (SNMP_ERR_WRONG_VALUE);
618
619 ld->type = value->v.integer;
620 ld->set |= LD_TYPE;
621 break;
622 }
623 return (SNMP_ERR_NOERROR);
624
625 case SNMP_OP_ROLLBACK:
626 case SNMP_OP_COMMIT:
627 return (SNMP_ERR_NOERROR);
628
629 default:
630 abort();
631 }
632
633 /*
634 * Come here to fetch the value
635 */
636 switch (which) {
637
638 case LEAF_begemotSnmpdLocalPortStatus:
639 value->v.integer = 1;
640 break;
641
642 case LEAF_begemotSnmpdLocalPortType:
643 value->v.integer = p->type;
644 break;
645
646 default:
647 abort();
648 }
649
650 return (SNMP_ERR_NOERROR);
651 }
652