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