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