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