/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: 2019-08-05 21:14:05 UTC
  • Revision ID: teddy@recompile.se-20190805211405-9m6hecekaihpttz9
Override lintian warnings about upgrading from old versions

There are some really things which are imperative that we fix in case
someone were to upgrade from a really old version.  We want to keep
these fixes in the postinst maintainer scripts, even though lintian
complains about such old upgrades not being supported by Debian in
general.  We prefer the code being there, for the sake of the users.

* debian/mandos-client.lintian-overrides
  (maintainer-script-supports-ancient-package-version): New.
  debian/mandos.lintian-overrides
  (maintainer-script-supports-ancient-package-version): - '' -

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-2021 Teddy Hogeborn
6
 
 * Copyright © 2019-2021 Björn Påhlsson
 
5
 * Copyright © 2019 Teddy Hogeborn
 
6
 * Copyright © 2019 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             /* 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 */
 
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 */
33
31
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
34
32
                                   getpid() */
35
33
#include <stdbool.h>            /* bool, true, false */
42
40
                                   NSIG, sigismember(), SA_ONSTACK,
43
41
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
44
42
                                   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() */
52
43
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
53
 
                                   malloc(), free(), realloc(),
54
 
                                   setenv(), calloc(), mkdtemp(),
55
 
                                   mkostemp() */
 
44
                                   malloc(), free(), strtoumax(),
 
45
                                   realloc(), setenv(), calloc(),
 
46
                                   mkdtemp(), mkostemp() */
56
47
#include <iso646.h>             /* not, or, and, xor */
57
48
#include <error.h>              /* error() */
58
49
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
59
50
#include <errno.h>              /* errno, error_t, EACCES,
60
51
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
61
 
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
52
                                   EEXIST, ECHILD, EPERM, ENOMEM,
62
53
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
63
54
                                   ECONNREFUSED, ECONNRESET,
64
55
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
66
57
#include <string.h>             /* strdup(), memcpy(),
67
58
                                   explicit_bzero(), memset(),
68
59
                                   strcmp(), strlen(), strncpy(),
69
 
                                   memcmp(), basename(), strerror() */
 
60
                                   memcmp(), basename() */
70
61
#include <argz.h>               /* argz_create(), argz_count(),
71
62
                                   argz_extract(), argz_next(),
72
63
                                   argz_add() */
82
73
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
83
74
                                   struct argp, argp_parse(),
84
75
                                   ARGP_NO_EXIT */
85
 
#include <stdint.h>             /* SIZE_MAX, uint32_t */
 
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() */
86
83
#include <sys/mman.h>           /* munlock(), mlock() */
87
84
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
88
85
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
98
95
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
99
96
                                   struct inotify_event */
100
97
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
101
 
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
102
 
                                   fclose(), getline(), sscanf(),
103
 
                                   feof(), ferror(), rename(),
104
 
                                   fdopen(), fprintf(), fscanf() */
 
98
#include <stdio.h>              /* asprintf(), FILE, fopen(),
 
99
                                   getline(), sscanf(), feof(),
 
100
                                   ferror(), fclose(), stderr,
 
101
                                   rename(), fdopen(), fprintf(),
 
102
                                   fscanf() */
105
103
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
106
104
                        GError, g_key_file_load_from_file(),
107
105
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
112
110
                        g_assert_null(), g_assert_false(),
113
111
                        g_assert_cmpint(), g_assert_cmpuint(),
114
112
                        g_test_skip(), g_assert_cmpstr(),
115
 
                        g_test_message(), g_test_init(), g_test_add(),
116
 
                        g_test_run(), GOptionContext,
117
 
                        g_option_context_new(),
 
113
                        g_test_init(), g_test_add(), g_test_run(),
 
114
                        GOptionContext, g_option_context_new(),
118
115
                        g_option_context_set_help_enabled(), FALSE,
119
116
                        g_option_context_set_ignore_unknown_options(),
120
117
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
151
148
  mono_microsecs next_run;
152
149
} __attribute__((designated_init)) task_queue;
153
150
 
154
 
/* "task_func" - A function type for task functions
 
151
/* "func_type" - A function type for task functions
155
152
 
156
153
   I.e. functions for the code which runs when a task is run, all have
157
154
   this type */
654
651
 
655
652
__attribute__((nonnull, warn_unused_result))
656
653
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
 
  }
664
654
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
665
655
  if(needed_size > (queue->allocated)){
666
656
    task_context *const new_tasks = realloc(queue->tasks,
871
861
  }
872
862
  close(pipefds[1]);
873
863
 
874
 
  if(pid == -1){
875
 
    error(0, errno, "Failed to fork()");
876
 
    close(pipefds[0]);
877
 
    return false;
878
 
  }
879
 
 
880
864
  if(not add_to_queue(queue, (task_context){
881
865
        .func=wait_for_mandos_client_exit,
882
866
        .pid=pid,
1196
1180
  bool *const password_is_read = task.password_is_read;
1197
1181
 
1198
1182
  /* We use the GLib "Key-value file parser" functions to parse the
1199
 
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
1200
 
     specification of contents */
 
1183
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1184
     /systemd/PasswordAgents/> for specification of contents */
1201
1185
  __attribute__((nonnull))
1202
1186
    void cleanup_g_key_file(GKeyFile **key_file){
1203
1187
    if(*key_file != NULL){
1493
1477
         not. You may but don't have to include a final NUL byte in
1494
1478
         your message.
1495
1479
 
1496
 
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1497
 
         14:24:20 GMT)
 
1480
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1481
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1498
1482
      */
1499
1483
      send_buffer[0] = '+';     /* Prefix with "+" */
1500
1484
      /* Always add an extra NUL */
1505
1489
      errno = 0;
1506
1490
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1507
1491
                           MSG_NOSIGNAL);
1508
 
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
1492
      const error_t saved_errno = errno;
1509
1493
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1510
1494
      explicit_bzero(send_buffer, send_buffer_length);
1511
1495
#else
1529
1513
          /* Retry, below */
1530
1514
          break;
1531
1515
        case EMSGSIZE:
1532
 
          error(0, saved_errno, "Password of size %" PRIuMAX
1533
 
                " is too big", (uintmax_t)password->length);
 
1516
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1517
                (uintmax_t)password->length);
1534
1518
#if __GNUC__ < 7
1535
1519
          /* FALLTHROUGH */
1536
1520
#else
1538
1522
#endif
1539
1523
        case 0:
1540
1524
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
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);
 
1525
            error(0, 0, "Password only partially sent to socket");
1544
1526
          }
1545
1527
#if __GNUC__ < 7
1546
1528
          /* FALLTHROUGH */
1902
1884
  g_assert_true(queue->tasks[0].func == dummy_func);
1903
1885
}
1904
1886
 
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
 
 
1928
1887
static void dummy_func(__attribute__((unused))
1929
1888
                       const task_context task,
1930
1889
                       __attribute__((unused))
2201
2160
    }
2202
2161
    exit(EXIT_SUCCESS);
2203
2162
  }
2204
 
  if(pid == -1){
2205
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
2206
 
  }
2207
 
 
2208
2163
  int status;
2209
2164
  waitpid(pid, &status, 0);
2210
2165
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
5812
5767
  char write_data[PIPE_BUF];
5813
5768
  {
5814
5769
    /* Construct test password buffer */
5815
 
    /* Start with + since that is what the real protocol uses */
 
5770
    /* Start with + since that is what the real procotol uses */
5816
5771
    write_data[0] = '+';
5817
5772
    /* Set a special character at string end just to mark the end */
5818
5773
    write_data[sizeof(write_data)-2] = 'y';
5970
5925
  char *const filename = strdup("/nonexistent/socket");
5971
5926
  __attribute__((cleanup(string_set_clear)))
5972
5927
    string_set cancelled_filenames = {};
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,
 
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,
6027
5934
  };
 
5935
  g_assert_nonnull(password.data);
6028
5936
  if(mlock(password.data, password.allocated) != 0){
6029
5937
    g_assert_true(errno == EPERM or errno == ENOMEM);
6030
5938
  }
 
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);
6031
5948
 
6032
5949
  __attribute__((cleanup(cleanup_queue)))
6033
5950
    task_queue *queue = create_queue();
6034
5951
  g_assert_nonnull(queue);
 
5952
  int socketfds[2];
6035
5953
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6036
5954
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6037
5955
                             socketfds), ==, 0);
7944
7862
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7945
7863
  test_add("/queue/create", test_create_queue);
7946
7864
  test_add("/queue/add", test_add_to_queue);
7947
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7948
7865
  test_add("/queue/has_question/empty",
7949
7866
           test_queue_has_question_empty);
7950
7867
  test_add("/queue/has_question/false",
8175
8092
  g_option_context_set_help_enabled(context, FALSE);
8176
8093
  g_option_context_set_ignore_unknown_options(context, TRUE);
8177
8094
 
8178
 
  gboolean should_run_tests = FALSE;
 
8095
  gboolean run_tests = FALSE;
8179
8096
  GOptionEntry entries[] = {
8180
8097
    { "test", 0, 0, G_OPTION_ARG_NONE,
8181
 
      &should_run_tests, "Run tests", NULL },
 
8098
      &run_tests, "Run tests", NULL },
8182
8099
    { NULL }
8183
8100
  };
8184
8101
  g_option_context_add_main_entries(context, entries, NULL);
8191
8108
  }
8192
8109
 
8193
8110
  g_option_context_free(context);
8194
 
  return should_run_tests != FALSE;
 
8111
  return run_tests != FALSE;
8195
8112
}