/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 Hogeborn
  • Date: 2008-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8; mode: c; mode: orgtbl -*- */
 
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
3
 * Mandos plugin runner - Run Mandos plugins
4
4
 *
5
 
 * Copyright © 2008-2018 Teddy Hogeborn
6
 
 * Copyright © 2008-2018 Björn Påhlsson
7
 
 * 
8
 
 * This file is part of Mandos.
9
 
 * 
10
 
 * Mandos is free software: you can redistribute it and/or modify it
11
 
 * under the terms of the GNU General Public License as published by
12
 
 * the Free Software Foundation, either version 3 of the License, or
13
 
 * (at your option) any later version.
14
 
 * 
15
 
 * Mandos is distributed in the hope that it will be useful, but
 
5
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
6
 * 
 
7
 * This program is free software: you can redistribute it and/or
 
8
 * modify it under the terms of the GNU General Public License as
 
9
 * published by the Free Software Foundation, either version 3 of the
 
10
 * License, or (at your option) any later version.
 
11
 * 
 
12
 * This program is distributed in the hope that it will be useful, but
16
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
17
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
15
 * General Public License for more details.
19
16
 * 
20
17
 * You should have received a copy of the GNU General Public License
21
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
18
 * along with this program.  If not, see
 
19
 * <http://www.gnu.org/licenses/>.
22
20
 * 
23
 
 * Contact the authors at <mandos@recompile.se>.
 
21
 * Contact the authors at <mandos@fukt.bsnet.se>.
24
22
 */
25
23
 
26
24
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
27
 
                                   O_CLOEXEC, pipe2() */
 
25
                                   asprintf() */
28
26
#include <stddef.h>             /* size_t, NULL */
29
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
30
 
                                   realloc() */
 
27
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
 
28
                                   EXIT_SUCCESS, realloc() */
31
29
#include <stdbool.h>            /* bool, true, false */
32
 
#include <stdio.h>              /* fileno(), fprintf(),
33
 
                                   stderr, STDOUT_FILENO, fclose() */
34
 
#include <sys/types.h>          /* fstat(), struct stat, waitpid(),
35
 
                                   WIFEXITED(), WEXITSTATUS(), wait(),
36
 
                                   pid_t, uid_t, gid_t, getuid(),
37
 
                                   getgid() */
 
30
#include <stdio.h>              /* perror, popen(), fileno(),
 
31
                                   fprintf(), stderr, STDOUT_FILENO */
 
32
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
 
33
                                   stat, waitpid(), WIFEXITED(),
 
34
                                   WEXITSTATUS(), wait(), pid_t,
 
35
                                   uid_t, gid_t, getuid(), getgid(),
 
36
                                   dirfd() */
38
37
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
39
38
                                   FD_SET(), FD_ISSET(), FD_CLR */
40
39
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
41
 
                                   WEXITSTATUS(), WTERMSIG() */
42
 
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
 
40
                                   WEXITSTATUS() */
 
41
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
43
42
#include <iso646.h>             /* and, or, not */
44
 
#include <dirent.h>             /* struct dirent, scandirat() */
45
 
#include <unistd.h>             /* fcntl(), F_GETFD, F_SETFD,
46
 
                                   FD_CLOEXEC, write(), STDOUT_FILENO,
47
 
                                   struct stat, fstat(), close(),
48
 
                                   setgid(), setuid(), S_ISREG(),
49
 
                                   faccessat() pipe2(), fork(),
50
 
                                   _exit(), dup2(), fexecve(), read()
51
 
                                */
 
43
#include <dirent.h>             /* DIR, struct dirent, opendir(),
 
44
                                   readdir(), closedir(), dirfd() */
 
45
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
 
46
                                   fcntl(), setuid(), setgid(),
 
47
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
 
48
                                   access(), pipe(), fork(), close()
 
49
                                   dup2, STDOUT_FILENO, _exit(),
 
50
                                   execv(), write(), read(),
 
51
                                   close() */
52
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
 
                                   FD_CLOEXEC, openat(), scandirat(),
54
 
                                   pipe2() */
55
 
#include <string.h>             /* strsep, strlen(), strsignal(),
56
 
                                   strcmp(), strncmp() */
 
53
                                   FD_CLOEXEC */
 
54
#include <string.h>             /* strsep, strlen(), asprintf() */
57
55
#include <errno.h>              /* errno */
58
56
#include <argp.h>               /* struct argp_option, struct
59
57
                                   argp_state, struct argp,
63
61
#include <signal.h>             /* struct sigaction, sigemptyset(),
64
62
                                   sigaddset(), sigaction(),
65
63
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
66
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
67
 
                                */
 
64
                                   SIG_UNBLOCK, kill() */
68
65
#include <errno.h>              /* errno, EBADF */
69
 
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
70
 
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_IOERR,
71
 
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
72
 
#include <errno.h>              /* errno */
73
 
#include <error.h>              /* error() */
74
 
#include <fnmatch.h>            /* fnmatch() */
75
66
 
76
67
#define BUFFER_SIZE 256
77
68
 
78
69
#define PDIR "/lib/mandos/plugins.d"
79
 
#define PHDIR "/lib/mandos/plugin-helpers"
80
70
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
81
71
 
82
 
const char *argp_program_version = "plugin-runner " VERSION;
83
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
72
const char *argp_program_version = "plugin-runner 1.0";
 
73
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
74
 
 
75
struct process;
 
76
 
 
77
typedef struct process{
 
78
  pid_t pid;
 
79
  int fd;
 
80
  char *buffer;
 
81
  size_t buffer_size;
 
82
  size_t buffer_length;
 
83
  bool eof;
 
84
  volatile bool completed;
 
85
  volatile int status;
 
86
  struct process *next;
 
87
} process;
84
88
 
85
89
typedef struct plugin{
86
90
  char *name;                   /* can be NULL or any plugin name */
89
93
  char **environ;
90
94
  int envc;
91
95
  bool disabled;
92
 
  
93
 
  /* Variables used for running processes*/
94
 
  pid_t pid;
95
 
  int fd;
96
 
  char *buffer;
97
 
  size_t buffer_size;
98
 
  size_t buffer_length;
99
 
  bool eof;
100
 
  volatile sig_atomic_t completed;
101
 
  int status;
102
96
  struct plugin *next;
103
97
} plugin;
104
98
 
105
 
static plugin *plugin_list = NULL;
106
 
 
107
 
/* Gets an existing plugin based on name,
108
 
   or if none is found, creates a new one */
109
 
__attribute__((warn_unused_result))
110
 
static plugin *getplugin(char *name){
111
 
  /* Check for existing plugin with that name */
112
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
113
 
    if((p->name == name)
114
 
       or (p->name and name and (strcmp(p->name, name) == 0))){
 
99
static plugin *getplugin(char *name, plugin **plugin_list){
 
100
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
 
101
    if ((p->name == name)
 
102
        or (p->name and name and (strcmp(p->name, name) == 0))){
115
103
      return p;
116
104
    }
117
105
  }
118
106
  /* Create a new plugin */
119
 
  plugin *new_plugin = NULL;
120
 
  do {
121
 
    new_plugin = malloc(sizeof(plugin));
122
 
  } while(new_plugin == NULL and errno == EINTR);
123
 
  if(new_plugin == NULL){
 
107
  plugin *new_plugin = malloc(sizeof(plugin));
 
108
  if (new_plugin == NULL){
124
109
    return NULL;
125
110
  }
126
111
  char *copy_name = NULL;
127
112
  if(name != NULL){
128
 
    do {
129
 
      copy_name = strdup(name);
130
 
    } while(copy_name == NULL and errno == EINTR);
 
113
    copy_name = strdup(name);
131
114
    if(copy_name == NULL){
132
 
      int e = errno;
133
 
      free(new_plugin);
134
 
      errno = e;
135
115
      return NULL;
136
116
    }
137
117
  }
138
118
  
139
 
  *new_plugin = (plugin){ .name = copy_name,
140
 
                          .argc = 1,
141
 
                          .disabled = false,
142
 
                          .next = plugin_list };
 
119
  *new_plugin = (plugin) { .name = copy_name,
 
120
                           .argc = 1,
 
121
                           .envc = 0,
 
122
                           .disabled = false,
 
123
                           .next = *plugin_list };
143
124
  
144
 
  do {
145
 
    new_plugin->argv = malloc(sizeof(char *) * 2);
146
 
  } while(new_plugin->argv == NULL and errno == EINTR);
147
 
  if(new_plugin->argv == NULL){
148
 
    int e = errno;
 
125
  new_plugin->argv = malloc(sizeof(char *) * 2);
 
126
  if (new_plugin->argv == NULL){
149
127
    free(copy_name);
150
128
    free(new_plugin);
151
 
    errno = e;
152
129
    return NULL;
153
130
  }
154
131
  new_plugin->argv[0] = copy_name;
155
132
  new_plugin->argv[1] = NULL;
156
 
  
157
 
  do {
158
 
    new_plugin->environ = malloc(sizeof(char *));
159
 
  } while(new_plugin->environ == NULL and errno == EINTR);
 
133
 
 
134
  new_plugin->environ = malloc(sizeof(char *));
160
135
  if(new_plugin->environ == NULL){
161
 
    int e = errno;
162
136
    free(copy_name);
163
137
    free(new_plugin->argv);
164
138
    free(new_plugin);
165
 
    errno = e;
166
139
    return NULL;
167
140
  }
168
141
  new_plugin->environ[0] = NULL;
169
 
  
170
142
  /* Append the new plugin to the list */
171
 
  plugin_list = new_plugin;
 
143
  *plugin_list = new_plugin;
172
144
  return new_plugin;
173
145
}
174
146
 
175
147
/* Helper function for add_argument and add_environment */
176
 
__attribute__((nonnull, warn_unused_result))
177
148
static bool add_to_char_array(const char *new, char ***array,
178
149
                              int *len){
179
150
  /* Resize the pointed-to array to hold one more pointer */
180
 
  char **new_array = NULL;
181
 
  do {
182
 
    new_array = realloc(*array, sizeof(char *)
183
 
                        * (size_t) ((*len) + 2));
184
 
  } while(new_array == NULL and errno == EINTR);
 
151
  *array = realloc(*array, sizeof(char *)
 
152
                   * (size_t) ((*len) + 2));
185
153
  /* Malloc check */
186
 
  if(new_array == NULL){
 
154
  if(*array == NULL){
187
155
    return false;
188
156
  }
189
 
  *array = new_array;
190
157
  /* Make a copy of the new string */
191
 
  char *copy;
192
 
  do {
193
 
    copy = strdup(new);
194
 
  } while(copy == NULL and errno == EINTR);
 
158
  char *copy = strdup(new);
195
159
  if(copy == NULL){
196
160
    return false;
197
161
  }
204
168
}
205
169
 
206
170
/* Add to a plugin's argument vector */
207
 
__attribute__((nonnull(2), warn_unused_result))
208
171
static bool add_argument(plugin *p, const char *arg){
209
172
  if(p == NULL){
210
173
    return false;
213
176
}
214
177
 
215
178
/* Add to a plugin's environment */
216
 
__attribute__((nonnull(2), warn_unused_result))
217
 
static bool add_environment(plugin *p, const char *def, bool replace){
 
179
static bool add_environment(plugin *p, const char *def){
218
180
  if(p == NULL){
219
181
    return false;
220
182
  }
221
 
  /* namelen = length of name of environment variable */
222
 
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
223
 
  /* Search for this environment variable */
224
 
  for(char **envdef = p->environ; *envdef != NULL; envdef++){
225
 
    if(strncmp(*envdef, def, namelen + 1) == 0){
226
 
      /* It already exists */
227
 
      if(replace){
228
 
        char *new_envdef;
229
 
        do {
230
 
          new_envdef = realloc(*envdef, strlen(def) + 1);
231
 
        } while(new_envdef == NULL and errno == EINTR);
232
 
        if(new_envdef == NULL){
233
 
          return false;
234
 
        }
235
 
        *envdef = new_envdef;
236
 
        strcpy(*envdef, def);
237
 
      }
238
 
      return true;
239
 
    }
240
 
  }
241
183
  return add_to_char_array(def, &(p->environ), &(p->envc));
242
184
}
243
185
 
244
 
#ifndef O_CLOEXEC
 
186
 
245
187
/*
246
188
 * Based on the example in the GNU LibC manual chapter 13.13 "File
247
189
 * Descriptor Flags".
248
 
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
 
190
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
249
191
 */
250
 
__attribute__((warn_unused_result))
251
 
static int set_cloexec_flag(int fd){
252
 
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
 
192
static int set_cloexec_flag(int fd)
 
193
{
 
194
  int ret = fcntl(fd, F_GETFD, 0);
253
195
  /* If reading the flags failed, return error indication now. */
254
196
  if(ret < 0){
255
197
    return ret;
256
198
  }
257
199
  /* Store modified flag word in the descriptor. */
258
 
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
259
 
                                       ret | FD_CLOEXEC));
 
200
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
260
201
}
261
 
#endif  /* not O_CLOEXEC */
262
202
 
 
203
process *process_list = NULL;
263
204
 
264
205
/* Mark processes as completed when they exit, and save their exit
265
206
   status. */
266
 
static void handle_sigchld(__attribute__((unused)) int sig){
267
 
  int old_errno = errno;
 
207
void handle_sigchld(__attribute__((unused)) int sig){
268
208
  while(true){
269
 
    plugin *proc = plugin_list;
 
209
    process *proc = process_list;
270
210
    int status;
271
211
    pid_t pid = waitpid(-1, &status, WNOHANG);
272
212
    if(pid == 0){
274
214
      break;
275
215
    }
276
216
    if(pid == -1){
277
 
      if(errno == ECHILD){
278
 
        /* No child processes */
279
 
        break;
 
217
      if (errno != ECHILD){
 
218
        perror("waitpid");
280
219
      }
281
 
      error(0, errno, "waitpid");
 
220
      /* No child processes */
 
221
      break;
282
222
    }
283
 
    
 
223
 
284
224
    /* A child exited, find it in process_list */
285
225
    while(proc != NULL and proc->pid != pid){
286
226
      proc = proc->next;
290
230
      continue;
291
231
    }
292
232
    proc->status = status;
293
 
    proc->completed = 1;
 
233
    proc->completed = true;
294
234
  }
295
 
  errno = old_errno;
296
235
}
297
236
 
298
 
/* Prints out a password to stdout */
299
 
__attribute__((nonnull, warn_unused_result))
300
 
static bool print_out_password(const char *buffer, size_t length){
 
237
bool print_out_password(const char *buffer, size_t length){
301
238
  ssize_t ret;
 
239
  if(length>0 and buffer[length-1] == '\n'){
 
240
    length--;
 
241
  }
302
242
  for(size_t written = 0; written < length; written += (size_t)ret){
303
243
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
304
244
                                   length - written));
309
249
  return true;
310
250
}
311
251
 
312
 
/* Removes and free a plugin from the plugin list */
313
 
__attribute__((nonnull))
314
 
static void free_plugin(plugin *plugin_node){
315
 
  
316
 
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
317
 
    free(*arg);
318
 
  }
319
 
  free(plugin_node->name);
320
 
  free(plugin_node->argv);
321
 
  for(char **env = plugin_node->environ; *env != NULL; env++){
322
 
    free(*env);
323
 
  }
324
 
  free(plugin_node->environ);
325
 
  free(plugin_node->buffer);
326
 
  
327
 
  /* Removes the plugin from the singly-linked list */
328
 
  if(plugin_node == plugin_list){
329
 
    /* First one - simple */
330
 
    plugin_list = plugin_list->next;
331
 
  } else {
332
 
    /* Second one or later */
333
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
334
 
      if(p->next == plugin_node){
335
 
        p->next = plugin_node->next;
336
 
        break;
337
 
      }
338
 
    }
339
 
  }
340
 
  
341
 
  free(plugin_node);
342
 
}
343
 
 
344
 
static void free_plugin_list(void){
345
 
  while(plugin_list != NULL){
346
 
    free_plugin(plugin_list);
 
252
static void free_plugin_list(plugin *plugin_list){
 
253
  for(plugin *next; plugin_list != NULL; plugin_list = next){
 
254
    next = plugin_list->next;
 
255
    for(char **arg = plugin_list->argv; *arg != NULL; arg++){
 
256
      free(*arg);
 
257
    }
 
258
    free(plugin_list->argv);
 
259
    for(char **env = plugin_list->environ; *env != NULL; env++){
 
260
      free(*env);
 
261
    }
 
262
    free(plugin_list->environ);
 
263
    free(plugin_list);
347
264
  }
348
265
}
349
266
 
350
267
int main(int argc, char *argv[]){
351
268
  char *plugindir = NULL;
352
 
  char *pluginhelperdir = NULL;
353
269
  char *argfile = NULL;
354
270
  FILE *conffp;
355
 
  struct dirent **direntries = NULL;
 
271
  size_t d_name_len;
 
272
  DIR *dir = NULL;
 
273
  struct dirent *dirst;
356
274
  struct stat st;
357
275
  fd_set rfds_all;
358
276
  int ret, maxfd = 0;
359
 
  ssize_t sret;
360
277
  uid_t uid = 65534;
361
278
  gid_t gid = 65534;
362
279
  bool debug = false;
366
283
                                      .sa_flags = SA_NOCLDSTOP };
367
284
  char **custom_argv = NULL;
368
285
  int custom_argc = 0;
369
 
  int dir_fd = -1;
370
286
  
371
287
  /* Establish a signal handler */
372
288
  sigemptyset(&sigchld_action.sa_mask);
373
289
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
374
290
  if(ret == -1){
375
 
    error(0, errno, "sigaddset");
376
 
    exitstatus = EX_OSERR;
 
291
    perror("sigaddset");
 
292
    exitstatus = EXIT_FAILURE;
377
293
    goto fallback;
378
294
  }
379
295
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
380
296
  if(ret == -1){
381
 
    error(0, errno, "sigaction");
382
 
    exitstatus = EX_OSERR;
 
297
    perror("sigaction");
 
298
    exitstatus = EXIT_FAILURE;
383
299
    goto fallback;
384
300
  }
385
301
  
388
304
    { .name = "global-options", .key = 'g',
389
305
      .arg = "OPTION[,OPTION[,...]]",
390
306
      .doc = "Options passed to all plugins" },
391
 
    { .name = "global-env", .key = 'G',
 
307
    { .name = "global-envs", .key = 'e',
392
308
      .arg = "VAR=value",
393
309
      .doc = "Environment variable passed to all plugins" },
394
310
    { .name = "options-for", .key = 'o',
395
311
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
396
312
      .doc = "Options passed only to specified plugin" },
397
 
    { .name = "env-for", .key = 'E',
 
313
    { .name = "envs-for", .key = 'f',
398
314
      .arg = "PLUGIN:ENV=value",
399
315
      .doc = "Environment variable passed to specified plugin" },
400
316
    { .name = "disable", .key = 'd',
401
317
      .arg = "PLUGIN",
402
318
      .doc = "Disable a specific plugin", .group = 1 },
403
 
    { .name = "enable", .key = 'e',
404
 
      .arg = "PLUGIN",
405
 
      .doc = "Enable a specific plugin", .group = 1 },
406
319
    { .name = "plugin-dir", .key = 128,
407
320
      .arg = "DIRECTORY",
408
321
      .doc = "Specify a different plugin directory", .group = 2 },
417
330
      .doc = "Group ID the plugins will run as", .group = 3 },
418
331
    { .name = "debug", .key = 132,
419
332
      .doc = "Debug mode", .group = 4 },
420
 
    { .name = "plugin-helper-dir", .key = 133,
421
 
      .arg = "DIRECTORY",
422
 
      .doc = "Specify a different plugin helper directory",
423
 
      .group = 2 },
424
 
    /*
425
 
     * These reproduce what we would get without ARGP_NO_HELP
426
 
     */
427
 
    { .name = "help", .key = '?',
428
 
      .doc = "Give this help list", .group = -1 },
429
 
    { .name = "usage", .key = -3,
430
 
      .doc = "Give a short usage message", .group = -1 },
431
 
    { .name = "version", .key = 'V',
432
 
      .doc = "Print program version", .group = -1 },
433
333
    { .name = NULL }
434
334
  };
435
335
  
436
 
  __attribute__((nonnull(3)))
437
 
  error_t parse_opt(int key, char *arg, struct argp_state *state){
438
 
    errno = 0;
439
 
    switch(key){
440
 
      char *tmp;
441
 
      intmax_t tmp_id;
442
 
    case 'g':                   /* --global-options */
443
 
      {
444
 
        char *plugin_option;
445
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
446
 
          if(not add_argument(getplugin(NULL), plugin_option)){
447
 
            break;
448
 
          }
449
 
        }
450
 
        errno = 0;
451
 
      }
452
 
      break;
453
 
    case 'G':                   /* --global-env */
454
 
      if(add_environment(getplugin(NULL), arg, true)){
455
 
        errno = 0;
456
 
      }
457
 
      break;
458
 
    case 'o':                   /* --options-for */
459
 
      {
460
 
        char *option_list = strchr(arg, ':');
461
 
        if(option_list == NULL){
462
 
          argp_error(state, "No colon in \"%s\"", arg);
463
 
          errno = EINVAL;
464
 
          break;
465
 
        }
466
 
        *option_list = '\0';
467
 
        option_list++;
468
 
        if(arg[0] == '\0'){
469
 
          argp_error(state, "Empty plugin name");
470
 
          errno = EINVAL;
471
 
          break;
472
 
        }
473
 
        char *option;
474
 
        while((option = strsep(&option_list, ",")) != NULL){
475
 
          if(not add_argument(getplugin(arg), option)){
476
 
            break;
477
 
          }
478
 
        }
479
 
        errno = 0;
480
 
      }
481
 
      break;
482
 
    case 'E':                   /* --env-for */
 
336
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
 
337
    /* Get the INPUT argument from `argp_parse', which we know is a
 
338
       pointer to our plugin list pointer. */
 
339
    plugin **plugins = state->input;
 
340
    switch (key) {
 
341
    case 'g':
 
342
      if (arg != NULL){
 
343
        char *p;
 
344
        while((p = strsep(&arg, ",")) != NULL){
 
345
          if(p[0] == '\0'){
 
346
            continue;
 
347
          }
 
348
          if(not add_argument(getplugin(NULL, plugins), p)){
 
349
            perror("add_argument");
 
350
            return ARGP_ERR_UNKNOWN;
 
351
          }
 
352
        }
 
353
      }
 
354
      break;
 
355
    case 'e':
 
356
      if(arg == NULL){
 
357
        break;
 
358
      }
 
359
      {
 
360
        char *envdef = strdup(arg);
 
361
        if(envdef == NULL){
 
362
          break;
 
363
        }
 
364
        if(not add_environment(getplugin(NULL, plugins), envdef)){
 
365
          perror("add_environment");
 
366
        }
 
367
      }
 
368
      break;
 
369
    case 'o':
 
370
      if (arg != NULL){
 
371
        char *p_name = strsep(&arg, ":");
 
372
        if(p_name[0] == '\0'){
 
373
          break;
 
374
        }
 
375
        char *opt = strsep(&arg, ":");
 
376
        if(opt[0] == '\0'){
 
377
          break;
 
378
        }
 
379
        if(opt != NULL){
 
380
          char *p;
 
381
          while((p = strsep(&opt, ",")) != NULL){
 
382
            if(p[0] == '\0'){
 
383
              continue;
 
384
            }
 
385
            if(not add_argument(getplugin(p_name, plugins), p)){
 
386
              perror("add_argument");
 
387
              return ARGP_ERR_UNKNOWN;
 
388
            }
 
389
          }
 
390
        }
 
391
      }
 
392
      break;
 
393
    case 'f':
 
394
      if(arg == NULL){
 
395
        break;
 
396
      }
483
397
      {
484
398
        char *envdef = strchr(arg, ':');
485
399
        if(envdef == NULL){
486
 
          argp_error(state, "No colon in \"%s\"", arg);
487
 
          errno = EINVAL;
488
 
          break;
489
 
        }
490
 
        *envdef = '\0';
 
400
          break;
 
401
        }
 
402
        char *p_name = strndup(arg, (size_t) (envdef-arg));
 
403
        if(p_name == NULL){
 
404
          break;
 
405
        }
491
406
        envdef++;
492
 
        if(arg[0] == '\0'){
493
 
          argp_error(state, "Empty plugin name");
494
 
          errno = EINVAL;
495
 
          break;
496
 
        }
497
 
        if(add_environment(getplugin(arg), envdef, true)){
498
 
          errno = 0;
499
 
        }
500
 
      }
501
 
      break;
502
 
    case 'd':                   /* --disable */
503
 
      {
504
 
        plugin *p = getplugin(arg);
505
 
        if(p != NULL){
506
 
          p->disabled = true;
507
 
          errno = 0;
508
 
        }
509
 
      }
510
 
      break;
511
 
    case 'e':                   /* --enable */
512
 
      {
513
 
        plugin *p = getplugin(arg);
514
 
        if(p != NULL){
515
 
          p->disabled = false;
516
 
          errno = 0;
517
 
        }
518
 
      }
519
 
      break;
520
 
    case 128:                   /* --plugin-dir */
521
 
      free(plugindir);
 
407
        if(not add_environment(getplugin(p_name, plugins), envdef)){
 
408
          perror("add_environment");
 
409
        }
 
410
      }
 
411
      break;
 
412
    case 'd':
 
413
      if (arg != NULL){
 
414
        plugin *p = getplugin(arg, plugins);
 
415
        if(p == NULL){
 
416
          return ARGP_ERR_UNKNOWN;
 
417
        }
 
418
        p->disabled = true;
 
419
      }
 
420
      break;
 
421
    case 128:
522
422
      plugindir = strdup(arg);
523
 
      if(plugindir != NULL){
524
 
        errno = 0;
525
 
      }
526
 
      break;
527
 
    case 129:                   /* --config-file */
528
 
      /* This is already done by parse_opt_config_file() */
529
 
      break;
530
 
    case 130:                   /* --userid */
531
 
      tmp_id = strtoimax(arg, &tmp, 10);
532
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
533
 
         or tmp_id != (uid_t)tmp_id){
534
 
        argp_error(state, "Bad user ID number: \"%s\", using %"
535
 
                   PRIdMAX, arg, (intmax_t)uid);
536
 
        break;
537
 
      }
538
 
      uid = (uid_t)tmp_id;
539
 
      errno = 0;
540
 
      break;
541
 
    case 131:                   /* --groupid */
542
 
      tmp_id = strtoimax(arg, &tmp, 10);
543
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
544
 
         or tmp_id != (gid_t)tmp_id){
545
 
        argp_error(state, "Bad group ID number: \"%s\", using %"
546
 
                   PRIdMAX, arg, (intmax_t)gid);
547
 
        break;
548
 
      }
549
 
      gid = (gid_t)tmp_id;
550
 
      errno = 0;
551
 
      break;
552
 
    case 132:                   /* --debug */
 
423
      if(plugindir == NULL){
 
424
        perror("strdup");
 
425
      }      
 
426
      break;
 
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
432
      break;      
 
433
    case 130:
 
434
      uid = (uid_t)strtol(arg, NULL, 10);
 
435
      break;
 
436
    case 131:
 
437
      gid = (gid_t)strtol(arg, NULL, 10);
 
438
      break;
 
439
    case 132:
553
440
      debug = true;
554
441
      break;
555
 
    case 133:                   /* --plugin-helper-dir */
556
 
      free(pluginhelperdir);
557
 
      pluginhelperdir = strdup(arg);
558
 
      if(pluginhelperdir != NULL){
559
 
        errno = 0;
560
 
      }
561
 
      break;
562
 
      /*
563
 
       * These reproduce what we would get without ARGP_NO_HELP
564
 
       */
565
 
    case '?':                   /* --help */
566
 
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
567
 
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
568
 
      __builtin_unreachable();
569
 
    case -3:                    /* --usage */
570
 
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
571
 
      argp_state_help(state, state->out_stream,
572
 
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
573
 
      __builtin_unreachable();
574
 
    case 'V':                   /* --version */
575
 
      fprintf(state->out_stream, "%s\n", argp_program_version);
576
 
      exit(EXIT_SUCCESS);
577
 
      break;
578
 
/*
579
 
 * When adding more options before this line, remember to also add a
580
 
 * "case" to the "parse_opt_config_file" function below.
581
 
 */
582
 
    case ARGP_KEY_ARG:
583
 
      /* Cryptsetup always passes an argument, which is an empty
584
 
         string if "none" was specified in /etc/crypttab.  So if
585
 
         argument was empty, we ignore it silently. */
586
 
      if(arg[0] == '\0'){
587
 
        break;
588
 
      }
589
 
#if __GNUC__ >= 7
590
 
      __attribute__((fallthrough));
591
 
#else
592
 
          /* FALLTHROUGH */
593
 
#endif
594
 
    default:
595
 
      return ARGP_ERR_UNKNOWN;
596
 
    }
597
 
    return errno;               /* Set to 0 at start */
598
 
  }
599
 
  
600
 
  /* This option parser is the same as parse_opt() above, except it
601
 
     ignores everything but the --config-file option. */
602
 
  error_t parse_opt_config_file(int key, char *arg,
603
 
                                __attribute__((unused))
604
 
                                struct argp_state *state){
605
 
    errno = 0;
606
 
    switch(key){
607
 
    case 'g':                   /* --global-options */
608
 
    case 'G':                   /* --global-env */
609
 
    case 'o':                   /* --options-for */
610
 
    case 'E':                   /* --env-for */
611
 
    case 'd':                   /* --disable */
612
 
    case 'e':                   /* --enable */
613
 
    case 128:                   /* --plugin-dir */
614
 
      break;
615
 
    case 129:                   /* --config-file */
616
 
      free(argfile);
617
 
      argfile = strdup(arg);
618
 
      if(argfile != NULL){
619
 
        errno = 0;
620
 
      }
621
 
      break;
622
 
    case 130:                   /* --userid */
623
 
    case 131:                   /* --groupid */
624
 
    case 132:                   /* --debug */
625
 
    case 133:                   /* --plugin-helper-dir */
626
 
    case '?':                   /* --help */
627
 
    case -3:                    /* --usage */
628
 
    case 'V':                   /* --version */
629
 
    case ARGP_KEY_ARG:
630
 
      break;
631
 
    default:
632
 
      return ARGP_ERR_UNKNOWN;
633
 
    }
634
 
    return errno;
635
 
  }
636
 
  
637
 
  struct argp argp = { .options = options,
638
 
                       .parser = parse_opt_config_file,
639
 
                       .args_doc = "",
 
442
    case ARGP_KEY_ARG:
 
443
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
 
444
      break;
 
445
    case ARGP_KEY_END:
 
446
      break;
 
447
    default:
 
448
      return ARGP_ERR_UNKNOWN;
 
449
    }
 
450
    return 0;
 
451
  }
 
452
  
 
453
  plugin *plugin_list = NULL;
 
454
  
 
455
  struct argp argp = { .options = options, .parser = parse_opt,
 
456
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
640
457
                       .doc = "Mandos plugin runner -- Run plugins" };
641
458
  
642
 
  /* Parse using parse_opt_config_file() in order to get the custom
643
 
     config file location, if any. */
644
 
  ret = argp_parse(&argp, argc, argv,
645
 
                   ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
646
 
                   NULL, NULL);
647
 
  switch(ret){
648
 
  case 0:
649
 
    break;
650
 
  case ENOMEM:
651
 
  default:
652
 
    errno = ret;
653
 
    error(0, errno, "argp_parse");
654
 
    exitstatus = EX_OSERR;
655
 
    goto fallback;
656
 
  case EINVAL:
657
 
    exitstatus = EX_USAGE;
 
459
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
 
460
  if (ret == ARGP_ERR_UNKNOWN){
 
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
 
462
    exitstatus = EXIT_FAILURE;
658
463
    goto fallback;
659
464
  }
660
 
  
661
 
  /* Reset to the normal argument parser */
662
 
  argp.parser = parse_opt;
663
 
  
664
 
  /* Open the configfile if available */
665
 
  if(argfile == NULL){
 
465
 
 
466
  if (argfile == NULL){
666
467
    conffp = fopen(AFILE, "r");
667
468
  } else {
668
469
    conffp = fopen(argfile, "r");
669
470
  }
 
471
  
670
472
  if(conffp != NULL){
671
473
    char *org_line = NULL;
672
474
    char *p, *arg, *new_arg, *line;
673
475
    size_t size = 0;
 
476
    ssize_t sret;
674
477
    const char whitespace_delims[] = " \r\t\f\v\n";
675
478
    const char comment_delim[] = "#";
676
 
    
 
479
 
677
480
    custom_argc = 1;
678
481
    custom_argv = malloc(sizeof(char*) * 2);
679
482
    if(custom_argv == NULL){
680
 
      error(0, errno, "malloc");
681
 
      exitstatus = EX_OSERR;
 
483
      perror("malloc");
 
484
      exitstatus = EXIT_FAILURE;
682
485
      goto fallback;
683
486
    }
684
487
    custom_argv[0] = argv[0];
685
488
    custom_argv[1] = NULL;
686
489
    
687
 
    /* for each line in the config file, strip whitespace and ignore
688
 
       commented text */
689
490
    while(true){
690
491
      sret = getline(&org_line, &size, conffp);
691
492
      if(sret == -1){
692
493
        break;
693
494
      }
694
 
      
 
495
 
695
496
      line = org_line;
696
497
      arg = strsep(&line, comment_delim);
697
498
      while((p = strsep(&arg, whitespace_delims)) != NULL){
700
501
        }
701
502
        new_arg = strdup(p);
702
503
        if(new_arg == NULL){
703
 
          error(0, errno, "strdup");
704
 
          exitstatus = EX_OSERR;
 
504
          perror("strdup");
 
505
          exitstatus = EXIT_FAILURE;
705
506
          free(org_line);
706
507
          goto fallback;
707
508
        }
708
509
        
709
510
        custom_argc += 1;
710
 
        {
711
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
712
 
                                    * ((size_t)custom_argc + 1));
713
 
          if(new_argv == NULL){
714
 
            error(0, errno, "realloc");
715
 
            exitstatus = EX_OSERR;
716
 
            free(new_arg);
717
 
            free(org_line);
718
 
            goto fallback;
719
 
          } else {
720
 
            custom_argv = new_argv;
721
 
          }
 
511
        custom_argv = realloc(custom_argv, sizeof(char *)
 
512
                              * ((unsigned int) custom_argc + 1));
 
513
        if(custom_argv == NULL){
 
514
          perror("realloc");
 
515
          exitstatus = EXIT_FAILURE;
 
516
          free(org_line);
 
517
          goto fallback;
722
518
        }
723
519
        custom_argv[custom_argc-1] = new_arg;
724
 
        custom_argv[custom_argc] = NULL;
 
520
        custom_argv[custom_argc] = NULL;        
725
521
      }
726
522
    }
727
 
    do {
728
 
      ret = fclose(conffp);
729
 
    } while(ret == EOF and errno == EINTR);
730
 
    if(ret == EOF){
731
 
      error(0, errno, "fclose");
732
 
      exitstatus = EX_IOERR;
733
 
      goto fallback;
734
 
    }
735
523
    free(org_line);
736
 
  } else {
 
524
  } else{
737
525
    /* Check for harmful errors and go to fallback. Other errors might
738
526
       not affect opening plugins */
739
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
740
 
      error(0, errno, "fopen");
741
 
      exitstatus = EX_OSERR;
 
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
528
      perror("fopen");
 
529
      exitstatus = EXIT_FAILURE;
742
530
      goto fallback;
743
531
    }
744
532
  }
745
 
  /* If there were any arguments from the configuration file, pass
746
 
     them to parser as command line arguments */
 
533
 
747
534
  if(custom_argv != NULL){
748
 
    ret = argp_parse(&argp, custom_argc, custom_argv,
749
 
                     ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
750
 
                     NULL, NULL);
751
 
    switch(ret){
752
 
    case 0:
753
 
      break;
754
 
    case ENOMEM:
755
 
    default:
756
 
      errno = ret;
757
 
      error(0, errno, "argp_parse");
758
 
      exitstatus = EX_OSERR;
759
 
      goto fallback;
760
 
    case EINVAL:
761
 
      exitstatus = EX_CONFIG;
762
 
      goto fallback;
763
 
    }
764
 
  }
765
 
  
766
 
  /* Parse actual command line arguments, to let them override the
767
 
     config file */
768
 
  ret = argp_parse(&argp, argc, argv,
769
 
                   ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
770
 
                   NULL, NULL);
771
 
  switch(ret){
772
 
  case 0:
773
 
    break;
774
 
  case ENOMEM:
775
 
  default:
776
 
    errno = ret;
777
 
    error(0, errno, "argp_parse");
778
 
    exitstatus = EX_OSERR;
779
 
    goto fallback;
780
 
  case EINVAL:
781
 
    exitstatus = EX_USAGE;
782
 
    goto fallback;
783
 
  }
784
 
  
785
 
  {
786
 
    char *pluginhelperenv;
787
 
    bool bret = true;
788
 
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
789
 
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
790
 
    if(ret != -1){
791
 
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
792
 
    }
793
 
    if(ret == -1 or not bret){
794
 
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
795
 
            " environment variable to \"%s\" for all plugins\n",
796
 
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
797
 
    }
798
 
    if(ret != -1){
799
 
      free(pluginhelperenv);
 
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
 
536
    if (ret == ARGP_ERR_UNKNOWN){
 
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
538
      exitstatus = EXIT_FAILURE;
 
539
      goto fallback;
800
540
    }
801
541
  }
802
542
  
803
543
  if(debug){
804
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
 
544
    for(plugin *p = plugin_list; p != NULL; p=p->next){
805
545
      fprintf(stderr, "Plugin: %s has %d arguments\n",
806
546
              p->name ? p->name : "Global", p->argc - 1);
807
547
      for(char **a = p->argv; *a != NULL; a++){
808
548
        fprintf(stderr, "\tArg: %s\n", *a);
809
549
      }
810
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
550
      fprintf(stderr, "...and %u environment variables\n", p->envc);
811
551
      for(char **a = p->environ; *a != NULL; a++){
812
552
        fprintf(stderr, "\t%s\n", *a);
813
553
      }
814
554
    }
815
555
  }
816
556
  
817
 
  if(getuid() == 0){
818
 
    /* Work around Debian bug #633582:
819
 
       <https://bugs.debian.org/633582> */
820
 
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
821
 
    if(plugindir_fd == -1){
822
 
      if(errno != ENOENT){
823
 
        error(0, errno, "open(\"" PDIR "\")");
824
 
      }
825
 
    } else {
826
 
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
827
 
      if(ret == -1){
828
 
        error(0, errno, "fstat");
829
 
      } else {
830
 
        if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
831
 
          ret = fchown(plugindir_fd, uid, gid);
832
 
          if(ret == -1){
833
 
            error(0, errno, "fchown");
834
 
          }
835
 
        }
836
 
      }
837
 
      close(plugindir_fd);
838
 
    }
839
 
  }
840
 
  
841
 
  /* Lower permissions */
842
 
  ret = setgid(gid);
843
 
  if(ret == -1){
844
 
    error(0, errno, "setgid");
845
 
  }
846
557
  ret = setuid(uid);
847
 
  if(ret == -1){
848
 
    error(0, errno, "setuid");
849
 
  }
850
 
  
851
 
  /* Open plugin directory with close_on_exec flag */
 
558
  if (ret == -1){
 
559
    perror("setuid");
 
560
  }
 
561
  
 
562
  setgid(gid);
 
563
  if (ret == -1){
 
564
    perror("setgid");
 
565
  }
 
566
 
 
567
  if (plugindir == NULL){
 
568
    dir = opendir(PDIR);
 
569
  } else {
 
570
    dir = opendir(plugindir);
 
571
  }
 
572
  
 
573
  if(dir == NULL){
 
574
    perror("Could not open plugin dir");
 
575
    exitstatus = EXIT_FAILURE;
 
576
    goto fallback;
 
577
  }
 
578
  
 
579
  /* Set the FD_CLOEXEC flag on the directory, if possible */
852
580
  {
853
 
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
854
 
#ifdef O_CLOEXEC
855
 
                  O_CLOEXEC
856
 
#else  /* not O_CLOEXEC */
857
 
                  0
858
 
#endif  /* not O_CLOEXEC */
859
 
                  );
860
 
    if(dir_fd == -1){
861
 
      error(0, errno, "Could not open plugin dir");
862
 
      exitstatus = EX_UNAVAILABLE;
863
 
      goto fallback;
864
 
    }
865
 
    
866
 
#ifndef O_CLOEXEC
867
 
  /* Set the FD_CLOEXEC flag on the directory */
868
 
    ret = set_cloexec_flag(dir_fd);
869
 
    if(ret < 0){
870
 
      error(0, errno, "set_cloexec_flag");
871
 
      exitstatus = EX_OSERR;
872
 
      goto fallback;
873
 
    }
874
 
#endif  /* O_CLOEXEC */
875
 
  }
876
 
  
877
 
  int good_name(const struct dirent * const dirent){
878
 
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
879
 
                                      "*.dpkg-old", "*.dpkg-bak",
880
 
                                      "*.dpkg-divert", NULL };
881
 
#ifdef __GNUC__
882
 
#pragma GCC diagnostic push
883
 
#pragma GCC diagnostic ignored "-Wcast-qual"
884
 
#endif
885
 
    for(const char **pat = (const char **)patterns;
886
 
        *pat != NULL; pat++){
887
 
#ifdef __GNUC__
888
 
#pragma GCC diagnostic pop
889
 
#endif
890
 
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
891
 
         != FNM_NOMATCH){
892
 
        if(debug){
893
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
894
 
                    " matching pattern %s\n", dirent->d_name, *pat);
895
 
        }
896
 
        return 0;
 
581
    int dir_fd = dirfd(dir);
 
582
    if(dir_fd >= 0){
 
583
      ret = set_cloexec_flag(dir_fd);
 
584
      if(ret < 0){
 
585
        perror("set_cloexec_flag");
 
586
        exitstatus = EXIT_FAILURE;
 
587
        goto fallback;
897
588
      }
898
589
    }
899
 
    return 1;
900
 
  }
901
 
  
902
 
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
903
 
                             alphasort);
904
 
  if(numplugins == -1){
905
 
    error(0, errno, "Could not scan plugin dir");
906
 
    direntries = NULL;
907
 
    exitstatus = EX_OSERR;
908
 
    goto fallback;
909
590
  }
910
591
  
911
592
  FD_ZERO(&rfds_all);
912
593
  
913
 
  /* Read and execute any executable in the plugin directory*/
914
 
  for(int i = 0; i < numplugins; i++){
915
 
    
916
 
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
917
 
    if(plugin_fd == -1){
918
 
      error(0, errno, "Could not open plugin");
919
 
      free(direntries[i]);
920
 
      continue;
921
 
    }
922
 
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
923
 
    if(ret == -1){
924
 
      error(0, errno, "stat");
925
 
      close(plugin_fd);
926
 
      free(direntries[i]);
927
 
      continue;
928
 
    }
929
 
    
930
 
    /* Ignore non-executable files */
931
 
    if(not S_ISREG(st.st_mode)
932
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
933
 
                                        X_OK, 0)) != 0)){
 
594
  while(true){
 
595
    dirst = readdir(dir);
 
596
    
 
597
    // All directory entries have been processed
 
598
    if(dirst == NULL){
 
599
      if (errno == EBADF){
 
600
        perror("readdir");
 
601
        exitstatus = EXIT_FAILURE;
 
602
        goto fallback;
 
603
      }
 
604
      break;
 
605
    }
 
606
    
 
607
    d_name_len = strlen(dirst->d_name);
 
608
    
 
609
    // Ignore dotfiles, backup files and other junk
 
610
    {
 
611
      bool bad_name = false;
 
612
      
 
613
      const char const *bad_prefixes[] = { ".", "#", NULL };
 
614
      
 
615
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
 
616
                                           ".dpkg-old",
 
617
                                           ".dpkg-divert", NULL };
 
618
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
 
619
        size_t pre_len = strlen(*pre);
 
620
        if((d_name_len >= pre_len)
 
621
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
 
622
          if(debug){
 
623
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
624
                    " with bad prefix %s\n", dirst->d_name, *pre);
 
625
          }
 
626
          bad_name = true;
 
627
          break;
 
628
        }
 
629
      }
 
630
      
 
631
      if(bad_name){
 
632
        continue;
 
633
      }
 
634
      
 
635
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
 
636
        size_t suf_len = strlen(*suf);
 
637
        if((d_name_len >= suf_len)
 
638
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
 
639
                == 0)){
 
640
          if(debug){
 
641
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
642
                    " with bad suffix %s\n", dirst->d_name, *suf);
 
643
          }
 
644
          bad_name = true;
 
645
          break;
 
646
        }
 
647
      }
 
648
      
 
649
      if(bad_name){
 
650
        continue;
 
651
      }
 
652
    }
 
653
 
 
654
    char *filename;
 
655
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
656
    if(ret < 0){
 
657
      perror("asprintf");
 
658
      continue;
 
659
    }
 
660
    
 
661
    ret = stat(filename, &st);
 
662
    if (ret == -1){
 
663
      perror("stat");
 
664
      free(filename);
 
665
      continue;
 
666
    }
 
667
    
 
668
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
934
669
      if(debug){
935
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
936
 
                " with bad type or mode\n",
937
 
                plugindir != NULL ? plugindir : PDIR,
938
 
                direntries[i]->d_name);
 
670
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
671
                " with bad type or mode\n", filename);
939
672
      }
940
 
      close(plugin_fd);
941
 
      free(direntries[i]);
 
673
      free(filename);
942
674
      continue;
943
675
    }
944
 
    
945
 
    plugin *p = getplugin(direntries[i]->d_name);
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
946
677
    if(p == NULL){
947
 
      error(0, errno, "getplugin");
948
 
      close(plugin_fd);
949
 
      free(direntries[i]);
 
678
      perror("getplugin");
 
679
      free(filename);
950
680
      continue;
951
681
    }
952
682
    if(p->disabled){
953
683
      if(debug){
954
684
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
955
 
                direntries[i]->d_name);
 
685
                dirst->d_name);
956
686
      }
957
 
      close(plugin_fd);
958
 
      free(direntries[i]);
 
687
      free(filename);
959
688
      continue;
960
689
    }
961
690
    {
962
691
      /* Add global arguments to argument list for this plugin */
963
 
      plugin *g = getplugin(NULL);
 
692
      plugin *g = getplugin(NULL, &plugin_list);
964
693
      if(g != NULL){
965
694
        for(char **a = g->argv + 1; *a != NULL; a++){
966
695
          if(not add_argument(p, *a)){
967
 
            error(0, errno, "add_argument");
 
696
            perror("add_argument");
968
697
          }
969
698
        }
970
699
        /* Add global environment variables */
971
700
        for(char **e = g->environ; *e != NULL; e++){
972
 
          if(not add_environment(p, *e, false)){
973
 
            error(0, errno, "add_environment");
 
701
          if(not add_environment(p, *e)){
 
702
            perror("add_environment");
974
703
          }
975
704
        }
976
705
      }
977
706
    }
978
 
    /* If this plugin has any environment variables, we need to
979
 
       duplicate the environment from this process, too. */
 
707
    /* If this plugin has any environment variables, we will call
 
708
       using execve and need to duplicate the environment from this
 
709
       process, too. */
980
710
    if(p->environ[0] != NULL){
981
711
      for(char **e = environ; *e != NULL; e++){
982
 
        if(not add_environment(p, *e, false)){
983
 
          error(0, errno, "add_environment");
 
712
        char *copy = strdup(*e);
 
713
        if(copy == NULL){
 
714
          perror("strdup");
 
715
          continue;
 
716
        }
 
717
        if(not add_environment(p, copy)){
 
718
          perror("add_environment");
984
719
        }
985
720
      }
986
721
    }
987
722
    
988
723
    int pipefd[2];
989
 
#ifndef O_CLOEXEC
990
 
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
991
 
#else  /* O_CLOEXEC */
992
 
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
993
 
#endif  /* O_CLOEXEC */
994
 
    if(ret == -1){
995
 
      error(0, errno, "pipe");
996
 
      exitstatus = EX_OSERR;
997
 
      free(direntries[i]);
998
 
      goto fallback;
999
 
    }
1000
 
    if(pipefd[0] >= FD_SETSIZE){
1001
 
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
1002
 
              FD_SETSIZE);
1003
 
      close(pipefd[0]);
1004
 
      close(pipefd[1]);
1005
 
      exitstatus = EX_OSERR;
1006
 
      free(direntries[i]);
1007
 
      goto fallback;
1008
 
    }
1009
 
#ifndef O_CLOEXEC
1010
 
    /* Ask OS to automatic close the pipe on exec */
 
724
    ret = pipe(pipefd);
 
725
    if (ret == -1){
 
726
      perror("pipe");
 
727
      exitstatus = EXIT_FAILURE;
 
728
      goto fallback;
 
729
    }
1011
730
    ret = set_cloexec_flag(pipefd[0]);
1012
731
    if(ret < 0){
1013
 
      error(0, errno, "set_cloexec_flag");
1014
 
      close(pipefd[0]);
1015
 
      close(pipefd[1]);
1016
 
      exitstatus = EX_OSERR;
1017
 
      free(direntries[i]);
 
732
      perror("set_cloexec_flag");
 
733
      exitstatus = EXIT_FAILURE;
1018
734
      goto fallback;
1019
735
    }
1020
736
    ret = set_cloexec_flag(pipefd[1]);
1021
737
    if(ret < 0){
1022
 
      error(0, errno, "set_cloexec_flag");
1023
 
      close(pipefd[0]);
1024
 
      close(pipefd[1]);
1025
 
      exitstatus = EX_OSERR;
1026
 
      free(direntries[i]);
 
738
      perror("set_cloexec_flag");
 
739
      exitstatus = EXIT_FAILURE;
1027
740
      goto fallback;
1028
741
    }
1029
 
#endif  /* not O_CLOEXEC */
1030
742
    /* Block SIGCHLD until process is safely in process list */
1031
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1032
 
                                              &sigchld_action.sa_mask,
1033
 
                                              NULL));
 
743
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1034
744
    if(ret < 0){
1035
 
      error(0, errno, "sigprocmask");
1036
 
      exitstatus = EX_OSERR;
1037
 
      free(direntries[i]);
 
745
      perror("sigprocmask");
 
746
      exitstatus = EXIT_FAILURE;
1038
747
      goto fallback;
1039
748
    }
1040
 
    /* Starting a new process to be watched */
1041
 
    pid_t pid;
1042
 
    do {
1043
 
      pid = fork();
1044
 
    } while(pid == -1 and errno == EINTR);
 
749
    // Starting a new process to be watched
 
750
    pid_t pid = fork();
1045
751
    if(pid == -1){
1046
 
      error(0, errno, "fork");
1047
 
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1048
 
                                     &sigchld_action.sa_mask, NULL));
1049
 
      close(pipefd[0]);
1050
 
      close(pipefd[1]);
1051
 
      exitstatus = EX_OSERR;
1052
 
      free(direntries[i]);
 
752
      perror("fork");
 
753
      exitstatus = EXIT_FAILURE;
1053
754
      goto fallback;
1054
755
    }
1055
756
    if(pid == 0){
1056
757
      /* this is the child process */
1057
758
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1058
759
      if(ret < 0){
1059
 
        error(0, errno, "sigaction");
1060
 
        _exit(EX_OSERR);
 
760
        perror("sigaction");
 
761
        _exit(EXIT_FAILURE);
1061
762
      }
1062
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
763
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1063
764
      if(ret < 0){
1064
 
        error(0, errno, "sigprocmask");
1065
 
        _exit(EX_OSERR);
 
765
        perror("sigprocmask");
 
766
        _exit(EXIT_FAILURE);
1066
767
      }
1067
 
      
 
768
 
1068
769
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
1069
770
      if(ret == -1){
1070
 
        error(0, errno, "dup2");
1071
 
        _exit(EX_OSERR);
 
771
        perror("dup2");
 
772
        _exit(EXIT_FAILURE);
1072
773
      }
1073
774
      
1074
 
      if(fexecve(plugin_fd, p->argv,
1075
 
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1076
 
        error(0, errno, "fexecve for %s/%s",
1077
 
              plugindir != NULL ? plugindir : PDIR,
1078
 
              direntries[i]->d_name);
1079
 
        _exit(EX_OSERR);
 
775
      if(dirfd(dir) < 0){
 
776
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
 
777
           above and must now close it manually here. */
 
778
        closedir(dir);
 
779
      }
 
780
      if(p->environ[0] == NULL){
 
781
        if(execv(filename, p->argv) < 0){
 
782
          perror("execv");
 
783
          _exit(EXIT_FAILURE);
 
784
        }
 
785
      } else {
 
786
        if(execve(filename, p->argv, p->environ) < 0){
 
787
          perror("execve");
 
788
          _exit(EXIT_FAILURE);
 
789
        }
1080
790
      }
1081
791
      /* no return */
1082
792
    }
1083
 
    /* Parent process */
1084
 
    close(pipefd[1]);           /* Close unused write end of pipe */
1085
 
    close(plugin_fd);
1086
 
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1087
 
    if(new_plugin == NULL){
1088
 
      error(0, errno, "getplugin");
1089
 
      ret = (int)(TEMP_FAILURE_RETRY
1090
 
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1091
 
                               NULL)));
 
793
    /* parent process */
 
794
    free(filename);
 
795
    close(pipefd[1]);           /* close unused write end of pipe */
 
796
    process *new_process = malloc(sizeof(process));
 
797
    if (new_process == NULL){
 
798
      perror("malloc");
 
799
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1092
800
      if(ret < 0){
1093
 
        error(0, errno, "sigprocmask");
 
801
        perror("sigprocmask");
1094
802
      }
1095
 
      exitstatus = EX_OSERR;
1096
 
      free(direntries[i]);
 
803
      exitstatus = EXIT_FAILURE;
1097
804
      goto fallback;
1098
805
    }
1099
 
    free(direntries[i]);
1100
806
    
1101
 
    new_plugin->pid = pid;
1102
 
    new_plugin->fd = pipefd[0];
1103
 
 
1104
 
    if(debug){
1105
 
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
1106
 
              new_plugin->name, (intmax_t) (new_plugin->pid));
1107
 
    }
1108
 
 
 
807
    *new_process = (struct process){ .pid = pid,
 
808
                                     .fd = pipefd[0],
 
809
                                     .next = process_list };
 
810
    // List handling
 
811
    process_list = new_process;
1109
812
    /* Unblock SIGCHLD so signal handler can be run if this process
1110
813
       has already completed */
1111
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1112
 
                                              &sigchld_action.sa_mask,
1113
 
                                              NULL));
 
814
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1114
815
    if(ret < 0){
1115
 
      error(0, errno, "sigprocmask");
1116
 
      exitstatus = EX_OSERR;
 
816
      perror("sigprocmask");
 
817
      exitstatus = EXIT_FAILURE;
1117
818
      goto fallback;
1118
819
    }
1119
820
    
1120
 
    FD_SET(new_plugin->fd, &rfds_all);
1121
 
    
1122
 
    if(maxfd < new_plugin->fd){
1123
 
      maxfd = new_plugin->fd;
1124
 
    }
1125
 
  }
1126
 
  
1127
 
  free(direntries);
1128
 
  direntries = NULL;
1129
 
  close(dir_fd);
1130
 
  dir_fd = -1;
1131
 
  free_plugin(getplugin(NULL));
1132
 
  
1133
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1134
 
    if(p->pid != 0){
1135
 
      break;
1136
 
    }
1137
 
    if(p->next == NULL){
1138
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
1139
 
              " directory?\n");
1140
 
      free_plugin_list();
1141
 
    }
1142
 
  }
1143
 
  
1144
 
  /* Main loop while running plugins exist */
1145
 
  while(plugin_list){
 
821
    FD_SET(new_process->fd, &rfds_all);
 
822
    
 
823
    if (maxfd < new_process->fd){
 
824
      maxfd = new_process->fd;
 
825
    }
 
826
    
 
827
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
 
831
  
 
832
  closedir(dir);
 
833
  dir = NULL;
 
834
    
 
835
  if (process_list == NULL){
 
836
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
837
            " directory?\n");
 
838
    process_list = NULL;
 
839
  }
 
840
  while(process_list){
1146
841
    fd_set rfds = rfds_all;
1147
842
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1148
 
    if(select_ret == -1 and errno != EINTR){
1149
 
      error(0, errno, "select");
1150
 
      exitstatus = EX_OSERR;
 
843
    if (select_ret == -1){
 
844
      perror("select");
 
845
      exitstatus = EXIT_FAILURE;
1151
846
      goto fallback;
1152
847
    }
1153
848
    /* OK, now either a process completed, or something can be read
1154
849
       from one of them */
1155
 
    for(plugin *proc = plugin_list; proc != NULL;){
 
850
    for(process *proc = process_list; proc ; proc = proc->next){
1156
851
      /* Is this process completely done? */
1157
 
      if(proc->completed and proc->eof){
 
852
      if(proc->eof and proc->completed){
1158
853
        /* Only accept the plugin output if it exited cleanly */
1159
854
        if(not WIFEXITED(proc->status)
1160
855
           or WEXITSTATUS(proc->status) != 0){
1161
856
          /* Bad exit by plugin */
1162
 
          
1163
857
          if(debug){
1164
858
            if(WIFEXITED(proc->status)){
1165
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1166
 
                      " status %d\n", proc->name,
1167
 
                      (intmax_t) (proc->pid),
 
859
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
860
                      (unsigned int) (proc->pid),
1168
861
                      WEXITSTATUS(proc->status));
1169
 
            } else if(WIFSIGNALED(proc->status)){
1170
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
1171
 
                      " signal %d: %s\n", proc->name,
1172
 
                      (intmax_t) (proc->pid),
1173
 
                      WTERMSIG(proc->status),
1174
 
                      strsignal(WTERMSIG(proc->status)));
 
862
            } else if(WIFSIGNALED(proc->status)) {
 
863
              fprintf(stderr, "Plugin %u killed by signal %d\n",
 
864
                      (unsigned int) (proc->pid),
 
865
                      WTERMSIG(proc->status));
 
866
            } else if(WCOREDUMP(proc->status)){
 
867
              fprintf(stderr, "Plugin %d dumped core\n",
 
868
                      (unsigned int) (proc->pid));
1175
869
            }
1176
870
          }
1177
 
          
1178
871
          /* Remove the plugin */
1179
872
          FD_CLR(proc->fd, &rfds_all);
1180
 
          
1181
873
          /* Block signal while modifying process_list */
1182
 
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1183
 
                                        (SIG_BLOCK,
1184
 
                                         &sigchld_action.sa_mask,
1185
 
                                         NULL));
 
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1186
875
          if(ret < 0){
1187
 
            error(0, errno, "sigprocmask");
1188
 
            exitstatus = EX_OSERR;
 
876
            perror("sigprocmask");
 
877
            exitstatus = EXIT_FAILURE;
1189
878
            goto fallback;
1190
879
          }
1191
 
          
1192
 
          plugin *next_plugin = proc->next;
1193
 
          free_plugin(proc);
1194
 
          proc = next_plugin;
1195
 
          
 
880
          /* Delete this process entry from the list */
 
881
          if(process_list == proc){
 
882
            /* First one - simple */
 
883
            process_list = proc->next;
 
884
          } else {
 
885
            /* Second one or later */
 
886
            for(process *p = process_list; p != NULL; p = p->next){
 
887
              if(p->next == proc){
 
888
                p->next = proc->next;
 
889
                break;
 
890
              }
 
891
            }
 
892
          }
1196
893
          /* We are done modifying process list, so unblock signal */
1197
 
          ret = (int)(TEMP_FAILURE_RETRY
1198
 
                      (sigprocmask(SIG_UNBLOCK,
1199
 
                                   &sigchld_action.sa_mask, NULL)));
 
894
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
895
                             NULL);
1200
896
          if(ret < 0){
1201
 
            error(0, errno, "sigprocmask");
1202
 
            exitstatus = EX_OSERR;
1203
 
            goto fallback;
1204
 
          }
1205
 
          
1206
 
          if(plugin_list == NULL){
1207
 
            break;
1208
 
          }
1209
 
          
1210
 
          continue;
 
897
            perror("sigprocmask");
 
898
          }
 
899
          free(proc->buffer);
 
900
          free(proc);
 
901
          /* We deleted this process from the list, so we can't go
 
902
             proc->next.  Therefore, start over from the beginning of
 
903
             the process list */
 
904
          break;
1211
905
        }
1212
 
        
1213
906
        /* This process exited nicely, so print its buffer */
1214
 
        
 
907
 
1215
908
        bool bret = print_out_password(proc->buffer,
1216
909
                                       proc->buffer_length);
1217
910
        if(not bret){
1218
 
          error(0, errno, "print_out_password");
1219
 
          exitstatus = EX_IOERR;
 
911
          perror("print_out_password");
 
912
          exitstatus = EXIT_FAILURE;
1220
913
        }
1221
914
        goto fallback;
1222
915
      }
1223
 
      
1224
916
      /* This process has not completed.  Does it have any output? */
1225
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1226
918
        /* This process had nothing to say at this time */
1227
 
        proc = proc->next;
1228
919
        continue;
1229
920
      }
1230
921
      /* Before reading, make the process' data buffer large enough */
1231
922
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1232
 
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
1233
 
                                   + (size_t) BUFFER_SIZE);
1234
 
        if(new_buffer == NULL){
1235
 
          error(0, errno, "malloc");
1236
 
          exitstatus = EX_OSERR;
 
923
        proc->buffer = realloc(proc->buffer, proc->buffer_size
 
924
                               + (size_t) BUFFER_SIZE);
 
925
        if (proc->buffer == NULL){
 
926
          perror("malloc");
 
927
          exitstatus = EXIT_FAILURE;
1237
928
          goto fallback;
1238
929
        }
1239
 
        proc->buffer = new_buffer;
1240
930
        proc->buffer_size += BUFFER_SIZE;
1241
931
      }
1242
932
      /* Read from the process */
1243
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1244
 
                                     proc->buffer
1245
 
                                     + proc->buffer_length,
1246
 
                                     BUFFER_SIZE));
1247
 
      if(sret < 0){
 
933
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
934
                 BUFFER_SIZE);
 
935
      if(ret < 0){
1248
936
        /* Read error from this process; ignore the error */
1249
 
        proc = proc->next;
1250
937
        continue;
1251
938
      }
1252
 
      if(sret == 0){
 
939
      if(ret == 0){
1253
940
        /* got EOF */
1254
941
        proc->eof = true;
1255
942
      } else {
1256
 
        proc->buffer_length += (size_t) sret;
 
943
        proc->buffer_length += (size_t) ret;
1257
944
      }
1258
945
    }
1259
946
  }
1260
 
  
1261
 
  
 
947
 
 
948
 
1262
949
 fallback:
1263
950
  
1264
 
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
1265
 
                             and exitstatus != EX_OK)){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
1266
952
    /* Fallback if all plugins failed, none are found or an error
1267
953
       occured */
1268
954
    bool bret;
1269
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1270
956
    char *passwordbuffer = getpass("Password: ");
1271
 
    size_t len = strlen(passwordbuffer);
1272
 
    /* Strip trailing newline */
1273
 
    if(len > 0 and passwordbuffer[len-1] == '\n'){
1274
 
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
1275
 
      len--;
1276
 
    }
1277
 
    bret = print_out_password(passwordbuffer, len);
 
957
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
1278
958
    if(not bret){
1279
 
      error(0, errno, "print_out_password");
1280
 
      exitstatus = EX_IOERR;
 
959
      perror("print_out_password");
 
960
      exitstatus = EXIT_FAILURE;
1281
961
    }
1282
962
  }
1283
963
  
1284
964
  /* Restore old signal handler */
1285
965
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1286
966
  if(ret == -1){
1287
 
    error(0, errno, "sigaction");
1288
 
    exitstatus = EX_OSERR;
 
967
    perror("sigaction");
 
968
    exitstatus = EXIT_FAILURE;
1289
969
  }
1290
 
  
 
970
 
1291
971
  if(custom_argv != NULL){
1292
972
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1293
973
      free(*arg);
1294
974
    }
1295
975
    free(custom_argv);
1296
976
  }
1297
 
  
1298
 
  free(direntries);
1299
 
  
1300
 
  if(dir_fd != -1){
1301
 
    close(dir_fd);
 
977
  free_plugin_list(plugin_list);
 
978
  
 
979
  if(dir != NULL){
 
980
    closedir(dir);
1302
981
  }
1303
982
  
1304
 
  /* Kill the processes */
1305
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1306
 
    if(p->pid != 0){
1307
 
      close(p->fd);
1308
 
      ret = kill(p->pid, SIGTERM);
1309
 
      if(ret == -1 and errno != ESRCH){
1310
 
        /* Set-uid proccesses might not get closed */
1311
 
        error(0, errno, "kill");
1312
 
      }
 
983
  /* Free the process list and kill the processes */
 
984
  for(process *next; process_list != NULL; process_list = next){
 
985
    next = process_list->next;
 
986
    close(process_list->fd);
 
987
    ret = kill(process_list->pid, SIGTERM);
 
988
    if(ret == -1 and errno != ESRCH){
 
989
      /* set-uid proccesses migth not get closed */
 
990
      perror("kill");
1313
991
    }
 
992
    free(process_list->buffer);
 
993
    free(process_list);
1314
994
  }
1315
995
  
1316
996
  /* Wait for any remaining child processes to terminate */
1317
 
  do {
 
997
  do{
1318
998
    ret = wait(NULL);
1319
999
  } while(ret >= 0);
1320
1000
  if(errno != ECHILD){
1321
 
    error(0, errno, "wait");
 
1001
    perror("wait");
1322
1002
  }
1323
 
  
1324
 
  free_plugin_list();
1325
 
  
 
1003
 
1326
1004
  free(plugindir);
1327
 
  free(pluginhelperdir);
1328
1005
  free(argfile);
1329
1006
  
1330
1007
  return exitstatus;