/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 dracut-module/password-agent.c

  • Committer: Teddy Hogeborn
  • Date: 2019-08-05 21:14:05 UTC
  • mto: This revision was merged to the branch mainline in revision 388.
  • 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-2020 Teddy Hogeborn
6
 
 * Copyright © 2019-2020 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
 * 
49
49
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
50
#include <errno.h>              /* errno, error_t, EACCES,
51
51
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
52
 
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
52
                                   EEXIST, ECHILD, EPERM, ENOMEM,
53
53
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
54
                                   ECONNREFUSED, ECONNRESET,
55
55
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
73
73
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
74
                                   struct argp, argp_parse(),
75
75
                                   ARGP_NO_EXIT */
76
 
#include <stdint.h>             /* SIZE_MAX */
77
76
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
78
77
                                   fork(), _exit(), dup2(),
79
78
                                   STDOUT_FILENO, setresgid(),
96
95
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
97
96
                                   struct inotify_event */
98
97
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
99
 
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
100
 
                                   fclose(), getline(), sscanf(),
101
 
                                   feof(), ferror(), rename(),
102
 
                                   fdopen(), fprintf(), fscanf() */
 
98
#include <stdio.h>              /* asprintf(), FILE, fopen(),
 
99
                                   getline(), sscanf(), feof(),
 
100
                                   ferror(), fclose(), stderr,
 
101
                                   rename(), fdopen(), fprintf(),
 
102
                                   fscanf() */
103
103
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
104
104
                        GError, g_key_file_load_from_file(),
105
105
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
148
148
  mono_microsecs next_run;
149
149
} __attribute__((designated_init)) task_queue;
150
150
 
151
 
/* "task_func" - A function type for task functions
 
151
/* "func_type" - A function type for task functions
152
152
 
153
153
   I.e. functions for the code which runs when a task is run, all have
154
154
   this type */
651
651
 
652
652
__attribute__((nonnull, warn_unused_result))
653
653
bool add_to_queue(task_queue *const queue, const task_context task){
654
 
  if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
655
 
    /* overflow */
656
 
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
657
 
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
658
 
    errno = ENOMEM;
659
 
    return false;
660
 
  }
661
654
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
662
655
  if(needed_size > (queue->allocated)){
663
656
    task_context *const new_tasks = realloc(queue->tasks,
868
861
  }
869
862
  close(pipefds[1]);
870
863
 
871
 
  if(pid == -1){
872
 
    error(0, errno, "Failed to fork()");
873
 
    close(pipefds[0]);
874
 
    return false;
875
 
  }
876
 
 
877
864
  if(not add_to_queue(queue, (task_context){
878
865
        .func=wait_for_mandos_client_exit,
879
866
        .pid=pid,
1193
1180
  bool *const password_is_read = task.password_is_read;
1194
1181
 
1195
1182
  /* We use the GLib "Key-value file parser" functions to parse the
1196
 
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
1197
 
     specification of contents */
 
1183
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1184
     /systemd/PasswordAgents/> for specification of contents */
1198
1185
  __attribute__((nonnull))
1199
1186
    void cleanup_g_key_file(GKeyFile **key_file){
1200
1187
    if(*key_file != NULL){
1490
1477
         not. You may but don't have to include a final NUL byte in
1491
1478
         your message.
1492
1479
 
1493
 
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1494
 
         14:24:20 GMT)
 
1480
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1481
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1495
1482
      */
1496
1483
      send_buffer[0] = '+';     /* Prefix with "+" */
1497
1484
      /* Always add an extra NUL */
1502
1489
      errno = 0;
1503
1490
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1504
1491
                           MSG_NOSIGNAL);
1505
 
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
1492
      const error_t saved_errno = errno;
1506
1493
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1507
1494
      explicit_bzero(send_buffer, send_buffer_length);
1508
1495
#else
1526
1513
          /* Retry, below */
1527
1514
          break;
1528
1515
        case EMSGSIZE:
1529
 
          error(0, saved_errno, "Password of size %" PRIuMAX
1530
 
                " is too big", (uintmax_t)password->length);
 
1516
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1517
                (uintmax_t)password->length);
1531
1518
#if __GNUC__ < 7
1532
1519
          /* FALLTHROUGH */
1533
1520
#else
1535
1522
#endif
1536
1523
        case 0:
1537
1524
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1538
 
            error(0, 0, "Password only partially sent to socket %s: %"
1539
 
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
1540
 
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
 
1525
            error(0, 0, "Password only partially sent to socket");
1541
1526
          }
1542
1527
#if __GNUC__ < 7
1543
1528
          /* FALLTHROUGH */
1899
1884
  g_assert_true(queue->tasks[0].func == dummy_func);
1900
1885
}
1901
1886
 
1902
 
static void test_add_to_queue_overflow(__attribute__((unused))
1903
 
                                       test_fixture *fixture,
1904
 
                                       __attribute__((unused))
1905
 
                                       gconstpointer user_data){
1906
 
  __attribute__((cleanup(cleanup_queue)))
1907
 
    task_queue *queue = create_queue();
1908
 
  g_assert_nonnull(queue);
1909
 
  g_assert_true(queue->length == 0);
1910
 
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1911
 
 
1912
 
  FILE *real_stderr = stderr;
1913
 
  FILE *devnull = fopen("/dev/null", "we");
1914
 
  g_assert_nonnull(devnull);
1915
 
  stderr = devnull;
1916
 
  const bool ret = add_to_queue(queue,
1917
 
                                (task_context){ .func=dummy_func });
1918
 
  g_assert_true(errno == ENOMEM);
1919
 
  g_assert_false(ret);
1920
 
  stderr = real_stderr;
1921
 
  g_assert_cmpint(fclose(devnull), ==, 0);
1922
 
  queue->length = 0;            /* Restore real size */
1923
 
}
1924
 
 
1925
1887
static void dummy_func(__attribute__((unused))
1926
1888
                       const task_context task,
1927
1889
                       __attribute__((unused))
2198
2160
    }
2199
2161
    exit(EXIT_SUCCESS);
2200
2162
  }
2201
 
  if(pid == -1){
2202
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
2203
 
  }
2204
 
 
2205
2163
  int status;
2206
2164
  waitpid(pid, &status, 0);
2207
2165
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
5809
5767
  char write_data[PIPE_BUF];
5810
5768
  {
5811
5769
    /* Construct test password buffer */
5812
 
    /* Start with + since that is what the real protocol uses */
 
5770
    /* Start with + since that is what the real procotol uses */
5813
5771
    write_data[0] = '+';
5814
5772
    /* Set a special character at string end just to mark the end */
5815
5773
    write_data[sizeof(write_data)-2] = 'y';
5967
5925
  char *const filename = strdup("/nonexistent/socket");
5968
5926
  __attribute__((cleanup(string_set_clear)))
5969
5927
    string_set cancelled_filenames = {};
5970
 
  int socketfds[2];
5971
 
 
5972
 
  /* Find a message size which triggers EMSGSIZE */
5973
 
  __attribute__((cleanup(cleanup_string)))
5974
 
    char *message_buffer = NULL;
5975
 
  size_t message_size = PIPE_BUF + 1;
5976
 
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
5977
 
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
5978
 
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
5979
 
      return;
5980
 
    }
5981
 
    free(message_buffer);
5982
 
    message_buffer = malloc(message_size);
5983
 
    if(message_buffer == NULL){
5984
 
      g_test_skip("Skipping EMSGSIZE test");
5985
 
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
5986
 
                     (uintmax_t)message_size);
5987
 
      return;
5988
 
    }
5989
 
    /* Fill buffer with 'x' */
5990
 
    memset(message_buffer, 'x', message_size);
5991
 
    /* Create a new socketpair for each message size to avoid having
5992
 
       to empty the pipe by reading the message to a separate buffer
5993
 
    */
5994
 
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5995
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5996
 
                               socketfds), ==, 0);
5997
 
    ssret = send(socketfds[1], message_buffer, message_size,
5998
 
                 MSG_NOSIGNAL);
5999
 
    error_t saved_errno = errno;
6000
 
    g_assert_cmpint(close(socketfds[0]), ==, 0);
6001
 
    g_assert_cmpint(close(socketfds[1]), ==, 0);
6002
 
 
6003
 
    if(ssret < 0){
6004
 
      if(saved_errno != EMSGSIZE) {
6005
 
        g_test_skip("Skipping EMSGSIZE test");
6006
 
        g_test_message("Error on send(): %s", strerror(saved_errno));
6007
 
        return;
6008
 
      }
6009
 
    } else if(ssret != (ssize_t)message_size){
6010
 
      g_test_skip("Skipping EMSGSIZE test");
6011
 
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
6012
 
                     " bytes", (uintmax_t)ssret,
6013
 
                     (intmax_t)message_size);
6014
 
      return;
6015
 
    }
6016
 
  }
6017
 
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
6018
 
                 (intmax_t)message_size);
6019
 
 
6020
 
  buffer password = {
6021
 
    .data=message_buffer,
6022
 
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
6023
 
    .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,
6024
5934
  };
 
5935
  g_assert_nonnull(password.data);
6025
5936
  if(mlock(password.data, password.allocated) != 0){
6026
5937
    g_assert_true(errno == EPERM or errno == ENOMEM);
6027
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);
6028
5948
 
6029
5949
  __attribute__((cleanup(cleanup_queue)))
6030
5950
    task_queue *queue = create_queue();
6031
5951
  g_assert_nonnull(queue);
 
5952
  int socketfds[2];
6032
5953
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6033
5954
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6034
5955
                             socketfds), ==, 0);
7941
7862
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7942
7863
  test_add("/queue/create", test_create_queue);
7943
7864
  test_add("/queue/add", test_add_to_queue);
7944
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7945
7865
  test_add("/queue/has_question/empty",
7946
7866
           test_queue_has_question_empty);
7947
7867
  test_add("/queue/has_question/false",
8172
8092
  g_option_context_set_help_enabled(context, FALSE);
8173
8093
  g_option_context_set_ignore_unknown_options(context, TRUE);
8174
8094
 
8175
 
  gboolean should_run_tests = FALSE;
 
8095
  gboolean run_tests = FALSE;
8176
8096
  GOptionEntry entries[] = {
8177
8097
    { "test", 0, 0, G_OPTION_ARG_NONE,
8178
 
      &should_run_tests, "Run tests", NULL },
 
8098
      &run_tests, "Run tests", NULL },
8179
8099
    { NULL }
8180
8100
  };
8181
8101
  g_option_context_add_main_entries(context, entries, NULL);
8188
8108
  }
8189
8109
 
8190
8110
  g_option_context_free(context);
8191
 
  return should_run_tests != FALSE;
 
8111
  return run_tests != FALSE;
8192
8112
}