/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 dracut-module/password-agent.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
 * Mandos password agent - Simple password agent to run Mandos client
4
4
 *
5
 
 * Copyright © 2019 Teddy Hogeborn
6
 
 * Copyright © 2019 Björn Påhlsson
 
5
 * Copyright © 2019-2021 Teddy Hogeborn
 
6
 * Copyright © 2019-2021 Björn Påhlsson
7
7
 * 
8
8
 * This file is part of Mandos.
9
9
 * 
23
23
 * Contact the authors at <mandos@recompile.se>.
24
24
 */
25
25
 
26
 
#define _GNU_SOURCE
27
 
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
28
 
                                   intmax_t, uint32_t, SCNx32,
29
 
                                   SCNuMAX, SCNxMAX */
30
 
#include <stddef.h>             /* size_t */
 
26
#define _GNU_SOURCE             /* pipe2(), O_CLOEXEC, setresgid(),
 
27
                                   setresuid(), asprintf(), getline(),
 
28
                                   basename() */
 
29
#include <inttypes.h>           /* uintmax_t, strtoumax(), PRIuMAX,
 
30
                                   PRIdMAX, intmax_t, uint32_t,
 
31
                                   SCNx32, SCNuMAX, SCNxMAX */
 
32
#include <stddef.h>             /* size_t, NULL */
31
33
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
32
34
                                   getpid() */
33
35
#include <stdbool.h>            /* bool, true, false */
40
42
                                   NSIG, sigismember(), SA_ONSTACK,
41
43
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
42
44
                                   SIGHUP, SIGSTOP, SIG_UNBLOCK */
 
45
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
46
                                   fork(), _exit(), dup2(),
 
47
                                   STDOUT_FILENO, setresgid(),
 
48
                                   setresuid(), execv(), ssize_t,
 
49
                                   read(), dup3(), getuid(), dup(),
 
50
                                   STDERR_FILENO, pause(), write(),
 
51
                                   rmdir(), unlink(), getpid() */
43
52
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
44
 
                                   malloc(), free(), strtoumax(),
45
 
                                   realloc(), setenv(), calloc(),
46
 
                                   mkdtemp(), mkostemp() */
 
53
                                   malloc(), free(), realloc(),
 
54
                                   setenv(), calloc(), mkdtemp(),
 
55
                                   mkostemp() */
47
56
#include <iso646.h>             /* not, or, and, xor */
48
57
#include <error.h>              /* error() */
49
58
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
59
#include <errno.h>              /* errno, error_t, EACCES,
51
60
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
52
 
                                   EEXIST, ECHILD, EPERM, ENOMEM,
 
61
                                   ENOMEM, EEXIST, ECHILD, EPERM,
53
62
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
63
                                   ECONNREFUSED, ECONNRESET,
55
64
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
57
66
#include <string.h>             /* strdup(), memcpy(),
58
67
                                   explicit_bzero(), memset(),
59
68
                                   strcmp(), strlen(), strncpy(),
60
 
                                   memcmp(), basename() */
 
69
                                   memcmp(), basename(), strerror() */
61
70
#include <argz.h>               /* argz_create(), argz_count(),
62
71
                                   argz_extract(), argz_next(),
63
72
                                   argz_add() */
73
82
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
83
                                   struct argp, argp_parse(),
75
84
                                   ARGP_NO_EXIT */
76
 
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
77
 
                                   fork(), _exit(), dup2(),
78
 
                                   STDOUT_FILENO, setresgid(),
79
 
                                   setresuid(), execv(), ssize_t,
80
 
                                   read(), dup3(), getuid(), dup(),
81
 
                                   STDERR_FILENO, pause(), write(),
82
 
                                   rmdir(), unlink(), getpid() */
 
85
#include <stdint.h>             /* SIZE_MAX, uint32_t */
83
86
#include <sys/mman.h>           /* munlock(), mlock() */
84
87
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
88
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
95
98
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
96
99
                                   struct inotify_event */
97
100
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
98
 
#include <stdio.h>              /* asprintf(), FILE, fopen(),
99
 
                                   getline(), sscanf(), feof(),
100
 
                                   ferror(), fclose(), stderr,
101
 
                                   rename(), fdopen(), fprintf(),
102
 
                                   fscanf() */
 
101
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
102
                                   fclose(), getline(), sscanf(),
 
103
                                   feof(), ferror(), rename(),
 
104
                                   fdopen(), fprintf(), fscanf() */
103
105
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
104
106
                        GError, g_key_file_load_from_file(),
105
107
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
110
112
                        g_assert_null(), g_assert_false(),
111
113
                        g_assert_cmpint(), g_assert_cmpuint(),
112
114
                        g_test_skip(), g_assert_cmpstr(),
113
 
                        g_test_init(), g_test_add(), g_test_run(),
114
 
                        GOptionContext, g_option_context_new(),
 
115
                        g_test_message(), g_test_init(), g_test_add(),
 
116
                        g_test_run(), GOptionContext,
 
117
                        g_option_context_new(),
115
118
                        g_option_context_set_help_enabled(), FALSE,
116
119
                        g_option_context_set_ignore_unknown_options(),
117
120
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
651
654
 
652
655
__attribute__((nonnull, warn_unused_result))
653
656
bool add_to_queue(task_queue *const queue, const task_context task){
 
657
  if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
 
658
    /* overflow */
 
659
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
 
660
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
 
661
    errno = ENOMEM;
 
662
    return false;
 
663
  }
654
664
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
655
665
  if(needed_size > (queue->allocated)){
656
666
    task_context *const new_tasks = realloc(queue->tasks,
861
871
  }
862
872
  close(pipefds[1]);
863
873
 
 
874
  if(pid == -1){
 
875
    error(0, errno, "Failed to fork()");
 
876
    close(pipefds[0]);
 
877
    return false;
 
878
  }
 
879
 
864
880
  if(not add_to_queue(queue, (task_context){
865
881
        .func=wait_for_mandos_client_exit,
866
882
        .pid=pid,
1180
1196
  bool *const password_is_read = task.password_is_read;
1181
1197
 
1182
1198
  /* We use the GLib "Key-value file parser" functions to parse the
1183
 
     question file.  See <https://www.freedesktop.org/wiki/Software
1184
 
     /systemd/PasswordAgents/> for specification of contents */
 
1199
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
1200
     specification of contents */
1185
1201
  __attribute__((nonnull))
1186
1202
    void cleanup_g_key_file(GKeyFile **key_file){
1187
1203
    if(*key_file != NULL){
1477
1493
         not. You may but don't have to include a final NUL byte in
1478
1494
         your message.
1479
1495
 
1480
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1481
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
1496
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
1497
         14:24:20 GMT)
1482
1498
      */
1483
1499
      send_buffer[0] = '+';     /* Prefix with "+" */
1484
1500
      /* Always add an extra NUL */
1489
1505
      errno = 0;
1490
1506
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1491
1507
                           MSG_NOSIGNAL);
1492
 
      const error_t saved_errno = errno;
 
1508
      const error_t saved_errno = (ssret < 0) ? errno : 0;
1493
1509
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1494
1510
      explicit_bzero(send_buffer, send_buffer_length);
1495
1511
#else
1513
1529
          /* Retry, below */
1514
1530
          break;
1515
1531
        case EMSGSIZE:
1516
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1517
 
                (uintmax_t)password->length);
 
1532
          error(0, saved_errno, "Password of size %" PRIuMAX
 
1533
                " is too big", (uintmax_t)password->length);
1518
1534
#if __GNUC__ < 7
1519
1535
          /* FALLTHROUGH */
1520
1536
#else
1522
1538
#endif
1523
1539
        case 0:
1524
1540
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1525
 
            error(0, 0, "Password only partially sent to socket");
 
1541
            error(0, 0, "Password only partially sent to socket %s: %"
 
1542
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
 
1543
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
1526
1544
          }
1527
1545
#if __GNUC__ < 7
1528
1546
          /* FALLTHROUGH */
1884
1902
  g_assert_true(queue->tasks[0].func == dummy_func);
1885
1903
}
1886
1904
 
 
1905
static void test_add_to_queue_overflow(__attribute__((unused))
 
1906
                                       test_fixture *fixture,
 
1907
                                       __attribute__((unused))
 
1908
                                       gconstpointer user_data){
 
1909
  __attribute__((cleanup(cleanup_queue)))
 
1910
    task_queue *queue = create_queue();
 
1911
  g_assert_nonnull(queue);
 
1912
  g_assert_true(queue->length == 0);
 
1913
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
 
1914
 
 
1915
  FILE *real_stderr = stderr;
 
1916
  FILE *devnull = fopen("/dev/null", "we");
 
1917
  g_assert_nonnull(devnull);
 
1918
  stderr = devnull;
 
1919
  const bool ret = add_to_queue(queue,
 
1920
                                (task_context){ .func=dummy_func });
 
1921
  g_assert_true(errno == ENOMEM);
 
1922
  g_assert_false(ret);
 
1923
  stderr = real_stderr;
 
1924
  g_assert_cmpint(fclose(devnull), ==, 0);
 
1925
  queue->length = 0;            /* Restore real size */
 
1926
}
 
1927
 
1887
1928
static void dummy_func(__attribute__((unused))
1888
1929
                       const task_context task,
1889
1930
                       __attribute__((unused))
2160
2201
    }
2161
2202
    exit(EXIT_SUCCESS);
2162
2203
  }
 
2204
  if(pid == -1){
 
2205
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2206
  }
 
2207
 
2163
2208
  int status;
2164
2209
  waitpid(pid, &status, 0);
2165
2210
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
5767
5812
  char write_data[PIPE_BUF];
5768
5813
  {
5769
5814
    /* Construct test password buffer */
5770
 
    /* Start with + since that is what the real procotol uses */
 
5815
    /* Start with + since that is what the real protocol uses */
5771
5816
    write_data[0] = '+';
5772
5817
    /* Set a special character at string end just to mark the end */
5773
5818
    write_data[sizeof(write_data)-2] = 'y';
5925
5970
  char *const filename = strdup("/nonexistent/socket");
5926
5971
  __attribute__((cleanup(string_set_clear)))
5927
5972
    string_set cancelled_filenames = {};
5928
 
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5929
 
  __attribute__((cleanup(cleanup_buffer)))
5930
 
    buffer password = {
5931
 
    .data=malloc(oversized),
5932
 
    .length=oversized,
5933
 
    .allocated=oversized,
 
5973
  int socketfds[2];
 
5974
 
 
5975
  /* Find a message size which triggers EMSGSIZE */
 
5976
  __attribute__((cleanup(cleanup_string)))
 
5977
    char *message_buffer = NULL;
 
5978
  size_t message_size = PIPE_BUF + 1;
 
5979
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
 
5980
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
 
5981
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
 
5982
      return;
 
5983
    }
 
5984
    message_buffer = realloc(message_buffer, message_size);
 
5985
    if(message_buffer == NULL){
 
5986
      g_test_skip("Skipping EMSGSIZE test");
 
5987
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
 
5988
                     (uintmax_t)message_size);
 
5989
      return;
 
5990
    }
 
5991
    /* Fill buffer with 'x' */
 
5992
    memset(message_buffer, 'x', message_size);
 
5993
    /* Create a new socketpair for each message size to avoid having
 
5994
       to empty the pipe by reading the message to a separate buffer
 
5995
    */
 
5996
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5997
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5998
                               socketfds), ==, 0);
 
5999
    ssret = send(socketfds[1], message_buffer, message_size,
 
6000
                 MSG_NOSIGNAL);
 
6001
    error_t saved_errno = errno;
 
6002
    g_assert_cmpint(close(socketfds[0]), ==, 0);
 
6003
    g_assert_cmpint(close(socketfds[1]), ==, 0);
 
6004
 
 
6005
    if(ssret < 0){
 
6006
      if(saved_errno != EMSGSIZE) {
 
6007
        g_test_skip("Skipping EMSGSIZE test");
 
6008
        g_test_message("Error on send(): %s", strerror(saved_errno));
 
6009
        return;
 
6010
      }
 
6011
      break;
 
6012
    } else if(ssret != (ssize_t)message_size){
 
6013
      g_test_skip("Skipping EMSGSIZE test");
 
6014
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
 
6015
                     " bytes", (uintmax_t)ssret,
 
6016
                     (intmax_t)message_size);
 
6017
      return;
 
6018
    }
 
6019
  }
 
6020
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
 
6021
                 (intmax_t)message_size);
 
6022
 
 
6023
  buffer password = {
 
6024
    .data=message_buffer,
 
6025
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
 
6026
    .allocated=message_size,
5934
6027
  };
5935
 
  g_assert_nonnull(password.data);
5936
6028
  if(mlock(password.data, password.allocated) != 0){
5937
6029
    g_assert_true(errno == EPERM or errno == ENOMEM);
5938
6030
  }
5939
 
  /* Construct test password buffer */
5940
 
  /* Start with + since that is what the real procotol uses */
5941
 
  password.data[0] = '+';
5942
 
  /* Set a special character at string end just to mark the end */
5943
 
  password.data[oversized-3] = 'y';
5944
 
  /* Set NUL at buffer end, as suggested by the protocol */
5945
 
  password.data[oversized-2] = '\0';
5946
 
  /* Fill rest of password with 'x' */
5947
 
  memset(password.data+1, 'x', oversized-3);
5948
6031
 
5949
6032
  __attribute__((cleanup(cleanup_queue)))
5950
6033
    task_queue *queue = create_queue();
5951
6034
  g_assert_nonnull(queue);
5952
 
  int socketfds[2];
5953
6035
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5954
6036
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5955
6037
                             socketfds), ==, 0);
7862
7944
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7863
7945
  test_add("/queue/create", test_create_queue);
7864
7946
  test_add("/queue/add", test_add_to_queue);
 
7947
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7865
7948
  test_add("/queue/has_question/empty",
7866
7949
           test_queue_has_question_empty);
7867
7950
  test_add("/queue/has_question/false",