1/***************************************************************************** 2 * 3 * saslauthd-main.c 4 * 5 * Description: Main program source. 6 * 7 * Copyright (c) 1997-2000 Messaging Direct Ltd. 8 * All rights reserved. 9 * 10 * Portions Copyright (c) 2003 Jeremy Rumpf 11 * jrumpf@heavyload.net 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 24 * THIS SOFTWARE IS PROVIDED BY MESSAGING DIRECT LTD. ``AS IS'' AND ANY 25 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MESSAGING DIRECT LTD. OR 28 * ITS EMPLOYEES OR AGENTS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 31 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 32 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 34 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 35 * DAMAGE. 36 * 37 * 38 * HISTORY 39 * 40 * saslauthd is a re-implementation of the pwcheck utility included 41 * with the CMU Cyrus IMAP server circa 1997. This implementation 42 * was written by Lyndon Nerenberg of Messaging Direct Inc. (which 43 * at that time was the Esys Corporation) and was included in the 44 * company's IMAP message store product (Simeon Message Service) as 45 * the smsauthd utility. 46 * 47 * This implementation was contributed to CMU by Messaging Direct Ltd. 48 * in September 2000. 49 * 50 * September 2001 (Ken Murchison of Oceana Matrix Ltd.): 51 * - Modified the protocol to use counted length strings instead of 52 * nul delimited strings. 53 * - Augmented the protocol to accept the service name and user realm. 54 * 55 * Feb 2003: Partial rewrite and cleanup by Jeremy Rumpf jrumpf@heavyload.net 56 * - Merge the doors and unix IPC methods under a common framework. 57 * 58 * OVERVIEW 59 * 60 * saslauthd provides an interface between the SASL library and various 61 * external authentication mechanisms. The primary goal is to isolate 62 * code that requires superuser privileges (for example, access to 63 * the shadow password file) into a single easily audited module. It 64 * can also act as an authentication proxy between plaintext-equivelent 65 * authentication schemes (i.e. CRAM-MD5) and more secure authentication 66 * services such as Kerberos, although such usage is STRONGLY discouraged 67 * because it exposes the strong credentials via the insecure plaintext 68 * mechanisms. 69 * 70 * The program listens for connections on a UNIX domain socket. Access to 71 * the service is controlled by the UNIX filesystem permissions on the 72 * socket. 73 * 74 * The service speaks a very simple protocol. The client connects and 75 * sends the authentication identifier, the plaintext password, the 76 * service name and user realm as counted length strings (a 16-bit 77 * unsigned integer in network byte order followed by the string 78 * itself). The server returns a single response as a counted length 79 * string. The response begins with "OK" or "NO", and is followed by 80 * an optional text string (separated from the OK/NO by a single space 81 * character), and a NUL. The server then closes the connection. 82 * 83 * An "OK" response indicates the authentication credentials are valid. 84 * A "NO" response indicates the authentication failed. 85 * 86 * The optional text string may be used to indicate an exceptional 87 * condition in the authentication environment that should be communicated 88 * to the client. 89 * 90 *****************************************************************************/ 91 92#include <unistd.h> 93#include <stdlib.h> 94#include <stdio.h> 95#include <errno.h> 96#include <string.h> 97 98#ifdef _AIX 99# include <strings.h> 100#endif /* _AIX */ 101 102#include <syslog.h> 103#include <signal.h> 104#include <sys/file.h> 105#include <sys/types.h> 106#include <sys/stat.h> 107#include <sys/socket.h> 108#include <sys/un.h> 109#include <sys/wait.h> 110#include <fcntl.h> 111#include <sys/uio.h> 112 113#include "globals.h" 114#include "saslauthd-main.h" 115#include "cache.h" 116#include "utils.h" 117 118/* max login + max realm + '@' */ 119#define MAX_LOGIN_REALM_LEN (MAX_REQ_LEN * 2) + 1 120 121/**************************************** 122 * declarations/protos 123 *****************************************/ 124static void show_version(); 125static void show_usage(); 126 127/**************************************** 128 * application globals 129 *****************************************/ 130int flags = 0; /* Runtime flags */ 131int g_argc; /* Copy of argc for those who need it*/ 132char **g_argv; /* Copy of argv for those who need it*/ 133char *run_path = NULL; /* path to our working directory */ 134authmech_t *auth_mech = NULL; /* Authentication mechanism to use */ 135char *mech_option = NULL; /* mechanism-specific option */ 136int num_procs = 5; /* The max number of worker processes*/ 137 138 139/**************************************** 140 * module globals 141*****************************************/ 142extern char *optarg; /* For getopt() */ 143static int master_pid; /* Pid of the master process */ 144static int pid_fd; /* Descriptor to the open pid file */ 145static int pid_file_lock_fd; /* Descriptor to the open pid lock file */ 146static char *pid_file; /* Pid file name */ 147static char *pid_file_lock; /* Pid lock file name */ 148static int startup_pipe[2] = { -1, -1 }; 149 150int main(int argc, char **argv) { 151 int option; 152 int rc; 153 int x; 154 struct flock lockinfo; 155 char *auth_mech_name = NULL; 156 size_t pid_file_size; 157 158 /* XXX force openlog() before any of our mechs try syslog() */ 159 logger(L_INFO, L_FUNC, "starting %s", argv[0]); 160 161 SET_AUTH_PARAMETERS(argc, argv); 162 163 g_argc = argc; 164 g_argv = argv; 165 166 /* default flags */ 167 flags |= USE_ACCEPT_LOCK; 168 flags |= DETACH_TTY; 169 flags |= LOG_USE_SYSLOG; 170 flags |= LOG_USE_STDERR; 171 flags |= AM_MASTER; 172 173 while ((option = getopt(argc, argv, "a:cdhO:lm:n:rs:t:vV")) != -1) { 174 switch(option) { 175 case 'a': 176 /* Only one at a time, please! */ 177 if(auth_mech_name) { 178 show_usage(); 179 break; 180 } 181 182 auth_mech_name = strdup(optarg); 183 if (!auth_mech_name) { 184 logger(L_ERR, L_FUNC, 185 "could not allocate memory"); 186 exit(1); 187 } 188 break; 189 190 case 'c': 191 flags |= CACHE_ENABLED; 192 break; 193 194 case 'd': 195 flags |= VERBOSE; 196 flags &= ~DETACH_TTY; 197 break; 198 199 case 'h': 200 show_usage(); 201 break; 202 203 case 'O': 204 set_mech_option(optarg); 205 break; 206 207 case 'l': 208 flags &= ~USE_ACCEPT_LOCK; 209 break; 210 211 case 'm': 212 set_run_path(optarg); 213 break; 214 215 case 'n': 216 set_max_procs(optarg); 217 break; 218 219 case 'r': 220 flags |= CONCAT_LOGIN_REALM; 221 break; 222 223 case 's': 224 cache_set_table_size(optarg); 225 break; 226 227 case 't': 228 cache_set_timeout(optarg); 229 break; 230 231 case 'V': 232 flags |= VERBOSE; 233 break; 234 235 case 'v': 236 show_version(); 237 break; 238 239 default: 240 show_usage(); 241 break; 242 } 243 } 244 245 if (run_path == NULL) 246 run_path = PATH_SASLAUTHD_RUNDIR; 247 248 if (auth_mech_name == NULL) { 249 logger(L_ERR, L_FUNC, "no authentication mechanism specified"); 250 show_usage(); 251 exit(1); 252 } 253 254 /* Create our working directory */ 255 if (mkdir(run_path, 0755) == -1 && errno != EEXIST) { 256 logger(L_ERR, L_FUNC, "can not mkdir: %s", run_path); 257 logger(L_ERR, L_FUNC, "Check to make sure the parent directory exists and is"); 258 logger(L_ERR, L_FUNC, "writeable by the user this process runs as."); 259 exit(1); 260 } 261 262 set_auth_mech(auth_mech_name); 263 264 if (flags & VERBOSE) { 265 logger(L_DEBUG, L_FUNC, "num_procs : %d", num_procs); 266 267 if (mech_option == NULL) 268 logger(L_DEBUG, L_FUNC, "mech_option: NULL"); 269 else 270 logger(L_DEBUG, L_FUNC, "mech_option: %s", mech_option); 271 272 logger(L_DEBUG, L_FUNC, "run_path : %s", run_path); 273 logger(L_DEBUG, L_FUNC, "auth_mech : %s", auth_mech->name); 274 } 275 276 /********************************************************* 277 * Change our working directory to the dir where the 278 * run path is set to, core dumps will go there to keep 279 * them intact. 280 **********************************************************/ 281 if (chdir(run_path) == -1) { 282 rc = errno; 283 logger(L_ERR, L_FUNC, "could not chdir to: %s", run_path); 284 logger(L_ERR, L_FUNC, "chdir: %s", strerror(rc)); 285 logger(L_ERR, L_FUNC, "Check to make sure the directory exists and is"); 286 logger(L_ERR, L_FUNC, "writeable by the user this process runs as."); 287 exit(1); 288 } 289 290 umask(0077); 291 292 pid_file_size = strlen(run_path) + sizeof(PID_FILE_LOCK) + 1; 293 if ((pid_file_lock = malloc(pid_file_size)) == NULL) { 294 logger(L_ERR, L_FUNC, "could not allocate memory"); 295 exit(1); 296 } 297 298 strlcpy(pid_file_lock, run_path, pid_file_size); 299 strlcat(pid_file_lock, PID_FILE_LOCK, pid_file_size); 300 301 if ((pid_file_lock_fd = open(pid_file_lock, O_CREAT|O_TRUNC|O_RDWR, 0644)) < 0) { 302 rc = errno; 303 logger(L_ERR, L_FUNC, "could not open pid lock file: %s", pid_file_lock); 304 logger(L_ERR, L_FUNC, "open: %s", strerror(rc)); 305 logger(L_ERR, L_FUNC, 306 "Check to make sure the directory exists and is"); 307 logger(L_ERR, L_FUNC, "writeable by the user this process runs as."); 308 exit(1); 309 } 310 311 lockinfo.l_type = F_WRLCK; 312 lockinfo.l_start = 0; 313 lockinfo.l_len = 0; 314 lockinfo.l_whence = SEEK_SET; 315 316 if (fcntl(pid_file_lock_fd, F_SETLK, &lockinfo) == -1) { 317 rc = errno; 318 logger(L_ERR, L_FUNC, "could not lock pid lock file: %s", pid_file_lock); 319 logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc)); 320 exit(1); 321 } 322 323 if(pipe(startup_pipe) == -1) { 324 logger(L_ERR, L_FUNC, "can't create startup pipe"); 325 exit(1); 326 } 327 328 /********************************************************* 329 * Enable signal handlers. 330 **********************************************************/ 331 signal_setup(); 332 333 /********************************************************* 334 * Cache setup, exit if it doesn't succeed (optional would 335 * be to disable the cache and log a warning). 336 **********************************************************/ 337 if (cache_init() != 0) 338 exit(1); 339 340 /********************************************************* 341 * Call the ipc specific initializer. This should also 342 * call detach_tty() at the appropriate point. 343 **********************************************************/ 344 ipc_init(); 345 346 /********************************************************* 347 * Enable general cleanup. 348 **********************************************************/ 349 atexit(server_exit); 350 351 /********************************************************* 352 * If required, enable the process model. 353 **********************************************************/ 354 if (flags & USE_PROCESS_MODEL) { 355 if (flags & VERBOSE) 356 logger(L_DEBUG, L_FUNC, "using process model"); 357 358 for (x = 1; x < num_procs; x++) { 359 if (have_baby() != 0) 360 continue; /* parent */ 361 362 break; /* child */ 363 } 364 } 365 366 /********************************************************* 367 * Enter the ipc loop, we should never return. 368 **********************************************************/ 369 ipc_loop(); 370 371 exit(0); 372} 373 374 375/************************************************************* 376 * Performs all authentication centric duties. We should be 377 * getting callbacks from the ipc method here. We'll simply 378 * return a pointer to a string to send back to the client. 379 * The caller is responsible for freeing the pointer. 380 **************************************************************/ 381char *do_auth(const char *_login, const char *password, const char *service, const char *realm) { 382 383 struct cache_result lkup_result; 384 char *response; 385 int cached = 0; 386 char login_buff[MAX_LOGIN_REALM_LEN]; 387 char *login; 388 389 390 /*********************************************************** 391 * Check to concat the login and realm into a single login. 392 * Aka, login: foo realm: bar becomes login: foo@bar. 393 * We do this because some mechs have no concept of a realm. 394 * Ie. auth_pam and friends. 395 ***********************************************************/ 396 if ((flags & CONCAT_LOGIN_REALM) && realm && realm[0] != '\0') { 397 strlcpy(login_buff, _login, sizeof(login_buff)); 398 strlcat(login_buff, "@", sizeof(login_buff)); 399 strlcat(login_buff, realm, sizeof(login_buff)); 400 401 login = login_buff; 402 } else { 403 login = (char *)_login; 404 } 405 406 if (cache_lookup(login, realm, service, password, &lkup_result) == CACHE_OK) { 407 response = strdup("OK"); 408 cached = 1; 409 } else { 410 response = auth_mech->authenticate(login, password, service, realm); 411 412 if (response == NULL) { 413 logger(L_ERR, L_FUNC, "internal mechanism failure: %s", auth_mech->name); 414 response = strdup("NO internal mechanism failure"); 415 } 416 } 417 418 if (strncmp(response, "OK", 2) == 0) { 419 cache_commit(&lkup_result); 420 421 if (flags & VERBOSE) { 422 if (cached) 423 logger(L_DEBUG, L_FUNC, "auth success (cached): [user=%s] [service=%s] [realm=%s]", \ 424 login, service, realm); 425 else 426 logger(L_DEBUG, L_FUNC, "auth success: [user=%s] [service=%s] [realm=%s] [mech=%s]", \ 427 login, service, realm, auth_mech->name); 428 } 429 return response; 430 } 431 432 if (strncmp(response, "NO", 2) == 0) { 433 logger(L_INFO, L_FUNC, "auth failure: [user=%s] [service=%s] [realm=%s] [mech=%s] [reason=%s]", \ 434 login, service, realm, auth_mech->name, 435 strlen(response) >= 4 ? response+3 : "Unknown"); 436 437 return response; 438 } 439 440 logger(L_ERR, L_FUNC, "mechanism returned unknown response: %s", auth_mech->name); 441 response = strdup("NO internal mechanism failure"); 442 443 return response; 444} 445 446 447/************************************************************* 448 * Allow someone to set the auth mech to use 449 **************************************************************/ 450void set_auth_mech(const char *mech) { 451 for (auth_mech = mechanisms; auth_mech->name != NULL; auth_mech++) { 452 if (strcasecmp(auth_mech->name, mech) == 0) 453 break; 454 } 455 456 if (auth_mech->name == NULL) { 457 logger(L_ERR, L_FUNC, "unknown authentication mechanism: %s", mech); 458 exit(1); 459 } 460 461 if (auth_mech->initialize) { 462 if(auth_mech->initialize() != 0) { 463 logger(L_ERR, L_FUNC, "failed to initialize mechanism %s", 464 auth_mech->name); 465 exit(1); 466 } 467 } 468} 469 470 471/************************************************************* 472 * Allow someone to set the number of worker processes we 473 * will use. Only applicable to unix ipc. 474 **************************************************************/ 475void set_max_procs(const char *procs) { 476 num_procs = atoi(procs); 477 478 if(num_procs < 0) { 479 logger(L_ERR, L_FUNC, "invalid number of worker processes defined"); 480 exit(1); 481 } 482 483 return; 484} 485 486 487/************************************************************* 488 * Allow someone to set the mechanism specific option 489 **************************************************************/ 490void set_mech_option(const char *option) { 491 492 free(mech_option); 493 mech_option = NULL; 494 495 if ((mech_option = strdup(option)) == NULL) { 496 logger(L_ERR, L_FUNC, "could not allocate memory"); 497 exit(1); 498 } 499 500 return; 501} 502 503 504/************************************************************* 505 * Allow someone to set the path to our working directory 506 **************************************************************/ 507void set_run_path(const char *path) { 508 509 if (*path != '/') { 510 logger(L_ERR, L_FUNC, "-m requires an absolute pathname"); 511 exit(1); 512 } 513 514 free(run_path); 515 run_path = NULL; 516 517 if ((run_path = strdup(path)) == NULL) { 518 logger(L_ERR, L_FUNC, "could not allocate memory"); 519 exit(1); 520 } 521 522 return; 523} 524 525 526 527/************************************************************* 528 * Setup all the proper signal masks. 529 **************************************************************/ 530void signal_setup() { 531 532 static struct sigaction act_sigchld; 533 static struct sigaction act_sigalrm; 534 static struct sigaction act_sigterm; 535 static struct sigaction act_sigpipe; 536 static struct sigaction act_sighup; 537 static struct sigaction act_sigint; 538 int rc; 539 540 /************************************************************** 541 * Handler for SIGCHLD 542 **************************************************************/ 543 act_sigchld.sa_handler = handle_sigchld; 544 sigemptyset(&act_sigchld.sa_mask); 545 546 if (sigaction(SIGCHLD, &act_sigchld, NULL) != 0) { 547 rc = errno; 548 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGCHLD"); 549 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc)); 550 exit(1); 551 } 552 553 /************************************************************** 554 * Handler for SIGALRM (IGNORE) 555 **************************************************************/ 556 act_sigalrm.sa_handler = SIG_IGN; 557 sigemptyset(&act_sigalrm.sa_mask); 558 559 if (sigaction(SIGALRM, &act_sigalrm, NULL) != 0) { 560 rc = errno; 561 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGALRM"); 562 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc)); 563 exit(1); 564 } 565 566 /************************************************************** 567 * Handler for SIGPIPE (IGNORE) 568 **************************************************************/ 569 act_sigpipe.sa_handler = SIG_IGN; 570 sigemptyset(&act_sigpipe.sa_mask); 571 572 if (sigaction(SIGPIPE, &act_sigpipe, NULL) != 0) { 573 rc = errno; 574 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGPIPE"); 575 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc)); 576 exit(1); 577 } 578 579 /************************************************************** 580 * Handler for SIGHUP (IGNORE) 581 **************************************************************/ 582 act_sighup.sa_handler = SIG_IGN; 583 sigemptyset(&act_sighup.sa_mask); 584 585 if (sigaction(SIGHUP, &act_sighup, NULL) != 0) { 586 rc = errno; 587 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGHUP"); 588 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc)); 589 exit(1); 590 } 591 592 /************************************************************** 593 * Handler for SIGTERM 594 **************************************************************/ 595 act_sigterm.sa_handler = server_exit; 596 sigemptyset(&act_sigterm.sa_mask); 597 598 if (sigaction(SIGTERM, &act_sigterm, NULL) != 0) { 599 rc = errno; 600 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGTERM"); 601 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc)); 602 exit(1); 603 } 604 605 /************************************************************** 606 * Handler for SIGINT 607 **************************************************************/ 608 act_sigint.sa_handler = server_exit; 609 sigemptyset(&act_sigint.sa_mask); 610 611 if (sigaction(SIGINT, &act_sigint, NULL) != 0) { 612 rc = errno; 613 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGINT"); 614 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc)); 615 exit(1); 616 } 617 618 return; 619} 620 621 622/************************************************************* 623 * Detaches us from the controlling tty (aka daemonize). 624 * More than likely this will be called from an ipc_init() 625 * function as we want to stay in the foreground for as long 626 * as possible. 627 **************************************************************/ 628void detach_tty() { 629 int x; 630 int rc; 631 int null_fd; 632 int exit_result; 633 pid_t pid; 634 char pid_buf[100]; 635 struct flock lockinfo; 636 637 /************************************************************** 638 * Make sure we're supposed to do this, the user may have 639 * requested us to stay in the foreground. 640 **************************************************************/ 641 if (flags & DETACH_TTY) { 642 for(x=5; x; x--) { 643 pid = fork(); 644 645 if ((pid == -1) && (errno == EAGAIN)) { 646 logger(L_ERR, L_FUNC, 647 "fork failed, retrying"); 648 sleep(5); 649 continue; 650 } 651 652 break; 653 } 654 655 if (pid == -1) { 656 /* Non retryable error. */ 657 rc = errno; 658 logger(L_ERR, L_FUNC, "Cannot start saslauthd"); 659 logger(L_ERR, L_FUNC, "saslauthd master fork failed: %s", 660 strerror(rc)); 661 exit(1); 662 } else if (pid != 0) { 663 int exit_code; 664 665 /* Parent, wait for child */ 666 if(read(startup_pipe[0], &exit_code, sizeof(exit_code)) == -1) { 667 logger(L_ERR, L_FUNC, 668 "Cannot start saslauthd"); 669 logger(L_ERR, L_FUNC, 670 "could not read from startup_pipe"); 671 unlink(pid_file_lock); 672 exit(1); 673 } else { 674 if (exit_code != 0) { 675 logger(L_ERR, L_FUNC, "Cannot start saslauthd"); 676 if (exit_code == 2) { 677 logger(L_ERR, L_FUNC, 678 "Another instance of saslauthd is currently running"); 679 } else { 680 logger(L_ERR, L_FUNC, "Check syslog for errors"); 681 } 682 } 683 unlink(pid_file_lock); 684 exit(exit_code); 685 } 686 } 687 688 /* Child! */ 689 close(startup_pipe[0]); 690 691 free(pid_file_lock); 692 693 if (setsid() == -1) { 694 exit_result = 1; 695 rc = errno; 696 697 logger(L_ERR, L_FUNC, "failed to set session id: %s", 698 strerror(rc)); 699 700 /* Tell our parent that we failed. */ 701 write(startup_pipe[1], &exit_result, sizeof(exit_result)); 702 703 exit(1); 704 } 705 706 if ((null_fd = open("/dev/null", O_RDWR, 0)) == -1) { 707 exit_result = 1; 708 rc = errno; 709 710 logger(L_ERR, L_FUNC, "failed to open /dev/null: %s", 711 strerror(rc)); 712 713 /* Tell our parent that we failed. */ 714 write(startup_pipe[1], &exit_result, sizeof(exit_result)); 715 716 exit(1); 717 } 718 719 /********************************************************* 720 * From this point on, stop printing errors out to stderr. 721 **********************************************************/ 722 flags &= ~LOG_USE_STDERR; 723 724 close(STDIN_FILENO); 725 close(STDOUT_FILENO); 726 close(STDERR_FILENO); 727 728 dup2(null_fd, STDIN_FILENO); 729 dup2(null_fd, STDOUT_FILENO); 730 dup2(null_fd, STDERR_FILENO); 731 732 if (null_fd > 2) 733 close(null_fd); 734 735 /********************************************************* 736 * Locks don't persist across forks. Relock the pid file 737 * to keep folks from having duplicate copies running... 738 *********************************************************/ 739 if (!(pid_file = malloc(strlen(run_path) + sizeof(PID_FILE) + 1))) { 740 exit_result = 1; 741 logger(L_ERR, L_FUNC, "could not allocate memory"); 742 write(startup_pipe[1], &exit_result, sizeof(exit_result)); 743 exit(1); 744 } 745 746 strcpy(pid_file, run_path); 747 strcat(pid_file, PID_FILE); 748 749 /* Write out the pidfile */ 750 pid_fd = open(pid_file, O_CREAT|O_RDWR, 0644); 751 if(pid_fd == -1) { 752 rc = errno; 753 exit_result = 1; 754 755 logger(L_ERR, L_FUNC, "could not open pid file %s: %s", 756 pid_file, strerror(rc)); 757 758 /* Tell our parent that we failed. */ 759 write(startup_pipe[1], &exit_result, sizeof(exit_result)); 760 761 exit(1); 762 } else { 763 char buf[100]; 764 765 lockinfo.l_type = F_WRLCK; 766 lockinfo.l_start = 0; 767 lockinfo.l_len = 0; 768 lockinfo.l_whence = SEEK_SET; 769 770 if (fcntl(pid_fd, F_SETLK, &lockinfo) == -1) { 771 exit_result = 2; 772 rc = errno; 773 774 logger(L_ERR, L_FUNC, "could not lock pid file %s: %s", 775 pid_file, strerror(rc)); 776 777 /* Tell our parent that we failed. */ 778 write(startup_pipe[1], &exit_result, sizeof(exit_result)); 779 780 exit(2); 781 } else { 782 int pid_fd_flags = fcntl(pid_fd, F_GETFD, 0); 783 784 if (pid_fd_flags != -1) { 785 pid_fd_flags = 786 fcntl(pid_fd, F_SETFD, pid_fd_flags | FD_CLOEXEC); 787 } 788 789 if (pid_fd_flags == -1) { 790 int exit_result = 1; 791 792 logger(L_ERR, L_FUNC, "unable to set close-on-exec for pidfile"); 793 794 /* Tell our parent that we failed. */ 795 write(startup_pipe[1], &exit_result, sizeof(exit_result)); 796 797 exit(1); 798 } 799 800 /* Write PID */ 801 master_pid = getpid(); 802 snprintf(buf, sizeof(buf), "%lu\n", (unsigned long)master_pid); 803 if (lseek(pid_fd, 0, SEEK_SET) == -1 || 804 ftruncate(pid_fd, 0) == -1 || 805 write(pid_fd, buf, strlen(buf)) == -1) { 806 int exit_result = 1; 807 rc = errno; 808 809 logger(L_ERR, L_FUNC, "could not write to pid file %s: %s", pid_file, strerror(rc)); 810 811 /* Tell our parent that we failed. */ 812 write(startup_pipe[1], &exit_result, sizeof(exit_result)); 813 814 exit(1); 815 } 816 fsync(pid_fd); 817 } 818 } 819 820 { 821 int exit_result = 0; 822 823 /* success! */ 824 if(write(startup_pipe[1], &exit_result, sizeof(exit_result)) == -1) { 825 logger(L_ERR, L_FUNC, 826 "could not write success result to startup pipe"); 827 exit(1); 828 } 829 } 830 831 close(startup_pipe[1]); 832 if(pid_file_lock_fd != -1) close(pid_file_lock_fd); 833 } 834 835 logger(L_INFO, L_FUNC, "master pid is: %lu", (unsigned long)master_pid); 836 837 return; 838} 839 840 841/************************************************************* 842 * Fork off a copy of ourselves. Return 0 if we're the child, 843 * > 0 for the parent. Die if we can't fork (the environment 844 * is probably unstable?). 845 **************************************************************/ 846pid_t have_baby() { 847 pid_t pid; 848 int rc; 849 850 pid = fork(); 851 852 if (pid < 0) { 853 rc = errno; 854 logger(L_ERR, L_FUNC, "could not fork child process"); 855 logger(L_ERR, L_FUNC, "fork: %s", strerror(rc)); 856 exit(1); 857 } 858 859 /********************************************************* 860 * If we're the child, clear the AM_MASTER flag. 861 **********************************************************/ 862 if (pid == 0) { 863 flags &= ~AM_MASTER; 864 return pid; 865 } 866 867 if (flags & VERBOSE) { 868 logger(L_DEBUG, L_FUNC, "forked child: %lu", 869 (unsigned long)pid); 870 } 871 872 return pid; 873} 874 875 876/************************************************************* 877 * Reap in all the dead children 878 **************************************************************/ 879void handle_sigchld() { 880 pid_t pid; 881 882 while ((pid = waitpid(-1, 0, WNOHANG)) > 0) { 883 if (flags & VERBOSE) 884 logger(L_DEBUG, L_FUNC, "child exited: %lu", (unsigned long)pid); 885 886 } 887 888 return; 889} 890 891 892/************************************************************* 893 * Do some final cleanup here. 894 **************************************************************/ 895void server_exit() { 896 struct flock lock_st; 897 898 /********************************************************* 899 * If we're not the master process, don't do anything 900 **********************************************************/ 901 if (!(flags & AM_MASTER)) { 902 if (flags & VERBOSE) 903 logger(L_DEBUG, L_FUNC, "child exited: %d", getpid()); 904 905 _exit(0); 906 } 907 908 kill(-master_pid, SIGTERM); 909 910 /********************************************************* 911 * Tidy up and delete the pid_file. (close will release the lock) 912 * besides, we want to unlink it first anyway to avoid a race. 913 * Note that only one process (the master, in our case) should 914 * unlink it. 915 **********************************************************/ 916 if(flags & DETACH_TTY) { 917 if(getpid() == master_pid) unlink(pid_file); 918 close(pid_fd); 919 920 if (flags & VERBOSE) 921 logger(L_DEBUG, L_FUNC, "pid file removed: %s", pid_file); 922 923 free(pid_file); 924 } else { 925 /* Tidy up and delete the pid_file_lock. (in the detached 926 case this is covered by the parent process already */ 927 928 unlink(pid_file_lock); 929 close(pid_file_lock_fd); 930 931 if (flags & VERBOSE) 932 logger(L_DEBUG, L_FUNC, "pid file lock removed: %s", 933 pid_file_lock); 934 free(pid_file_lock); 935 } 936 937 938 939 /********************************************************* 940 * Cleanup the cache, if it's enabled 941 **********************************************************/ 942 if (flags & CACHE_ENABLED) { 943 cache_cleanup_lock(); 944 cache_cleanup_mm(); 945 } 946 947 /********************************************************* 948 * Tell the IPC method to clean its room. 949 **********************************************************/ 950 ipc_cleanup(); 951 952 /********************************************************* 953 * Any other cleanup should go here 954 **********************************************************/ 955 956 logger(L_INFO, L_FUNC, "master exited: %d", master_pid); 957 958 _exit(0); 959} 960 961 962/************************************************************* 963 * Dump out our version and all the auth mechs we support 964 **************************************************************/ 965void show_version() { 966 authmech_t *authmech; 967 968 fprintf(stderr, "saslauthd %s\nauthentication mechanisms:", VERSION); 969 970 for (authmech = mechanisms; authmech->name != NULL; authmech++) { 971 fprintf(stderr, " %s", authmech->name); 972 } 973 974 fprintf(stderr, "\n\n"); 975 exit(0); 976} 977 978 979/************************************************************* 980 * Dump out our usage info and tag a show_version after it 981 **************************************************************/ 982void show_usage() { 983 fprintf(stderr, "usage: saslauthd [options]\n\n"); 984 fprintf(stderr, "option information:\n"); 985 fprintf(stderr, " -a <authmech> Selects the authentication mechanism to use.\n"); 986 fprintf(stderr, " -c Enable credential caching.\n"); 987 fprintf(stderr, " -d Debugging (don't detach from tty, implies -V)\n"); 988 fprintf(stderr, " -r Combine the realm with the login before passing to authentication mechanism\n"); 989 fprintf(stderr, " Ex. login: \"foo\" realm: \"bar\" will get passed as login: \"foo@bar\"\n"); 990 fprintf(stderr, " The realm name is passed untouched.\n"); 991 fprintf(stderr, " -O <option> Optional argument to pass to the authentication\n"); 992 fprintf(stderr, " mechanism.\n"); 993 fprintf(stderr, " -l Disable accept() locking. Increases performance, but\n"); 994 fprintf(stderr, " may not be compatible with some operating systems.\n"); 995 fprintf(stderr, " -m <path> Alternate path for the saslauthd working directory,\n"); 996 fprintf(stderr, " must be absolute.\n"); 997 fprintf(stderr, " -n <procs> Number of worker processes to create.\n"); 998 fprintf(stderr, " -s <kilobytes> Size of the credential cache (in kilobytes)\n"); 999 fprintf(stderr, " -t <seconds> Timeout for items in the credential cache (in seconds)\n"); 1000 fprintf(stderr, " -v Display version information and available mechs\n"); 1001 fprintf(stderr, " -V Enable verbose logging\n"); 1002 fprintf(stderr, " -h Display this message.\n\n"); 1003 1004 show_version(); 1005 exit(0); 1006} 1007 1008