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

  • Committer: Teddy Hogeborn
  • Date: 2015-05-23 20:18:34 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • 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
 * Usplash - Read a password from usplash and output it
4
4
 * 
5
 
 * Copyright © 2008-2011 Teddy Hogeborn
6
 
 * Copyright © 2008-2011 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             /* asprintf(), TEMP_FAILURE_RETRY() */
35
35
#include <sys/types.h>          /* size_t, ssize_t, pid_t, DIR, struct
36
36
                                   dirent */
37
37
#include <stddef.h>             /* NULL */
38
 
#include <string.h>             /* strlen(), memcmp() */
39
 
#include <stdio.h>              /* asprintf()*/
 
38
#include <string.h>             /* strlen(), memcmp(), strerror() */
 
39
#include <stdio.h>              /* asprintf(), vasprintf(), vprintf(),
 
40
                                   fprintf() */
40
41
#include <unistd.h>             /* close(), write(), readlink(),
41
42
                                   read(), STDOUT_FILENO, sleep(),
42
43
                                   fork(), setuid(), geteuid(),
50
51
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
51
52
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
52
53
#include <argz.h>               /* argz_count(), argz_extract() */
 
54
#include <stdarg.h>             /* va_list, va_start(), ... */
53
55
 
54
56
sig_atomic_t interrupted_by_signal = 0;
55
57
int signal_received;
56
58
const char usplash_name[] = "/sbin/usplash";
57
59
 
 
60
/* Function to use when printing errors */
 
61
__attribute__((format (gnu_printf, 3, 4)))
 
62
void error_plus(int status, int errnum, const char *formatstring,
 
63
                ...){
 
64
  va_list ap;
 
65
  char *text;
 
66
  int ret;
 
67
  
 
68
  va_start(ap, formatstring);
 
69
  ret = vasprintf(&text, formatstring, ap);
 
70
  if(ret == -1){
 
71
    fprintf(stderr, "Mandos plugin %s: ",
 
72
            program_invocation_short_name);
 
73
    vfprintf(stderr, formatstring, ap);
 
74
    fprintf(stderr, ": ");
 
75
    fprintf(stderr, "%s\n", strerror(errnum));
 
76
    error(status, errno, "vasprintf while printing error");
 
77
    return;
 
78
  }
 
79
  fprintf(stderr, "Mandos plugin ");
 
80
  error(status, errnum, "%s", text);
 
81
  free(text);
 
82
}
 
83
 
58
84
static void termination_handler(int signum){
59
85
  if(interrupted_by_signal){
60
86
    return;
155
181
  size_t cmdline_len = 0;
156
182
  DIR *proc_dir = opendir("/proc");
157
183
  if(proc_dir == NULL){
158
 
    error(0, errno, "opendir");
 
184
    error_plus(0, errno, "opendir");
159
185
    return -1;
160
186
  }
161
187
  errno = 0;
183
209
      char *exe_link;
184
210
      ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
185
211
      if(ret == -1){
186
 
        error(0, errno, "asprintf");
 
212
        error_plus(0, errno, "asprintf");
187
213
        goto fail_find_usplash;
188
214
      }
189
215
      
195
221
          free(exe_link);
196
222
          continue;
197
223
        }
198
 
        error(0, errno, "lstat");
 
224
        error_plus(0, errno, "lstat");
199
225
        free(exe_link);
200
226
        goto fail_find_usplash;
201
227
      }
226
252
        ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
227
253
                       proc_ent->d_name);
228
254
        if(ret == -1){
229
 
          error(0, errno, "asprintf");
 
255
          error_plus(0, errno, "asprintf");
230
256
          goto fail_find_usplash;
231
257
        }
232
258
        cl_fd = open(cmdline_filename, O_RDONLY);
233
259
        free(cmdline_filename);
234
260
        if(cl_fd == -1){
235
 
          error(0, errno, "open");
 
261
          error_plus(0, errno, "open");
236
262
          goto fail_find_usplash;
237
263
        }
238
264
      }
244
270
        if(cmdline_len + blocksize > cmdline_allocated){
245
271
          tmp = realloc(cmdline, cmdline_allocated + blocksize);
246
272
          if(tmp == NULL){
247
 
            error(0, errno, "realloc");
 
273
            error_plus(0, errno, "realloc");
248
274
            close(cl_fd);
249
275
            goto fail_find_usplash;
250
276
          }
255
281
        sret = read(cl_fd, cmdline + cmdline_len,
256
282
                    cmdline_allocated - cmdline_len);
257
283
        if(sret == -1){
258
 
          error(0, errno, "read");
 
284
          error_plus(0, errno, "read");
259
285
          close(cl_fd);
260
286
          goto fail_find_usplash;
261
287
        }
263
289
      } while(sret != 0);
264
290
      ret = close(cl_fd);
265
291
      if(ret == -1){
266
 
        error(0, errno, "close");
 
292
        error_plus(0, errno, "close");
267
293
        goto fail_find_usplash;
268
294
      }
269
295
    }
270
296
    /* Close directory */
271
297
    ret = closedir(proc_dir);
272
298
    if(ret == -1){
273
 
      error(0, errno, "closedir");
 
299
      error_plus(0, errno, "closedir");
274
300
      goto fail_find_usplash;
275
301
    }
276
302
    /* Success */
325
351
    sigemptyset(&new_action.sa_mask);
326
352
    ret = sigaddset(&new_action.sa_mask, SIGINT);
327
353
    if(ret == -1){
328
 
      error(0, errno, "sigaddset");
 
354
      error_plus(0, errno, "sigaddset");
329
355
      status = EX_OSERR;
330
356
      goto failure;
331
357
    }
332
358
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
333
359
    if(ret == -1){
334
 
      error(0, errno, "sigaddset");
 
360
      error_plus(0, errno, "sigaddset");
335
361
      status = EX_OSERR;
336
362
      goto failure;
337
363
    }
338
364
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
339
365
    if(ret == -1){
340
 
      error(0, errno, "sigaddset");
 
366
      error_plus(0, errno, "sigaddset");
341
367
      status = EX_OSERR;
342
368
      goto failure;
343
369
    }
344
370
    ret = sigaction(SIGINT, NULL, &old_action);
345
371
    if(ret == -1){
346
372
      if(errno != EINTR){
347
 
        error(0, errno, "sigaction");
 
373
        error_plus(0, errno, "sigaction");
348
374
        status = EX_OSERR;
349
375
      }
350
376
      goto failure;
353
379
      ret = sigaction(SIGINT, &new_action, NULL);
354
380
      if(ret == -1){
355
381
        if(errno != EINTR){
356
 
          error(0, errno, "sigaction");
 
382
          error_plus(0, errno, "sigaction");
357
383
          status = EX_OSERR;
358
384
        }
359
385
        goto failure;
362
388
    ret = sigaction(SIGHUP, NULL, &old_action);
363
389
    if(ret == -1){
364
390
      if(errno != EINTR){
365
 
        error(0, errno, "sigaction");
 
391
        error_plus(0, errno, "sigaction");
366
392
        status = EX_OSERR;
367
393
      }
368
394
      goto failure;
371
397
      ret = sigaction(SIGHUP, &new_action, NULL);
372
398
      if(ret == -1){
373
399
        if(errno != EINTR){
374
 
          error(0, errno, "sigaction");
 
400
          error_plus(0, errno, "sigaction");
375
401
          status = EX_OSERR;
376
402
        }
377
403
        goto failure;
380
406
    ret = sigaction(SIGTERM, NULL, &old_action);
381
407
    if(ret == -1){
382
408
      if(errno != EINTR){
383
 
        error(0, errno, "sigaction");
 
409
        error_plus(0, errno, "sigaction");
384
410
        status = EX_OSERR;
385
411
      }
386
412
      goto failure;
389
415
      ret = sigaction(SIGTERM, &new_action, NULL);
390
416
      if(ret == -1){
391
417
        if(errno != EINTR){
392
 
          error(0, errno, "sigaction");
 
418
          error_plus(0, errno, "sigaction");
393
419
          status = EX_OSERR;
394
420
        }
395
421
        goto failure;
401
427
  /* Write command to FIFO */
402
428
  if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){
403
429
    if(errno != EINTR){
404
 
      error(0, errno, "usplash_write");
 
430
      error_plus(0, errno, "usplash_write");
405
431
      status = EX_OSERR;
406
432
    }
407
433
    goto failure;
413
439
  
414
440
  if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){
415
441
    if(errno != EINTR){
416
 
      error(0, errno, "usplash_write");
 
442
      error_plus(0, errno, "usplash_write");
417
443
      status = EX_OSERR;
418
444
    }
419
445
    goto failure;
431
457
  outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
432
458
  if(outfifo_fd == -1){
433
459
    if(errno != EINTR){
434
 
      error(0, errno, "open");
 
460
      error_plus(0, errno, "open");
435
461
      status = EX_OSERR;
436
462
    }
437
463
    goto failure;
450
476
      char *tmp = realloc(buf, buf_allocated + blocksize);
451
477
      if(tmp == NULL){
452
478
        if(errno != EINTR){
453
 
          error(0, errno, "realloc");
 
479
          error_plus(0, errno, "realloc");
454
480
          status = EX_OSERR;
455
481
        }
456
482
        goto failure;
462
488
                buf_allocated - buf_len);
463
489
    if(sret == -1){
464
490
      if(errno != EINTR){
465
 
        error(0, errno, "read");
 
491
        error_plus(0, errno, "read");
466
492
        status = EX_OSERR;
467
493
      }
468
494
      TEMP_FAILURE_RETRY(close(outfifo_fd));
477
503
  ret = close(outfifo_fd);
478
504
  if(ret == -1){
479
505
    if(errno != EINTR){
480
 
      error(0, errno, "close");
 
506
      error_plus(0, errno, "close");
481
507
      status = EX_OSERR;
482
508
    }
483
509
    goto failure;
490
516
  
491
517
  if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){
492
518
    if(errno != EINTR){
493
 
      error(0, errno, "usplash_write");
 
519
      error_plus(0, errno, "usplash_write");
494
520
      status = EX_OSERR;
495
521
    }
496
522
    goto failure;
503
529
  ret = close(fifo_fd);
504
530
  if(ret == -1){
505
531
    if(errno != EINTR){
506
 
      error(0, errno, "close");
 
532
      error_plus(0, errno, "close");
507
533
      status = EX_OSERR;
508
534
    }
509
535
    goto failure;
517
543
      sret = write(STDOUT_FILENO, buf + written, buf_len - written);
518
544
      if(sret == -1){
519
545
        if(errno != EINTR){
520
 
          error(0, errno, "write");
 
546
          error_plus(0, errno, "write");
521
547
          status = EX_OSERR;
522
548
        }
523
549
        goto failure;
554
580
  if(fifo_fd != -1){
555
581
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
556
582
    if(ret == -1 and errno != EINTR){
557
 
      error(0, errno, "close");
 
583
      error_plus(0, errno, "close");
558
584
    }
559
585
    fifo_fd = -1;
560
586
  }
563
589
  if(outfifo_fd != -1){
564
590
    ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd));
565
591
    if(ret == -1){
566
 
      error(0, errno, "close");
 
592
      error_plus(0, errno, "close");
567
593
    }
568
594
  }
569
595
  
571
597
  char **cmdline_argv = malloc((argz_count(cmdline, cmdline_len) + 1)
572
598
                               * sizeof(char *)); /* Count args */
573
599
  if(cmdline_argv == NULL){
574
 
    error(0, errno, "malloc");
 
600
    error_plus(0, errno, "malloc");
575
601
    return status;
576
602
  }
577
603
  argz_extract(cmdline, cmdline_len, cmdline_argv); /* Create argv */
592
618
       the real user ID (_mandos) */
593
619
    ret = setuid(geteuid());
594
620
    if(ret == -1){
595
 
      error(0, errno, "setuid");
 
621
      error_plus(0, errno, "setuid");
596
622
    }
597
623
    
598
624
    setsid();
599
625
    ret = chdir("/");
600
626
    if(ret == -1){
601
 
      error(0, errno, "chdir");
 
627
      error_plus(0, errno, "chdir");
602
628
      _exit(EX_OSERR);
603
629
    }
604
630
/*     if(fork() != 0){ */
606
632
/*     } */
607
633
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
608
634
    if(ret == -1){
609
 
      error(0, errno, "dup2");
 
635
      error_plus(0, errno, "dup2");
610
636
      _exit(EX_OSERR);
611
637
    }
612
638
    
613
639
    execv(usplash_name, cmdline_argv);
614
640
    if(not interrupted_by_signal){
615
 
      error(0, errno, "execv");
 
641
      error_plus(0, errno, "execv");
616
642
    }
617
643
    free(cmdline);
618
644
    free(cmdline_argv);
623
649
  sleep(2);
624
650
  if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
625
651
    if(errno != EINTR){
626
 
      error(0, errno, "usplash_write");
 
652
      error_plus(0, errno, "usplash_write");
627
653
    }
628
654
  }
629
655
  
631
657
  if(fifo_fd != -1){
632
658
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
633
659
    if(ret == -1 and errno != EINTR){
634
 
      error(0, errno, "close");
 
660
      error_plus(0, errno, "close");
635
661
    }
636
662
    fifo_fd = -1;
637
663
  }
642
668
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
643
669
                                            &signal_action, NULL));
644
670
    if(ret == -1){
645
 
      error(0, errno, "sigaction");
 
671
      error_plus(0, errno, "sigaction");
646
672
    }
647
673
    do {
648
674
      ret = raise(signal_received);
649
675
    } while(ret != 0 and errno == EINTR);
650
676
    if(ret != 0){
651
 
      error(0, errno, "raise");
 
677
      error_plus(0, errno, "raise");
652
678
      abort();
653
679
    }
654
680
    TEMP_FAILURE_RETRY(pause());