/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/usplash.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
 * Usplash - Read a password from usplash 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             /* asprintf(), TEMP_FAILURE_RETRY() */
31
31
#include <fcntl.h>              /* open(), O_WRONLY, O_RDONLY */
32
32
#include <iso646.h>             /* and, or, not*/
33
33
#include <errno.h>              /* errno, EINTR */
 
34
#include <error.h>
34
35
#include <sys/types.h>          /* size_t, ssize_t, pid_t, DIR, struct
35
36
                                   dirent */
36
37
#include <stddef.h>             /* NULL */
37
 
#include <string.h>             /* strlen(), memcmp() */
38
 
#include <stdio.h>              /* asprintf(), perror() */
 
38
#include <string.h>             /* strlen(), memcmp(), strerror() */
 
39
#include <stdio.h>              /* asprintf(), vasprintf(), vprintf(),
 
40
                                   fprintf() */
39
41
#include <unistd.h>             /* close(), write(), readlink(),
40
42
                                   read(), STDOUT_FILENO, sleep(),
41
43
                                   fork(), setuid(), geteuid(),
48
50
#include <inttypes.h>           /* intmax_t, strtoimax() */
49
51
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
50
52
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
 
53
#include <argz.h>               /* argz_count(), argz_extract() */
 
54
#include <stdarg.h>             /* va_list, va_start(), ... */
51
55
 
52
56
sig_atomic_t interrupted_by_signal = 0;
53
57
int signal_received;
54
58
const char usplash_name[] = "/sbin/usplash";
55
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
 
56
84
static void termination_handler(int signum){
57
85
  if(interrupted_by_signal){
58
86
    return;
153
181
  size_t cmdline_len = 0;
154
182
  DIR *proc_dir = opendir("/proc");
155
183
  if(proc_dir == NULL){
156
 
    perror("opendir");
 
184
    error_plus(0, errno, "opendir");
157
185
    return -1;
158
186
  }
159
187
  errno = 0;
181
209
      char *exe_link;
182
210
      ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
183
211
      if(ret == -1){
184
 
        perror("asprintf");
 
212
        error_plus(0, errno, "asprintf");
185
213
        goto fail_find_usplash;
186
214
      }
187
215
      
193
221
          free(exe_link);
194
222
          continue;
195
223
        }
196
 
        perror("lstat");
 
224
        error_plus(0, errno, "lstat");
197
225
        free(exe_link);
198
226
        goto fail_find_usplash;
199
227
      }
224
252
        ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
225
253
                       proc_ent->d_name);
226
254
        if(ret == -1){
227
 
          perror("asprintf");
 
255
          error_plus(0, errno, "asprintf");
228
256
          goto fail_find_usplash;
229
257
        }
230
258
        cl_fd = open(cmdline_filename, O_RDONLY);
231
259
        free(cmdline_filename);
232
260
        if(cl_fd == -1){
233
 
          perror("open");
 
261
          error_plus(0, errno, "open");
234
262
          goto fail_find_usplash;
235
263
        }
236
264
      }
242
270
        if(cmdline_len + blocksize > cmdline_allocated){
243
271
          tmp = realloc(cmdline, cmdline_allocated + blocksize);
244
272
          if(tmp == NULL){
245
 
            perror("realloc");
 
273
            error_plus(0, errno, "realloc");
246
274
            close(cl_fd);
247
275
            goto fail_find_usplash;
248
276
          }
253
281
        sret = read(cl_fd, cmdline + cmdline_len,
254
282
                    cmdline_allocated - cmdline_len);
255
283
        if(sret == -1){
256
 
          perror("read");
 
284
          error_plus(0, errno, "read");
257
285
          close(cl_fd);
258
286
          goto fail_find_usplash;
259
287
        }
261
289
      } while(sret != 0);
262
290
      ret = close(cl_fd);
263
291
      if(ret == -1){
264
 
        perror("close");
 
292
        error_plus(0, errno, "close");
265
293
        goto fail_find_usplash;
266
294
      }
267
295
    }
268
296
    /* Close directory */
269
297
    ret = closedir(proc_dir);
270
298
    if(ret == -1){
271
 
      perror("closedir");
 
299
      error_plus(0, errno, "closedir");
272
300
      goto fail_find_usplash;
273
301
    }
274
302
    /* Success */
323
351
    sigemptyset(&new_action.sa_mask);
324
352
    ret = sigaddset(&new_action.sa_mask, SIGINT);
325
353
    if(ret == -1){
326
 
      perror("sigaddset");
 
354
      error_plus(0, errno, "sigaddset");
327
355
      status = EX_OSERR;
328
356
      goto failure;
329
357
    }
330
358
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
331
359
    if(ret == -1){
332
 
      perror("sigaddset");
 
360
      error_plus(0, errno, "sigaddset");
333
361
      status = EX_OSERR;
334
362
      goto failure;
335
363
    }
336
364
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
337
365
    if(ret == -1){
338
 
      perror("sigaddset");
 
366
      error_plus(0, errno, "sigaddset");
339
367
      status = EX_OSERR;
340
368
      goto failure;
341
369
    }
342
370
    ret = sigaction(SIGINT, NULL, &old_action);
343
371
    if(ret == -1){
344
372
      if(errno != EINTR){
345
 
        perror("sigaction");
 
373
        error_plus(0, errno, "sigaction");
346
374
        status = EX_OSERR;
347
375
      }
348
376
      goto failure;
351
379
      ret = sigaction(SIGINT, &new_action, NULL);
352
380
      if(ret == -1){
353
381
        if(errno != EINTR){
354
 
          perror("sigaction");
 
382
          error_plus(0, errno, "sigaction");
355
383
          status = EX_OSERR;
356
384
        }
357
385
        goto failure;
360
388
    ret = sigaction(SIGHUP, NULL, &old_action);
361
389
    if(ret == -1){
362
390
      if(errno != EINTR){
363
 
        perror("sigaction");
 
391
        error_plus(0, errno, "sigaction");
364
392
        status = EX_OSERR;
365
393
      }
366
394
      goto failure;
369
397
      ret = sigaction(SIGHUP, &new_action, NULL);
370
398
      if(ret == -1){
371
399
        if(errno != EINTR){
372
 
          perror("sigaction");
 
400
          error_plus(0, errno, "sigaction");
373
401
          status = EX_OSERR;
374
402
        }
375
403
        goto failure;
378
406
    ret = sigaction(SIGTERM, NULL, &old_action);
379
407
    if(ret == -1){
380
408
      if(errno != EINTR){
381
 
        perror("sigaction");
 
409
        error_plus(0, errno, "sigaction");
382
410
        status = EX_OSERR;
383
411
      }
384
412
      goto failure;
387
415
      ret = sigaction(SIGTERM, &new_action, NULL);
388
416
      if(ret == -1){
389
417
        if(errno != EINTR){
390
 
          perror("sigaction");
 
418
          error_plus(0, errno, "sigaction");
391
419
          status = EX_OSERR;
392
420
        }
393
421
        goto failure;
399
427
  /* Write command to FIFO */
400
428
  if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){
401
429
    if(errno != EINTR){
402
 
      perror("usplash_write");
 
430
      error_plus(0, errno, "usplash_write");
403
431
      status = EX_OSERR;
404
432
    }
405
433
    goto failure;
411
439
  
412
440
  if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){
413
441
    if(errno != EINTR){
414
 
      perror("usplash_write");
 
442
      error_plus(0, errno, "usplash_write");
415
443
      status = EX_OSERR;
416
444
    }
417
445
    goto failure;
429
457
  outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
430
458
  if(outfifo_fd == -1){
431
459
    if(errno != EINTR){
432
 
      perror("open");
 
460
      error_plus(0, errno, "open");
433
461
      status = EX_OSERR;
434
462
    }
435
463
    goto failure;
448
476
      char *tmp = realloc(buf, buf_allocated + blocksize);
449
477
      if(tmp == NULL){
450
478
        if(errno != EINTR){
451
 
          perror("realloc");
 
479
          error_plus(0, errno, "realloc");
452
480
          status = EX_OSERR;
453
481
        }
454
482
        goto failure;
460
488
                buf_allocated - buf_len);
461
489
    if(sret == -1){
462
490
      if(errno != EINTR){
463
 
        perror("read");
 
491
        error_plus(0, errno, "read");
464
492
        status = EX_OSERR;
465
493
      }
466
494
      TEMP_FAILURE_RETRY(close(outfifo_fd));
475
503
  ret = close(outfifo_fd);
476
504
  if(ret == -1){
477
505
    if(errno != EINTR){
478
 
      perror("close");
 
506
      error_plus(0, errno, "close");
479
507
      status = EX_OSERR;
480
508
    }
481
509
    goto failure;
488
516
  
489
517
  if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){
490
518
    if(errno != EINTR){
491
 
      perror("usplash_write");
 
519
      error_plus(0, errno, "usplash_write");
492
520
      status = EX_OSERR;
493
521
    }
494
522
    goto failure;
501
529
  ret = close(fifo_fd);
502
530
  if(ret == -1){
503
531
    if(errno != EINTR){
504
 
      perror("close");
 
532
      error_plus(0, errno, "close");
505
533
      status = EX_OSERR;
506
534
    }
507
535
    goto failure;
515
543
      sret = write(STDOUT_FILENO, buf + written, buf_len - written);
516
544
      if(sret == -1){
517
545
        if(errno != EINTR){
518
 
          perror("write");
 
546
          error_plus(0, errno, "write");
519
547
          status = EX_OSERR;
520
548
        }
521
549
        goto failure;
552
580
  if(fifo_fd != -1){
553
581
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
554
582
    if(ret == -1 and errno != EINTR){
555
 
      perror("close");
 
583
      error_plus(0, errno, "close");
556
584
    }
557
585
    fifo_fd = -1;
558
586
  }
561
589
  if(outfifo_fd != -1){
562
590
    ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd));
563
591
    if(ret == -1){
564
 
      perror("close");
565
 
    }
566
 
  }
567
 
  
568
 
  /* Create argc and argv for new usplash*/
569
 
  int cmdline_argc = 0;
570
 
  char **cmdline_argv = malloc(sizeof(char *));
571
 
  {
572
 
    size_t position = 0;
573
 
    while(position < cmdline_len){
574
 
      char **tmp = realloc(cmdline_argv,
575
 
                           (sizeof(char *)
576
 
                            * (size_t)(cmdline_argc + 2)));
577
 
      if(tmp == NULL){
578
 
        perror("realloc");
579
 
        free(cmdline_argv);
580
 
        return status;
581
 
      }
582
 
      cmdline_argv = tmp;
583
 
      cmdline_argv[cmdline_argc] = cmdline + position;
584
 
      cmdline_argc++;
585
 
      position += strlen(cmdline + position) + 1;
586
 
    }
587
 
    cmdline_argv[cmdline_argc] = NULL;
588
 
  }
 
592
      error_plus(0, errno, "close");
 
593
    }
 
594
  }
 
595
  
 
596
  /* Create argv for new usplash*/
 
597
  char **cmdline_argv = malloc((argz_count(cmdline, cmdline_len) + 1)
 
598
                               * sizeof(char *)); /* Count args */
 
599
  if(cmdline_argv == NULL){
 
600
    error_plus(0, errno, "malloc");
 
601
    return status;
 
602
  }
 
603
  argz_extract(cmdline, cmdline_len, cmdline_argv); /* Create argv */
 
604
  
589
605
  /* Kill old usplash */
590
606
  kill(usplash_pid, SIGTERM);
591
607
  sleep(2);
602
618
       the real user ID (_mandos) */
603
619
    ret = setuid(geteuid());
604
620
    if(ret == -1){
605
 
      perror("setuid");
 
621
      error_plus(0, errno, "setuid");
606
622
    }
607
623
    
608
624
    setsid();
609
625
    ret = chdir("/");
610
626
    if(ret == -1){
611
 
      perror("chdir");
 
627
      error_plus(0, errno, "chdir");
612
628
      _exit(EX_OSERR);
613
629
    }
614
630
/*     if(fork() != 0){ */
616
632
/*     } */
617
633
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
618
634
    if(ret == -1){
619
 
      perror("dup2");
 
635
      error_plus(0, errno, "dup2");
620
636
      _exit(EX_OSERR);
621
637
    }
622
638
    
623
639
    execv(usplash_name, cmdline_argv);
624
640
    if(not interrupted_by_signal){
625
 
      perror("execv");
 
641
      error_plus(0, errno, "execv");
626
642
    }
627
643
    free(cmdline);
628
644
    free(cmdline_argv);
633
649
  sleep(2);
634
650
  if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
635
651
    if(errno != EINTR){
636
 
      perror("usplash_write");
 
652
      error_plus(0, errno, "usplash_write");
637
653
    }
638
654
  }
639
655
  
641
657
  if(fifo_fd != -1){
642
658
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
643
659
    if(ret == -1 and errno != EINTR){
644
 
      perror("close");
 
660
      error_plus(0, errno, "close");
645
661
    }
646
662
    fifo_fd = -1;
647
663
  }
652
668
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
653
669
                                            &signal_action, NULL));
654
670
    if(ret == -1){
655
 
      perror("sigaction");
 
671
      error_plus(0, errno, "sigaction");
656
672
    }
657
673
    do {
658
674
      ret = raise(signal_received);
659
675
    } while(ret != 0 and errno == EINTR);
660
676
    if(ret != 0){
661
 
      perror("raise");
 
677
      error_plus(0, errno, "raise");
662
678
      abort();
663
679
    }
664
680
    TEMP_FAILURE_RETRY(pause());