]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/bsnmp/snmpd/trans_lsock.c
MFC r310586,r310587,r310588,r311381:
[FreeBSD/stable/10.git] / contrib / bsnmp / snmpd / trans_lsock.c
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     const struct sockaddr *, size_t);
62 static ssize_t lsock_recv(struct port_input *);
63
64 /* exported */
65 const 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 };
75 static struct transport *my_trans;
76
77 static int
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
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
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
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
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
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
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
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
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
398 lsock_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
422 static void
423 check_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  */
441 static ssize_t
442 lsock_recv(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  */
492 struct 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  */
519 static int
520 lsock_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  */
570 int
571 op_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 }