/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,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 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(), perror() */
 
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,
49
50
                                   E2BIG, EFAULT, EIO, ETXTBSY,
50
51
                                   EISDIR, ELIBBAD, EPERM, EINTR,
51
52
                                   ECHILD */
 
53
#include <error.h>              /* error() */
52
54
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
53
55
                                   WEXITSTATUS() */
54
56
#include <sysexits.h>           /* EX_OSERR, EX_OSFILE,
55
57
                                   EX_UNAVAILABLE */
 
58
#include <stdarg.h>             /* va_list, va_start(), ... */
56
59
 
57
60
sig_atomic_t interrupted_by_signal = 0;
58
61
int signal_received;
59
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
 
60
88
static void termination_handler(int signum){
61
89
  if(interrupted_by_signal){
62
90
    return;
109
137
    proc_dir = opendir("/proc");
110
138
    if(proc_dir == NULL){
111
139
      int e = errno;
112
 
      perror("opendir");
 
140
      error_plus(0, errno, "opendir");
113
141
      switch(e){
114
142
      case EACCES:
115
143
      case ENOTDIR:
151
179
        char *exe_link;
152
180
        ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
153
181
        if(ret == -1){
154
 
          perror("asprintf");
 
182
          error_plus(0, errno, "asprintf");
155
183
          exitstatus = EX_OSERR;
156
184
          goto failure;
157
185
        }
165
193
            continue;
166
194
          }
167
195
          int e = errno;
168
 
          perror("lstat");
 
196
          error_plus(0, errno, "lstat");
169
197
          free(exe_link);
170
198
          switch(e){
171
199
          case EACCES:
213
241
    sigemptyset(&new_action.sa_mask);
214
242
    ret = sigaddset(&new_action.sa_mask, SIGINT);
215
243
    if(ret == -1){
216
 
      perror("sigaddset");
 
244
      error_plus(0, errno, "sigaddset");
217
245
      exitstatus = EX_OSERR;
218
246
      goto failure;
219
247
    }
220
248
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
221
249
    if(ret == -1){
222
 
      perror("sigaddset");
 
250
      error_plus(0, errno, "sigaddset");
223
251
      exitstatus = EX_OSERR;
224
252
      goto failure;
225
253
    }
226
254
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
227
255
    if(ret == -1){
228
 
      perror("sigaddset");
 
256
      error_plus(0, errno, "sigaddset");
229
257
      exitstatus = EX_OSERR;
230
258
      goto failure;
231
259
    }
232
260
    ret = sigaction(SIGINT, NULL, &old_action);
233
261
    if(ret == -1){
234
 
      perror("sigaction");
 
262
      error_plus(0, errno, "sigaction");
235
263
      exitstatus = EX_OSERR;
236
264
      goto failure;
237
265
    }
238
266
    if(old_action.sa_handler != SIG_IGN){
239
267
      ret = sigaction(SIGINT, &new_action, NULL);
240
268
      if(ret == -1){
241
 
        perror("sigaction");
 
269
        error_plus(0, errno, "sigaction");
242
270
        exitstatus = EX_OSERR;
243
271
        goto failure;
244
272
      }
245
273
    }
246
274
    ret = sigaction(SIGHUP, NULL, &old_action);
247
275
    if(ret == -1){
248
 
      perror("sigaction");
 
276
      error_plus(0, errno, "sigaction");
249
277
      exitstatus = EX_OSERR;
250
278
      goto failure;
251
279
    }
252
280
    if(old_action.sa_handler != SIG_IGN){
253
281
      ret = sigaction(SIGHUP, &new_action, NULL);
254
282
      if(ret == -1){
255
 
        perror("sigaction");
 
283
        error_plus(0, errno, "sigaction");
256
284
        exitstatus = EX_OSERR;
257
285
        goto failure;
258
286
      }
259
287
    }
260
288
    ret = sigaction(SIGTERM, NULL, &old_action);
261
289
    if(ret == -1){
262
 
      perror("sigaction");
 
290
      error_plus(0, errno, "sigaction");
263
291
      exitstatus = EX_OSERR;
264
292
      goto failure;
265
293
    }
266
294
    if(old_action.sa_handler != SIG_IGN){
267
295
      ret = sigaction(SIGTERM, &new_action, NULL);
268
296
      if(ret == -1){
269
 
        perror("sigaction");
 
297
        error_plus(0, errno, "sigaction");
270
298
        exitstatus = EX_OSERR;
271
299
        goto failure;
272
300
      }
283
311
    goto failure;
284
312
  }
285
313
  if(splashy_command_pid == -1){
286
 
    perror("fork");
 
314
    error_plus(0, errno, "fork");
287
315
    exitstatus = EX_OSERR;
288
316
    goto failure;
289
317
  }
293
321
      const char splashy_command[] = "/sbin/splashy_update";
294
322
      execl(splashy_command, splashy_command, prompt, (char *)NULL);
295
323
      int e = errno;
296
 
      perror("execl");
 
324
      error_plus(0, errno, "execl");
297
325
      switch(e){
298
326
      case EACCES:
299
327
      case ENOENT:
313
341
      case ENOTDIR:
314
342
      case ELOOP:
315
343
      case EISDIR:
316
 
      case ELIBBAD:
 
344
#ifdef ELIBBAD
 
345
      case ELIBBAD:             /* Linux only */
 
346
#endif
317
347
      case EPERM:
318
348
        _exit(EX_OSFILE);
319
349
      }
341
371
      goto failure;
342
372
    }
343
373
    if(ret == -1){
344
 
      perror("waitpid");
 
374
      error_plus(0, errno, "waitpid");
345
375
      if(errno == ECHILD){
346
376
        splashy_command_pid = 0;
347
377
      }
379
409
         the real user ID (_mandos) */
380
410
      ret = setuid(geteuid());
381
411
      if(ret == -1){
382
 
        perror("setuid");
 
412
        error_plus(0, errno, "setuid");
383
413
      }
384
414
      
385
415
      setsid();
386
416
      ret = chdir("/");
387
417
      if(ret == -1){
388
 
        perror("chdir");
 
418
        error_plus(0, errno, "chdir");
389
419
      }
390
420
/*       if(fork() != 0){ */
391
421
/*      _exit(EXIT_SUCCESS); */
392
422
/*       } */
393
423
      ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */
394
424
      if(ret == -1){
395
 
        perror("dup2");
 
425
        error_plus(0, errno, "dup2");
396
426
        _exit(EX_OSERR);
397
427
      }
398
428
      
399
429
      execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL);
400
430
      {
401
431
        int e = errno;
402
 
        perror("execl");
 
432
        error_plus(0, errno, "execl");
403
433
        switch(e){
404
434
        case EACCES:
405
435
        case ENOENT:
425
455
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
426
456
                                            &signal_action, NULL));
427
457
    if(ret == -1){
428
 
      perror("sigaction");
 
458
      error_plus(0, errno, "sigaction");
429
459
    }
430
460
    do {
431
461
      ret = raise(signal_received);
432
462
    } while(ret != 0 and errno == EINTR);
433
463
    if(ret != 0){
434
 
      perror("raise");
 
464
      error_plus(0, errno, "raise");
435
465
      abort();
436
466
    }
437
467
    TEMP_FAILURE_RETRY(pause());