1 /* $OpenBSD: check_script.c,v 1.16 2014/06/25 11:05:15 reyk Exp $ */ 2 3 /* 4 * Copyright (c) 2007 - 2014 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 if ((strlcpy(scr.name, host->conf.name,sizeof(scr.name)) >= 58 sizeof(scr.name)) || 59 (strlcpy(scr.path, table->conf.path, sizeof(scr.path)) >= 60 sizeof(scr.path))) 61 fatalx("invalid script path"); 62 memcpy(&scr.timeout, &table->conf.timeout, sizeof(scr.timeout)); 63 64 proc_compose_imsg(env->sc_ps, PROC_PARENT, 0, IMSG_SCRIPT, 65 -1, &scr, sizeof(scr)); 66 } 67 68 void 69 script_done(struct relayd *env, struct ctl_script *scr) 70 { 71 struct host *host; 72 73 if ((host = host_find(env, scr->host)) == NULL) 74 fatalx("hce_dispatch_parent: invalid host id"); 75 76 if (scr->retval < 0) 77 host->up = HOST_UNKNOWN; 78 else if (scr->retval == 0) 79 host->up = HOST_DOWN; 80 else 81 host->up = HOST_UP; 82 host->flags |= F_CHECK_DONE; 83 84 hce_notify_done(host, host->up == HOST_UP ? 85 HCE_SCRIPT_OK : HCE_SCRIPT_FAIL); 86 } 87 88 void 89 script_sig_alarm(int sig) 90 { 91 int save_errno = errno; 92 93 if (child != -1) 94 kill(child, SIGKILL); 95 errno = save_errno; 96 } 97 98 int 99 script_exec(struct relayd *env, struct ctl_script *scr) 100 { 101 int status = 0, ret = 0; 102 sig_t save_quit, save_int, save_chld; 103 struct itimerval it; 104 struct timeval *tv; 105 const char *file, *arg; 106 struct passwd *pw; 107 108 if ((env->sc_flags & F_SCRIPT) == 0) { 109 log_warnx("%s: script disabled", __func__); 110 return (-1); 111 } 112 113 DPRINTF("%s: running script %s, host %s", 114 __func__, scr->path, scr->name); 115 116 arg = scr->name; 117 file = scr->path; 118 tv = &scr->timeout; 119 120 save_quit = signal(SIGQUIT, SIG_IGN); 121 save_int = signal(SIGINT, SIG_IGN); 122 save_chld = signal(SIGCHLD, SIG_DFL); 123 124 switch (child = fork()) { 125 case -1: 126 ret = -1; 127 goto done; 128 case 0: 129 signal(SIGQUIT, SIG_DFL); 130 signal(SIGINT, SIG_DFL); 131 signal(SIGCHLD, SIG_DFL); 132 133 if ((pw = getpwnam(RELAYD_USER)) == NULL) 134 fatal("script_exec: getpwnam"); 135 if (chdir("/") == -1) 136 fatal("script_exec: chdir(\"/\")"); 137 if (setgroups(1, &pw->pw_gid) || 138 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 139 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 140 fatal("script_exec: can't drop privileges"); 141 142 /* 143 * close fds before executing an external program, to 144 * prevent access to internal fds, eg. IMSG connections 145 * of internal processes. 146 */ 147 closefrom(STDERR_FILENO + 1); 148 149 execlp(file, file, arg, (char *)NULL); 150 _exit(0); 151 break; 152 default: 153 /* Kill the process after a timeout */ 154 signal(SIGALRM, script_sig_alarm); 155 bzero(&it, sizeof(it)); 156 bcopy(tv, &it.it_value, sizeof(it.it_value)); 157 setitimer(ITIMER_REAL, &it, NULL); 158 159 waitpid(child, &status, 0); 160 break; 161 } 162 163 switch (ret) { 164 case -1: 165 ret = -1; 166 break; 167 default: 168 if (WIFEXITED(status)) 169 ret = WEXITSTATUS(status); 170 else 171 ret = 0; 172 } 173 174 done: 175 /* Disable the process timeout timer */ 176 bzero(&it, sizeof(it)); 177 setitimer(ITIMER_REAL, &it, NULL); 178 child = -1; 179 180 signal(SIGQUIT, save_quit); 181 signal(SIGINT, save_int); 182 signal(SIGCHLD, save_chld); 183 signal(SIGALRM, SIG_DFL); 184 185 return (ret); 186 } 187