monitor.c revision 1.1
1/* $OpenBSD: monitor.c,v 1.1 2004/11/28 18:49:30 henning Exp $ */ 2 3/* 4 * Copyright (c) 2004 Moritz Jodeit <moritz@jodeit.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/types.h> 20#include <sys/socket.h> 21#include <sys/wait.h> 22#include <netinet/in.h> 23 24#include <errno.h> 25#include <fcntl.h> 26#include <paths.h> 27#include <pwd.h> 28#include <signal.h> 29#include <stdarg.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <syslog.h> 34#include <unistd.h> 35 36#include "extern.h" 37#include "monitor.h" 38 39enum monitor_command { 40 CMD_USER, 41 CMD_PASS, 42 CMD_BIND 43}; 44 45enum monitor_state { 46 PREAUTH, 47 POSTAUTH 48}; 49 50#ifdef HASSETPROCTITLE 51extern char remotehost[]; 52#endif 53extern int debug; 54 55extern void set_slave_signals(void); 56 57int fd_monitor = -1; 58int fd_slave = -1; 59int nullfd; 60pid_t slave_pid; 61enum monitor_state state = PREAUTH; 62volatile sig_atomic_t quit = 0; 63 64void send_data(int, void *, size_t); 65void recv_data(int, void *, size_t); 66int recv_cmd(int, void *, size_t); 67int handle_cmds(void); 68void set_monitor_signals(void); 69void sig_pass_to_slave(int); 70void sig_chld(int); 71void fatalx(char *, ...); 72void debugmsg(char *, ...); 73 74/* 75 * Send data over a socket and exit if something fails. 76 */ 77void 78send_data(int sock, void *buf, size_t len) 79{ 80 ssize_t n; 81 size_t pos; 82 char *ptr = buf; 83 84 for (pos = 0; len > pos; pos += n) { 85 n = write(sock, ptr + pos, len - pos); 86 87 if (n == -1 && !(errno == EINTR || errno == EAGAIN)) 88 fatalx("send_data: %m"); 89 90 if (n == 0) 91 fatalx("send_data: connection closed"); 92 } 93} 94 95/* 96 * Receive data from socket and exit if something fails. 97 */ 98void 99recv_data(int sock, void *buf, size_t len) 100{ 101 ssize_t n; 102 size_t pos; 103 char *ptr = buf; 104 105 for (pos = 0; len > pos; pos += n) { 106 n = read(sock, ptr + pos, len - pos); 107 108 if (n == -1 && !(errno == EINTR || errno == EAGAIN)) 109 fatalx("recv_data: %m"); 110 111 if (n == 0) 112 fatalx("recv_data: connection closed"); 113 } 114} 115 116/* 117 * Receive command from socket and return 1 if something fails. 118 * If command was received successfuly, 0 is returned. 119 */ 120int 121recv_cmd(int sock, void *buf, size_t len) 122{ 123 ssize_t n; 124 125 n = read(sock, buf, len); 126 if (n <= 0) 127 return (1); 128 129 return (0); 130} 131 132void 133set_monitor_signals(void) 134{ 135 struct sigaction act; 136 int i; 137 138 sigemptyset(&act.sa_mask); 139 act.sa_flags = 0; 140 141 act.sa_handler = SIG_DFL; 142 for (i = 1; i < _NSIG; i++) 143 sigaction(i, &act, NULL); 144 145 act.sa_handler = sig_chld; 146 sigaction(SIGCHLD, &act, NULL); 147 148 act.sa_handler = sig_pass_to_slave; 149 sigaction(SIGHUP, &act, NULL); 150 sigaction(SIGINT, &act, NULL); 151 sigaction(SIGQUIT, &act, NULL); 152 sigaction(SIGTERM, &act, NULL); 153} 154 155/* 156 * Creates the privileged monitor process. It returns twice. 157 * It returns 1 for the unprivileged slave process and 0 for the 158 * user-privileged slave process after successful authentication. 159 */ 160int 161monitor_init(void) 162{ 163 struct passwd *pw; 164 int pair[2]; 165 166 if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, pair) == -1) 167 fatalx("socketpair failed"); 168 169 fd_monitor = pair[0]; 170 fd_slave = pair[1]; 171 172 set_monitor_signals(); 173 174 slave_pid = fork(); 175 if (slave_pid == -1) 176 fatalx("fork of unprivileged slave failed"); 177 if (slave_pid == 0) { 178 /* Unprivileged slave */ 179 set_slave_signals(); 180 181 if ((pw = getpwnam(FTPD_PRIVSEP_USER)) == NULL) 182 fatalx("privilege separation user %s not found", 183 FTPD_PRIVSEP_USER); 184 endpwent(); 185 186 if (chroot(pw->pw_dir) == -1) 187 fatalx("chroot %s: %m", pw->pw_dir); 188 if (chdir("/") == -1) 189 fatalx("chdir /: %m"); 190 191 if (setgroups(1, &pw->pw_gid) == -1) 192 fatalx("setgroups: %m"); 193 if (setegid(pw->pw_gid) == -1) 194 fatalx("setegid failed"); 195 if (setgid(pw->pw_gid) == -1) 196 fatalx("setgid failed"); 197 if (seteuid(pw->pw_uid) == -1) 198 fatalx("seteuid failed"); 199 if (setuid(pw->pw_uid) == -1) 200 fatalx("setuid failed"); 201 close(fd_slave); 202 return (1); 203 } 204 205#ifdef HASSETPROCTITLE 206 setproctitle("%s: [priv pre-auth]", remotehost); 207#endif 208 209 if (handle_cmds() == 1) { 210 debugmsg("slave lost. monitor quits now."); 211 _exit(0); 212 } 213 214 /* User-privileged slave */ 215 return (0); 216} 217 218/* 219 * Creates the user-privileged slave process. It is called 220 * from the privileged monitor process and returns twice. It returns 0 221 * for the user-privileged slave process and 1 for the monitor process. 222 */ 223int 224monitor_post_auth() 225{ 226 slave_pid = fork(); 227 if (slave_pid == -1) 228 fatalx("fork of user-privileged slave failed"); 229 if (slave_pid == 0) { 230 /* User privileged slave */ 231 close(fd_slave); 232 set_slave_signals(); 233 return (0); 234 } 235 236 /* We have to keep stdout open, because reply() needs it. */ 237 if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) 238 fatalx("cannot open %s: %m", _PATH_DEVNULL); 239 dup2(nullfd, STDIN_FILENO); 240 dup2(nullfd, STDERR_FILENO); 241 close(nullfd); 242 close(fd_monitor); 243 244 return (1); 245} 246 247/* 248 * Handles commands received from the slave process. It returns twice. 249 * It returns 0 for the user-privileged slave process after successful 250 * authentication and 1 if the user-privileged slave died. 251 */ 252int 253handle_cmds(void) 254{ 255 enum monitor_command cmd; 256 enum auth_ret auth; 257 int err, s, slavequit, serrno; 258 size_t len; 259 struct sockaddr sa; 260 socklen_t salen; 261 char *name, *pw; 262 263 while (quit == 0) { 264 if (recv_cmd(fd_slave, &cmd, sizeof(cmd)) != 0) { 265 if (quit == 1) 266 break; 267 else 268 continue; 269 } 270 271 switch (cmd) { 272 case CMD_USER: 273 debugmsg("CMD_USER received"); 274 275 recv_data(fd_slave, &len, sizeof(len)); 276 if ((name = malloc(len + 1)) == NULL) 277 fatalx("malloc: %m"); 278 if (len > 0) 279 recv_data(fd_slave, name, len); 280 name[len] = '\0'; 281 282 user(name); 283 free(name); 284 break; 285 case CMD_PASS: 286 debugmsg("CMD_PASS received"); 287 288 recv_data(fd_slave, &len, sizeof(len)); 289 if ((pw = malloc(len + 1)) == NULL) 290 fatalx("malloc: %m"); 291 if (len > 0) 292 recv_data(fd_slave, pw, len); 293 pw[len] = '\0'; 294 295 auth = pass(pw); 296 bzero(pw, len); 297 free(pw); 298 299 switch (auth) { 300 case AUTH_FAILED: 301 /* Authentication failure */ 302 debugmsg("authentication failed"); 303 slavequit = 0; 304 send_data(fd_slave, &slavequit, 305 sizeof(slavequit)); 306 break; 307 case AUTH_SLAVE: 308 /* User-privileged slave */ 309 debugmsg("user-privileged slave started"); 310 return (0); 311 /* NOTREACHED */ 312 break; 313 case AUTH_MONITOR: 314 /* Post-auth monitor */ 315 debugmsg("monitor went into post-auth phase"); 316 state = POSTAUTH; 317#ifdef HASSETPROCTITLE 318 setproctitle("%s: [priv post-auth]", 319 remotehost); 320#endif 321 slavequit = 1; 322 323 send_data(fd_slave, &slavequit, 324 sizeof(slavequit)); 325 break; 326 default: 327 fatalx("bad return value from pass()"); 328 /* NOTREACHED */ 329 break; 330 } 331 break; 332 case CMD_BIND: 333 debugmsg("CMD_BIND received"); 334 335 if (state != POSTAUTH) 336 fatalx("CMD_BIND received in invalid state"); 337 338 s = recv_fd(fd_slave); 339 340 recv_data(fd_slave, &salen, sizeof(salen)); 341 if (salen == 0 || salen > sizeof(sa)) 342 fatalx("monitor received invalid sockaddr len"); 343 344 bzero(&sa, sizeof(sa)); 345 recv_data(fd_slave, &sa, salen); 346 347 if (sa.sa_len != salen) 348 fatalx("monitor received invalid sockaddr len"); 349 350 if (sa.sa_family != AF_INET && sa.sa_family != AF_INET6) 351 fatalx("monitor received invalid addr family"); 352 353 err = bind(s, &sa, salen); 354 serrno = errno; 355 356 if (s >= 0) 357 close(s); 358 359 send_data(fd_slave, &err, sizeof(err)); 360 if (err == -1) 361 send_data(fd_slave, &serrno, sizeof(serrno)); 362 break; 363 default: 364 fatalx("monitor received unknown command %d", cmd); 365 /* NOTREACHED */ 366 break; 367 } 368 } 369 370 return (1); 371} 372 373void 374sig_pass_to_slave(int signo) 375{ 376 int olderrno = errno; 377 378 if (slave_pid != 0) 379 kill(slave_pid, signo); 380 381 errno = olderrno; 382} 383 384/* ARGSUSED */ 385void 386sig_chld(int signo) 387{ 388 pid_t pid; 389 int stat, olderrno = errno; 390 391 do { 392 pid = waitpid(-1, &stat, WNOHANG); 393 } while (pid == -1 && errno == EINTR); 394 395 if (pid == slave_pid && stat != PREAUTH_SLAVE_DIED) 396 quit = 1; 397 398 errno = olderrno; 399} 400 401void 402kill_slave(void) 403{ 404 if (slave_pid != 0) 405 kill(slave_pid, SIGQUIT); 406} 407 408void 409fatalx(char *fmt, ...) 410{ 411 va_list ap; 412 413 va_start(ap, fmt); 414 vsyslog(LOG_ERR, fmt, ap); 415 va_end(ap); 416 417 kill_slave(); 418 419 _exit(0); 420} 421 422void 423debugmsg(char *fmt, ...) 424{ 425 va_list ap; 426 427 if (debug) { 428 va_start(ap, fmt); 429 vsyslog(LOG_DEBUG, fmt, ap); 430 va_end(ap); 431 } 432} 433 434void 435monitor_user(char *name) 436{ 437 enum monitor_command cmd; 438 size_t len; 439 440 cmd = CMD_USER; 441 send_data(fd_monitor, &cmd, sizeof(cmd)); 442 443 len = strlen(name); 444 send_data(fd_monitor, &len, sizeof(len)); 445 if (len > 0) 446 send_data(fd_monitor, name, len); 447} 448 449int 450monitor_pass(char *pass) 451{ 452 enum monitor_command cmd; 453 int quitnow; 454 size_t len; 455 456 cmd = CMD_PASS; 457 send_data(fd_monitor, &cmd, sizeof(cmd)); 458 459 len = strlen(pass); 460 send_data(fd_monitor, &len, sizeof(len)); 461 if (len > 0) 462 send_data(fd_monitor, pass, len); 463 464 recv_data(fd_monitor, &quitnow, sizeof(quitnow)); 465 466 return (quitnow); 467} 468 469int 470monitor_bind(int s, struct sockaddr *name, socklen_t namelen) 471{ 472 enum monitor_command cmd; 473 int ret, serrno; 474 475 cmd = CMD_BIND; 476 send_data(fd_monitor, &cmd, sizeof(cmd)); 477 478 send_fd(fd_monitor, s); 479 send_data(fd_monitor, &namelen, sizeof(namelen)); 480 send_data(fd_monitor, name, namelen); 481 482 recv_data(fd_monitor, &ret, sizeof(ret)); 483 if (ret == -1) { 484 recv_data(fd_monitor, &serrno, sizeof(serrno)); 485 errno = serrno; 486 } 487 488 return (ret); 489} 490