/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/splashy.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
 * Splashy - Read a password from splashy and output 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             /* TEMP_FAILURE_RETRY(), asprintf() */
29
29
                                   SIG_IGN, kill(), SIGKILL */
30
30
#include <stddef.h>             /* NULL */
31
31
#include <stdlib.h>             /* getenv() */
32
 
#include <stdio.h>              /* asprintf() */
 
32
#include <stdio.h>              /* asprintf(), vasprintf(), vprintf(),
 
33
                                   fprintf() */
33
34
#include <stdlib.h>             /* EXIT_FAILURE, free(),
34
35
                                   EXIT_SUCCESS */
35
36
#include <sys/types.h>          /* pid_t, DIR, struct dirent,
42
43
                                   sleep(), dup2() STDERR_FILENO,
43
44
                                   STDOUT_FILENO, _exit(),
44
45
                                   pause() */
45
 
#include <string.h>             /* memcmp() */
 
46
#include <string.h>             /* memcmp(), strerror() */
46
47
#include <errno.h>              /* errno, EACCES, ENOTDIR, ELOOP,
47
48
                                   ENOENT, ENAMETOOLONG, EMFILE,
48
49
                                   ENFILE, ENOMEM, ENOEXEC, EINVAL,
54
55
                                   WEXITSTATUS() */
55
56
#include <sysexits.h>           /* EX_OSERR, EX_OSFILE,
56
57
                                   EX_UNAVAILABLE */
 
58
#include <stdarg.h>             /* va_list, va_start(), ... */
57
59
 
58
60
sig_atomic_t interrupted_by_signal = 0;
59
61
int signal_received;
60
62
 
 
63
/* Function to use when printing errors */
 
64
__attribute__((format (gnu_printf, 3, 4)))
 
65
void error_plus(int status, int errnum, const char *formatstring,
 
66
                ...){
 
67
  va_list ap;
 
68
  char *text;
 
69
  int ret;
 
70
  
 
71
  va_start(ap, formatstring);
 
72
  ret = vasprintf(&text, formatstring, ap);
 
73
  if(ret == -1){
 
74
    fprintf(stderr, "Mandos plugin %s: ",
 
75
            program_invocation_short_name);
 
76
    vfprintf(stderr, formatstring, ap);
 
77
    fprintf(stderr, ": ");
 
78
    fprintf(stderr, "%s\n", strerror(errnum));
 
79
    error(status, errno, "vasprintf while printing error");
 
80
    return;
 
81
  }
 
82
  fprintf(stderr, "Mandos plugin ");
 
83
  error(status, errnum, "%s", text);
 
84
  free(text);
 
85
}
 
86
 
 
87
 
61
88
static void termination_handler(int signum){
62
89
  if(interrupted_by_signal){
63
90
    return;
110
137
    proc_dir = opendir("/proc");
111
138
    if(proc_dir == NULL){
112
139
      int e = errno;
113
 
      error(0, errno, "opendir");
 
140
      error_plus(0, errno, "opendir");
114
141
      switch(e){
115
142
      case EACCES:
116
143
      case ENOTDIR:
152
179
        char *exe_link;
153
180
        ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
154
181
        if(ret == -1){
155
 
          error(0, errno, "asprintf");
 
182
          error_plus(0, errno, "asprintf");
156
183
          exitstatus = EX_OSERR;
157
184
          goto failure;
158
185
        }
166
193
            continue;
167
194
          }
168
195
          int e = errno;
169
 
          error(0, errno, "lstat");
 
196
          error_plus(0, errno, "lstat");
170
197
          free(exe_link);
171
198
          switch(e){
172
199
          case EACCES:
214
241
    sigemptyset(&new_action.sa_mask);
215
242
    ret = sigaddset(&new_action.sa_mask, SIGINT);
216
243
    if(ret == -1){
217
 
      error(0, errno, "sigaddset");
 
244
      error_plus(0, errno, "sigaddset");
218
245
      exitstatus = EX_OSERR;
219
246
      goto failure;
220
247
    }
221
248
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
222
249
    if(ret == -1){
223
 
      error(0, errno, "sigaddset");
 
250
      error_plus(0, errno, "sigaddset");
224
251
      exitstatus = EX_OSERR;
225
252
      goto failure;
226
253
    }
227
254
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
228
255
    if(ret == -1){
229
 
      error(0, errno, "sigaddset");
 
256
      error_plus(0, errno, "sigaddset");
230
257
      exitstatus = EX_OSERR;
231
258
      goto failure;
232
259
    }
233
260
    ret = sigaction(SIGINT, NULL, &old_action);
234
261
    if(ret == -1){
235
 
      error(0, errno, "sigaction");
 
262
      error_plus(0, errno, "sigaction");
236
263
      exitstatus = EX_OSERR;
237
264
      goto failure;
238
265
    }
239
266
    if(old_action.sa_handler != SIG_IGN){
240
267
      ret = sigaction(SIGINT, &new_action, NULL);
241
268
      if(ret == -1){
242
 
        error(0, errno, "sigaction");
 
269
        error_plus(0, errno, "sigaction");
243
270
        exitstatus = EX_OSERR;
244
271
        goto failure;
245
272
      }
246
273
    }
247
274
    ret = sigaction(SIGHUP, NULL, &old_action);
248
275
    if(ret == -1){
249
 
      error(0, errno, "sigaction");
 
276
      error_plus(0, errno, "sigaction");
250
277
      exitstatus = EX_OSERR;
251
278
      goto failure;
252
279
    }
253
280
    if(old_action.sa_handler != SIG_IGN){
254
281
      ret = sigaction(SIGHUP, &new_action, NULL);
255
282
      if(ret == -1){
256
 
        error(0, errno, "sigaction");
 
283
        error_plus(0, errno, "sigaction");
257
284
        exitstatus = EX_OSERR;
258
285
        goto failure;
259
286
      }
260
287
    }
261
288
    ret = sigaction(SIGTERM, NULL, &old_action);
262
289
    if(ret == -1){
263
 
      error(0, errno, "sigaction");
 
290
      error_plus(0, errno, "sigaction");
264
291
      exitstatus = EX_OSERR;
265
292
      goto failure;
266
293
    }
267
294
    if(old_action.sa_handler != SIG_IGN){
268
295
      ret = sigaction(SIGTERM, &new_action, NULL);
269
296
      if(ret == -1){
270
 
        error(0, errno, "sigaction");
 
297
        error_plus(0, errno, "sigaction");
271
298
        exitstatus = EX_OSERR;
272
299
        goto failure;
273
300
      }
284
311
    goto failure;
285
312
  }
286
313
  if(splashy_command_pid == -1){
287
 
    error(0, errno, "fork");
 
314
    error_plus(0, errno, "fork");
288
315
    exitstatus = EX_OSERR;
289
316
    goto failure;
290
317
  }
294
321
      const char splashy_command[] = "/sbin/splashy_update";
295
322
      execl(splashy_command, splashy_command, prompt, (char *)NULL);
296
323
      int e = errno;
297
 
      error(0, errno, "execl");
 
324
      error_plus(0, errno, "execl");
298
325
      switch(e){
299
326
      case EACCES:
300
327
      case ENOENT:
314
341
      case ENOTDIR:
315
342
      case ELOOP:
316
343
      case EISDIR:
317
 
      case ELIBBAD:
 
344
#ifdef ELIBBAD
 
345
      case ELIBBAD:             /* Linux only */
 
346
#endif
318
347
      case EPERM:
319
348
        _exit(EX_OSFILE);
320
349
      }
342
371
      goto failure;
343
372
    }
344
373
    if(ret == -1){
345
 
      error(0, errno, "waitpid");
 
374
      error_plus(0, errno, "waitpid");
346
375
      if(errno == ECHILD){
347
376
        splashy_command_pid = 0;
348
377
      }
380
409
         the real user ID (_mandos) */
381
410
      ret = setuid(geteuid());
382
411
      if(ret == -1){
383
 
        error(0, errno, "setuid");
 
412
        error_plus(0, errno, "setuid");
384
413
      }
385
414
      
386
415
      setsid();
387
416
      ret = chdir("/");
388
417
      if(ret == -1){
389
 
        error(0, errno, "chdir");
 
418
        error_plus(0, errno, "chdir");
390
419
      }
391
420
/*       if(fork() != 0){ */
392
421
/*      _exit(EXIT_SUCCESS); */
393
422
/*       } */
394
423
      ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */
395
424
      if(ret == -1){
396
 
        error(0, errno, "dup2");
 
425
        error_plus(0, errno, "dup2");
397
426
        _exit(EX_OSERR);
398
427
      }
399
428
      
400
429
      execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL);
401
430
      {
402
431
        int e = errno;
403
 
        error(0, errno, "execl");
 
432
        error_plus(0, errno, "execl");
404
433
        switch(e){
405
434
        case EACCES:
406
435
        case ENOENT:
426
455
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
427
456
                                            &signal_action, NULL));
428
457
    if(ret == -1){
429
 
      error(0, errno, "sigaction");
 
458
      error_plus(0, errno, "sigaction");
430
459
    }
431
460
    do {
432
461
      ret = raise(signal_received);
433
462
    } while(ret != 0 and errno == EINTR);
434
463
    if(ret != 0){
435
 
      error(0, errno, "raise");
 
464
      error_plus(0, errno, "raise");
436
465
      abort();
437
466
    }
438
467
    TEMP_FAILURE_RETRY(pause());