GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / saslauthd / saslauthd-main.c
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  *****************************************/
124 static void     show_version();
125 static void     show_usage();
126
127 /****************************************
128  * application globals
129  *****************************************/
130 int             flags = 0;              /* Runtime flags                     */
131 int             g_argc;                 /* Copy of argc for those who need it*/
132 char            **g_argv;               /* Copy of argv for those who need it*/
133 char            *run_path = NULL;       /* path to our working directory     */
134 authmech_t      *auth_mech = NULL;      /* Authentication mechanism to use   */
135 char            *mech_option = NULL;    /* mechanism-specific option         */
136 int             num_procs = 5;          /* The max number of worker processes*/
137
138
139 /****************************************
140  * module globals
141 *****************************************/
142 extern char     *optarg;                /* For getopt()                          */
143 static int      master_pid;             /* Pid of the master process             */
144 static int      pid_fd;                 /* Descriptor to the open pid file       */
145 static int      pid_file_lock_fd;               /* Descriptor to the open pid lock file  */
146 static char     *pid_file;              /* Pid file name                         */
147 static char     *pid_file_lock;         /* Pid lock file name                    */
148 static int       startup_pipe[2] = { -1, -1 };
149
150 int 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         SET_AUTH_PARAMETERS(argc, argv);
159
160         g_argc = argc;
161         g_argv = argv;
162
163         /* default flags */
164         flags |= USE_ACCEPT_LOCK;
165         flags |= DETACH_TTY;
166         flags |= LOG_USE_SYSLOG;
167         flags |= LOG_USE_STDERR;
168         flags |= AM_MASTER;
169
170         while ((option = getopt(argc, argv, "a:cdhO:lm:n:rs:t:vV")) != -1) {
171                 switch(option) {
172                         case 'a':
173                                 /* Only one at a time, please! */
174                                 if(auth_mech_name) {
175                                     show_usage();
176                                     break;
177                                 }
178
179                                 auth_mech_name = strdup(optarg);
180                                 if (!auth_mech_name) {
181                                     logger(L_ERR, L_FUNC,
182                                            "could not allocate memory");
183                                     exit(1);
184                                 }
185                                 break;
186
187                         case 'c':
188                                 flags |= CACHE_ENABLED;
189                                 break;
190
191                         case 'd':
192                                 flags |= VERBOSE;
193                                 flags &= ~DETACH_TTY;
194                                 break;
195
196                         case 'h':
197                                 show_usage();
198                                 break;
199                                 
200                         case 'O':
201                                 set_mech_option(optarg);
202                                 break;
203
204                         case 'l':
205                                 flags &= ~USE_ACCEPT_LOCK;
206                                 break;
207
208                         case 'm':
209                                 set_run_path(optarg);
210                                 break;
211
212                         case 'n':
213                                 set_max_procs(optarg);
214                                 break;
215
216                         case 'r':
217                                 flags |= CONCAT_LOGIN_REALM;
218                                 break;
219
220                         case 's':
221                                 cache_set_table_size(optarg);
222                                 break;
223
224                         case 't':
225                                 cache_set_timeout(optarg);
226                                 break;
227
228                         case 'V':
229                                 flags |= VERBOSE;                   
230                                 break;
231
232                         case 'v':
233                                 show_version();
234                                 break;
235                                 
236                         default:
237                                 show_usage();
238                                 break;
239                 }
240         }
241
242         if (run_path == NULL)
243                 run_path = PATH_SASLAUTHD_RUNDIR;
244
245         if (auth_mech_name == NULL) {
246                 logger(L_ERR, L_FUNC, "no authentication mechanism specified");
247                 show_usage();
248                 exit(1);
249         }
250
251         set_auth_mech(auth_mech_name);
252
253         if (flags & VERBOSE)  {
254                 logger(L_DEBUG, L_FUNC, "num_procs  : %d", num_procs);
255
256                 if (mech_option == NULL)
257                         logger(L_DEBUG, L_FUNC, "mech_option: NULL");
258                 else
259                         logger(L_DEBUG, L_FUNC, "mech_option: %s", mech_option);
260
261                 logger(L_DEBUG, L_FUNC, "run_path   : %s", run_path);
262                 logger(L_DEBUG, L_FUNC, "auth_mech  : %s", auth_mech->name);
263         }
264
265         /*********************************************************
266          * Change our working directory to the dir where the
267          * run path is set to, core dumps will go there to keep
268          * them intact.
269          **********************************************************/
270         if (chdir(run_path) == -1) {
271                 rc = errno;
272                 logger(L_ERR, L_FUNC, "could not chdir to: %s", run_path);
273                 logger(L_ERR, L_FUNC, "chdir: %s", strerror(rc));
274                 logger(L_ERR, L_FUNC, "Check to make sure the directory exists and is");
275                 logger(L_ERR, L_FUNC, "writeable by the user this process runs as.");
276                 exit(1);
277         }
278
279         umask(077);
280
281         pid_file_size = strlen(run_path) + sizeof(PID_FILE_LOCK) + 1;
282         if ((pid_file_lock = malloc(pid_file_size)) == NULL) {
283                 logger(L_ERR, L_FUNC, "could not allocate memory");
284                 exit(1);
285         }
286     
287         strlcpy(pid_file_lock, run_path, pid_file_size);
288         strlcat(pid_file_lock, PID_FILE_LOCK, pid_file_size);
289
290         if ((pid_file_lock_fd = open(pid_file_lock, O_CREAT|O_TRUNC|O_RDWR, 644)) < 0) {
291                 rc = errno;
292                 logger(L_ERR, L_FUNC, "could not open pid lock file: %s", pid_file_lock);
293                 logger(L_ERR, L_FUNC, "open: %s", strerror(rc));
294                 logger(L_ERR, L_FUNC,
295                        "Check to make sure the directory exists and is");
296                 logger(L_ERR, L_FUNC, "writeable by the user this process runs as.");
297                 exit(1);
298         }
299
300         lockinfo.l_type = F_WRLCK;
301         lockinfo.l_start = 0;
302         lockinfo.l_len = 0;
303         lockinfo.l_whence = SEEK_SET;
304
305         if (fcntl(pid_file_lock_fd, F_SETLK, &lockinfo) == -1) {
306                 rc = errno;
307                 logger(L_ERR, L_FUNC, "could not lock pid lock file: %s", pid_file_lock);
308                 logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
309                 exit(1);
310         }
311     
312         if(pipe(startup_pipe) == -1) {
313                 logger(L_ERR, L_FUNC, "can't create startup pipe");
314                 exit(1);
315         }
316
317         /*********************************************************
318          * Enable signal handlers.
319          **********************************************************/
320         signal_setup();
321
322         /*********************************************************
323          * Cache setup, exit if it doesn't succeed (optional would
324          * be to disable the cache and log a warning).
325          **********************************************************/
326         if (cache_init() != 0)
327                 exit(1);
328
329         /*********************************************************
330          * Call the ipc specific initializer. This should also
331          * call detach_tty() at the appropriate point.
332          **********************************************************/
333         ipc_init();
334
335         /*********************************************************
336          * Enable general cleanup.
337          **********************************************************/
338         atexit(server_exit);
339
340         /*********************************************************
341          * If required, enable the process model.
342          **********************************************************/
343         if (flags & USE_PROCESS_MODEL) {
344                 if (flags & VERBOSE)
345                         logger(L_DEBUG, L_FUNC, "using process model");
346
347                 for (x = 1; x < num_procs; x++) {
348                         if (have_baby() != 0)
349                                 continue;               /* parent */
350
351                         break;                          /* child  */
352                 }
353         }
354
355         /*********************************************************
356          * Enter the ipc loop, we should never return.
357          **********************************************************/
358         ipc_loop();
359
360         exit(0);
361 }
362
363
364 /*************************************************************
365  * Performs all authentication centric duties. We should be
366  * getting callbacks from the ipc method here. We'll simply 
367  * return a pointer to a string to send back to the client.
368  * The caller is responsible for freeing the pointer. 
369  **************************************************************/
370 char *do_auth(const char *_login, const char *password, const char *service, const char *realm) {
371
372         struct cache_result     lkup_result;
373         char                    *response;
374         int                     cached = 0;
375         char                    login_buff[MAX_LOGIN_REALM_LEN];
376         char                    *login;
377
378
379         /***********************************************************
380          * Check to concat the login and realm into a single login.
381          * Aka, login: foo realm: bar becomes login: foo@bar.
382          * We do this because some mechs have no concept of a realm.
383          * Ie. auth_pam and friends.
384          ***********************************************************/
385         if ((flags & CONCAT_LOGIN_REALM) && realm && realm[0] != '\0') {
386             strlcpy(login_buff, _login, sizeof(login_buff));
387             strlcat(login_buff, "@", sizeof(login_buff));
388             strlcat(login_buff, realm, sizeof(login_buff));
389
390             login = login_buff;
391         } else {
392             login = (char *)_login;
393         }
394
395         if (cache_lookup(login, realm, service, password, &lkup_result) == CACHE_OK) {  
396                 response = strdup("OK");
397                 cached = 1;
398         } else {
399                 response = auth_mech->authenticate(login, password, service, realm);
400
401                 if (response == NULL) {
402                         logger(L_ERR, L_FUNC, "internal mechanism failure: %s", auth_mech->name);
403                         response = strdup("NO internal mechanism failure");
404                 }
405         }
406
407         if (strncmp(response, "OK", 2) == 0) {
408                 cache_commit(&lkup_result);
409
410                 if (flags & VERBOSE) {
411                         if (cached) 
412                                 logger(L_DEBUG, L_FUNC, "auth success (cached): [user=%s] [service=%s] [realm=%s]", \
413                                         login, service, realm);
414                         else
415                                 logger(L_DEBUG, L_FUNC, "auth success: [user=%s] [service=%s] [realm=%s] [mech=%s]", \
416                                         login, service, realm, auth_mech->name);
417                 }
418                 return response;
419         }
420
421         if (strncmp(response, "NO", 2) == 0) {
422                 logger(L_INFO, L_FUNC, "auth failure: [user=%s] [service=%s] [realm=%s] [mech=%s] [reason=%s]", \
423                         login, service, realm, auth_mech->name,
424                         strlen(response) >= 4 ? response+3 : "Unknown");
425
426                 return response;
427         }
428
429         logger(L_ERR, L_FUNC, "mechanism returned unknown response: %s", auth_mech->name);
430         response = strdup("NO internal mechanism failure");
431
432         return response;
433 }
434
435
436 /*************************************************************
437  * Allow someone to set the auth mech to use
438  **************************************************************/
439 void set_auth_mech(const char *mech) {
440         for (auth_mech = mechanisms; auth_mech->name != NULL; auth_mech++) {
441                 if (strcasecmp(auth_mech->name, mech) == 0)
442                         break;
443         }
444
445         if (auth_mech->name == NULL) {
446                 logger(L_ERR, L_FUNC, "unknown authentication mechanism: %s", mech);
447                 exit(1);
448         }
449
450         if (auth_mech->initialize) {
451                 if(auth_mech->initialize() != 0) {
452                     logger(L_ERR, L_FUNC, "failed to initialize mechanism %s",
453                            auth_mech->name);
454                     exit(1);
455                 }
456         }
457 }
458
459
460 /*************************************************************
461  * Allow someone to set the number of worker processes we
462  * will use. Only applicable to unix ipc.
463  **************************************************************/
464 void set_max_procs(const char *procs) {
465         num_procs = atoi(procs);
466
467         if(num_procs < 0) {
468                 logger(L_ERR, L_FUNC, "invalid number of worker processes defined");
469                 exit(1);
470         }
471
472         return;
473 }
474
475
476 /*************************************************************
477  * Allow someone to set the mechanism specific option
478  **************************************************************/
479 void set_mech_option(const char *option) {
480
481         free(mech_option);
482         mech_option = NULL;
483
484         if ((mech_option = strdup(option)) == NULL) {
485                 logger(L_ERR, L_FUNC, "could not allocate memory");
486                 exit(1);
487         }
488
489         return;
490 }
491
492
493 /*************************************************************
494  * Allow someone to set the path to our working directory
495  **************************************************************/
496 void set_run_path(const char *path) {
497
498         if (*path != '/') {
499                 logger(L_ERR, L_FUNC, "-m requires an absolute pathname");
500                 exit(1);
501         }
502
503         free(run_path);
504         run_path = NULL;
505
506         if ((run_path = strdup(path)) == NULL) {
507                 logger(L_ERR, L_FUNC, "could not allocate memory");
508                 exit(1);
509         }
510
511         return;
512 }
513
514
515
516 /*************************************************************
517  * Setup all the proper signal masks. 
518  **************************************************************/
519 void signal_setup() {
520
521         static struct sigaction act_sigchld;
522         static struct sigaction act_sigalrm;
523         static struct sigaction act_sigterm;
524         static struct sigaction act_sigpipe;
525         static struct sigaction act_sighup;
526         static struct sigaction act_sigint;
527         int                     rc;
528
529         /**************************************************************
530          * Handler for SIGCHLD
531          **************************************************************/
532         act_sigchld.sa_handler = handle_sigchld;
533         sigemptyset(&act_sigchld.sa_mask);
534
535         if (sigaction(SIGCHLD, &act_sigchld, NULL) != 0) {
536                 rc = errno;
537                 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGCHLD");
538                 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc));
539                 exit(1);
540         }
541
542         /**************************************************************
543          * Handler for SIGALRM  (IGNORE)
544          **************************************************************/
545         act_sigalrm.sa_handler = SIG_IGN;
546         sigemptyset(&act_sigalrm.sa_mask);
547
548         if (sigaction(SIGALRM, &act_sigalrm, NULL) != 0) {
549                 rc = errno;
550                 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGALRM");
551                 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc));
552                 exit(1);
553         }
554
555         /**************************************************************
556          * Handler for SIGPIPE  (IGNORE)
557          **************************************************************/
558         act_sigpipe.sa_handler = SIG_IGN;
559         sigemptyset(&act_sigpipe.sa_mask);
560
561         if (sigaction(SIGPIPE, &act_sigpipe, NULL) != 0) {
562                 rc = errno;
563                 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGPIPE");
564                 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc));
565                 exit(1);
566         }
567
568         /**************************************************************
569          * Handler for SIGHUP  (IGNORE)
570          **************************************************************/
571         act_sighup.sa_handler = SIG_IGN;
572         sigemptyset(&act_sighup.sa_mask);
573
574         if (sigaction(SIGHUP, &act_sighup, NULL) != 0) {
575                 rc = errno;
576                 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGHUP");
577                 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc));
578                 exit(1);
579         }
580
581         /**************************************************************
582          * Handler for SIGTERM
583          **************************************************************/
584         act_sigterm.sa_handler = server_exit;
585         sigemptyset(&act_sigterm.sa_mask);
586
587         if (sigaction(SIGTERM, &act_sigterm, NULL) != 0) {
588                 rc = errno;
589                 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGTERM");
590                 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc));
591                 exit(1);
592         }
593
594         /**************************************************************
595          * Handler for SIGINT
596          **************************************************************/
597         act_sigint.sa_handler = server_exit;
598         sigemptyset(&act_sigint.sa_mask);
599
600         if (sigaction(SIGINT, &act_sigint, NULL) != 0) {
601                 rc = errno;
602                 logger(L_ERR, L_FUNC, "failed to set sigaction for SIGINT");
603                 logger(L_ERR, L_FUNC, "sigaction: %s", strerror(rc));
604                 exit(1);
605         }
606
607         return;
608 }
609
610
611 /*************************************************************
612  * Detaches us from the controlling tty (aka daemonize). 
613  * More than likely this will be called from an ipc_init()
614  * function as we want to stay in the foreground for as long
615  * as possible.
616  **************************************************************/
617 void detach_tty() {
618     int         x;
619     int         rc;
620     int         null_fd;
621     int         exit_result;
622     pid_t       pid;
623     char        pid_buf[100];
624     struct flock        lockinfo;
625     
626     /**************************************************************
627      * Make sure we're supposed to do this, the user may have 
628      * requested us to stay in the foreground.
629      **************************************************************/
630     if (flags & DETACH_TTY) {
631         for(x=5; x; x--) {
632             pid = fork();
633             
634             if ((pid == -1) && (errno == EAGAIN)) {
635                 logger(L_ERR, L_FUNC,
636                        "fork failed, retrying");
637                 sleep(5);
638                 continue;
639             }
640             
641             break;
642         }
643         
644         if (pid == -1) {
645             /* Non retryable error. */
646             rc = errno;
647             logger(L_ERR, L_FUNC, "Cannot start saslauthd");
648             logger(L_ERR, L_FUNC, "saslauthd master fork failed: %s",
649                    strerror(rc));
650             exit(1);
651         } else if (pid != 0) {
652             int exit_code;
653             
654             /* Parent, wait for child */
655             if(read(startup_pipe[0], &exit_code, sizeof(exit_code)) == -1) {
656                 logger(L_ERR, L_FUNC,
657                        "Cannot start saslauthd");
658                 logger(L_ERR, L_FUNC,
659                        "could not read from startup_pipe");
660                 unlink(pid_file_lock);
661                 exit(1);
662             } else {
663                 if (exit_code != 0) {
664                     logger(L_ERR, L_FUNC, "Cannot start saslauthd");
665                     if (exit_code == 2) {
666                         logger(L_ERR, L_FUNC,
667                                "Another instance of saslauthd is currently running");
668                     } else {
669                         logger(L_ERR, L_FUNC, "Check syslog for errors");
670                     }
671                 }
672                 unlink(pid_file_lock);
673                 exit(exit_code);
674             }
675         }
676         
677         /* Child! */
678         close(startup_pipe[0]);
679         
680         free(pid_file_lock);
681         
682         if (setsid() == -1) {
683             exit_result = 1;
684             rc = errno;
685             
686             logger(L_ERR, L_FUNC, "failed to set session id: %s",
687                    strerror(rc));
688             
689             /* Tell our parent that we failed. */
690             write(startup_pipe[1], &exit_result, sizeof(exit_result));
691             
692             exit(1);
693         }
694         
695         if ((null_fd = open("/dev/null", O_RDWR, 0)) == -1) {
696             exit_result = 1;
697             rc = errno;
698             
699             logger(L_ERR, L_FUNC, "failed to open /dev/null: %s",
700                    strerror(rc));
701             
702             /* Tell our parent that we failed. */
703             write(startup_pipe[1], &exit_result, sizeof(exit_result));
704             
705             exit(1);
706         }
707         
708         /*********************************************************
709          * From this point on, stop printing errors out to stderr.
710          **********************************************************/
711         flags &= ~LOG_USE_STDERR;
712
713         close(STDIN_FILENO);
714         close(STDOUT_FILENO);
715         close(STDERR_FILENO);
716         
717         dup2(null_fd, STDIN_FILENO);
718         dup2(null_fd, STDOUT_FILENO);
719         dup2(null_fd, STDERR_FILENO);
720
721         if (null_fd > 2)
722             close(null_fd);
723                 
724         /*********************************************************
725          * Locks don't persist across forks. Relock the pid file
726          * to keep folks from having duplicate copies running...
727          *********************************************************/
728         if (!(pid_file = malloc(strlen(run_path) + sizeof(PID_FILE) + 1))) {
729             exit_result = 1;
730             logger(L_ERR, L_FUNC, "could not allocate memory");
731             write(startup_pipe[1], &exit_result, sizeof(exit_result));
732             exit(1);
733         }
734         
735         strcpy(pid_file, run_path);
736         strcat(pid_file, PID_FILE);
737         
738         /* Write out the pidfile */
739         pid_fd = open(pid_file, O_CREAT|O_RDWR, 0644);
740         if(pid_fd == -1) {
741             rc = errno;
742             exit_result = 1;
743
744             logger(L_ERR, L_FUNC, "could not open pid file %s: %s",
745                    pid_file, strerror(rc));
746             
747             /* Tell our parent that we failed. */
748             write(startup_pipe[1], &exit_result, sizeof(exit_result));
749             
750             exit(1);
751         } else {
752             char buf[100];
753             
754             lockinfo.l_type = F_WRLCK;
755             lockinfo.l_start = 0;
756             lockinfo.l_len = 0;
757             lockinfo.l_whence = SEEK_SET;
758             
759             if (fcntl(pid_fd, F_SETLK, &lockinfo) == -1) {
760                 exit_result = 2;
761                 rc = errno;
762                 
763                 logger(L_ERR, L_FUNC, "could not lock pid file %s: %s",
764                        pid_file, strerror(rc));
765                 
766                 /* Tell our parent that we failed. */
767                 write(startup_pipe[1], &exit_result, sizeof(exit_result));
768                 
769                 exit(2);
770             } else {
771                 int pid_fd_flags = fcntl(pid_fd, F_GETFD, 0);
772                 
773                 if (pid_fd_flags != -1) {
774                     pid_fd_flags =
775                         fcntl(pid_fd, F_SETFD, pid_fd_flags | FD_CLOEXEC);
776                 }
777                 
778                 if (pid_fd_flags == -1) {
779                     int exit_result = 1;
780                     
781                     logger(L_ERR, L_FUNC, "unable to set close-on-exec for pidfile");
782                     
783                     /* Tell our parent that we failed. */
784                     write(startup_pipe[1], &exit_result, sizeof(exit_result));
785                     
786                     exit(1);
787                 }
788                 
789                 /* Write PID */
790                 master_pid = getpid();
791                 snprintf(buf, sizeof(buf), "%lu\n", (unsigned long)master_pid);
792                 if (lseek(pid_fd, 0, SEEK_SET) == -1 ||
793                     ftruncate(pid_fd, 0) == -1 ||
794                     write(pid_fd, buf, strlen(buf)) == -1) {
795                     int exit_result = 1;
796                     rc = errno;
797                     
798                     logger(L_ERR, L_FUNC, "could not write to pid file %s: %s", pid_file, strerror(rc));
799                     
800                     /* Tell our parent that we failed. */
801                     write(startup_pipe[1], &exit_result, sizeof(exit_result));
802                     
803                     exit(1);
804                 }
805                 fsync(pid_fd);
806             }
807         }
808         
809         {
810             int exit_result = 0;
811             
812             /* success! */
813             if(write(startup_pipe[1], &exit_result, sizeof(exit_result)) == -1) {
814                 logger(L_ERR, L_FUNC,
815                        "could not write success result to startup pipe");
816                 exit(1);
817             }
818         }
819         
820         close(startup_pipe[1]);
821         if(pid_file_lock_fd != -1) close(pid_file_lock_fd);
822     }
823     
824     logger(L_INFO, L_FUNC, "master pid is: %lu", (unsigned long)master_pid);
825     
826     return;
827 }
828
829
830 /*************************************************************
831  * Fork off a copy of ourselves. Return 0 if we're the child,
832  * > 0 for the parent. Die if we can't fork (the environment
833  * is probably unstable?).
834  **************************************************************/
835 pid_t have_baby() {
836         pid_t   pid;
837         int     rc;
838
839         pid = fork();
840
841         if (pid < 0) {
842                 rc = errno;
843                 logger(L_ERR, L_FUNC, "could not fork child process");
844                 logger(L_ERR, L_FUNC, "fork: %s", strerror(rc));
845                 exit(1);
846         }
847
848         /*********************************************************
849          * If we're the child, clear the AM_MASTER flag.
850          **********************************************************/
851         if (pid == 0) {
852                 flags &= ~AM_MASTER;
853                 return pid;
854         }
855
856         if (flags & VERBOSE) {
857                 logger(L_DEBUG, L_FUNC, "forked child: %lu",
858                        (unsigned long)pid);
859         }
860
861         return pid;
862 }
863
864
865 /*************************************************************
866  * Reap in all the dead children
867  **************************************************************/
868 void handle_sigchld() {
869         pid_t pid;
870
871         while ((pid = waitpid(-1, 0, WNOHANG)) > 0) {
872                 if (flags & VERBOSE) 
873                         logger(L_DEBUG, L_FUNC, "child exited: %lu", (unsigned long)pid);
874
875         }
876
877         return;
878 }
879
880
881 /*************************************************************
882  * Do some final cleanup here.
883  **************************************************************/
884 void server_exit() {
885         struct flock    lock_st;
886
887         /*********************************************************
888          * If we're not the master process, don't do anything
889          **********************************************************/
890         if (!(flags & AM_MASTER)) {
891                 if (flags & VERBOSE)
892                         logger(L_DEBUG, L_FUNC, "child exited: %d", getpid());
893
894                 _exit(0);
895         }
896
897         kill(-master_pid, SIGTERM);
898
899         /*********************************************************
900          * Tidy up and delete the pid_file. (close will release the lock)
901          * besides, we want to unlink it first anyway to avoid a race.
902          * Note that only one process (the master, in our case) should
903          * unlink it.
904          **********************************************************/
905         if(flags & DETACH_TTY) {
906             if(getpid() == master_pid) unlink(pid_file);
907             close(pid_fd);
908
909             if (flags & VERBOSE)
910                 logger(L_DEBUG, L_FUNC, "pid file removed: %s", pid_file);
911
912             free(pid_file);
913         } else {
914             /* Tidy up and delete the pid_file_lock. (in the detached
915                case this is covered by the parent process already */
916
917             unlink(pid_file_lock);
918             close(pid_file_lock_fd);
919             
920             if (flags & VERBOSE)
921                 logger(L_DEBUG, L_FUNC, "pid file lock removed: %s",
922                        pid_file_lock);
923             free(pid_file_lock);
924         }
925
926         
927
928         /*********************************************************
929          * Cleanup the cache, if it's enabled
930          **********************************************************/
931         if (flags & CACHE_ENABLED) {
932                 cache_cleanup_lock();
933                 cache_cleanup_mm();
934         }
935
936         /*********************************************************
937          * Tell the IPC method to clean its room. 
938          **********************************************************/
939         ipc_cleanup();
940
941         /*********************************************************
942          * Any other cleanup should go here
943          **********************************************************/
944
945         logger(L_INFO, L_FUNC, "master exited: %d", master_pid);
946
947         _exit(0);
948 }
949
950
951 /*************************************************************
952  * Dump out our version and all the auth mechs we support
953  **************************************************************/
954 void show_version() {
955     authmech_t *authmech;
956     
957     fprintf(stderr, "saslauthd %s\nauthentication mechanisms:", VERSION);
958
959     for (authmech = mechanisms; authmech->name != NULL; authmech++) {
960         fprintf(stderr, " %s", authmech->name);
961     }
962
963     fprintf(stderr, "\n\n");
964     exit(0);
965 }
966
967
968 /*************************************************************
969  * Dump out our usage info and tag a show_version after it
970  **************************************************************/
971 void show_usage() {
972     fprintf(stderr, "usage: saslauthd [options]\n\n");
973     fprintf(stderr, "option information:\n");
974     fprintf(stderr, "  -a <authmech>  Selects the authentication mechanism to use.\n");
975     fprintf(stderr, "  -c             Enable credential caching.\n");
976     fprintf(stderr, "  -d             Debugging (don't detach from tty, implies -V)\n");
977     fprintf(stderr, "  -r             Combine the realm with the login before passing to authentication mechanism\n");
978     fprintf(stderr, "                 Ex. login: \"foo\" realm: \"bar\" will get passed as login: \"foo@bar\"\n");
979     fprintf(stderr, "                 The realm name is passed untouched.\n");
980     fprintf(stderr, "  -O <option>    Optional argument to pass to the authentication\n");
981     fprintf(stderr, "                 mechanism.\n");
982     fprintf(stderr, "  -l             Disable accept() locking. Increases performance, but\n");
983     fprintf(stderr, "                 may not be compatible with some operating systems.\n");
984     fprintf(stderr, "  -m <path>      Alternate path for the saslauthd working directory,\n");
985     fprintf(stderr, "                 must be absolute.\n"); 
986     fprintf(stderr, "  -n <procs>     Number of worker processes to create.\n");
987     fprintf(stderr, "  -s <kilobytes> Size of the credential cache (in kilobytes)\n");
988     fprintf(stderr, "  -t <seconds>   Timeout for items in the credential cache (in seconds)\n");
989     fprintf(stderr, "  -v             Display version information and available mechs\n");
990     fprintf(stderr, "  -V             Enable verbose logging\n");
991     fprintf(stderr, "  -h             Display this message.\n\n");
992
993     show_version();
994     exit(0);
995 }
996