/mandos/release

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2019-03-18 22:29:25 UTC
  • mto: This revision was merged to the branch mainline in revision 382.
  • Revision ID: teddy@recompile.se-20190318222925-jvhek84dgcfgj6g3
mandos-ctl: Refactor tests

* mandos-ctl: Where the clients names "foo" and "barbar" do not refer
              to the actual mock clients in the TestCommand class,
              change all occurrences of these names to "client1" and
              "client2" (or just "client" when only one is used) .
              Also change all test doubles to use correct terminology;
              some things called mocks are actually stubs or spies,
              and rename all true mocks to have "mock" in their names.
              Also eliminate duplicate values in tests; derive values
              from previously defined values whenever possible.

Show diffs side-by-side

added added

removed removed

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