diff options
author | SUZUKI, Shinsuke <suz@kame.net> | 2007-02-27 14:47:11 +0000 |
---|---|---|
committer | Bjørn Mork <bjorn@mork.no> | 2010-08-06 15:37:36 +0200 |
commit | fc15adcff189d5fb96a9802307f8b82866e12ac7 (patch) | |
tree | 9795f2174138334f799ac987c2545791f98589c2 | |
parent | fe2c74572f875332b461ba2a17a594a7ee4eb2d3 (diff) |
supported script execution for dhcp6relay (contributed by Bruno STEVANT)
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | Makefile.in | 4 | ||||
-rw-r--r-- | common.c | 40 | ||||
-rw-r--r-- | common.h | 1 | ||||
-rw-r--r-- | dhcp6c_script.c | 41 | ||||
-rw-r--r-- | dhcp6relay.8 | 7 | ||||
-rw-r--r-- | dhcp6relay.c | 20 | ||||
-rw-r--r-- | dhcp6relay_script.c | 304 |
8 files changed, 376 insertions, 45 deletions
@@ -1,4 +1,8 @@ 2007-02-27 SUZUKI, Shinsuke <suz@kame.net> + * dhcp6relay.c, dhcp6relay_script.c, common.[ch], dhcp6c_script.c, + Makefile.in, dhcp6relay.8: + supported script execution for dhcp6relay (contributed by Bruno STEVANT) + * Makefile.in, configure.in, configure, auth.h, common.c, control.h, dhcp6.h, dhcp6s.c, missing/{arc4random.h, daemon.c, err.h, getifaddrs.c, ifaddrs.h, sys/queue.h, warnx.c} diff --git a/Makefile.in b/Makefile.in index 3e6f606..558520d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,7 +25,7 @@ # SUCH DAMAGE. # -# $Id: Makefile.in,v 1.15 2007-02-27 14:21:40 suzsuz Exp $ +# $Id: Makefile.in,v 1.16 2007-02-27 14:47:11 suzsuz Exp $ # $KAME: Makefile.in,v 1.45 2005/10/16 16:25:38 suz Exp $ # @@ -59,7 +59,7 @@ CLIENTOBJS= dhcp6c.o common.o config.o prefixconf.o dhcp6c_ia.o timer.o \ $(GENSRCS:%.c=%.o) SERVOBJS= dhcp6s.o common.o if.o config.o timer.o lease.o \ base64.o auth.o dhcp6_ctl.o $(GENSRCS:%.c=%.o) -RELAYOBJS = dhcp6relay.o common.o timer.o +RELAYOBJS = dhcp6relay.o dhcp6relay_script.o common.o timer.o CTLOBJS= dhcp6_ctlclient.o base64.o auth.o CLEANFILES+= y.tab.h @@ -32,6 +32,7 @@ #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/queue.h> +#include <sys/stat.h> #if TIME_WITH_SYS_TIME # include <sys/time.h> # include <time.h> @@ -3339,3 +3340,42 @@ ifaddrconf(cmd, ifname, addr, plen, pltime, vltime) close(s); return (0); } + +int +safefile(path) + const char *path; +{ + struct stat s; + uid_t myuid; + + /* no setuid */ + if (getuid() != geteuid()) { + dprintf(LOG_NOTICE, FNAME, + "setuid'ed execution not allowed"); + return (-1); + } + + if (lstat(path, &s) != 0) { + dprintf(LOG_NOTICE, FNAME, "lstat failed: %s", + strerror(errno)); + return (-1); + } + + /* the file must be owned by the running uid */ + myuid = getuid(); + if (s.st_uid != myuid) { + dprintf(LOG_NOTICE, FNAME, "%s has invalid owner uid", path); + return (-1); + } + + switch (s.st_mode & S_IFMT) { + case S_IFREG: + break; + default: + dprintf(LOG_NOTICE, FNAME, "%s is an invalid file type 0x%o", + path, (s.st_mode & S_IFMT)); + return (-1); + } + + return (0); +} @@ -178,6 +178,7 @@ extern int duidcmp __P((struct duid *, struct duid *)); extern void duidfree __P((struct duid *)); extern int ifaddrconf __P((ifaddrconf_cmd_t, char *, struct sockaddr_in6 *, int, int, int)); +extern int safefile __P((const char *)); /* missing */ #ifndef HAVE_STRLCAT diff --git a/dhcp6c_script.c b/dhcp6c_script.c index b052f18..05ccf8c 100644 --- a/dhcp6c_script.c +++ b/dhcp6c_script.c @@ -72,8 +72,6 @@ static char nispname_str[] = "new_nisp_name"; static char bcmcsserver_str[] = "new_bcmcs_servers"; static char bcmcsname_str[] = "new_bcmcs_name"; -static int safefile __P((const char *)); - int client6_script(scriptpath, state, optinfo) char *scriptpath; @@ -437,42 +435,3 @@ client6_script(scriptpath, state, optinfo) return ret; } - -static int -safefile(path) - const char *path; -{ - struct stat s; - uid_t myuid; - - /* no setuid */ - if (getuid() != geteuid()) { - dprintf(LOG_NOTICE, FNAME, - "setuid'ed execution not allowed"); - return (-1); - } - - if (lstat(path, &s) != 0) { - dprintf(LOG_NOTICE, FNAME, "lstat failed: %s", - strerror(errno)); - return (-1); - } - - /* the file must be owned by the running uid */ - myuid = getuid(); - if (s.st_uid != myuid) { - dprintf(LOG_NOTICE, FNAME, "%s has invalid owner uid", path); - return (-1); - } - - switch (s.st_mode & S_IFMT) { - case S_IFREG: - break; - default: - dprintf(LOG_NOTICE, FNAME, "%s is an invalid file type 0x%o", - path, (s.st_mode & S_IFMT)); - return (-1); - } - - return (0); -} diff --git a/dhcp6relay.8 b/dhcp6relay.8 index 5d23ed9..4f761b0 100644 --- a/dhcp6relay.8 +++ b/dhcp6relay.8 @@ -41,6 +41,7 @@ .Op Fl H Ar hoplim .Op Fl r Ar relay-IF .Op Fl s Ar serveraddr +.Op Fl S Ar script-file .Op Fl p Ar pid-file .Ar interface ... .\" @@ -87,6 +88,12 @@ are specified, this option cannot be omitted. .It Fl s Ar serveraddr Specifies the DHCPv6 server address to relay packets to. If not specified, packets are relayed to ff05::1:3 (All DHCPv6 servers). +.It Fl S Ar script-file +Specifies the script file to be executed when +.Nm +receives a RELAY-REPLY message from a DHCPv6 server. Further detail of the +script file syntax is available in +.Xr dhcp6c 8 .It Fl p Ar pid-file Use .Ar pid-file diff --git a/dhcp6relay.c b/dhcp6relay.c index 78d18df..c06db45 100644 --- a/dhcp6relay.c +++ b/dhcp6relay.c @@ -75,6 +75,7 @@ static u_long sig_flags = 0; static char *relaydevice; static char *boundaddr; static char *serveraddr = DH6ADDR_ALLSERVER; +static char *scriptpath; static char *rmsgctlbuf; static socklen_t rmsgctllen; @@ -117,13 +118,16 @@ static void relay_to_server __P((struct dhcp6 *, ssize_t, struct sockaddr_in6 *, char *, unsigned int)); static void relay_to_client __P((struct dhcp6_relay *, ssize_t, struct sockaddr *)); +extern int relay6_script __P((char *, struct sockaddr_in6 *, + struct dhcp6 *, int)); + static void usage() { fprintf(stderr, "usage: dhcp6relay [-dDf] [-b boundaddr] [-H hoplim] " - "[-r relay-IF] [-s serveraddr] [-p pidfile] IF ...\n"); + "[-r relay-IF] [-s serveraddr] [-p pidfile] [-S script] IF ...\n"); exit(0); } @@ -142,7 +146,7 @@ main(argc, argv) else progname++; - while((ch = getopt(argc, argv, "b:dDfH:r:s:p:")) != -1) { + while((ch = getopt(argc, argv, "b:dDfH:r:s:S:p:")) != -1) { switch(ch) { case 'b': boundaddr = optarg; @@ -174,6 +178,9 @@ main(argc, argv) case 's': serveraddr = optarg; break; + case 'S': + scriptpath = optarg; + break; case 'p': pid_file = optarg; break; @@ -916,6 +923,8 @@ relay_to_client(dh6relay, len, from) unsigned int ifid; char ifnamebuf[IFNAMSIZ]; int cc; + int relayed = 0; + struct dhcp6 *dh6; struct msghdr mh; struct in6_pktinfo pktinfo; static struct iovec iov[2]; @@ -997,6 +1006,10 @@ relay_to_client(dh6relay, len, from) } peer = sa6_client; + dh6 = (struct dhcp6 *) optinfo.relaymsg_msg; + if (dh6->dh6_msgtype != DH6_RELAY_REPLY) { + relayed++; + } memcpy(&peer.sin6_addr, &dh6relay->dh6relay_peeraddr, sizeof (peer.sin6_addr)); if (IN6_IS_ADDR_LINKLOCAL(&peer.sin6_addr)) @@ -1033,6 +1046,9 @@ relay_to_client(dh6relay, len, from) addr2str((struct sockaddr *)&peer)); } + if (relayed && scriptpath != NULL) + relay6_script(scriptpath, &peer, dh6, optinfo.relaymsg_len); + out: dhcp6_clear_options(&optinfo); return; diff --git a/dhcp6relay_script.c b/dhcp6relay_script.c new file mode 100644 index 0000000..82f5eab --- /dev/null +++ b/dhcp6relay_script.c @@ -0,0 +1,304 @@ +/*from $KAME: dhcp6c_script.c,v 1.11 2004/11/28 10:48:38 jinmei Exp $ */ + +/* + * Copyright (C) 2003 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#include <netinet/in.h> + +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <errno.h> + +#include "dhcp6.h" +#include "config.h" +#include "common.h" + +static char client_str[] = "client"; +static char buf[BUFSIZ]; + +static char *iapd2str __P((int, struct dhcp6_listval *)); +static char *iana2str __P((int, struct dhcp6_listval *)); + +int +relay6_script(scriptpath, client, dh6, len) + char *scriptpath; + struct sockaddr_in6 *client; + struct dhcp6 *dh6; + int len; +{ + struct dhcp6_optinfo optinfo; + struct dhcp6opt *optend; + int i, j, iapds, ianas, envc, elen, ret = 0; + char **envp, *s, *t; + struct dhcp6_listval *v; + pid_t pid, wpid; + + /* if a script is not specified, do nothing */ + if (scriptpath == NULL || strlen(scriptpath) == 0) + return -1; + + /* only replies are interesting */ + if (dh6->dh6_msgtype != DH6_REPLY) { + if (dh6->dh6_msgtype != DH6_ADVERTISE) { + dprintf(LOG_INFO, FNAME, "forward msg#%d to client?", + dh6->dh6_msgtype); + return -1; + } + return 0; + } + + /* parse options */ + optend = (struct dhcp6opt *)((caddr_t) dh6 + len); + dhcp6_init_options(&optinfo); + if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1), optend, + &optinfo) < 0) { + dprintf(LOG_INFO, FNAME, "failed to parse options"); + return -1; + } + + /* initialize counters */ + iapds = 0; + ianas = 0; + envc = 2; /* we at least include the address and the terminator */ + + /* count the number of variables */ + for (v = TAILQ_FIRST(&optinfo.iapd_list); v; v = TAILQ_NEXT(v, link)) + iapds++; + envc += iapds; + for (v = TAILQ_FIRST(&optinfo.iana_list); v; v = TAILQ_NEXT(v, link)) + ianas++; + envc += ianas; + + /* allocate an environments array */ + if ((envp = malloc(sizeof (char *) * envc)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate environment buffer"); + dhcp6_clear_options(&optinfo); + return -1; + } + memset(envp, 0, sizeof (char *) * envc); + + /* + * Copy the parameters as environment variables + */ + i = 0; + /* address */ + t = addr2str((struct sockaddr *) client); + if (t == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to get address of client"); + ret = -1; + goto clean; + } + elen = sizeof (client_str) + 1 + strlen(t) + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate string for client"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=%s", client_str, t); + /* IAs */ + j = 0; + for (v = TAILQ_FIRST(&optinfo.iapd_list); v; + v = TAILQ_NEXT(v, link)) { + if ((s = envp[i++] = iapd2str(j++, v)) == NULL) { + ret = -1; + goto clean; + } + } + j = 0; + for (v = TAILQ_FIRST(&optinfo.iana_list); v; + v = TAILQ_NEXT(v, link)) { + if ((s = envp[i++] = iana2str(j++, v)) == NULL) { + ret = -1; + goto clean; + } + } + + /* launch the script */ + pid = fork(); + if (pid < 0) { + dprintf(LOG_ERR, FNAME, "failed to fork: %s", strerror(errno)); + ret = -1; + goto clean; + } else if (pid) { + int wstatus; + + do { + wpid = wait(&wstatus); + } while (wpid != pid && wpid > 0); + + if (wpid < 0) + dprintf(LOG_ERR, FNAME, "wait: %s", strerror(errno)); + else { + dprintf(LOG_DEBUG, FNAME, + "script \"%s\" terminated", scriptpath); + } + } else { + char *argv[2]; + int fd; + + argv[0] = scriptpath; + argv[1] = NULL; + + if (safefile(scriptpath)) { + dprintf(LOG_ERR, FNAME, + "script \"%s\" cannot be executed safely", + scriptpath); + exit(1); + } + + if (foreground == 0 && + (fd = open("/dev/null", O_RDWR)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + } + + execve(scriptpath, argv, envp); + + dprintf(LOG_ERR, FNAME, "child: exec failed: %s", + strerror(errno)); + exit(0); + } + + clean: + for (i = 0; i < envc; i++) + free(envp[i]); + free(envp); + dhcp6_clear_options(&optinfo); + + return ret; +} + +static char * +iapd2str(num, iav) + int num; + struct dhcp6_listval *iav; +{ + struct dhcp6_listval *siav; + char *s, *r, *comma; + + s = buf; + memset(s, 0, BUFSIZ); + + snprintf(s, BUFSIZ, "iapd_%d=", num); + comma = ""; + + for (siav = TAILQ_FIRST(&iav->sublist); siav; + siav = TAILQ_NEXT(siav, link)) { + switch (siav->type) { + case DHCP6_LISTVAL_PREFIX6: + snprintf(s + strlen(s), BUFSIZ - strlen(s), + "%s%s/%d", comma, + in6addr2str(&siav->val_prefix6.addr, 0), + siav->val_prefix6.plen); + comma = ","; + break; + + case DHCP6_LISTVAL_STCODE: + snprintf(s + strlen(s), BUFSIZ - strlen(s), + "%s#%d", comma, siav->val_num16); + comma = ","; + break; + + default: + dprintf(LOG_ERR, FNAME, "impossible subopt"); + } + } + + if ((r = strdup(s)) == NULL) + dprintf(LOG_ERR, FNAME, "failed to allocate iapd_%d", num); + return r; +} + +static char * +iana2str(num, iav) + int num; + struct dhcp6_listval *iav; +{ + struct dhcp6_listval *siav; + char *s, *r, *comma; + + s = buf; + memset(s, 0, BUFSIZ); + + snprintf(s, BUFSIZ, "iana_%d=", num); + comma = ""; + + for (siav = TAILQ_FIRST(&iav->sublist); siav; + siav = TAILQ_NEXT(siav, link)) { + switch (siav->type) { + case DHCP6_LISTVAL_STATEFULADDR6: + snprintf(s + strlen(s), BUFSIZ - strlen(s), + "%s%s", comma, + in6addr2str(&siav->val_statefuladdr6.addr, 0)); + comma = ","; + break; + + case DHCP6_LISTVAL_STCODE: + snprintf(s + strlen(s), BUFSIZ - strlen(s), + "%s#%d", comma, siav->val_num16); + comma = ","; + break; + + default: + dprintf(LOG_ERR, FNAME, "impossible subopt"); + } + } + + if ((r = strdup(s)) == NULL) + dprintf(LOG_ERR, FNAME, "failed to allocate iana_%d", num); + return r; +} |