/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(),
42
44
                                   setsid(), chdir(), dup2(),
43
45
                                   STDERR_FILENO, execv() */
44
46
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
45
 
                                   EXIT_SUCCESS, malloc(), _exit() */
46
 
#include <stdlib.h>             /* getenv() */
 
47
                                   EXIT_SUCCESS, malloc(), _exit(),
 
48
                                   getenv() */
47
49
#include <dirent.h>             /* opendir(), readdir(), closedir() */
48
50
#include <inttypes.h>           /* intmax_t, strtoimax() */
49
51
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
 
52
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
 
53
#include <argz.h>               /* argz_count(), argz_extract() */
 
54
#include <stdarg.h>             /* va_list, va_start(), ... */
50
55
 
51
56
sig_atomic_t interrupted_by_signal = 0;
52
57
int signal_received;
53
58
const char usplash_name[] = "/sbin/usplash";
54
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
 
55
84
static void termination_handler(int signum){
56
85
  if(interrupted_by_signal){
57
86
    return;
152
181
  size_t cmdline_len = 0;
153
182
  DIR *proc_dir = opendir("/proc");
154
183
  if(proc_dir == NULL){
155
 
    perror("opendir");
 
184
    error_plus(0, errno, "opendir");
156
185
    return -1;
157
186
  }
158
187
  errno = 0;
180
209
      char *exe_link;
181
210
      ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
182
211
      if(ret == -1){
183
 
        perror("asprintf");
 
212
        error_plus(0, errno, "asprintf");
184
213
        goto fail_find_usplash;
185
214
      }
186
215
      
192
221
          free(exe_link);
193
222
          continue;
194
223
        }
195
 
        perror("lstat");
 
224
        error_plus(0, errno, "lstat");
196
225
        free(exe_link);
197
226
        goto fail_find_usplash;
198
227
      }
223
252
        ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
224
253
                       proc_ent->d_name);
225
254
        if(ret == -1){
226
 
          perror("asprintf");
 
255
          error_plus(0, errno, "asprintf");
227
256
          goto fail_find_usplash;
228
257
        }
229
258
        cl_fd = open(cmdline_filename, O_RDONLY);
230
259
        free(cmdline_filename);
231
260
        if(cl_fd == -1){
232
 
          perror("open");
 
261
          error_plus(0, errno, "open");
233
262
          goto fail_find_usplash;
234
263
        }
235
264
      }
241
270
        if(cmdline_len + blocksize > cmdline_allocated){
242
271
          tmp = realloc(cmdline, cmdline_allocated + blocksize);
243
272
          if(tmp == NULL){
244
 
            perror("realloc");
 
273
            error_plus(0, errno, "realloc");
245
274
            close(cl_fd);
246
275
            goto fail_find_usplash;
247
276
          }
252
281
        sret = read(cl_fd, cmdline + cmdline_len,
253
282
                    cmdline_allocated - cmdline_len);
254
283
        if(sret == -1){
255
 
          perror("read");
 
284
          error_plus(0, errno, "read");
256
285
          close(cl_fd);
257
286
          goto fail_find_usplash;
258
287
        }
260
289
      } while(sret != 0);
261
290
      ret = close(cl_fd);
262
291
      if(ret == -1){
263
 
        perror("close");
 
292
        error_plus(0, errno, "close");
264
293
        goto fail_find_usplash;
265
294
      }
266
295
    }
267
296
    /* Close directory */
268
297
    ret = closedir(proc_dir);
269
298
    if(ret == -1){
270
 
      perror("closedir");
 
299
      error_plus(0, errno, "closedir");
271
300
      goto fail_find_usplash;
272
301
    }
273
302
    /* Success */
297
326
  size_t buf_len = 0;
298
327
  pid_t usplash_pid = -1;
299
328
  bool usplash_accessed = false;
 
329
  int status = EXIT_FAILURE;    /* Default failure exit status */
300
330
  
301
331
  char *prompt = makeprompt();
302
332
  if(prompt == NULL){
 
333
    status = EX_OSERR;
303
334
    goto failure;
304
335
  }
305
336
  
308
339
  size_t cmdline_len = 0;
309
340
  usplash_pid = find_usplash(&cmdline, &cmdline_len);
310
341
  if(usplash_pid == 0){
 
342
    status = EX_UNAVAILABLE;
311
343
    goto failure;
312
344
  }
313
345
  
319
351
    sigemptyset(&new_action.sa_mask);
320
352
    ret = sigaddset(&new_action.sa_mask, SIGINT);
321
353
    if(ret == -1){
322
 
      perror("sigaddset");
 
354
      error_plus(0, errno, "sigaddset");
 
355
      status = EX_OSERR;
323
356
      goto failure;
324
357
    }
325
358
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
326
359
    if(ret == -1){
327
 
      perror("sigaddset");
 
360
      error_plus(0, errno, "sigaddset");
 
361
      status = EX_OSERR;
328
362
      goto failure;
329
363
    }
330
364
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
331
365
    if(ret == -1){
332
 
      perror("sigaddset");
 
366
      error_plus(0, errno, "sigaddset");
 
367
      status = EX_OSERR;
333
368
      goto failure;
334
369
    }
335
370
    ret = sigaction(SIGINT, NULL, &old_action);
336
371
    if(ret == -1){
337
372
      if(errno != EINTR){
338
 
        perror("sigaction");
 
373
        error_plus(0, errno, "sigaction");
 
374
        status = EX_OSERR;
339
375
      }
340
376
      goto failure;
341
377
    }
343
379
      ret = sigaction(SIGINT, &new_action, NULL);
344
380
      if(ret == -1){
345
381
        if(errno != EINTR){
346
 
          perror("sigaction");
 
382
          error_plus(0, errno, "sigaction");
 
383
          status = EX_OSERR;
347
384
        }
348
385
        goto failure;
349
386
      }
351
388
    ret = sigaction(SIGHUP, NULL, &old_action);
352
389
    if(ret == -1){
353
390
      if(errno != EINTR){
354
 
        perror("sigaction");
 
391
        error_plus(0, errno, "sigaction");
 
392
        status = EX_OSERR;
355
393
      }
356
394
      goto failure;
357
395
    }
359
397
      ret = sigaction(SIGHUP, &new_action, NULL);
360
398
      if(ret == -1){
361
399
        if(errno != EINTR){
362
 
          perror("sigaction");
 
400
          error_plus(0, errno, "sigaction");
 
401
          status = EX_OSERR;
363
402
        }
364
403
        goto failure;
365
404
      }
367
406
    ret = sigaction(SIGTERM, NULL, &old_action);
368
407
    if(ret == -1){
369
408
      if(errno != EINTR){
370
 
        perror("sigaction");
 
409
        error_plus(0, errno, "sigaction");
 
410
        status = EX_OSERR;
371
411
      }
372
412
      goto failure;
373
413
    }
375
415
      ret = sigaction(SIGTERM, &new_action, NULL);
376
416
      if(ret == -1){
377
417
        if(errno != EINTR){
378
 
          perror("sigaction");
 
418
          error_plus(0, errno, "sigaction");
 
419
          status = EX_OSERR;
379
420
        }
380
421
        goto failure;
381
422
      }
386
427
  /* Write command to FIFO */
387
428
  if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){
388
429
    if(errno != EINTR){
389
 
      perror("usplash_write");
 
430
      error_plus(0, errno, "usplash_write");
 
431
      status = EX_OSERR;
390
432
    }
391
433
    goto failure;
392
434
  }
397
439
  
398
440
  if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){
399
441
    if(errno != EINTR){
400
 
      perror("usplash_write");
 
442
      error_plus(0, errno, "usplash_write");
 
443
      status = EX_OSERR;
401
444
    }
402
445
    goto failure;
403
446
  }
414
457
  outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
415
458
  if(outfifo_fd == -1){
416
459
    if(errno != EINTR){
417
 
      perror("open");
 
460
      error_plus(0, errno, "open");
 
461
      status = EX_OSERR;
418
462
    }
419
463
    goto failure;
420
464
  }
432
476
      char *tmp = realloc(buf, buf_allocated + blocksize);
433
477
      if(tmp == NULL){
434
478
        if(errno != EINTR){
435
 
          perror("realloc");
 
479
          error_plus(0, errno, "realloc");
 
480
          status = EX_OSERR;
436
481
        }
437
482
        goto failure;
438
483
      }
443
488
                buf_allocated - buf_len);
444
489
    if(sret == -1){
445
490
      if(errno != EINTR){
446
 
        perror("read");
 
491
        error_plus(0, errno, "read");
 
492
        status = EX_OSERR;
447
493
      }
448
494
      TEMP_FAILURE_RETRY(close(outfifo_fd));
449
495
      goto failure;
457
503
  ret = close(outfifo_fd);
458
504
  if(ret == -1){
459
505
    if(errno != EINTR){
460
 
      perror("close");
 
506
      error_plus(0, errno, "close");
 
507
      status = EX_OSERR;
461
508
    }
462
509
    goto failure;
463
510
  }
469
516
  
470
517
  if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){
471
518
    if(errno != EINTR){
472
 
      perror("usplash_write");
 
519
      error_plus(0, errno, "usplash_write");
 
520
      status = EX_OSERR;
473
521
    }
474
522
    goto failure;
475
523
  }
481
529
  ret = close(fifo_fd);
482
530
  if(ret == -1){
483
531
    if(errno != EINTR){
484
 
      perror("close");
 
532
      error_plus(0, errno, "close");
 
533
      status = EX_OSERR;
485
534
    }
486
535
    goto failure;
487
536
  }
494
543
      sret = write(STDOUT_FILENO, buf + written, buf_len - written);
495
544
      if(sret == -1){
496
545
        if(errno != EINTR){
497
 
          perror("write");
 
546
          error_plus(0, errno, "write");
 
547
          status = EX_OSERR;
498
548
        }
499
549
        goto failure;
500
550
      }
523
573
  
524
574
  /* If usplash was never accessed, we can stop now */
525
575
  if(not usplash_accessed){
526
 
    return EXIT_FAILURE;
 
576
    return status;
527
577
  }
528
578
  
529
579
  /* Close FIFO */
530
580
  if(fifo_fd != -1){
531
581
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
532
582
    if(ret == -1 and errno != EINTR){
533
 
      perror("close");
 
583
      error_plus(0, errno, "close");
534
584
    }
535
585
    fifo_fd = -1;
536
586
  }
539
589
  if(outfifo_fd != -1){
540
590
    ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd));
541
591
    if(ret == -1){
542
 
      perror("close");
543
 
    }
544
 
  }
545
 
  
546
 
  /* Create argc and argv for new usplash*/
547
 
  int cmdline_argc = 0;
548
 
  char **cmdline_argv = malloc(sizeof(char *));
549
 
  {
550
 
    size_t position = 0;
551
 
    while(position < cmdline_len){
552
 
      char **tmp = realloc(cmdline_argv,
553
 
                           (sizeof(char *)
554
 
                            * (size_t)(cmdline_argc + 2)));
555
 
      if(tmp == NULL){
556
 
        perror("realloc");
557
 
        free(cmdline_argv);
558
 
        return EXIT_FAILURE;
559
 
      }
560
 
      cmdline_argv = tmp;
561
 
      cmdline_argv[cmdline_argc] = cmdline + position;
562
 
      cmdline_argc++;
563
 
      position += strlen(cmdline + position) + 1;
564
 
    }
565
 
    cmdline_argv[cmdline_argc] = NULL;
566
 
  }
 
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
  
567
605
  /* Kill old usplash */
568
606
  kill(usplash_pid, SIGTERM);
569
607
  sleep(2);
580
618
       the real user ID (_mandos) */
581
619
    ret = setuid(geteuid());
582
620
    if(ret == -1){
583
 
      perror("setuid");
 
621
      error_plus(0, errno, "setuid");
584
622
    }
585
623
    
586
624
    setsid();
587
625
    ret = chdir("/");
 
626
    if(ret == -1){
 
627
      error_plus(0, errno, "chdir");
 
628
      _exit(EX_OSERR);
 
629
    }
588
630
/*     if(fork() != 0){ */
589
631
/*       _exit(EXIT_SUCCESS); */
590
632
/*     } */
591
633
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
592
634
    if(ret == -1){
593
 
      perror("dup2");
594
 
      _exit(EXIT_FAILURE);
 
635
      error_plus(0, errno, "dup2");
 
636
      _exit(EX_OSERR);
595
637
    }
596
638
    
597
639
    execv(usplash_name, cmdline_argv);
598
640
    if(not interrupted_by_signal){
599
 
      perror("execv");
 
641
      error_plus(0, errno, "execv");
600
642
    }
601
643
    free(cmdline);
602
644
    free(cmdline_argv);
603
 
    _exit(EXIT_FAILURE);
 
645
    _exit(EX_OSERR);
604
646
  }
605
647
  free(cmdline);
606
648
  free(cmdline_argv);
607
649
  sleep(2);
608
650
  if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
609
651
    if(errno != EINTR){
610
 
      perror("usplash_write");
 
652
      error_plus(0, errno, "usplash_write");
611
653
    }
612
654
  }
613
655
  
615
657
  if(fifo_fd != -1){
616
658
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
617
659
    if(ret == -1 and errno != EINTR){
618
 
      perror("close");
 
660
      error_plus(0, errno, "close");
619
661
    }
620
662
    fifo_fd = -1;
621
663
  }
626
668
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
627
669
                                            &signal_action, NULL));
628
670
    if(ret == -1){
629
 
      perror("sigaction");
 
671
      error_plus(0, errno, "sigaction");
630
672
    }
631
673
    do {
632
674
      ret = raise(signal_received);
633
675
    } while(ret != 0 and errno == EINTR);
634
676
    if(ret != 0){
635
 
      perror("raise");
 
677
      error_plus(0, errno, "raise");
636
678
      abort();
637
679
    }
638
680
    TEMP_FAILURE_RETRY(pause());
639
681
  }
640
682
  
641
 
  return EXIT_FAILURE;
 
683
  return status;
642
684
}