/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 plugins.d/password-prompt.c

  • Committer: Teddy Hogeborn
  • Date: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Password-prompt - Read a password from the terminal and print it
4
4
 * 
5
 
 * Copyright © 2008-2010 Teddy Hogeborn
6
 
 * Copyright © 2008-2010 Björn Påhlsson
 
5
 * Copyright © 2008-2014 Teddy Hogeborn
 
6
 * Copyright © 2008-2014 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
22
 * Contact the authors at <mandos@recompile.se>.
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* getline(), asprintf() */
41
41
                                   getenv(), free() */
42
42
#include <dirent.h>             /* scandir(), alphasort() */
43
43
#include <stdio.h>              /* fprintf(), stderr, getline(),
44
 
                                   stdin, feof(), fputc()
45
 
                                */
 
44
                                   stdin, feof(), fputc(), vfprintf(),
 
45
                                   vasprintf() */
46
46
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
47
47
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
48
48
                                */
51
51
#include <stdbool.h>            /* bool, false, true */
52
52
#include <inttypes.h>           /* strtoumax() */
53
53
#include <sys/stat.h>           /* struct stat, lstat(), open() */
54
 
#include <string.h>             /* strlen, rindex, memcmp */
 
54
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
55
                                 */
55
56
#include <argp.h>               /* struct argp_option, struct
56
57
                                   argp_state, struct argp,
57
58
                                   argp_parse(), error_t,
60
61
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
61
62
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
62
63
#include <fcntl.h>              /* open() */
 
64
#include <stdarg.h>             /* va_list, va_start(), ... */
63
65
 
64
66
volatile sig_atomic_t quit_now = 0;
65
67
int signal_received;
66
68
bool debug = false;
67
69
const char *argp_program_version = "password-prompt " VERSION;
68
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
70
const char *argp_program_bug_address = "<mandos@recompile.se>";
69
71
 
70
72
/* Needed for conflict resolution */
71
73
const char plymouth_name[] = "plymouthd";
72
74
 
 
75
/* Function to use when printing errors */
 
76
__attribute__((format (gnu_printf, 3, 4)))
 
77
void error_plus(int status, int errnum, const char *formatstring,
 
78
                ...){
 
79
  va_list ap;
 
80
  char *text;
 
81
  int ret;
 
82
  
 
83
  va_start(ap, formatstring);
 
84
  ret = vasprintf(&text, formatstring, ap);
 
85
  if(ret == -1){
 
86
    fprintf(stderr, "Mandos plugin %s: ",
 
87
            program_invocation_short_name);
 
88
    vfprintf(stderr, formatstring, ap);
 
89
    fprintf(stderr, ": %s\n", strerror(errnum));
 
90
    error(status, errno, "vasprintf while printing error");
 
91
    return;
 
92
  }
 
93
  fprintf(stderr, "Mandos plugin ");
 
94
  error(status, errnum, "%s", text);
 
95
  free(text);
 
96
}
 
97
 
73
98
static void termination_handler(int signum){
74
99
  if(quit_now){
75
100
    return;
84
109
     from the terminal.  Password-prompt will exit if it detects
85
110
     plymouth since plymouth performs the same functionality.
86
111
   */
 
112
  __attribute__((nonnull))
87
113
  int is_plymouth(const struct dirent *proc_entry){
88
114
    int ret;
89
115
    int cl_fd;
90
116
    {
91
 
      uintmax_t maxvalue;
 
117
      uintmax_t proc_id;
92
118
      char *tmp;
93
119
      errno = 0;
94
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
120
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
95
121
      
96
122
      if(errno != 0 or *tmp != '\0'
97
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
123
         or proc_id != (uintmax_t)((pid_t)proc_id)){
98
124
        return 0;
99
125
      }
100
126
    }
103
129
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
104
130
                   proc_entry->d_name);
105
131
    if(ret == -1){
106
 
      error(0, errno, "asprintf");
 
132
      error_plus(0, errno, "asprintf");
107
133
      return 0;
108
134
    }
109
135
    
112
138
    free(cmdline_filename);
113
139
    if(cl_fd == -1){
114
140
      if(errno != ENOENT){
115
 
        error(0, errno, "open");
 
141
        error_plus(0, errno, "open");
116
142
      }
117
143
      return 0;
118
144
    }
129
155
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
130
156
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
131
157
          if(tmp == NULL){
132
 
            error(0, errno, "realloc");
 
158
            error_plus(0, errno, "realloc");
133
159
            free(cmdline);
134
160
            close(cl_fd);
135
161
            return 0;
142
168
        sret = read(cl_fd, cmdline + cmdline_len,
143
169
                    cmdline_allocated - cmdline_len);
144
170
        if(sret == -1){
145
 
          error(0, errno, "read");
 
171
          error_plus(0, errno, "read");
146
172
          free(cmdline);
147
173
          close(cl_fd);
148
174
          return 0;
151
177
      } while(sret != 0);
152
178
      ret = close(cl_fd);
153
179
      if(ret == -1){
154
 
        error(0, errno, "close");
 
180
        error_plus(0, errno, "close");
155
181
        free(cmdline);
156
182
        return 0;
157
183
      }
183
209
    return 1;
184
210
  }
185
211
  
186
 
  struct dirent **direntries;
 
212
  struct dirent **direntries = NULL;
187
213
  int ret;
188
214
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
189
 
  if (ret == -1){
190
 
    error(1, errno, "scandir");
 
215
  if(ret == -1){
 
216
    error_plus(1, errno, "scandir");
191
217
  }
 
218
  free(direntries);
192
219
  return ret > 0;
193
220
}
194
221
 
223
250
      { .name = NULL }
224
251
    };
225
252
    
 
253
    __attribute__((nonnull(3)))
226
254
    error_t parse_opt (int key, char *arg, struct argp_state *state){
227
255
      errno = 0;
228
256
      switch (key){
264
292
    case ENOMEM:
265
293
    default:
266
294
      errno = ret;
267
 
      error(0, errno, "argp_parse");
 
295
      error_plus(0, errno, "argp_parse");
268
296
      return EX_OSERR;
269
297
    case EINVAL:
270
298
      return EX_USAGE;
275
303
    fprintf(stderr, "Starting %s\n", argv[0]);
276
304
  }
277
305
 
278
 
  if (conflict_detection()){
 
306
  if(conflict_detection()){
279
307
    if(debug){
280
308
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
281
309
    }
288
316
  
289
317
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
290
318
    int e = errno;
291
 
    error(0, errno, "tcgetattr");
 
319
    error_plus(0, errno, "tcgetattr");
292
320
    switch(e){
293
321
    case EBADF:
294
322
    case ENOTTY:
301
329
  sigemptyset(&new_action.sa_mask);
302
330
  ret = sigaddset(&new_action.sa_mask, SIGINT);
303
331
  if(ret == -1){
304
 
    error(0, errno, "sigaddset");
 
332
    error_plus(0, errno, "sigaddset");
305
333
    return EX_OSERR;
306
334
  }
307
335
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
308
336
  if(ret == -1){
309
 
    error(0, errno, "sigaddset");
 
337
    error_plus(0, errno, "sigaddset");
310
338
    return EX_OSERR;
311
339
  }
312
340
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
313
341
  if(ret == -1){
314
 
    error(0, errno, "sigaddset");
 
342
    error_plus(0, errno, "sigaddset");
315
343
    return EX_OSERR;
316
344
  }
317
345
  /* Need to check if the handler is SIG_IGN before handling:
320
348
  */
321
349
  ret = sigaction(SIGINT, NULL, &old_action);
322
350
  if(ret == -1){
323
 
    error(0, errno, "sigaction");
 
351
    error_plus(0, errno, "sigaction");
324
352
    return EX_OSERR;
325
353
  }
326
354
  if(old_action.sa_handler != SIG_IGN){
327
355
    ret = sigaction(SIGINT, &new_action, NULL);
328
356
    if(ret == -1){
329
 
      error(0, errno, "sigaction");
 
357
      error_plus(0, errno, "sigaction");
330
358
      return EX_OSERR;
331
359
    }
332
360
  }
333
361
  ret = sigaction(SIGHUP, NULL, &old_action);
334
362
  if(ret == -1){
335
 
    error(0, errno, "sigaction");
 
363
    error_plus(0, errno, "sigaction");
336
364
    return EX_OSERR;
337
365
  }
338
366
  if(old_action.sa_handler != SIG_IGN){
339
367
    ret = sigaction(SIGHUP, &new_action, NULL);
340
368
    if(ret == -1){
341
 
      error(0, errno, "sigaction");
 
369
      error_plus(0, errno, "sigaction");
342
370
      return EX_OSERR;
343
371
    }
344
372
  }
345
373
  ret = sigaction(SIGTERM, NULL, &old_action);
346
374
  if(ret == -1){
347
 
    error(0, errno, "sigaction");
 
375
    error_plus(0, errno, "sigaction");
348
376
    return EX_OSERR;
349
377
  }
350
378
  if(old_action.sa_handler != SIG_IGN){
351
379
    ret = sigaction(SIGTERM, &new_action, NULL);
352
380
    if(ret == -1){
353
 
      error(0, errno, "sigaction");
 
381
      error_plus(0, errno, "sigaction");
354
382
      return EX_OSERR;
355
383
    }
356
384
  }
364
392
  t_new.c_lflag &= ~(tcflag_t)ECHO;
365
393
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
366
394
    int e = errno;
367
 
    error(0, errno, "tcsetattr-echo");
 
395
    error_plus(0, errno, "tcsetattr-echo");
368
396
    switch(e){
369
397
    case EBADF:
370
398
    case ENOTTY:
434
462
        sret = write(STDOUT_FILENO, buffer + written, n - written);
435
463
        if(sret < 0){
436
464
          int e = errno;
437
 
          error(0, errno, "write");
 
465
          error_plus(0, errno, "write");
438
466
          switch(e){
439
467
          case EBADF:
440
468
          case EFAULT:
456
484
      sret = close(STDOUT_FILENO);
457
485
      if(sret == -1){
458
486
        int e = errno;
459
 
        error(0, errno, "close");
 
487
        error_plus(0, errno, "close");
460
488
        switch(e){
461
489
        case EBADF:
462
490
          status = EX_OSFILE;
472
500
    if(sret < 0){
473
501
      int e = errno;
474
502
      if(errno != EINTR and not feof(stdin)){
475
 
        error(0, errno, "getline");
 
503
        error_plus(0, errno, "getline");
476
504
        switch(e){
477
505
        case EBADF:
478
506
          status = EX_UNAVAILABLE;
 
507
          break;
479
508
        case EIO:
480
509
        case EINVAL:
481
510
        default:
501
530
    fprintf(stderr, "Restoring terminal attributes\n");
502
531
  }
503
532
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
504
 
    error(0, errno, "tcsetattr+echo");
 
533
    error_plus(0, errno, "tcsetattr+echo");
505
534
  }
506
535
  
507
536
  if(quit_now){
509
538
    old_action.sa_handler = SIG_DFL;
510
539
    ret = sigaction(signal_received, &old_action, NULL);
511
540
    if(ret == -1){
512
 
      error(0, errno, "sigaction");
 
541
      error_plus(0, errno, "sigaction");
513
542
    }
514
543
    raise(signal_received);
515
544
  }