/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: 2019-08-02 22:16:53 UTC
  • mto: This revision was merged to the branch mainline in revision 386.
  • Revision ID: teddy@recompile.se-20190802221653-ic1iko9hbefzwsk7
Fix bug in server Debian package: Fails to start on first install

There has been a very long-standing bug where installation of the
server (the "mandos" Debian package) would fail to start the server
properly right after installation.  It would work on manual (re)start
after installation, or after reboot, and even after package purge and
reinstall, it would then work the first time.  The problem, it turns
out, is when the new "_mandos" user (and corresponding group) is
created, the D-Bus server is not reloaded, and is therefore not aware
of that user, and does not recognize the user and group name in the
/etc/dbus-1/system.d/mandos.conf file.  The Mandos server, when it
tries to start and access the D-Bus, is then not permitted to connect
to its D-Bus bus name, and disables D-Bus use as a fallback measure;
i.e. the server works, but it is not controllable via D-Bus commands
(via mandos-ctl or mandos-monitor).  The next time the D-Bus daemon is
reloaded for any reason, the new user & group would become visible to
the D-Bus daemon and after that, any restart of the Mandos server
would succeed and it would bind to its D-Bus name properly, and
thereby be visible and controllable by mandos-ctl & mandos-monitor.
This was mostly invisible when using sysvinit, but systemd makes the
problem visible since the systemd service file for the Mandos server
is configured to not consider the Mandos server "started" until the
D-Bus name has been bound; this makes the starting of the service wait
for 90 seconds and then fail with a timeout error.

Fixing this should also make the Debian CI autopkgtest tests work.

* debian/mandos.postinst (configure): After creating (or renaming)
                                      user & group, reload D-Bus
                                      daemon (if present).

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
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
 
5
 * Copyright © 2008-2018 Teddy Hogeborn
 
6
 * Copyright © 2008-2018 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
26
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
31
32
#include <fcntl.h>              /* open(), O_WRONLY, O_RDONLY */
32
33
#include <iso646.h>             /* and, or, not*/
33
34
#include <errno.h>              /* errno, EINTR */
 
35
#include <error.h>
34
36
#include <sys/types.h>          /* size_t, ssize_t, pid_t, DIR, struct
35
37
                                   dirent */
36
38
#include <stddef.h>             /* NULL */
37
 
#include <string.h>             /* strlen(), memcmp() */
38
 
#include <stdio.h>              /* asprintf(), perror() */
 
39
#include <string.h>             /* strlen(), memcmp(), strerror() */
 
40
#include <stdio.h>              /* asprintf(), vasprintf(), vprintf(),
 
41
                                   fprintf() */
39
42
#include <unistd.h>             /* close(), write(), readlink(),
40
43
                                   read(), STDOUT_FILENO, sleep(),
41
44
                                   fork(), setuid(), geteuid(),
42
45
                                   setsid(), chdir(), dup2(),
43
46
                                   STDERR_FILENO, execv() */
44
47
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
45
 
                                   EXIT_SUCCESS, malloc(), _exit() */
46
 
#include <stdlib.h>             /* getenv() */
 
48
                                   EXIT_SUCCESS, malloc(), _exit(),
 
49
                                   getenv() */
47
50
#include <dirent.h>             /* opendir(), readdir(), closedir() */
48
51
#include <inttypes.h>           /* intmax_t, strtoimax() */
49
52
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
50
53
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
 
54
#include <argz.h>               /* argz_count(), argz_extract() */
 
55
#include <stdarg.h>             /* va_list, va_start(), ... */
51
56
 
52
57
sig_atomic_t interrupted_by_signal = 0;
53
58
int signal_received;
54
59
const char usplash_name[] = "/sbin/usplash";
55
60
 
 
61
/* Function to use when printing errors */
 
62
__attribute__((format (gnu_printf, 3, 4)))
 
63
void error_plus(int status, int errnum, const char *formatstring,
 
64
                ...){
 
65
  va_list ap;
 
66
  char *text;
 
67
  int ret;
 
68
  
 
69
  va_start(ap, formatstring);
 
70
  ret = vasprintf(&text, formatstring, ap);
 
71
  if(ret == -1){
 
72
    fprintf(stderr, "Mandos plugin %s: ",
 
73
            program_invocation_short_name);
 
74
    vfprintf(stderr, formatstring, ap);
 
75
    fprintf(stderr, ": ");
 
76
    fprintf(stderr, "%s\n", strerror(errnum));
 
77
    error(status, errno, "vasprintf while printing error");
 
78
    return;
 
79
  }
 
80
  fprintf(stderr, "Mandos plugin ");
 
81
  error(status, errnum, "%s", text);
 
82
  free(text);
 
83
}
 
84
 
56
85
static void termination_handler(int signum){
57
86
  if(interrupted_by_signal){
58
87
    return;
89
118
      ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg);
90
119
      if(ret == -1){
91
120
        int e = errno;
92
 
        TEMP_FAILURE_RETRY(close(*fifo_fd_r));
 
121
        close(*fifo_fd_r);
93
122
        errno = e;
94
123
        return false;
95
124
      }
105
134
                 cmd_line_len - written);
106
135
    if(sret == -1){
107
136
      int e = errno;
108
 
      TEMP_FAILURE_RETRY(close(*fifo_fd_r));
 
137
      close(*fifo_fd_r);
109
138
      free(cmd_line_alloc);
110
139
      errno = e;
111
140
      return false;
153
182
  size_t cmdline_len = 0;
154
183
  DIR *proc_dir = opendir("/proc");
155
184
  if(proc_dir == NULL){
156
 
    perror("opendir");
 
185
    error_plus(0, errno, "opendir");
157
186
    return -1;
158
187
  }
159
188
  errno = 0;
181
210
      char *exe_link;
182
211
      ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
183
212
      if(ret == -1){
184
 
        perror("asprintf");
 
213
        error_plus(0, errno, "asprintf");
185
214
        goto fail_find_usplash;
186
215
      }
187
216
      
193
222
          free(exe_link);
194
223
          continue;
195
224
        }
196
 
        perror("lstat");
 
225
        error_plus(0, errno, "lstat");
197
226
        free(exe_link);
198
227
        goto fail_find_usplash;
199
228
      }
224
253
        ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
225
254
                       proc_ent->d_name);
226
255
        if(ret == -1){
227
 
          perror("asprintf");
 
256
          error_plus(0, errno, "asprintf");
228
257
          goto fail_find_usplash;
229
258
        }
230
259
        cl_fd = open(cmdline_filename, O_RDONLY);
231
260
        free(cmdline_filename);
232
261
        if(cl_fd == -1){
233
 
          perror("open");
 
262
          error_plus(0, errno, "open");
234
263
          goto fail_find_usplash;
235
264
        }
236
265
      }
242
271
        if(cmdline_len + blocksize > cmdline_allocated){
243
272
          tmp = realloc(cmdline, cmdline_allocated + blocksize);
244
273
          if(tmp == NULL){
245
 
            perror("realloc");
 
274
            error_plus(0, errno, "realloc");
246
275
            close(cl_fd);
247
276
            goto fail_find_usplash;
248
277
          }
253
282
        sret = read(cl_fd, cmdline + cmdline_len,
254
283
                    cmdline_allocated - cmdline_len);
255
284
        if(sret == -1){
256
 
          perror("read");
 
285
          error_plus(0, errno, "read");
257
286
          close(cl_fd);
258
287
          goto fail_find_usplash;
259
288
        }
261
290
      } while(sret != 0);
262
291
      ret = close(cl_fd);
263
292
      if(ret == -1){
264
 
        perror("close");
 
293
        error_plus(0, errno, "close");
265
294
        goto fail_find_usplash;
266
295
      }
267
296
    }
268
297
    /* Close directory */
269
298
    ret = closedir(proc_dir);
270
299
    if(ret == -1){
271
 
      perror("closedir");
 
300
      error_plus(0, errno, "closedir");
272
301
      goto fail_find_usplash;
273
302
    }
274
303
    /* Success */
323
352
    sigemptyset(&new_action.sa_mask);
324
353
    ret = sigaddset(&new_action.sa_mask, SIGINT);
325
354
    if(ret == -1){
326
 
      perror("sigaddset");
 
355
      error_plus(0, errno, "sigaddset");
327
356
      status = EX_OSERR;
328
357
      goto failure;
329
358
    }
330
359
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
331
360
    if(ret == -1){
332
 
      perror("sigaddset");
 
361
      error_plus(0, errno, "sigaddset");
333
362
      status = EX_OSERR;
334
363
      goto failure;
335
364
    }
336
365
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
337
366
    if(ret == -1){
338
 
      perror("sigaddset");
 
367
      error_plus(0, errno, "sigaddset");
339
368
      status = EX_OSERR;
340
369
      goto failure;
341
370
    }
342
371
    ret = sigaction(SIGINT, NULL, &old_action);
343
372
    if(ret == -1){
344
373
      if(errno != EINTR){
345
 
        perror("sigaction");
 
374
        error_plus(0, errno, "sigaction");
346
375
        status = EX_OSERR;
347
376
      }
348
377
      goto failure;
351
380
      ret = sigaction(SIGINT, &new_action, NULL);
352
381
      if(ret == -1){
353
382
        if(errno != EINTR){
354
 
          perror("sigaction");
 
383
          error_plus(0, errno, "sigaction");
355
384
          status = EX_OSERR;
356
385
        }
357
386
        goto failure;
360
389
    ret = sigaction(SIGHUP, NULL, &old_action);
361
390
    if(ret == -1){
362
391
      if(errno != EINTR){
363
 
        perror("sigaction");
 
392
        error_plus(0, errno, "sigaction");
364
393
        status = EX_OSERR;
365
394
      }
366
395
      goto failure;
369
398
      ret = sigaction(SIGHUP, &new_action, NULL);
370
399
      if(ret == -1){
371
400
        if(errno != EINTR){
372
 
          perror("sigaction");
 
401
          error_plus(0, errno, "sigaction");
373
402
          status = EX_OSERR;
374
403
        }
375
404
        goto failure;
378
407
    ret = sigaction(SIGTERM, NULL, &old_action);
379
408
    if(ret == -1){
380
409
      if(errno != EINTR){
381
 
        perror("sigaction");
 
410
        error_plus(0, errno, "sigaction");
382
411
        status = EX_OSERR;
383
412
      }
384
413
      goto failure;
387
416
      ret = sigaction(SIGTERM, &new_action, NULL);
388
417
      if(ret == -1){
389
418
        if(errno != EINTR){
390
 
          perror("sigaction");
 
419
          error_plus(0, errno, "sigaction");
391
420
          status = EX_OSERR;
392
421
        }
393
422
        goto failure;
399
428
  /* Write command to FIFO */
400
429
  if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){
401
430
    if(errno != EINTR){
402
 
      perror("usplash_write");
 
431
      error_plus(0, errno, "usplash_write");
403
432
      status = EX_OSERR;
404
433
    }
405
434
    goto failure;
411
440
  
412
441
  if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){
413
442
    if(errno != EINTR){
414
 
      perror("usplash_write");
 
443
      error_plus(0, errno, "usplash_write");
415
444
      status = EX_OSERR;
416
445
    }
417
446
    goto failure;
429
458
  outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
430
459
  if(outfifo_fd == -1){
431
460
    if(errno != EINTR){
432
 
      perror("open");
 
461
      error_plus(0, errno, "open");
433
462
      status = EX_OSERR;
434
463
    }
435
464
    goto failure;
448
477
      char *tmp = realloc(buf, buf_allocated + blocksize);
449
478
      if(tmp == NULL){
450
479
        if(errno != EINTR){
451
 
          perror("realloc");
 
480
          error_plus(0, errno, "realloc");
452
481
          status = EX_OSERR;
453
482
        }
454
483
        goto failure;
460
489
                buf_allocated - buf_len);
461
490
    if(sret == -1){
462
491
      if(errno != EINTR){
463
 
        perror("read");
 
492
        error_plus(0, errno, "read");
464
493
        status = EX_OSERR;
465
494
      }
466
 
      TEMP_FAILURE_RETRY(close(outfifo_fd));
 
495
      close(outfifo_fd);
467
496
      goto failure;
468
497
    }
469
498
    if(interrupted_by_signal){
475
504
  ret = close(outfifo_fd);
476
505
  if(ret == -1){
477
506
    if(errno != EINTR){
478
 
      perror("close");
 
507
      error_plus(0, errno, "close");
479
508
      status = EX_OSERR;
480
509
    }
481
510
    goto failure;
488
517
  
489
518
  if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){
490
519
    if(errno != EINTR){
491
 
      perror("usplash_write");
 
520
      error_plus(0, errno, "usplash_write");
492
521
      status = EX_OSERR;
493
522
    }
494
523
    goto failure;
501
530
  ret = close(fifo_fd);
502
531
  if(ret == -1){
503
532
    if(errno != EINTR){
504
 
      perror("close");
 
533
      error_plus(0, errno, "close");
505
534
      status = EX_OSERR;
506
535
    }
507
536
    goto failure;
515
544
      sret = write(STDOUT_FILENO, buf + written, buf_len - written);
516
545
      if(sret == -1){
517
546
        if(errno != EINTR){
518
 
          perror("write");
 
547
          error_plus(0, errno, "write");
519
548
          status = EX_OSERR;
520
549
        }
521
550
        goto failure;
550
579
  
551
580
  /* Close FIFO */
552
581
  if(fifo_fd != -1){
553
 
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
 
582
    ret = close(fifo_fd);
554
583
    if(ret == -1 and errno != EINTR){
555
 
      perror("close");
 
584
      error_plus(0, errno, "close");
556
585
    }
557
586
    fifo_fd = -1;
558
587
  }
559
588
  
560
589
  /* Close output FIFO */
561
590
  if(outfifo_fd != -1){
562
 
    ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd));
 
591
    ret = close(outfifo_fd);
563
592
    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
 
  }
 
593
      error_plus(0, errno, "close");
 
594
    }
 
595
  }
 
596
  
 
597
  /* Create argv for new usplash*/
 
598
  char **cmdline_argv = malloc((argz_count(cmdline, cmdline_len) + 1)
 
599
                               * sizeof(char *)); /* Count args */
 
600
  if(cmdline_argv == NULL){
 
601
    error_plus(0, errno, "malloc");
 
602
    return status;
 
603
  }
 
604
  argz_extract(cmdline, cmdline_len, cmdline_argv); /* Create argv */
 
605
  
589
606
  /* Kill old usplash */
590
607
  kill(usplash_pid, SIGTERM);
591
608
  sleep(2);
602
619
       the real user ID (_mandos) */
603
620
    ret = setuid(geteuid());
604
621
    if(ret == -1){
605
 
      perror("setuid");
 
622
      error_plus(0, errno, "setuid");
606
623
    }
607
624
    
608
625
    setsid();
609
626
    ret = chdir("/");
 
627
    if(ret == -1){
 
628
      error_plus(0, errno, "chdir");
 
629
      _exit(EX_OSERR);
 
630
    }
610
631
/*     if(fork() != 0){ */
611
632
/*       _exit(EXIT_SUCCESS); */
612
633
/*     } */
613
634
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
614
635
    if(ret == -1){
615
 
      perror("dup2");
 
636
      error_plus(0, errno, "dup2");
616
637
      _exit(EX_OSERR);
617
638
    }
618
639
    
619
640
    execv(usplash_name, cmdline_argv);
620
641
    if(not interrupted_by_signal){
621
 
      perror("execv");
 
642
      error_plus(0, errno, "execv");
622
643
    }
623
644
    free(cmdline);
624
645
    free(cmdline_argv);
629
650
  sleep(2);
630
651
  if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
631
652
    if(errno != EINTR){
632
 
      perror("usplash_write");
 
653
      error_plus(0, errno, "usplash_write");
633
654
    }
634
655
  }
635
656
  
636
657
  /* Close FIFO (again) */
637
658
  if(fifo_fd != -1){
638
 
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
 
659
    ret = close(fifo_fd);
639
660
    if(ret == -1 and errno != EINTR){
640
 
      perror("close");
 
661
      error_plus(0, errno, "close");
641
662
    }
642
663
    fifo_fd = -1;
643
664
  }
648
669
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
649
670
                                            &signal_action, NULL));
650
671
    if(ret == -1){
651
 
      perror("sigaction");
 
672
      error_plus(0, errno, "sigaction");
652
673
    }
653
674
    do {
654
675
      ret = raise(signal_received);
655
676
    } while(ret != 0 and errno == EINTR);
656
677
    if(ret != 0){
657
 
      perror("raise");
 
678
      error_plus(0, errno, "raise");
658
679
      abort();
659
680
    }
660
681
    TEMP_FAILURE_RETRY(pause());