1 /*****************************************************************************
2  *  Written by Chris Dunlap <cdunlap@llnl.gov>.
3  *  Copyright (C) 2007-2020 Lawrence Livermore National Security, LLC.
4  *  Copyright (C) 2002-2007 The Regents of the University of California.
5  *  UCRL-CODE-155910.
6  *
7  *  This file is part of the MUNGE Uid 'N' Gid Emporium (MUNGE).
8  *  For details, see <https://dun.github.io/munge/>.
9  *
10  *  MUNGE is free software: you can redistribute it and/or modify it under
11  *  the terms of the GNU General Public License as published by the Free
12  *  Software Foundation, either version 3 of the License, or (at your option)
13  *  any later version.  Additionally for the MUNGE library (libmunge), you
14  *  can redistribute it and/or modify it under the terms of the GNU Lesser
15  *  General Public License as published by the Free Software Foundation,
16  *  either version 3 of the License, or (at your option) any later version.
17  *
18  *  MUNGE is distributed in the hope that it will be useful, but WITHOUT
19  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21  *  and GNU Lesser General Public License for more details.
22  *
23  *  You should have received a copy of the GNU General Public License
24  *  and GNU Lesser General Public License along with MUNGE.  If not, see
25  *  <http://www.gnu.org/licenses/>.
26  *****************************************************************************/
27 
28 
29 #if HAVE_CONFIG_H
30 #  include <config.h>
31 #endif /* HAVE_CONFIG_H */
32 
33 #include <arpa/inet.h>                  /* for inet_ntop() */
34 #include <assert.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <limits.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/socket.h>                 /* for AF_INET */
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <time.h>
45 #include <unistd.h>
46 #include <munge.h>
47 #include "conf.h"
48 #include "license.h"
49 #include "lock.h"
50 #include "log.h"
51 #include "md.h"
52 #include "missing.h"                    /* for inet_ntop() */
53 #include "munge_defs.h"
54 #include "net.h"
55 #include "path.h"
56 #include "str.h"
57 #include "version.h"
58 #include "zip.h"
59 
60 
61 /*****************************************************************************
62  *  Command-Line Options
63  *****************************************************************************/
64 
65 #define OPT_ADVICE              256
66 #define OPT_KEY_FILE            257
67 #define OPT_NUM_THREADS         258
68 #define OPT_AUTH_SERVER         259
69 #define OPT_AUTH_CLIENT         260
70 #define OPT_GROUP_CHECK         261
71 #define OPT_GROUP_UPDATE        262
72 #define OPT_SYSLOG              263
73 #define OPT_BENCHMARK           264
74 #define OPT_MAX_TTL             265
75 #define OPT_PID_FILE            266
76 #define OPT_LOG_FILE            267
77 #define OPT_SEED_FILE           268
78 #define OPT_TRUSTED_GROUP       269
79 #define OPT_ORIGIN              270
80 #define OPT_LAST                271
81 
82 const char * const short_opts = ":hLVfFMsS:v";
83 
84 #include <getopt.h>
85 struct option long_opts[] = {
86     { "help",              no_argument,       NULL, 'h'               },
87     { "license",           no_argument,       NULL, 'L'               },
88     { "version",           no_argument,       NULL, 'V'               },
89     { "force",             no_argument,       NULL, 'f'               },
90     { "foreground",        no_argument,       NULL, 'F'               },
91     { "mlockall",          no_argument,       NULL, 'M'               },
92     { "stop",              no_argument,       NULL, 's'               },
93     { "socket",            required_argument, NULL, 'S'               },
94     { "verbose",           no_argument,       NULL, 'v'               },
95     { "advice",            no_argument,       NULL, OPT_ADVICE        },
96 #if defined(AUTH_METHOD_RECVFD_MKFIFO) || defined(AUTH_METHOD_RECVFD_MKNOD)
97     { "auth-server-dir",   required_argument, NULL, OPT_AUTH_SERVER   },
98     { "auth-client-dir",   required_argument, NULL, OPT_AUTH_CLIENT   },
99 #endif /* AUTH_METHOD_RECVFD_MKFIFO || AUTH_METHOD_RECVFD_MKNOD */
100     { "benchmark",         no_argument,       NULL, OPT_BENCHMARK     },
101     { "group-check-mtime", required_argument, NULL, OPT_GROUP_CHECK   },
102     { "group-update-time", required_argument, NULL, OPT_GROUP_UPDATE  },
103     { "key-file",          required_argument, NULL, OPT_KEY_FILE      },
104     { "log-file",          required_argument, NULL, OPT_LOG_FILE      },
105     { "max-ttl",           required_argument, NULL, OPT_MAX_TTL       },
106     { "num-threads",       required_argument, NULL, OPT_NUM_THREADS   },
107     { "origin",            required_argument, NULL, OPT_ORIGIN        },
108     { "pid-file",          required_argument, NULL, OPT_PID_FILE      },
109     { "seed-file",         required_argument, NULL, OPT_SEED_FILE     },
110     { "syslog",            no_argument,       NULL, OPT_SYSLOG        },
111     { "trusted-group",     required_argument, NULL, OPT_TRUSTED_GROUP },
112     {  NULL,               0,                 NULL, 0                 }
113 };
114 
115 
116 /*****************************************************************************
117  *  Internal Prototypes
118  *****************************************************************************/
119 
120 static void _conf_display_help (char *prog);
121 
122 static void _conf_process_stop (conf_t conf);
123 
124 static int _conf_send_signal (pid_t pid, int signum, int msecs);
125 
126 static void _conf_set_origin_addr (conf_t conf);
127 
128 static int _conf_open_keyfile (const char *keyfile, int got_force);
129 
130 
131 /*****************************************************************************
132  *  Global Variables
133  *****************************************************************************/
134 
135 conf_t conf = NULL;                     /* global configuration struct       */
136 
137 
138 /*****************************************************************************
139  *  External Functions
140  *****************************************************************************/
141 
142 conf_t
create_conf(void)143 create_conf (void)
144 {
145     conf_t conf;
146 
147     if (!(conf = malloc (sizeof (struct conf)))) {
148         log_errno (EMUNGE_NO_MEMORY, LOG_ERR, "Failed to allocate conf");
149     }
150     conf->ld = -1;
151     conf->got_benchmark = 0;
152     conf->got_clock_skew = 1;
153     conf->got_force = 0;
154     conf->got_foreground = 0;
155     conf->got_group_stat = !! MUNGE_GROUP_STAT_FLAG;
156     conf->got_stop = 0;
157     conf->got_mlockall = 0;
158     conf->got_root_auth = !! MUNGE_AUTH_ROOT_ALLOW_FLAG;
159     conf->got_socket_retry = !! MUNGE_SOCKET_RETRY_FLAG;
160     conf->got_syslog = 0;
161     conf->got_verbose = 0;
162     conf->def_cipher = MUNGE_DEFAULT_CIPHER;
163     conf->def_zip = zip_select_default_type (MUNGE_DEFAULT_ZIP);
164     conf->def_mac = MUNGE_DEFAULT_MAC;
165     conf->def_ttl = MUNGE_DEFAULT_TTL;
166     conf->max_ttl = MUNGE_MAXIMUM_TTL;
167     /*
168      *  FIXME: Add support for default realm.
169      */
170     conf->config_name = NULL;
171     conf->lockfile_fd = -1;
172     conf->lockfile_name = NULL;
173 
174     if (!(conf->logfile_name = strdup (MUNGE_LOGFILE_PATH))) {
175         log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
176             "Failed to copy logfile name string");
177     }
178     if (!(conf->pidfile_name = strdup (MUNGE_PIDFILE_PATH))) {
179         log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
180             "Failed to copy pidfile name string");
181     }
182     if (!(conf->socket_name = strdup (MUNGE_SOCKET_NAME))) {
183         log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
184             "Failed to copy socket name string");
185     }
186     if (!(conf->seed_name = strdup (MUNGE_SEEDFILE_PATH))) {
187         log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
188             "Failed to copy seed name string");
189     }
190     if (!(conf->key_name = strdup (MUNGE_KEYFILE_PATH))) {
191         log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
192             "Failed to copy key name string");
193     }
194     conf->dek_key = NULL;
195     conf->dek_key_len = 0;
196     conf->mac_key = NULL;
197     conf->mac_key_len = 0;
198     conf->origin_name = NULL;
199     conf->origin_ifname = NULL;
200     memset (&conf->addr, 0, sizeof (conf->addr));
201     conf->gids = NULL;
202     conf->gids_update_secs = MUNGE_GROUP_UPDATE_SECS;
203     conf->nthreads = MUNGE_THREADS;
204     conf->auth_server_dir = NULL;
205     conf->auth_client_dir = NULL;
206     conf->auth_rnd_bytes = MUNGE_AUTH_RND_BYTES;
207 
208 #if defined(AUTH_METHOD_RECVFD_MKFIFO) || defined(AUTH_METHOD_RECVFD_MKNOD)
209     if (!(conf->auth_server_dir = strdup (MUNGE_AUTH_SERVER_DIR))) {
210         log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
211             "Failed to copy auth-server-dir default string");
212     }
213     if (!(conf->auth_client_dir = strdup (MUNGE_AUTH_CLIENT_DIR))) {
214         log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
215             "Failed to copy auth-client-dir default string");
216     }
217 #endif /* AUTH_METHOD_RECVFD_MKFIFO || AUTH_METHOD_RECVFD_MKNOD */
218 
219     return (conf);
220 }
221 
222 
223 void
destroy_conf(conf_t conf,int do_unlink)224 destroy_conf (conf_t conf, int do_unlink)
225 {
226     assert (conf != NULL);
227     assert (conf->ld < 0);              /* sock_destroy() already called */
228     assert (conf->lockfile_fd < 0);
229 
230     if (conf->config_name) {
231         free (conf->config_name);
232         conf->config_name = NULL;
233     }
234     if (conf->lockfile_name) {
235         free (conf->lockfile_name);
236         conf->lockfile_name = NULL;
237     }
238     if (conf->logfile_name) {
239         free (conf->logfile_name);
240         conf->logfile_name = NULL;
241     }
242     if (conf->pidfile_name) {
243         if (do_unlink) {
244             (void) unlink (conf->pidfile_name);
245         }
246         free (conf->pidfile_name);
247         conf->pidfile_name = NULL;
248     }
249     if (conf->socket_name) {
250         free (conf->socket_name);
251         conf->socket_name = NULL;
252     }
253     if (conf->seed_name) {
254         free (conf->seed_name);
255         conf->seed_name = NULL;
256     }
257     if (conf->key_name) {
258         free (conf->key_name);
259         conf->key_name = NULL;
260     }
261     if (conf->dek_key) {
262         memburn (conf->dek_key, 0, conf->dek_key_len);
263         free (conf->dek_key);
264         conf->dek_key = NULL;
265     }
266     if (conf->mac_key) {
267         memburn (conf->mac_key, 0, conf->mac_key_len);
268         free (conf->mac_key);
269         conf->mac_key = NULL;
270     }
271     if (conf->origin_name) {
272         free (conf->origin_name);
273         conf->origin_name = NULL;
274     }
275     if (conf->origin_ifname) {
276         free (conf->origin_ifname);
277         conf->origin_ifname = NULL;
278     }
279     if (conf->auth_server_dir) {
280         free (conf->auth_server_dir);
281         conf->auth_server_dir = NULL;
282     }
283     if (conf->auth_client_dir) {
284         free (conf->auth_client_dir);
285         conf->auth_client_dir = NULL;
286     }
287     free (conf);
288 
289     return;
290 }
291 
292 
293 void
parse_cmdline(conf_t conf,int argc,char ** argv)294 parse_cmdline (conf_t conf, int argc, char **argv)
295 {
296     char *prog;
297     int   c;
298     long  l;
299     char *p;
300 
301     assert (conf != NULL);
302 
303     opterr = 0;                         /* suppress default getopt err msgs */
304 
305     prog = (prog = strrchr (argv[0], '/')) ? prog + 1 : argv[0];
306 
307     for (;;) {
308 
309         c = getopt_long (argc, argv, short_opts, long_opts, NULL);
310 
311         if (c == -1) {                  /* reached end of option list */
312             break;
313         }
314         switch (c) {
315             case 'h':
316                 _conf_display_help (prog);
317                 exit (EMUNGE_SUCCESS);
318                 break;
319             case 'L':
320                 display_license ();
321                 exit (EMUNGE_SUCCESS);
322                 break;
323             case 'V':
324                 display_version ();
325                 exit (EMUNGE_SUCCESS);
326                 break;
327             case 'f':
328                 conf->got_force = 1;
329                 break;
330             case 'F':
331                 conf->got_foreground = 1;
332                 break;
333             case 'M':
334                 conf->got_mlockall = 1;
335                 break;
336             case 's':
337                 conf->got_stop = 1;
338                 break;
339             case 'S':
340                 if (conf->socket_name)
341                     free (conf->socket_name);
342                 if (!(conf->socket_name = strdup (optarg)))
343                     log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
344                         "Failed to copy socket name string");
345                 break;
346             case 'v':
347                 conf->got_verbose = 1;
348                 break;
349             case OPT_ADVICE:
350                 printf ("Don't Panic!\n");
351                 exit (42);
352 #if defined(AUTH_METHOD_RECVFD_MKFIFO) || defined(AUTH_METHOD_RECVFD_MKNOD)
353             case OPT_AUTH_SERVER:
354                 if (conf->auth_server_dir)
355                     free (conf->auth_server_dir);
356                 if (!(conf->auth_server_dir = strdup (optarg)))
357                     log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
358                         "Failed to copy auth-server-dir cmdline string");
359                 break;
360             case OPT_AUTH_CLIENT:
361                 if (conf->auth_client_dir)
362                     free (conf->auth_client_dir);
363                 if (!(conf->auth_client_dir = strdup (optarg)))
364                     log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
365                         "Failed to copy auth-client-dir cmdline string");
366                 break;
367 #endif /* AUTH_METHOD_RECVFD_MKFIFO || AUTH_METHOD_RECVFD_MKNOD */
368             case OPT_BENCHMARK:
369                 conf->got_benchmark = 1;
370                 break;
371             case OPT_GROUP_CHECK:
372                 errno = 0;
373                 l = strtol (optarg, &p, 10);
374                 if (((errno == ERANGE) && ((l == LONG_MIN) || (l == LONG_MAX)))
375                         || (optarg == p) || (*p != '\0')) {
376                     log_err (EMUNGE_SNAFU, LOG_ERR,
377                         "Invalid value \"%s\" for group-check-mtime", optarg);
378                 }
379                 conf->got_group_stat = !! l;
380                 break;
381             case OPT_GROUP_UPDATE:
382                 errno = 0;
383                 l = strtol (optarg, &p, 10);
384                 if (((errno == ERANGE) && ((l == LONG_MIN) || (l == LONG_MAX)))
385                         || (optarg == p) || (*p != '\0')
386                         || (l < INT_MIN) || (l > INT_MAX)) {
387                     log_err (EMUNGE_SNAFU, LOG_ERR,
388                         "Invalid value \"%s\" for group-update-time", optarg);
389                 }
390                 conf->gids_update_secs = l;
391                 break;
392             case OPT_KEY_FILE:
393                 if (conf->key_name)
394                     free (conf->key_name);
395                 if (!(conf->key_name = strdup (optarg)))
396                     log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
397                         "Failed to copy key-file name string");
398                 break;
399             case OPT_LOG_FILE:
400                 if (conf->logfile_name)
401                     free (conf->logfile_name);
402                 if (!(conf->logfile_name = strdup (optarg)))
403                     log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
404                         "Failed to copy log-file name string");
405                 break;
406             case OPT_MAX_TTL:
407                 l = strtol (optarg, &p, 10);
408                 if (((errno == ERANGE) && ((l == LONG_MIN) || (l == LONG_MAX)))
409                         || (optarg == p) || (*p != '\0')
410                         || (l < 1) || (l > MUNGE_MAXIMUM_TTL)) {
411                     log_err (EMUNGE_SNAFU, LOG_ERR,
412                         "Invalid value \"%s\" for max-ttl", optarg);
413                 }
414                 conf->max_ttl = l;
415                 break;
416             case OPT_NUM_THREADS:
417                 errno = 0;
418                 l = strtol (optarg, &p, 10);
419                 if (((errno == ERANGE) && ((l == LONG_MIN) || (l == LONG_MAX)))
420                         || (optarg == p) || (*p != '\0')
421                         || (l <= 0) || (l > INT_MAX)) {
422                     log_err (EMUNGE_SNAFU, LOG_ERR,
423                         "Invalid value \"%s\" for num-threads", optarg);
424                 }
425                 conf->nthreads = l;
426                 break;
427             case OPT_ORIGIN:
428                 if (conf->origin_name)
429                     free (conf->origin_name);
430                 if (!(conf->origin_name = strdup (optarg)))
431                     log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
432                         "Failed to copy origin string");
433                 break;
434             case OPT_PID_FILE:
435                 if (conf->pidfile_name)
436                     free (conf->pidfile_name);
437                 if (!(conf->pidfile_name = strdup (optarg)))
438                     log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
439                         "Failed to copy pid-file name string");
440                 break;
441             case OPT_SEED_FILE:
442                 if (conf->seed_name)
443                     free (conf->seed_name);
444                 if (!(conf->seed_name = strdup (optarg)))
445                     log_errno (EMUNGE_NO_MEMORY, LOG_ERR,
446                         "Failed to copy seed-file name string");
447                 break;
448             case OPT_SYSLOG:
449                 conf->got_syslog = 1;
450                 break;
451             case OPT_TRUSTED_GROUP:
452                 if (path_set_trusted_group (optarg) < 0) {
453                     log_err (EMUNGE_SNAFU, LOG_ERR,
454                         "Invalid value \"%s\" for trusted-group", optarg);
455                 }
456                 break;
457             case '?':
458                 if (optopt > 0) {
459                     log_err (EMUNGE_SNAFU, LOG_ERR,
460                         "Invalid option \"-%c\"", optopt);
461                 }
462                 else if (optind > 1) {
463                     log_err (EMUNGE_SNAFU, LOG_ERR,
464                         "Invalid option \"%s\"", argv[optind - 1]);
465                 }
466                 else {
467                     log_err (EMUNGE_SNAFU, LOG_ERR,
468                         "Failed to process command-line");
469                 }
470                 break;
471             case ':':
472                 if ((optind > 1)
473                         && (strncmp (argv[optind - 1], "--", 2) == 0)) {
474                     log_err (EMUNGE_SNAFU, LOG_ERR,
475                         "Missing argument for option \"%s\"",
476                         argv[optind - 1]);
477                 }
478                 else if (optopt > 0) {
479                     log_err (EMUNGE_SNAFU, LOG_ERR,
480                         "Missing argument for option \"-%c\"", optopt);
481                 }
482                 else {
483                     log_err (EMUNGE_SNAFU, LOG_ERR,
484                         "Failed to process command-line");
485                 }
486                 break;
487             default:
488                 if ((optind > 1)
489                         && (strncmp (argv[optind - 1], "--", 2) == 0)) {
490                     log_err (EMUNGE_SNAFU, LOG_ERR,
491                         "Unimplemented option \"%s\"", argv[optind - 1]);
492                 }
493                 else {
494                     log_err (EMUNGE_SNAFU, LOG_ERR,
495                         "Unimplemented option \"-%c\"", c);
496                 }
497                 break;
498         }
499     }
500     if (argv[optind]) {
501         log_err (EMUNGE_SNAFU, LOG_ERR,
502             "Unrecognized parameter \"%s\"", argv[optind]);
503     }
504 }
505 
506 
507 void
process_conf(conf_t conf)508 process_conf (conf_t conf)
509 {
510 /*  Process the configuration.
511  */
512     assert (conf != NULL);
513 
514     if (conf->got_stop) {
515         _conf_process_stop (conf);
516     }
517     _conf_set_origin_addr (conf);
518     return;
519 }
520 
521 
522 void
write_origin_addr(conf_t conf)523 write_origin_addr (conf_t conf)
524 {
525 /*  Write a log message indicating the origin address that will be encoded into
526  *    the credential metadata.
527  */
528     char buf[INET_ADDRSTRLEN];
529 
530     assert (conf != NULL);
531 
532     if (!inet_ntop (AF_INET, &conf->addr, buf, sizeof (buf))) {
533         log_msg (LOG_WARNING, "Failed to convert origin address to a string");
534     }
535     else if (conf->origin_ifname != NULL) {
536         log_msg (LOG_INFO, "Set origin address to %s (%s)", buf,
537                 conf->origin_ifname);
538     }
539     else {
540         log_msg (LOG_INFO, "Set origin address to %s", buf);
541     }
542     return;
543 }
544 
545 
546 void
create_subkeys(conf_t conf)547 create_subkeys (conf_t conf)
548 {
549     int fd;
550     int n;
551     int n_total;
552     unsigned char buf[1024];
553     md_ctx dek_ctx;
554     md_ctx mac_ctx;
555 
556     assert (conf != NULL);
557     assert (conf->dek_key == NULL);
558     assert (conf->mac_key == NULL);
559 
560     /*  Allocate memory for subkeys.
561      */
562     if ((conf->dek_key_len = md_size (MUNGE_MAC_SHA1)) <= 0) {
563         log_err (EMUNGE_NO_MEMORY, LOG_ERR,
564             "Failed to determine DEK key length");
565     }
566     if (!(conf->dek_key = malloc (conf->dek_key_len))) {
567         log_err (EMUNGE_NO_MEMORY, LOG_ERR,
568             "Failed to allocate %d bytes for cipher subkey",
569             conf->dek_key_len);
570     }
571     if ((conf->mac_key_len = md_size (MUNGE_MAC_SHA1)) <= 0) {
572         log_err (EMUNGE_NO_MEMORY, LOG_ERR,
573             "Failed to determine MAC key length");
574     }
575     if (!(conf->mac_key = malloc (conf->mac_key_len))) {
576         log_err (EMUNGE_NO_MEMORY, LOG_ERR,
577             "Failed to allocate %d bytes for MAC subkey",
578             conf->mac_key_len);
579     }
580     if (md_init (&dek_ctx, MUNGE_MAC_SHA1) < 0) {
581         log_err (EMUNGE_SNAFU, LOG_ERR,
582             "Failed to compute subkeys: Cannot init md ctx");
583     }
584     /*  Compute keyfile's message digest.
585      */
586     fd = _conf_open_keyfile (conf->key_name, conf->got_force);
587     assert (fd >= 0);
588 
589     n_total = 0;
590     for (;;) {
591         n = read (fd, buf, sizeof (buf));
592         if (n == 0)
593             break;
594         if ((n < 0) && (errno == EINTR))
595             continue;
596         if (n < 0)
597             log_errno (EMUNGE_SNAFU, LOG_ERR,
598                 "Failed to read keyfile \"%s\"", conf->key_name);
599         if (md_update (&dek_ctx, buf, n) < 0)
600             log_err (EMUNGE_SNAFU, LOG_ERR,
601                 "Failed to compute subkeys: Cannot update md ctx");
602         n_total += n;
603     }
604     if (close (fd) < 0) {
605         log_errno (EMUNGE_SNAFU, LOG_ERR,
606             "Failed to close keyfile \"%s\"", conf->key_name);
607     }
608     if (n_total < MUNGE_KEY_LEN_MIN_BYTES) {
609         log_err (EMUNGE_SNAFU, LOG_ERR,
610             "Keyfile must be at least %d bytes", MUNGE_KEY_LEN_MIN_BYTES);
611     }
612     if (md_copy (&mac_ctx, &dek_ctx) < 0) {
613         log_err (EMUNGE_SNAFU, LOG_ERR,
614             "Failed to compute subkeys: Cannot copy md ctx");
615     }
616     /*  Append "1" to keyfile in order to compute cipher subkey.
617      */
618     n = conf->dek_key_len;
619     if ( (md_update (&dek_ctx, "1", 1) < 0)
620       || (md_final (&dek_ctx, conf->dek_key, &n) < 0)
621       || (md_cleanup (&dek_ctx) < 0) ) {
622         log_err (EMUNGE_SNAFU, LOG_ERR, "Failed to compute cipher subkey");
623     }
624     assert (n <= conf->dek_key_len);
625 
626     /*  Append "2" to keyfile in order to compute mac subkey.
627      */
628     n = conf->mac_key_len;
629     if ( (md_update (&mac_ctx, "2", 1) < 0)
630       || (md_final (&mac_ctx, conf->mac_key, &n) < 0)
631       || (md_cleanup (&mac_ctx) < 0) ) {
632         log_err (EMUNGE_SNAFU, LOG_ERR, "Failed to compute MAC subkey");
633     }
634     assert (n <= conf->mac_key_len);
635 
636     return;
637 }
638 
639 
640 /*****************************************************************************
641  *  Internal Functions
642  *****************************************************************************/
643 
644 static void
_conf_display_help(char * prog)645 _conf_display_help (char *prog)
646 {
647 /*  Displays a help message describing the command-line options.
648  */
649     const int w = -24;                  /* pad for width of option string */
650 
651     assert (prog != NULL);
652 
653     printf ("Usage: %s [OPTIONS]\n", prog);
654     printf ("\n");
655 
656     printf ("  %*s %s\n", w, "-h, --help",
657             "Display this help");
658 
659     printf ("  %*s %s\n", w, "-L, --license",
660             "Display license information");
661 
662     printf ("  %*s %s\n", w, "-V, --version",
663             "Display version information");
664 
665     printf ("\n");
666 
667     printf ("  %*s %s\n", w, "-f, --force",
668             "Force daemon to run if possible");
669 
670     printf ("  %*s %s\n", w, "-F, --foreground",
671             "Run daemon in the foreground (do not fork)");
672 
673     printf ("  %*s %s\n", w, "-M, --mlockall",
674             "Lock all pages in memory");
675 
676     printf ("  %*s %s\n", w, "-s, --stop",
677             "Stop daemon bound to socket");
678 
679     printf ("  %*s %s [%s]\n", w, "-S, --socket=PATH",
680             "Specify local socket", MUNGE_SOCKET_NAME);
681 
682     printf ("  %*s %s\n", w, "-v, --verbose",
683             "Be verbose");
684 
685     printf ("\n");
686 
687 #if defined(AUTH_METHOD_RECVFD_MKFIFO) || defined(AUTH_METHOD_RECVFD_MKNOD)
688     printf ("  %*s %s [%s]\n", w, "--auth-server-dir=DIR",
689             "Specify auth-server directory", MUNGE_AUTH_SERVER_DIR);
690 
691     printf ("  %*s %s [%s]\n", w, "--auth-client-dir=DIR",
692             "Specify auth-client directory", MUNGE_AUTH_CLIENT_DIR);
693 #endif /* AUTH_METHOD_RECVFD_MKFIFO || AUTH_METHOD_RECVFD_MKNOD */
694 
695     printf ("  %*s %s\n", w, "--benchmark",
696             "Disable timers to reduce noise while benchmarking");
697 
698     printf ("  %*s Specify whether to check \"%s\" mtime [%d]\n",
699             w, "--group-check-mtime=BOOL", GIDS_GROUP_FILE,
700             MUNGE_GROUP_STAT_FLAG);
701 
702     printf ("  %*s %s [%d]\n", w, "--group-update-time=INT",
703             "Specify seconds between group info updates",
704             MUNGE_GROUP_UPDATE_SECS);
705 
706     printf ("  %*s %s [%s]\n", w, "--key-file=PATH",
707             "Specify key file", MUNGE_KEYFILE_PATH);
708 
709     printf ("  %*s %s [%s]\n", w, "--log-file=PATH",
710             "Specify log file", MUNGE_LOGFILE_PATH);
711 
712     printf ("  %*s %s [%d]\n", w, "--max-ttl=INT",
713             "Specify maximum time-to-live (in seconds)", MUNGE_MAXIMUM_TTL);
714 
715     printf ("  %*s %s [%d]\n", w, "--num-threads=INT",
716             "Specify number of threads to spawn", MUNGE_THREADS);
717 
718     printf ("  %*s %s\n", w, "--origin=ADDRESS",
719             "Specify origin address via hostname/IPaddr/interface");
720 
721     printf ("  %*s %s [%s]\n", w, "--pid-file=PATH",
722             "Specify PID file", MUNGE_PIDFILE_PATH);
723 
724     printf ("  %*s %s [%s]\n", w, "--seed-file=PATH",
725             "Specify PRNG seed file", MUNGE_SEEDFILE_PATH);
726 
727     printf ("  %*s %s\n", w, "--syslog",
728             "Redirect log messages to syslog");
729 
730     printf ("  %*s %s\n", w, "--trusted-group=GROUP",
731             "Specify trusted group/GID for directory checks");
732 
733     printf ("\n");
734     return;
735 }
736 
737 
738 static void
_conf_process_stop(conf_t conf)739 _conf_process_stop (conf_t conf)
740 {
741 /*  Processes the -s/--stop option.
742  *  A series of SIGTERMs are sent to the process holding the write-lock.
743  *    If the process fails to terminate, a final SIGKILL is sent.
744  */
745     pid_t pid;
746     int   signum;
747     int   msecs;
748     int   i;
749     int   rv;
750 
751     assert (conf != NULL);
752     assert (MUNGE_SIGNAL_ATTEMPTS > 0);
753     assert (MUNGE_SIGNAL_DELAY_MSECS > 0);
754 
755     pid = lock_query (conf);
756     if (pid <= 0) {
757         if (conf->got_verbose) {
758             log_err (EMUNGE_SNAFU, LOG_ERR,
759                     "Failed to query socket lockfile \"%s\": %s",
760                     conf->lockfile_name,
761                     (pid == 0) ? "Lock not held" : strerror (errno));
762         }
763         exit (EXIT_FAILURE);
764     }
765     rv = kill (pid, 0);
766     if (rv < 0) {
767         if (conf->got_verbose) {
768             log_errno (EMUNGE_SNAFU, LOG_ERR,
769                     "Failed to signal daemon bound to socket \"%s\" (pid %d)",
770                     conf->socket_name, pid);
771         }
772         exit (EXIT_FAILURE);
773     }
774     signum = SIGTERM;
775     msecs = 0;
776     for (i = 0; i < (MUNGE_SIGNAL_ATTEMPTS + 1); i++) {
777         if (i == MUNGE_SIGNAL_ATTEMPTS) {
778             signum = SIGKILL;           /* Kill me harder! */
779         }
780         msecs += MUNGE_SIGNAL_DELAY_MSECS;
781         rv = _conf_send_signal (pid, signum, msecs);
782         if (rv == 0) {
783             if (conf->got_verbose) {
784                 log_msg (LOG_NOTICE,
785                         "%s daemon bound to socket \"%s\" (pid %d)",
786                         (signum == SIGTERM) ? "Terminated" : "Killed",
787                         conf->socket_name, pid);
788             }
789             exit (EXIT_SUCCESS);
790         }
791     }
792     if (conf->got_verbose) {
793         log_err (EMUNGE_SNAFU, LOG_ERR,
794                 "Failed to terminate daemon bound to socket \"%s\" (pid %d)",
795                 conf->socket_name, pid);
796     }
797     exit (EXIT_FAILURE);
798 }
799 
800 
801 static int
_conf_send_signal(pid_t pid,int signum,int msecs)802 _conf_send_signal (pid_t pid, int signum, int msecs)
803 {
804 /*  Sends the signal [signum] to the process specified by [pid].
805  *  Returns 1 if the process is still running after a delay of [msecs],
806  *    or 0 if the process cannot be found.
807  */
808     struct timespec ts;
809     int             rv;
810 
811     assert (pid > 0);
812     assert (signum > 0);
813     assert (msecs > 0);
814 
815     log_msg (LOG_DEBUG, "Signaling pid %d with sig %d and %dms delay",
816             pid, signum, msecs);
817 
818     rv = kill (pid, signum);
819     if (rv < 0) {
820         if (errno == ESRCH) {
821             return (0);
822         }
823         log_errno (EMUNGE_SNAFU, LOG_ERR,
824                 "Failed to signal daemon (pid %d, sig %d)", pid, signum);
825     }
826     ts.tv_sec = msecs / 1000;
827     ts.tv_nsec = (msecs % 1000) * 1000 * 1000;
828 retry:
829     rv = nanosleep (&ts, &ts);
830     if (rv < 0) {
831         if (errno == EINTR) {
832             goto retry;
833         }
834         log_errno (EMUNGE_SNAFU, LOG_ERR,
835                 "Failed to sleep while awaiting signal result");
836     }
837     rv = kill (pid, 0);
838     if (rv < 0) {
839         if (errno == ESRCH) {
840             return (0);
841         }
842         log_errno (EMUNGE_SNAFU, LOG_ERR,
843                 "Failed to check daemon (pid %d, sig 0)", pid);
844     }
845     return (1);
846 }
847 
848 
849 static void
_conf_set_origin_addr(conf_t conf)850 _conf_set_origin_addr (conf_t conf)
851 {
852 /*  Set the origin address to be encoded into credential metadata.
853  *  If an origin is explicitly specified, failure to lookup its network address
854  *    results in an error which can be overridden.
855  *  If an orgin is not specified or the error is overridden, a warning is
856  *    logged and the origin address is set to the null address.
857  */
858     int is_origin_specified;
859     int rv = 0;
860 
861     assert (conf != NULL);
862 
863     memset (&conf->addr, 0, sizeof (conf->addr));
864     conf->origin_ifname = NULL;
865 
866     is_origin_specified = (conf->origin_name != NULL) ? 1 : 0;
867 
868     if (conf->origin_name == NULL) {
869         rv = net_get_hostname (&conf->origin_name);
870         if (rv < 0) {
871             log_msg (LOG_WARNING, "Failed to get name of current host");
872         }
873     }
874     if (conf->origin_name != NULL) {
875         errno = 0;
876         rv = net_get_hostaddr (conf->origin_name, &conf->addr,
877                 &conf->origin_ifname);
878         if (rv < 0) {
879             log_err_or_warn (conf->got_force || !is_origin_specified,
880                     "Failed to lookup origin \"%s\"%s%s", conf->origin_name,
881                     (errno != 0) ? ": " : "",
882                     (errno != 0) ? strerror (errno) : "");
883         }
884     }
885     if (rv != 0) {
886         log_msg (LOG_WARNING, "Continuing with origin set to null address");
887     }
888     return;
889 }
890 
891 
892 static int
_conf_open_keyfile(const char * keyfile,int got_force)893 _conf_open_keyfile (const char *keyfile, int got_force)
894 {
895 /*  Returns a valid file-descriptor to the opened [keyfile], or dies trying.
896  */
897     int          is_symlink;
898     struct stat  st;
899     int          n;
900     char         keydir [PATH_MAX];
901     char         ebuf [1024];
902     int          fd;
903 
904     if ((keyfile == NULL) || (*keyfile == '\0')) {
905         log_err (EMUNGE_SNAFU, LOG_ERR, "Keyfile name is undefined");
906     }
907     is_symlink = (lstat (keyfile, &st) == 0) ? S_ISLNK (st.st_mode) : 0;
908 
909     if (stat (keyfile, &st) < 0) {
910         log_errno (EMUNGE_SNAFU, LOG_ERR,
911             "Failed to check keyfile \"%s\"", keyfile);
912     }
913     if (!S_ISREG (st.st_mode)) {
914         log_err (EMUNGE_SNAFU, LOG_ERR,
915             "Keyfile is insecure: \"%s\" must be a regular file (type=%07o)",
916             keyfile, (st.st_mode & S_IFMT));
917     }
918     if (is_symlink) {
919         log_err_or_warn (got_force,
920             "Keyfile is insecure: \"%s\" should not be a symbolic link",
921             keyfile);
922     }
923     if (st.st_uid != geteuid ()) {
924         log_err_or_warn (got_force,
925             "Keyfile is insecure: \"%s\" should be owned by UID %u instead of "
926             "UID %u", keyfile, (unsigned) geteuid (), (unsigned) st.st_uid);
927     }
928     if (st.st_mode & (S_IRGRP | S_IWGRP)) {
929         log_err_or_warn (got_force,
930             "Keyfile is insecure: \"%s\" should not be readable or writable "
931             "by group (perms=%04o)", keyfile, (st.st_mode & ~S_IFMT));
932     }
933     if (st.st_mode & (S_IROTH | S_IWOTH)) {
934         log_err_or_warn (got_force,
935             "Keyfile is insecure: \"%s\" should not be readable or writable "
936             "by other (perms=%04o)", keyfile, (st.st_mode & ~S_IFMT));
937     }
938     /*  Ensure keyfile dir is secure against modification by others.
939      */
940     if (path_dirname (keyfile, keydir, sizeof (keydir)) < 0) {
941         log_err (EMUNGE_SNAFU, LOG_ERR,
942             "Failed to determine dirname of keyfile \"%s\"", keyfile);
943     }
944     n = path_is_secure (keydir, ebuf, sizeof (ebuf), PATH_SECURITY_NO_FLAGS);
945     if (n < 0) {
946         log_err (EMUNGE_SNAFU, LOG_ERR,
947             "Failed to check keyfile dir \"%s\": %s", keydir, ebuf);
948     }
949     else if (n == 0) {
950         log_err_or_warn (got_force, "Keyfile is insecure: %s", ebuf);
951     }
952     /*  Open keyfile for reading only.
953      */
954     if ((fd = open (keyfile, O_RDONLY)) < 0) {
955         log_errno (EMUNGE_SNAFU, LOG_ERR,
956             "Failed to open keyfile \"%s\"", keyfile);
957     }
958     return (fd);
959 }
960