/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: 2021-02-03 23:10:42 UTC
  • Revision ID: teddy@recompile.se-20210203231042-2z3egrvpo1zt7nej
mandos-ctl: Fix bad test for command.Remove and related minor issues

The test for command.Remove removes all clients from the spy server,
and then loops over all clients, looking for the corresponding Remove
command as recorded by the spy server.  But since since there aren't
any clients left after they were removed, no assertions are made, and
the test therefore does nothing.  Fix this.

In tests for command.Approve and command.Deny, add checks that clients
were not somehow removed by the command (in which case, likewise, no
assertions are made).

Add related checks to TestPropertySetterCmd.runTest; i.e. test that a
sequence is not empty before looping over it and making assertions.

* mandos-ctl (TestBaseCommands.test_Remove): Save a copy of the
  original "clients" dict, and loop over those instead.  Add assertion
  that all clients were indeed removed.  Also fix the code which looks
  for the Remove command, which now needs to actually work.
  (TestBaseCommands.test_Approve, TestBaseCommands.test_Deny): Add
  assertion that there are still clients before looping over them.
  (TestPropertySetterCmd.runTest): Add assertion that the list of
  values to get is not empty before looping over them.  Also add check
  that there are still clients before looping over clients.

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, 2021 Teddy Hogeborn
 
6
 * Copyright © 2008-2018, 2021 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
 
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
26
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
27
 
                                   sigemptyset(), sigaddset(), SIGINT,
28
 
                                   SIGHUP, SIGTERM, sigaction(),
29
 
                                   SIG_IGN, kill(), SIGKILL */
 
26
#define _GNU_SOURCE             /* vasprintf(),
 
27
                                   program_invocation_short_name,
 
28
                                   asprintf(), TEMP_FAILURE_RETRY() */
 
29
#include <sys/types.h>          /* sig_atomic_t, pid_t, setuid(),
 
30
                                   geteuid(), setsid() */
 
31
#include <stdarg.h>             /* va_list, va_start(), vfprintf() */
 
32
#include <stdio.h>              /* vasprintf(), fprintf(), stderr,
 
33
                                   vfprintf(), asprintf() */
 
34
#include <errno.h>              /* program_invocation_short_name,
 
35
                                   errno, ENOENT, EINTR */
 
36
#include <string.h>             /* strerror(), strlen(), memcmp() */
 
37
#include <error.h>              /* error() */
 
38
#include <stdlib.h>             /* free(), getenv(), realloc(),
 
39
                                   EXIT_FAILURE, EXIT_SUCCESS,
 
40
                                   malloc(), abort() */
30
41
#include <stdbool.h>            /* bool, false, true */
31
42
#include <fcntl.h>              /* open(), O_WRONLY, O_RDONLY */
32
 
#include <iso646.h>             /* and, or, not*/
33
 
#include <errno.h>              /* errno, EINTR */
34
 
#include <sys/types.h>          /* size_t, ssize_t, pid_t, DIR, struct
35
 
                                   dirent */
36
 
#include <stddef.h>             /* NULL */
37
 
#include <string.h>             /* strlen(), memcmp() */
38
 
#include <stdio.h>              /* asprintf(), perror() */
39
 
#include <unistd.h>             /* close(), write(), readlink(),
40
 
                                   read(), STDOUT_FILENO, sleep(),
41
 
                                   fork(), setuid(), geteuid(),
42
 
                                   setsid(), chdir(), dup2(),
43
 
                                   STDERR_FILENO, execv() */
44
 
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
45
 
                                   EXIT_SUCCESS, malloc(), _exit() */
46
 
#include <stdlib.h>             /* getenv() */
47
 
#include <dirent.h>             /* opendir(), readdir(), closedir() */
 
43
#include <stddef.h>             /* size_t, NULL */
 
44
#include <unistd.h>             /* close(), ssize_t, write(),
 
45
                                   readlink(), read(), STDOUT_FILENO,
 
46
                                   sleep(), fork(), setuid(),
 
47
                                   geteuid(), setsid(), chdir(),
 
48
                                   _exit(), dup2(), STDERR_FILENO,
 
49
                                   execv(), TEMP_FAILURE_RETRY(),
 
50
                                   pause() */
 
51
#include <dirent.h>             /* DIR, opendir(), struct dirent,
 
52
                                   readdir(), closedir() */
48
53
#include <inttypes.h>           /* intmax_t, strtoimax() */
49
 
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
 
54
#include <iso646.h>             /* or, not, and */
 
55
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK() */
 
56
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
 
57
#include <signal.h>             /* struct sigaction, sigemptyset(),
 
58
                                   sigaddset(), SIGINT, SIGHUP,
 
59
                                   SIGTERM, SIG_IGN, kill(), SIGKILL,
 
60
                                   SIG_DFL, raise() */
 
61
#include <argz.h>               /* argz_count(), argz_extract() */
50
62
 
51
63
sig_atomic_t interrupted_by_signal = 0;
52
64
int signal_received;
53
65
const char usplash_name[] = "/sbin/usplash";
54
66
 
 
67
/* Function to use when printing errors */
 
68
__attribute__((format (gnu_printf, 3, 4)))
 
69
void error_plus(int status, int errnum, const char *formatstring,
 
70
                ...){
 
71
  va_list ap;
 
72
  char *text;
 
73
  int ret;
 
74
  
 
75
  va_start(ap, formatstring);
 
76
  ret = vasprintf(&text, formatstring, ap);
 
77
  if(ret == -1){
 
78
    fprintf(stderr, "Mandos plugin %s: ",
 
79
            program_invocation_short_name);
 
80
    vfprintf(stderr, formatstring, ap);
 
81
    fprintf(stderr, ": ");
 
82
    fprintf(stderr, "%s\n", strerror(errnum));
 
83
    error(status, errno, "vasprintf while printing error");
 
84
    return;
 
85
  }
 
86
  fprintf(stderr, "Mandos plugin ");
 
87
  error(status, errnum, "%s", text);
 
88
  free(text);
 
89
}
 
90
 
55
91
static void termination_handler(int signum){
56
92
  if(interrupted_by_signal){
57
93
    return;
88
124
      ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg);
89
125
      if(ret == -1){
90
126
        int e = errno;
91
 
        TEMP_FAILURE_RETRY(close(*fifo_fd_r));
 
127
        close(*fifo_fd_r);
92
128
        errno = e;
93
129
        return false;
94
130
      }
104
140
                 cmd_line_len - written);
105
141
    if(sret == -1){
106
142
      int e = errno;
107
 
      TEMP_FAILURE_RETRY(close(*fifo_fd_r));
 
143
      close(*fifo_fd_r);
108
144
      free(cmd_line_alloc);
109
145
      errno = e;
110
146
      return false;
152
188
  size_t cmdline_len = 0;
153
189
  DIR *proc_dir = opendir("/proc");
154
190
  if(proc_dir == NULL){
155
 
    perror("opendir");
 
191
    error_plus(0, errno, "opendir");
156
192
    return -1;
157
193
  }
158
194
  errno = 0;
180
216
      char *exe_link;
181
217
      ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
182
218
      if(ret == -1){
183
 
        perror("asprintf");
 
219
        error_plus(0, errno, "asprintf");
184
220
        goto fail_find_usplash;
185
221
      }
186
222
      
192
228
          free(exe_link);
193
229
          continue;
194
230
        }
195
 
        perror("lstat");
 
231
        error_plus(0, errno, "lstat");
196
232
        free(exe_link);
197
233
        goto fail_find_usplash;
198
234
      }
223
259
        ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
224
260
                       proc_ent->d_name);
225
261
        if(ret == -1){
226
 
          perror("asprintf");
 
262
          error_plus(0, errno, "asprintf");
227
263
          goto fail_find_usplash;
228
264
        }
229
265
        cl_fd = open(cmdline_filename, O_RDONLY);
230
266
        free(cmdline_filename);
231
267
        if(cl_fd == -1){
232
 
          perror("open");
 
268
          error_plus(0, errno, "open");
233
269
          goto fail_find_usplash;
234
270
        }
235
271
      }
241
277
        if(cmdline_len + blocksize > cmdline_allocated){
242
278
          tmp = realloc(cmdline, cmdline_allocated + blocksize);
243
279
          if(tmp == NULL){
244
 
            perror("realloc");
 
280
            error_plus(0, errno, "realloc");
245
281
            close(cl_fd);
246
282
            goto fail_find_usplash;
247
283
          }
252
288
        sret = read(cl_fd, cmdline + cmdline_len,
253
289
                    cmdline_allocated - cmdline_len);
254
290
        if(sret == -1){
255
 
          perror("read");
 
291
          error_plus(0, errno, "read");
256
292
          close(cl_fd);
257
293
          goto fail_find_usplash;
258
294
        }
260
296
      } while(sret != 0);
261
297
      ret = close(cl_fd);
262
298
      if(ret == -1){
263
 
        perror("close");
 
299
        error_plus(0, errno, "close");
264
300
        goto fail_find_usplash;
265
301
      }
266
302
    }
267
303
    /* Close directory */
268
304
    ret = closedir(proc_dir);
269
305
    if(ret == -1){
270
 
      perror("closedir");
 
306
      error_plus(0, errno, "closedir");
271
307
      goto fail_find_usplash;
272
308
    }
273
309
    /* Success */
297
333
  size_t buf_len = 0;
298
334
  pid_t usplash_pid = -1;
299
335
  bool usplash_accessed = false;
 
336
  int status = EXIT_FAILURE;    /* Default failure exit status */
300
337
  
301
338
  char *prompt = makeprompt();
302
339
  if(prompt == NULL){
 
340
    status = EX_OSERR;
303
341
    goto failure;
304
342
  }
305
343
  
308
346
  size_t cmdline_len = 0;
309
347
  usplash_pid = find_usplash(&cmdline, &cmdline_len);
310
348
  if(usplash_pid == 0){
 
349
    status = EX_UNAVAILABLE;
311
350
    goto failure;
312
351
  }
313
352
  
319
358
    sigemptyset(&new_action.sa_mask);
320
359
    ret = sigaddset(&new_action.sa_mask, SIGINT);
321
360
    if(ret == -1){
322
 
      perror("sigaddset");
 
361
      error_plus(0, errno, "sigaddset");
 
362
      status = EX_OSERR;
323
363
      goto failure;
324
364
    }
325
365
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
326
366
    if(ret == -1){
327
 
      perror("sigaddset");
 
367
      error_plus(0, errno, "sigaddset");
 
368
      status = EX_OSERR;
328
369
      goto failure;
329
370
    }
330
371
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
331
372
    if(ret == -1){
332
 
      perror("sigaddset");
 
373
      error_plus(0, errno, "sigaddset");
 
374
      status = EX_OSERR;
333
375
      goto failure;
334
376
    }
335
377
    ret = sigaction(SIGINT, NULL, &old_action);
336
378
    if(ret == -1){
337
379
      if(errno != EINTR){
338
 
        perror("sigaction");
 
380
        error_plus(0, errno, "sigaction");
 
381
        status = EX_OSERR;
339
382
      }
340
383
      goto failure;
341
384
    }
343
386
      ret = sigaction(SIGINT, &new_action, NULL);
344
387
      if(ret == -1){
345
388
        if(errno != EINTR){
346
 
          perror("sigaction");
 
389
          error_plus(0, errno, "sigaction");
 
390
          status = EX_OSERR;
347
391
        }
348
392
        goto failure;
349
393
      }
351
395
    ret = sigaction(SIGHUP, NULL, &old_action);
352
396
    if(ret == -1){
353
397
      if(errno != EINTR){
354
 
        perror("sigaction");
 
398
        error_plus(0, errno, "sigaction");
 
399
        status = EX_OSERR;
355
400
      }
356
401
      goto failure;
357
402
    }
359
404
      ret = sigaction(SIGHUP, &new_action, NULL);
360
405
      if(ret == -1){
361
406
        if(errno != EINTR){
362
 
          perror("sigaction");
 
407
          error_plus(0, errno, "sigaction");
 
408
          status = EX_OSERR;
363
409
        }
364
410
        goto failure;
365
411
      }
367
413
    ret = sigaction(SIGTERM, NULL, &old_action);
368
414
    if(ret == -1){
369
415
      if(errno != EINTR){
370
 
        perror("sigaction");
 
416
        error_plus(0, errno, "sigaction");
 
417
        status = EX_OSERR;
371
418
      }
372
419
      goto failure;
373
420
    }
375
422
      ret = sigaction(SIGTERM, &new_action, NULL);
376
423
      if(ret == -1){
377
424
        if(errno != EINTR){
378
 
          perror("sigaction");
 
425
          error_plus(0, errno, "sigaction");
 
426
          status = EX_OSERR;
379
427
        }
380
428
        goto failure;
381
429
      }
386
434
  /* Write command to FIFO */
387
435
  if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){
388
436
    if(errno != EINTR){
389
 
      perror("usplash_write");
 
437
      error_plus(0, errno, "usplash_write");
 
438
      status = EX_OSERR;
390
439
    }
391
440
    goto failure;
392
441
  }
397
446
  
398
447
  if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){
399
448
    if(errno != EINTR){
400
 
      perror("usplash_write");
 
449
      error_plus(0, errno, "usplash_write");
 
450
      status = EX_OSERR;
401
451
    }
402
452
    goto failure;
403
453
  }
414
464
  outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
415
465
  if(outfifo_fd == -1){
416
466
    if(errno != EINTR){
417
 
      perror("open");
 
467
      error_plus(0, errno, "open");
 
468
      status = EX_OSERR;
418
469
    }
419
470
    goto failure;
420
471
  }
432
483
      char *tmp = realloc(buf, buf_allocated + blocksize);
433
484
      if(tmp == NULL){
434
485
        if(errno != EINTR){
435
 
          perror("realloc");
 
486
          error_plus(0, errno, "realloc");
 
487
          status = EX_OSERR;
436
488
        }
437
489
        goto failure;
438
490
      }
443
495
                buf_allocated - buf_len);
444
496
    if(sret == -1){
445
497
      if(errno != EINTR){
446
 
        perror("read");
 
498
        error_plus(0, errno, "read");
 
499
        status = EX_OSERR;
447
500
      }
448
 
      TEMP_FAILURE_RETRY(close(outfifo_fd));
 
501
      close(outfifo_fd);
449
502
      goto failure;
450
503
    }
451
504
    if(interrupted_by_signal){
457
510
  ret = close(outfifo_fd);
458
511
  if(ret == -1){
459
512
    if(errno != EINTR){
460
 
      perror("close");
 
513
      error_plus(0, errno, "close");
 
514
      status = EX_OSERR;
461
515
    }
462
516
    goto failure;
463
517
  }
469
523
  
470
524
  if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){
471
525
    if(errno != EINTR){
472
 
      perror("usplash_write");
 
526
      error_plus(0, errno, "usplash_write");
 
527
      status = EX_OSERR;
473
528
    }
474
529
    goto failure;
475
530
  }
481
536
  ret = close(fifo_fd);
482
537
  if(ret == -1){
483
538
    if(errno != EINTR){
484
 
      perror("close");
 
539
      error_plus(0, errno, "close");
 
540
      status = EX_OSERR;
485
541
    }
486
542
    goto failure;
487
543
  }
494
550
      sret = write(STDOUT_FILENO, buf + written, buf_len - written);
495
551
      if(sret == -1){
496
552
        if(errno != EINTR){
497
 
          perror("write");
 
553
          error_plus(0, errno, "write");
 
554
          status = EX_OSERR;
498
555
        }
499
556
        goto failure;
500
557
      }
523
580
  
524
581
  /* If usplash was never accessed, we can stop now */
525
582
  if(not usplash_accessed){
526
 
    return EXIT_FAILURE;
 
583
    return status;
527
584
  }
528
585
  
529
586
  /* Close FIFO */
530
587
  if(fifo_fd != -1){
531
 
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
 
588
    ret = close(fifo_fd);
532
589
    if(ret == -1 and errno != EINTR){
533
 
      perror("close");
 
590
      error_plus(0, errno, "close");
534
591
    }
535
592
    fifo_fd = -1;
536
593
  }
537
594
  
538
595
  /* Close output FIFO */
539
596
  if(outfifo_fd != -1){
540
 
    ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd));
 
597
    ret = close(outfifo_fd);
541
598
    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
 
  }
 
599
      error_plus(0, errno, "close");
 
600
    }
 
601
  }
 
602
  
 
603
  /* Create argv for new usplash*/
 
604
  char **cmdline_argv = malloc((argz_count(cmdline, cmdline_len) + 1)
 
605
                               * sizeof(char *)); /* Count args */
 
606
  if(cmdline_argv == NULL){
 
607
    error_plus(0, errno, "malloc");
 
608
    return status;
 
609
  }
 
610
  argz_extract(cmdline, cmdline_len, cmdline_argv); /* Create argv */
 
611
  
567
612
  /* Kill old usplash */
568
613
  kill(usplash_pid, SIGTERM);
569
614
  sleep(2);
580
625
       the real user ID (_mandos) */
581
626
    ret = setuid(geteuid());
582
627
    if(ret == -1){
583
 
      perror("setuid");
 
628
      error_plus(0, errno, "setuid");
584
629
    }
585
630
    
586
631
    setsid();
587
632
    ret = chdir("/");
 
633
    if(ret == -1){
 
634
      error_plus(0, errno, "chdir");
 
635
      _exit(EX_OSERR);
 
636
    }
588
637
/*     if(fork() != 0){ */
589
638
/*       _exit(EXIT_SUCCESS); */
590
639
/*     } */
591
640
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
592
641
    if(ret == -1){
593
 
      perror("dup2");
594
 
      _exit(EXIT_FAILURE);
 
642
      error_plus(0, errno, "dup2");
 
643
      _exit(EX_OSERR);
595
644
    }
596
645
    
597
646
    execv(usplash_name, cmdline_argv);
598
647
    if(not interrupted_by_signal){
599
 
      perror("execv");
 
648
      error_plus(0, errno, "execv");
600
649
    }
601
650
    free(cmdline);
602
651
    free(cmdline_argv);
603
 
    _exit(EXIT_FAILURE);
 
652
    _exit(EX_OSERR);
604
653
  }
605
654
  free(cmdline);
606
655
  free(cmdline_argv);
607
656
  sleep(2);
608
657
  if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
609
658
    if(errno != EINTR){
610
 
      perror("usplash_write");
 
659
      error_plus(0, errno, "usplash_write");
611
660
    }
612
661
  }
613
662
  
614
663
  /* Close FIFO (again) */
615
664
  if(fifo_fd != -1){
616
 
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
 
665
    ret = close(fifo_fd);
617
666
    if(ret == -1 and errno != EINTR){
618
 
      perror("close");
 
667
      error_plus(0, errno, "close");
619
668
    }
620
669
    fifo_fd = -1;
621
670
  }
626
675
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
627
676
                                            &signal_action, NULL));
628
677
    if(ret == -1){
629
 
      perror("sigaction");
 
678
      error_plus(0, errno, "sigaction");
630
679
    }
631
680
    do {
632
681
      ret = raise(signal_received);
633
682
    } while(ret != 0 and errno == EINTR);
634
683
    if(ret != 0){
635
 
      perror("raise");
 
684
      error_plus(0, errno, "raise");
636
685
      abort();
637
686
    }
638
687
    TEMP_FAILURE_RETRY(pause());
639
688
  }
640
689
  
641
 
  return EXIT_FAILURE;
 
690
  return status;
642
691
}