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