]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bsnmp/snmpd/trans_lsock.c
Merge release 1.14 of bsnmp.
[FreeBSD/FreeBSD.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 tport *, 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         NULL
75 };
76 static struct transport *my_trans;
77
78 static int
79 lsock_remove(struct tport *tp, intptr_t arg __unused)
80 {
81         struct lsock_port *port = (struct lsock_port *)tp;
82
83         (void)remove(port->name);
84
85         return (-1);
86 }
87
88 static int
89 lsock_stop(int force)
90 {
91
92         if (my_trans != NULL) {
93                 if (!force && trans_first_port(my_trans) != NULL)
94                         return (SNMP_ERR_GENERR);
95                 trans_iter_port(my_trans, lsock_remove, 0);
96                 return (trans_unregister(my_trans));
97         }
98         return (SNMP_ERR_NOERROR);
99 }
100
101 static int
102 lsock_start(void)
103 {
104         return (trans_register(&lsock_trans, &my_trans));
105 }
106
107 /*
108  * Open a local port. If this is a datagram socket create also the
109  * one and only peer.
110  */
111 static int
112 lsock_open_port(u_char *name, size_t namelen, struct lsock_port **pp,
113     int type)
114 {
115         struct lsock_port *port;
116         struct lsock_peer *peer = NULL;
117         int is_stream, need_cred;
118         size_t u;
119         int err;
120         struct sockaddr_un sa;
121
122         if (namelen == 0 || namelen + 1 > sizeof(sa.sun_path))
123                 return (SNMP_ERR_BADVALUE);
124
125         switch (type) {
126           case LOCP_DGRAM_UNPRIV:
127                 is_stream = 0;
128                 need_cred = 0;
129                 break;
130
131           case LOCP_DGRAM_PRIV:
132                 is_stream = 0;
133                 need_cred = 1;
134                 break;
135
136           case LOCP_STREAM_UNPRIV:
137                 is_stream = 1;
138                 need_cred = 0;
139                 break;
140
141           case LOCP_STREAM_PRIV:
142                 is_stream = 1;
143                 need_cred = 1;
144                 break;
145
146           default:
147                 return (SNMP_ERR_BADVALUE);
148         }
149
150         if ((port = calloc(1, sizeof(*port))) == NULL)
151                 return (SNMP_ERR_GENERR);
152
153         if (!is_stream) {
154                 if ((peer = calloc(1, sizeof(*peer))) == NULL) {
155                         free(port);
156                         return (SNMP_ERR_GENERR);
157                 }
158         }
159         if ((port->name = malloc(namelen + 1)) == NULL) {
160                 free(port);
161                 if (!is_stream)
162                         free(peer);
163                 return (SNMP_ERR_GENERR);
164         }
165         strncpy(port->name, name, namelen);
166         port->name[namelen] = '\0';
167
168         port->type = type;
169         port->str_sock = -1;
170         LIST_INIT(&port->peers);
171
172         port->tport.index.len = namelen + 1;
173         port->tport.index.subs[0] = namelen;
174         for (u = 0; u < namelen; u++)
175                 port->tport.index.subs[u + 1] = name[u];
176
177         if (peer != NULL) {
178                 LIST_INSERT_HEAD(&port->peers, peer, link);
179
180                 peer->port = port;
181
182                 peer->input.fd = -1;
183                 peer->input.id = NULL;
184                 peer->input.stream = is_stream;
185                 peer->input.cred = need_cred;
186                 peer->input.peer = (struct sockaddr *)&peer->peer;
187         }
188
189         trans_insert_port(my_trans, &port->tport);
190
191         if (community != COMM_INITIALIZE &&
192             (err = lsock_init_port(&port->tport)) != SNMP_ERR_NOERROR) {
193                 lsock_close_port(&port->tport);
194                 return (err);
195         }
196
197         *pp = port;
198
199         return (SNMP_ERR_NOERROR);
200 }
201
202 /*
203  * Close a local domain peer
204  */
205 static void
206 lsock_peer_close(struct lsock_peer *peer)
207 {
208
209         LIST_REMOVE(peer, link);
210         snmpd_input_close(&peer->input);
211         free(peer);
212 }
213
214 /*
215  * Close a local port
216  */
217 static void
218 lsock_close_port(struct tport *tp)
219 {
220         struct lsock_port *port = (struct lsock_port *)tp;
221         struct lsock_peer *peer;
222
223         if (port->str_id != NULL)
224                 fd_deselect(port->str_id);
225         if (port->str_sock >= 0)
226                 (void)close(port->str_sock);
227         (void)remove(port->name);
228
229         trans_remove_port(tp);
230
231         while ((peer = LIST_FIRST(&port->peers)) != NULL)
232                 lsock_peer_close(peer);
233
234         free(port->name);
235         free(port);
236 }
237
238 /*
239  * Input on a local socket (either datagram or stream)
240  */
241 static void
242 lsock_input(int fd __unused, void *udata)
243 {
244         struct lsock_peer *peer = udata;
245         struct lsock_port *p = peer->port;
246
247         peer->input.peerlen = sizeof(peer->peer);
248         if (snmpd_input(&peer->input, &p->tport) == -1 && peer->input.stream)
249                 /* framing or other input error */
250                 lsock_peer_close(peer);
251 }
252
253 /*
254  * A UNIX domain listening socket is ready. This means we have a peer
255  * that we need to accept
256  */
257 static void
258 lsock_listen_input(int fd, void *udata)
259 {
260         struct lsock_port *p = udata;
261         struct lsock_peer *peer;
262
263         if ((peer = calloc(1, sizeof(*peer))) == NULL) {
264                 syslog(LOG_WARNING, "%s: peer malloc failed", p->name);
265                 (void)close(accept(fd, NULL, NULL));
266                 return;
267         }
268
269         peer->port = p;
270
271         peer->input.stream = 1;
272         peer->input.cred = (p->type == LOCP_DGRAM_PRIV ||
273             p->type == LOCP_STREAM_PRIV);
274         peer->input.peerlen = sizeof(peer->peer);
275         peer->input.peer = (struct sockaddr *)&peer->peer;
276
277         peer->input.fd = accept(fd, peer->input.peer, &peer->input.peerlen);
278         if (peer->input.fd == -1) {
279                 syslog(LOG_WARNING, "%s: accept failed: %m", p->name);
280                 free(peer);
281                 return;
282         }
283
284         if ((peer->input.id = fd_select(peer->input.fd, lsock_input,
285             peer, NULL)) == NULL) {
286                 close(peer->input.fd);
287                 free(peer);
288                 return;
289         }
290
291         LIST_INSERT_HEAD(&p->peers, peer, link);
292 }
293
294 /*
295  * Create a local socket
296  */
297 static int
298 lsock_init_port(struct tport *tp)
299 {
300         struct lsock_port *p = (struct lsock_port *)tp;
301         struct sockaddr_un sa;
302
303         if (p->type == LOCP_STREAM_PRIV || p->type == LOCP_STREAM_UNPRIV) {
304                 if ((p->str_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
305                         syslog(LOG_ERR, "creating local socket: %m");
306                         return (SNMP_ERR_RES_UNAVAIL);
307                 }
308
309                 strlcpy(sa.sun_path, p->name, sizeof(sa.sun_path));
310                 sa.sun_family = AF_LOCAL;
311                 sa.sun_len = SUN_LEN(&sa);
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                 strlcpy(sa.sun_path, p->name, sizeof(sa.sun_path));
364                 sa.sun_family = AF_LOCAL;
365                 sa.sun_len = SUN_LEN(&sa);
366
367                 (void)remove(p->name);
368
369                 if (bind(peer->input.fd, (struct sockaddr *)&sa, sizeof(sa))) {
370                         if (errno == EADDRNOTAVAIL) {
371                                 close(peer->input.fd);
372                                 peer->input.fd = -1;
373                                 return (SNMP_ERR_INCONS_NAME);
374                         }
375                         syslog(LOG_ERR, "bind: %s %m", p->name);
376                         close(peer->input.fd);
377                         peer->input.fd = -1;
378                         return (SNMP_ERR_GENERR);
379                 }
380                 if (chmod(p->name, 0666) == -1)
381                         syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
382
383                 peer->input.id = fd_select(peer->input.fd, lsock_input,
384                     peer, NULL);
385                 if (peer->input.id == NULL) {
386                         (void)remove(p->name);
387                         close(peer->input.fd);
388                         peer->input.fd = -1;
389                         return (SNMP_ERR_GENERR);
390                 }
391         }
392         return (SNMP_ERR_NOERROR);
393 }
394
395 /*
396  * Send something
397  */
398 static ssize_t
399 lsock_send(struct tport *tp, const u_char *buf, size_t len,
400     const struct sockaddr *addr, size_t addrlen)
401 {
402         struct lsock_port *p = (struct lsock_port *)tp;
403         struct lsock_peer *peer;
404
405         if (p->type == LOCP_DGRAM_PRIV || p->type == LOCP_DGRAM_UNPRIV) {
406                 peer = LIST_FIRST(&p->peers);
407
408         } else {
409                 /* search for the peer */
410                 LIST_FOREACH(peer, &p->peers, link)
411                         if (peer->input.peerlen == addrlen &&
412                             memcmp(peer->input.peer, addr, addrlen) == 0)
413                                 break;
414                 if (peer == NULL) {
415                         errno = ENOTCONN;
416                         return (-1);
417                 }
418         }
419
420         return (sendto(peer->input.fd, buf, len, 0, addr, addrlen));
421 }
422
423 static void
424 check_priv_stream(struct port_input *pi)
425 {
426         struct xucred ucred;
427         socklen_t ucredlen;
428
429         /* obtain the accept time credentials */
430         ucredlen = sizeof(ucred);
431
432         if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 &&
433             ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION)
434                 pi->priv = (ucred.cr_uid == 0);
435         else
436                 pi->priv = 0;
437 }
438
439 /*
440  * Receive something
441  */
442 static ssize_t
443 lsock_recv(struct tport *tp __unused, struct port_input *pi)
444 {
445         struct msghdr msg;
446         struct iovec iov[1];
447         ssize_t len;
448
449         msg.msg_control = NULL;
450         msg.msg_controllen = 0;
451
452         if (pi->buf == NULL) {
453                 /* no buffer yet - allocate one */
454                 if ((pi->buf = buf_alloc(0)) == NULL) {
455                         /* ups - could not get buffer. Return an error
456                          * the caller must close the transport. */
457                         return (-1);
458                 }
459                 pi->buflen = buf_size(0);
460                 pi->consumed = 0;
461                 pi->length = 0;
462         }
463
464         /* try to get a message */
465         msg.msg_name = pi->peer;
466         msg.msg_namelen = pi->peerlen;
467         msg.msg_iov = iov;
468         msg.msg_iovlen = 1;
469         msg.msg_control = NULL;
470         msg.msg_controllen = 0;
471         msg.msg_flags = 0;
472
473         iov[0].iov_base = pi->buf + pi->length;
474         iov[0].iov_len = pi->buflen - pi->length;
475
476         len = recvmsg(pi->fd, &msg, 0);
477
478         if (len == -1 || len == 0)
479                 /* receive error */
480                 return (-1);
481
482         pi->length += len;
483
484         if (pi->cred)
485                 check_priv_stream(pi);
486
487         return (0);
488 }
489
490 /*
491  * Dependency to create a lsock port
492  */
493 struct lsock_dep {
494         struct snmp_dependency dep;
495
496         /* index (path name) */
497         u_char *path;
498         size_t pathlen;
499
500         /* the port */
501         struct lsock_port *port;
502
503         /* which of the fields are set */
504         u_int set;
505
506         /* type of the port */
507         int type;
508
509         /* status */
510         int status;
511 };
512 #define LD_TYPE         0x01
513 #define LD_STATUS       0x02
514 #define LD_CREATE       0x04    /* rollback create */
515 #define LD_DELETE       0x08    /* rollback delete */
516
517 /*
518  * dependency handler for lsock ports
519  */
520 static int
521 lsock_func(struct snmp_context *ctx, struct snmp_dependency *dep,
522     enum snmp_depop op)
523 {
524         struct lsock_dep *ld = (struct lsock_dep *)(void *)dep;
525         int err = SNMP_ERR_NOERROR;
526
527         switch (op) {
528
529           case SNMP_DEPOP_COMMIT:
530                 if (!(ld->set & LD_STATUS))
531                         err = SNMP_ERR_BADVALUE;
532                 else if (ld->port == NULL) {
533                         if (!ld->status)
534                                 err = SNMP_ERR_BADVALUE;
535
536                         else {
537                                 /* create */
538                                 err = lsock_open_port(ld->path, ld->pathlen,
539                                     &ld->port, ld->type);
540                                 if (err == SNMP_ERR_NOERROR)
541                                         ld->set |= LD_CREATE;
542                         }
543                 } else if (!ld->status) {
544                         /* delete - hard to roll back so defer to finalizer */
545                         ld->set |= LD_DELETE;
546                 } else
547                         /* modify - read-only */
548                         err = SNMP_ERR_READONLY;
549
550                 return (err);
551
552           case SNMP_DEPOP_ROLLBACK:
553                 if (ld->set & LD_CREATE) {
554                         /* was create */
555                         lsock_close_port(&ld->port->tport);
556                 }
557                 return (SNMP_ERR_NOERROR);
558
559           case SNMP_DEPOP_FINISH:
560                 if ((ld->set & LD_DELETE) && ctx->code == SNMP_RET_OK)
561                         lsock_close_port(&ld->port->tport);
562                 free(ld->path);
563                 return (SNMP_ERR_NOERROR);
564         }
565         abort();
566 }
567
568 /*
569  * Local port table
570  */
571 int
572 op_lsock_port(struct snmp_context *ctx, struct snmp_value *value,
573     u_int sub, u_int iidx, enum snmp_op op)
574 {
575         asn_subid_t which = value->var.subs[sub-1];
576         struct lsock_port *p;
577         u_char *name;
578         size_t namelen;
579         struct lsock_dep *ld;
580         struct asn_oid didx;
581
582         switch (op) {
583
584           case SNMP_OP_GETNEXT:
585                 if ((p = (struct lsock_port *)trans_next_port(my_trans,
586                     &value->var, sub)) == NULL)
587                         return (SNMP_ERR_NOSUCHNAME);
588                 index_append(&value->var, sub, &p->tport.index);
589                 break;
590
591           case SNMP_OP_GET:
592                 if ((p = (struct lsock_port *)trans_find_port(my_trans,
593                     &value->var, sub)) == NULL)
594                         return (SNMP_ERR_NOSUCHNAME);
595                 break;
596
597           case SNMP_OP_SET:
598                 p = (struct lsock_port *)trans_find_port(my_trans,
599                     &value->var, sub);
600
601                 if (index_decode(&value->var, sub, iidx, &name, &namelen))
602                         return (SNMP_ERR_NO_CREATION);
603
604                 asn_slice_oid(&didx, &value->var, sub, value->var.len);
605                 if ((ld = (struct lsock_dep *)(void *)snmp_dep_lookup(ctx,
606                     &oid_begemotSnmpdLocalPortTable, &didx, sizeof(*ld),
607                     lsock_func)) == NULL) {
608                         free(name);
609                         return (SNMP_ERR_GENERR);
610                 }
611
612                 if (ld->path == NULL) {
613                         ld->path = name;
614                         ld->pathlen = namelen;
615                 } else {
616                         free(name);
617                 }
618                 ld->port = p;
619
620                 switch (which) {
621
622                   case LEAF_begemotSnmpdLocalPortStatus:
623                         if (ld->set & LD_STATUS)
624                                 return (SNMP_ERR_INCONS_VALUE);
625                         if (!TRUTH_OK(value->v.integer))
626                                 return (SNMP_ERR_WRONG_VALUE);
627
628                         ld->status = TRUTH_GET(value->v.integer);
629                         ld->set |= LD_STATUS;
630                         break;
631
632                   case LEAF_begemotSnmpdLocalPortType:
633                         if (ld->set & LD_TYPE)
634                                 return (SNMP_ERR_INCONS_VALUE);
635                         if (value->v.integer < 1 || value->v.integer > 4)
636                                 return (SNMP_ERR_WRONG_VALUE);
637
638                         ld->type = value->v.integer;
639                         ld->set |= LD_TYPE;
640                         break;
641                 }
642                 return (SNMP_ERR_NOERROR);
643
644           case SNMP_OP_ROLLBACK:
645           case SNMP_OP_COMMIT:
646                 return (SNMP_ERR_NOERROR);
647
648           default:
649                 abort();
650         }
651
652         /*
653          * Come here to fetch the value
654          */
655         switch (which) {
656
657           case LEAF_begemotSnmpdLocalPortStatus:
658                 value->v.integer = 1;
659                 break;
660
661           case LEAF_begemotSnmpdLocalPortType:
662                 value->v.integer = p->type;
663                 break;
664
665           default:
666                 abort();
667         }
668
669         return (SNMP_ERR_NOERROR);
670 }