/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: teddy at recompile
  • Date: 2020-04-05 21:30:59 UTC
  • Revision ID: teddy@recompile.se-20200405213059-fb2a61ckqynrmatk
Fix file descriptor leak in mandos-client

When the local network has Mandos servers announcing themselves using
real, globally reachable, IPv6 addresses (i.e. not link-local
addresses), but there is no router on the local network providing IPv6
RA (Router Advertisement) packets, the client cannot reach the server
by normal means, since the client only has a link-local IPv6 address,
and has no usable route to reach the server's global IPv6 address.
(This is not a common situation, and usually only happens when the
router itself reboots and runs a Mandos client, since it cannot then
give RA packets to itself.)  The client code has a solution for
this, which consists of adding a temporary local route to reach the
address of the server during communication, and removing this
temporary route afterwards.

This solution with a temporary route works, but has a file descriptor
leak; it leaks one file descriptor for each addition and for each
removal of a route.  If one server requiring an added route is present
on the network, but no servers gives a password, making the client
retry after the default ten seconds, and we furthermore assume a
default 1024 open files limit, the client runs out of file descriptors
after about 90 minutes, after which time the client process will be
useless and fail to retrieve any passwords, necessitating manual
password entry via the keyboard.

Fix this by eliminating the file descriptor leak in the client.

* plugins.d/mandos-client.c (add_delete_local_route): Do
  close(devnull) also in parent process, also if fork() fails, and on
  any failure in child process.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Mandos plugin runner - Run Mandos plugins
4
4
 *
5
 
 * Copyright © 2008-2017 Teddy Hogeborn
6
 
 * Copyright © 2008-2017 Björn Påhlsson
 
5
 * Copyright © 2008-2018 Teddy Hogeborn
 
6
 * Copyright © 2008-2018 Björn Påhlsson
7
7
 * 
8
8
 * This file is part of Mandos.
9
9
 * 
26
26
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
27
27
                                   O_CLOEXEC, pipe2() */
28
28
#include <stddef.h>             /* size_t, NULL */
29
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
30
 
                                   realloc() */
 
29
#include <stdlib.h>             /* malloc(), reallocarray(), realloc(),
 
30
                                   EXIT_SUCCESS, exit() */
31
31
#include <stdbool.h>            /* bool, true, false */
32
32
#include <stdio.h>              /* fileno(), fprintf(),
33
33
                                   stderr, STDOUT_FILENO, fclose() */
179
179
  /* Resize the pointed-to array to hold one more pointer */
180
180
  char **new_array = NULL;
181
181
  do {
182
 
    new_array = realloc(*array, sizeof(char *)
183
 
                        * (size_t) ((*len) + 2));
 
182
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
183
    new_array = reallocarray(*array, (size_t)((*len) + 2),
 
184
                             sizeof(char *));
 
185
#else
 
186
    if(((size_t)((*len) + 2)) > (SIZE_MAX / sizeof(char *))){
 
187
      /* overflow */
 
188
      new_array = NULL;
 
189
      errno = ENOMEM;
 
190
    } else {
 
191
      new_array = realloc(*array, (size_t)((*len) + 2)
 
192
                          * sizeof(char *));
 
193
    }
 
194
#endif
184
195
  } while(new_array == NULL and errno == EINTR);
185
196
  /* Malloc check */
186
197
  if(new_array == NULL){
313
324
__attribute__((nonnull))
314
325
static void free_plugin(plugin *plugin_node){
315
326
  
316
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
 
327
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
317
328
    free(*arg);
318
329
  }
 
330
  free(plugin_node->name);
319
331
  free(plugin_node->argv);
320
332
  for(char **env = plugin_node->environ; *env != NULL; env++){
321
333
    free(*env);
564
576
    case '?':                   /* --help */
565
577
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
566
578
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
579
      __builtin_unreachable();
567
580
    case -3:                    /* --usage */
568
581
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
569
582
      argp_state_help(state, state->out_stream,
570
583
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
584
      __builtin_unreachable();
571
585
    case 'V':                   /* --version */
572
586
      fprintf(state->out_stream, "%s\n", argp_program_version);
573
587
      exit(EXIT_SUCCESS);
583
597
      if(arg[0] == '\0'){
584
598
        break;
585
599
      }
 
600
#if __GNUC__ >= 7
 
601
      __attribute__((fallthrough));
 
602
#else
 
603
          /* FALLTHROUGH */
 
604
#endif
586
605
    default:
587
606
      return ARGP_ERR_UNKNOWN;
588
607
    }
700
719
        
701
720
        custom_argc += 1;
702
721
        {
703
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
704
 
                                    * ((size_t)custom_argc + 1));
 
722
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
723
          char **new_argv = reallocarray(custom_argv, (size_t)custom_argc + 1,
 
724
                                         sizeof(char *));
 
725
#else
 
726
          char **new_argv = NULL;
 
727
          if(((size_t)custom_argc + 1) > (SIZE_MAX / sizeof(char *))){
 
728
            /* overflow */
 
729
            errno = ENOMEM;
 
730
          } else {
 
731
            new_argv = realloc(custom_argv, ((size_t)custom_argc + 1)
 
732
                               * sizeof(char *));
 
733
          }
 
734
#endif
705
735
          if(new_argv == NULL){
706
 
            error(0, errno, "realloc");
 
736
            error(0, errno, "reallocarray");
707
737
            exitstatus = EX_OSERR;
708
738
            free(new_arg);
709
739
            free(org_line);
1092
1122
    
1093
1123
    new_plugin->pid = pid;
1094
1124
    new_plugin->fd = pipefd[0];
1095
 
    
 
1125
 
 
1126
    if(debug){
 
1127
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1128
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1129
    }
 
1130
 
1096
1131
    /* Unblock SIGCHLD so signal handler can be run if this process
1097
1132
       has already completed */
1098
1133
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,