/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-01 19:30:45 UTC
  • Revision ID: teddy@recompile.se-20210201193045-lpg6aprpc4srem6k
Fix issue with french translation

Initial white space was missing in both msgid and msgstr of the french
translation, leading to checking tools reporing an incomplete
translation.  The string is a raw command line command, and therefore
did not need translation, so this was never a user-visible issue.

* debian/po/fr.po: Add missing whitespace to the id and translation
  for msgid " mandos-keygen -F/dev/null|grep ^key_id".

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-2020 Teddy Hogeborn
 
6
 * Copyright © 2019-2020 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
 
                                   EEXIST, ECHILD, EPERM, ENOMEM,
 
52
                                   ENOMEM, EEXIST, ECHILD, EPERM,
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 */
76
77
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
77
78
                                   fork(), _exit(), dup2(),
78
79
                                   STDOUT_FILENO, setresgid(),
95
96
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
96
97
                                   struct inotify_event */
97
98
#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() */
 
99
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
100
                                   fclose(), getline(), sscanf(),
 
101
                                   feof(), ferror(), rename(),
 
102
                                   fdopen(), fprintf(), 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
 
/* "func_type" - A function type for task functions
 
151
/* "task_func" - 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
  }
654
661
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
655
662
  if(needed_size > (queue->allocated)){
656
663
    task_context *const new_tasks = realloc(queue->tasks,
861
868
  }
862
869
  close(pipefds[1]);
863
870
 
 
871
  if(pid == -1){
 
872
    error(0, errno, "Failed to fork()");
 
873
    close(pipefds[0]);
 
874
    return false;
 
875
  }
 
876
 
864
877
  if(not add_to_queue(queue, (task_context){
865
878
        .func=wait_for_mandos_client_exit,
866
879
        .pid=pid,
1180
1193
  bool *const password_is_read = task.password_is_read;
1181
1194
 
1182
1195
  /* 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 */
 
1196
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
1197
     specification of contents */
1185
1198
  __attribute__((nonnull))
1186
1199
    void cleanup_g_key_file(GKeyFile **key_file){
1187
1200
    if(*key_file != NULL){
1477
1490
         not. You may but don't have to include a final NUL byte in
1478
1491
         your message.
1479
1492
 
1480
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1481
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
1493
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
1494
         14:24:20 GMT)
1482
1495
      */
1483
1496
      send_buffer[0] = '+';     /* Prefix with "+" */
1484
1497
      /* Always add an extra NUL */
1489
1502
      errno = 0;
1490
1503
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1491
1504
                           MSG_NOSIGNAL);
1492
 
      const error_t saved_errno = errno;
 
1505
      const error_t saved_errno = (ssret < 0) ? errno : 0;
1493
1506
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1494
1507
      explicit_bzero(send_buffer, send_buffer_length);
1495
1508
#else
1513
1526
          /* Retry, below */
1514
1527
          break;
1515
1528
        case EMSGSIZE:
1516
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1517
 
                (uintmax_t)password->length);
 
1529
          error(0, saved_errno, "Password of size %" PRIuMAX
 
1530
                " is too big", (uintmax_t)password->length);
1518
1531
#if __GNUC__ < 7
1519
1532
          /* FALLTHROUGH */
1520
1533
#else
1522
1535
#endif
1523
1536
        case 0:
1524
1537
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1525
 
            error(0, 0, "Password only partially sent to socket");
 
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);
1526
1541
          }
1527
1542
#if __GNUC__ < 7
1528
1543
          /* FALLTHROUGH */
1884
1899
  g_assert_true(queue->tasks[0].func == dummy_func);
1885
1900
}
1886
1901
 
 
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
 
1887
1925
static void dummy_func(__attribute__((unused))
1888
1926
                       const task_context task,
1889
1927
                       __attribute__((unused))
2160
2198
    }
2161
2199
    exit(EXIT_SUCCESS);
2162
2200
  }
 
2201
  if(pid == -1){
 
2202
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2203
  }
 
2204
 
2163
2205
  int status;
2164
2206
  waitpid(pid, &status, 0);
2165
2207
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
5767
5809
  char write_data[PIPE_BUF];
5768
5810
  {
5769
5811
    /* Construct test password buffer */
5770
 
    /* Start with + since that is what the real procotol uses */
 
5812
    /* Start with + since that is what the real protocol uses */
5771
5813
    write_data[0] = '+';
5772
5814
    /* Set a special character at string end just to mark the end */
5773
5815
    write_data[sizeof(write_data)-2] = 'y';
5925
5967
  char *const filename = strdup("/nonexistent/socket");
5926
5968
  __attribute__((cleanup(string_set_clear)))
5927
5969
    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,
 
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
    message_buffer = realloc(message_buffer, message_size);
 
5982
    if(message_buffer == NULL){
 
5983
      g_test_skip("Skipping EMSGSIZE test");
 
5984
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
 
5985
                     (uintmax_t)message_size);
 
5986
      return;
 
5987
    }
 
5988
    /* Fill buffer with 'x' */
 
5989
    memset(message_buffer, 'x', message_size);
 
5990
    /* Create a new socketpair for each message size to avoid having
 
5991
       to empty the pipe by reading the message to a separate buffer
 
5992
    */
 
5993
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5994
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5995
                               socketfds), ==, 0);
 
5996
    ssret = send(socketfds[1], message_buffer, message_size,
 
5997
                 MSG_NOSIGNAL);
 
5998
    error_t saved_errno = errno;
 
5999
    g_assert_cmpint(close(socketfds[0]), ==, 0);
 
6000
    g_assert_cmpint(close(socketfds[1]), ==, 0);
 
6001
 
 
6002
    if(ssret < 0){
 
6003
      if(saved_errno != EMSGSIZE) {
 
6004
        g_test_skip("Skipping EMSGSIZE test");
 
6005
        g_test_message("Error on send(): %s", strerror(saved_errno));
 
6006
        return;
 
6007
      }
 
6008
      break;
 
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,
5934
6024
  };
5935
 
  g_assert_nonnull(password.data);
5936
6025
  if(mlock(password.data, password.allocated) != 0){
5937
6026
    g_assert_true(errno == EPERM or errno == ENOMEM);
5938
6027
  }
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
6028
 
5949
6029
  __attribute__((cleanup(cleanup_queue)))
5950
6030
    task_queue *queue = create_queue();
5951
6031
  g_assert_nonnull(queue);
5952
 
  int socketfds[2];
5953
6032
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5954
6033
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5955
6034
                             socketfds), ==, 0);
7862
7941
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7863
7942
  test_add("/queue/create", test_create_queue);
7864
7943
  test_add("/queue/add", test_add_to_queue);
 
7944
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7865
7945
  test_add("/queue/has_question/empty",
7866
7946
           test_queue_has_question_empty);
7867
7947
  test_add("/queue/has_question/false",
8092
8172
  g_option_context_set_help_enabled(context, FALSE);
8093
8173
  g_option_context_set_ignore_unknown_options(context, TRUE);
8094
8174
 
8095
 
  gboolean run_tests = FALSE;
 
8175
  gboolean should_run_tests = FALSE;
8096
8176
  GOptionEntry entries[] = {
8097
8177
    { "test", 0, 0, G_OPTION_ARG_NONE,
8098
 
      &run_tests, "Run tests", NULL },
 
8178
      &should_run_tests, "Run tests", NULL },
8099
8179
    { NULL }
8100
8180
  };
8101
8181
  g_option_context_add_main_entries(context, entries, NULL);
8108
8188
  }
8109
8189
 
8110
8190
  g_option_context_free(context);
8111
 
  return run_tests != FALSE;
 
8191
  return should_run_tests != FALSE;
8112
8192
}