diff options
author | SUZUKI, Shinsuke <suz@kame.net> | 2005-12-01 06:35:48 +0000 |
---|---|---|
committer | SUZUKI, Shinsuke <suz@kame.net> | 2005-12-01 06:35:48 +0000 |
commit | 48c68dfb9b77069425980bd155e331e64dc3c64e (patch) | |
tree | 0a824722c4c612d5390716f01242faa520bb7af1 /dhcp6c_script.c |
imported KAME-DHCPv6 snapshot at 20051201KAME_20051201
Diffstat (limited to 'dhcp6c_script.c')
-rw-r--r-- | dhcp6c_script.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/dhcp6c_script.c b/dhcp6c_script.c new file mode 100644 index 0000000..28179eb --- /dev/null +++ b/dhcp6c_script.c @@ -0,0 +1,324 @@ +/* $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 sipserver_str[] = "new_sip_name_servers"; +static char sipname_str[] = "new_sip_name"; +static char dnsserver_str[] = "new_domain_name_servers"; +static char dnsname_str[] = "new_domain_name"; +static char ntpserver_str[] = "new_ntp_servers"; + +static int safefile __P((const char *)); + +int +client6_script(scriptpath, state, optinfo) + char *scriptpath; + int state; + struct dhcp6_optinfo *optinfo; +{ + int i, dnsservers, ntpservers, dnsnamelen, envc, elen, ret = 0; + int sipservers, sipnamelen; + char **envp, *s; + char reason[] = "REASON=NBI"; + struct dhcp6_listval *v; + pid_t pid, wpid; + + /* if a script is not specified, do nothing */ + if (scriptpath == NULL || strlen(scriptpath) == 0) + return -1; + + /* initialize counters */ + dnsservers = 0; + ntpservers = 0; + dnsnamelen = 0; + sipservers = 0; + sipnamelen = 0; + envc = 2; /* we at least include the reason and the terminator */ + + /* count the number of variables */ + for (v = TAILQ_FIRST(&optinfo->dns_list); v; v = TAILQ_NEXT(v, link)) + dnsservers++; + envc += dnsservers ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->dnsname_list); v; + v = TAILQ_NEXT(v, link)) { + dnsnamelen += v->val_vbuf.dv_len; + } + envc += dnsnamelen ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->ntp_list); v; v = TAILQ_NEXT(v, link)) + ntpservers++; + envc += ntpservers ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->sip_list); v; v = TAILQ_NEXT(v, link)) + sipservers++; + envc += sipservers ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->sipname_list); v; + v = TAILQ_NEXT(v, link)) { + sipnamelen += v->val_vbuf.dv_len; + } + envc += sipnamelen ? 1 : 0; + + /* allocate an environments array */ + if ((envp = malloc(sizeof (char *) * envc)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate environment buffer"); + return -1; + } + memset(envp, 0, sizeof (char *) * envc); + + /* + * Copy the parameters as environment variables + */ + i = 0; + /* reason */ + if ((envp[i++] = strdup(reason)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate reason strings"); + ret = -1; + goto clean; + } + /* "var=addr1 addr2 ... addrN" + null char for termination */ + if (dnsservers) { + elen = sizeof (dnsserver_str) + + (INET6_ADDRSTRLEN + 1) * dnsservers + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for DNS servers"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", dnsserver_str); + for (v = TAILQ_FIRST(&optinfo->dns_list); v; + v = TAILQ_NEXT(v, link)) { + char *addr; + + addr = in6addr2str(&v->val_addr6, 0); + strlcat(s, addr, elen); + strlcat(s, " ", elen); + } + } + if (ntpservers) { + elen = sizeof (ntpserver_str) + + (INET6_ADDRSTRLEN + 1) * ntpservers + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for NTP servers"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", ntpserver_str); + for (v = TAILQ_FIRST(&optinfo->ntp_list); v; + v = TAILQ_NEXT(v, link)) { + char *addr; + + addr = in6addr2str(&v->val_addr6, 0); + strlcat(s, addr, elen); + strlcat(s, " ", elen); + } + } + + if (dnsnamelen) { + elen = sizeof (dnsname_str) + dnsnamelen + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for DNS name"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", dnsname_str); + for (v = TAILQ_FIRST(&optinfo->dnsname_list); v; + v = TAILQ_NEXT(v, link)) { + strlcat(s, v->val_vbuf.dv_buf, elen); + strlcat(s, " ", elen); + } + } + + if (sipservers) { + elen = sizeof (sipserver_str) + + (INET6_ADDRSTRLEN + 1) * sipservers + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for SIP servers"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", sipserver_str); + for (v = TAILQ_FIRST(&optinfo->sip_list); v; + v = TAILQ_NEXT(v, link)) { + char *addr; + + addr = in6addr2str(&v->val_addr6, 0); + strlcat(s, addr, elen); + strlcat(s, " ", elen); + } + } + if (sipnamelen) { + elen = sizeof (sipname_str) + sipnamelen + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for SIP server domain name"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", sipname_str); + for (v = TAILQ_FIRST(&optinfo->sipname_list); v; + v = TAILQ_NEXT(v, link)) { + strlcat(s, v->val_vbuf.dv_buf, elen); + strlcat(s, " ", elen); + } + } + /* 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); + + 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); +} |