1 /* $OpenBSD: check_script.c,v 1.14 2011/05/26 14:48:20 reyk Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/queue.h> 21 #include <sys/socket.h> 22 #include <sys/wait.h> 23 24 #include <net/if.h> 25 26 #include <limits.h> 27 #include <event.h> 28 #include <errno.h> 29 #include <unistd.h> 30 #include <string.h> 31 #include <stdlib.h> 32 #include <signal.h> 33 #include <pwd.h> 34 #include <err.h> 35 36 #include <openssl/ssl.h> 37 38 #include "relayd.h" 39 40 void script_sig_alarm(int); 41 42 pid_t child = -1; 43 44 void 45 check_script(struct relayd *env, struct host *host) 46 { 47 struct ctl_script scr; 48 struct table *table; 49 50 if ((table = table_find(env, host->conf.tableid)) == NULL) 51 fatalx("check_script: invalid table id"); 52 53 host->last_up = host->up; 54 host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE); 55 56 scr.host = host->conf.id; 57 strlcpy(scr.name, host->conf.name, sizeof(host->conf.name)); 58 strlcpy(scr.path, table->conf.path, sizeof(table->conf.path)); 59 memcpy(&scr.timeout, &table->conf.timeout, sizeof(scr.timeout)); 60 61 proc_compose_imsg(env->sc_ps, PROC_PARENT, 0, IMSG_SCRIPT, 62 -1, &scr, sizeof(scr)); 63 } 64 65 void 66 script_done(struct relayd *env, struct ctl_script *scr) 67 { 68 struct host *host; 69 70 if ((host = host_find(env, scr->host)) == NULL) 71 fatalx("hce_dispatch_parent: invalid host id"); 72 73 if (scr->retval < 0) 74 host->up = HOST_UNKNOWN; 75 else if (scr->retval == 0) 76 host->up = HOST_DOWN; 77 else 78 host->up = HOST_UP; 79 host->flags |= F_CHECK_DONE; 80 81 hce_notify_done(host, host->up == HOST_UP ? 82 HCE_SCRIPT_OK : HCE_SCRIPT_FAIL); 83 } 84 85 void 86 script_sig_alarm(int sig) 87 { 88 int save_errno = errno; 89 90 if (child != -1) 91 kill(child, SIGKILL); 92 errno = save_errno; 93 } 94 95 int 96 script_exec(struct relayd *env, struct ctl_script *scr) 97 { 98 int status = 0, ret = 0; 99 sig_t save_quit, save_int, save_chld; 100 struct itimerval it; 101 struct timeval *tv; 102 const char *file, *arg; 103 struct passwd *pw; 104 105 if ((env->sc_flags & F_SCRIPT) == 0) { 106 log_warnx("%s: script disabled", __func__); 107 return (-1); 108 } 109 110 DPRINTF("%s: running script %s, host %s", 111 __func__, scr->path, scr->name); 112 113 arg = scr->name; 114 file = scr->path; 115 tv = &scr->timeout; 116 117 save_quit = signal(SIGQUIT, SIG_IGN); 118 save_int = signal(SIGINT, SIG_IGN); 119 save_chld = signal(SIGCHLD, SIG_DFL); 120 121 switch (child = fork()) { 122 case -1: 123 ret = -1; 124 goto done; 125 case 0: 126 signal(SIGQUIT, SIG_DFL); 127 signal(SIGINT, SIG_DFL); 128 signal(SIGCHLD, SIG_DFL); 129 130 if ((pw = getpwnam(RELAYD_USER)) == NULL) 131 fatal("script_exec: getpwnam"); 132 if (chdir("/") == -1) 133 fatal("script_exec: chdir(\"/\")"); 134 if (setgroups(1, &pw->pw_gid) || 135 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 136 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 137 fatal("script_exec: can't drop privileges"); 138 139 /* 140 * close fds before executing an external program, to 141 * prevent access to internal fds, eg. IMSG connections 142 * of internal processes. 143 */ 144 closefrom(STDERR_FILENO + 1); 145 146 execlp(file, file, arg, (char *)NULL); 147 _exit(0); 148 break; 149 default: 150 /* Kill the process after a timeout */ 151 signal(SIGALRM, script_sig_alarm); 152 bzero(&it, sizeof(it)); 153 bcopy(tv, &it.it_value, sizeof(it.it_value)); 154 setitimer(ITIMER_REAL, &it, NULL); 155 156 waitpid(child, &status, 0); 157 break; 158 } 159 160 switch (ret) { 161 case -1: 162 ret = -1; 163 break; 164 default: 165 if (WIFEXITED(status)) 166 ret = WEXITSTATUS(status); 167 else 168 ret = 0; 169 } 170 171 done: 172 /* Disable the process timeout timer */ 173 bzero(&it, sizeof(it)); 174 setitimer(ITIMER_REAL, &it, NULL); 175 child = -1; 176 177 signal(SIGQUIT, save_quit); 178 signal(SIGINT, save_int); 179 signal(SIGCHLD, save_chld); 180 signal(SIGALRM, SIG_DFL); 181 182 return (ret); 183 } 184