/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: 2016-03-17 20:40:55 UTC
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 341.
  • Revision ID: teddy@recompile.se-20160317204055-bhsh5xsidq7w5cxu
Client: Fix plymouth agent; broken since 1.7.2.

Fix an very old memory bug in the plymouth agent (which has been
present since its apperance in version 1.2), but which was only
recently detected at run time due to the new -fsanitize=address
compile- time flag, which has been used since version 1.7.2.  This
detection of a memory access violation causes the program to abort,
making the Plymouth graphical boot system unable to accept interactive
input of passwords when using the Mandos client.

* plugins.d/plymouth.c (exec_and_wait): Fix memory allocation bug when
  allocating new_argv.  Also tolerate a zero-length argv.

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-2016 Teddy Hogeborn
 
6
 * Copyright © 2008-2016 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 */
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;
89
117
      ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg);
90
118
      if(ret == -1){
91
119
        int e = errno;
92
 
        TEMP_FAILURE_RETRY(close(*fifo_fd_r));
 
120
        close(*fifo_fd_r);
93
121
        errno = e;
94
122
        return false;
95
123
      }
105
133
                 cmd_line_len - written);
106
134
    if(sret == -1){
107
135
      int e = errno;
108
 
      TEMP_FAILURE_RETRY(close(*fifo_fd_r));
 
136
      close(*fifo_fd_r);
109
137
      free(cmd_line_alloc);
110
138
      errno = e;
111
139
      return false;
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
 
      TEMP_FAILURE_RETRY(close(outfifo_fd));
 
494
      close(outfifo_fd);
467
495
      goto failure;
468
496
    }
469
497
    if(interrupted_by_signal){
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;
550
578
  
551
579
  /* Close FIFO */
552
580
  if(fifo_fd != -1){
553
 
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
 
581
    ret = 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
  }
559
587
  
560
588
  /* Close output FIFO */
561
589
  if(outfifo_fd != -1){
562
 
    ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd));
 
590
    ret = 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("/");
 
626
    if(ret == -1){
 
627
      error_plus(0, errno, "chdir");
 
628
      _exit(EX_OSERR);
 
629
    }
610
630
/*     if(fork() != 0){ */
611
631
/*       _exit(EXIT_SUCCESS); */
612
632
/*     } */
613
633
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
614
634
    if(ret == -1){
615
 
      perror("dup2");
 
635
      error_plus(0, errno, "dup2");
616
636
      _exit(EX_OSERR);
617
637
    }
618
638
    
619
639
    execv(usplash_name, cmdline_argv);
620
640
    if(not interrupted_by_signal){
621
 
      perror("execv");
 
641
      error_plus(0, errno, "execv");
622
642
    }
623
643
    free(cmdline);
624
644
    free(cmdline_argv);
629
649
  sleep(2);
630
650
  if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
631
651
    if(errno != EINTR){
632
 
      perror("usplash_write");
 
652
      error_plus(0, errno, "usplash_write");
633
653
    }
634
654
  }
635
655
  
636
656
  /* Close FIFO (again) */
637
657
  if(fifo_fd != -1){
638
 
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
 
658
    ret = close(fifo_fd);
639
659
    if(ret == -1 and errno != EINTR){
640
 
      perror("close");
 
660
      error_plus(0, errno, "close");
641
661
    }
642
662
    fifo_fd = -1;
643
663
  }
648
668
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
649
669
                                            &signal_action, NULL));
650
670
    if(ret == -1){
651
 
      perror("sigaction");
 
671
      error_plus(0, errno, "sigaction");
652
672
    }
653
673
    do {
654
674
      ret = raise(signal_received);
655
675
    } while(ret != 0 and errno == EINTR);
656
676
    if(ret != 0){
657
 
      perror("raise");
 
677
      error_plus(0, errno, "raise");
658
678
      abort();
659
679
    }
660
680
    TEMP_FAILURE_RETRY(pause());