/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 at recompile
  • Date: 2020-12-03 20:30:45 UTC
  • Revision ID: teddy@recompile.se-20201203203045-iqd6nq9y5nwalh1x
Minor fix of a test function

In dracut-module/password-agent, the test function
test_send_password_to_socket_EMSGSIZE() (which tests that the
send_password_to_socket() task function aborts properly when getting
EMSGSIZE when writing to the password socket), part of the test code
is supposed to find a message size which definitely does trigger
EMSGSIZE when send()ing to a socket.  Without a "break" in the proper
place, however, the size given is always exactly 1024 bytes too large.

This is very probably not a problem, since a too large message will
still be too large if it is increased by 1024 bytes, and send(2) in
practice checks the size before reading the buffer.  The biggest issue
would be if some version of send(2) would try to look at the last 1024
bytes of the message buffer before checking the message size; this
would then lead to a buffer over-read when running this test function.
(But even then there would be no security implications since the tests
are not run in the normal operation of the program.)

* dracut-module/password-agent.c
  (test_send_password_to_socket_EMSGSIZE): Break out early when ssret
  < 0 and errno == EMSGSIZE; don't allow loop to increase message_size
  again.

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
 * 
868
868
  }
869
869
  close(pipefds[1]);
870
870
 
 
871
  if(pid == -1){
 
872
    error(0, errno, "Failed to fork()");
 
873
    close(pipefds[0]);
 
874
    return false;
 
875
  }
 
876
 
871
877
  if(not add_to_queue(queue, (task_context){
872
878
        .func=wait_for_mandos_client_exit,
873
879
        .pid=pid,
1187
1193
  bool *const password_is_read = task.password_is_read;
1188
1194
 
1189
1195
  /* We use the GLib "Key-value file parser" functions to parse the
1190
 
     question file.  See <https://www.freedesktop.org/wiki/Software
1191
 
     /systemd/PasswordAgents/> for specification of contents */
 
1196
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
1197
     specification of contents */
1192
1198
  __attribute__((nonnull))
1193
1199
    void cleanup_g_key_file(GKeyFile **key_file){
1194
1200
    if(*key_file != NULL){
1484
1490
         not. You may but don't have to include a final NUL byte in
1485
1491
         your message.
1486
1492
 
1487
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1488
 
         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)
1489
1495
      */
1490
1496
      send_buffer[0] = '+';     /* Prefix with "+" */
1491
1497
      /* Always add an extra NUL */
1496
1502
      errno = 0;
1497
1503
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1498
1504
                           MSG_NOSIGNAL);
1499
 
      const error_t saved_errno = errno;
 
1505
      const error_t saved_errno = (ssret < 0) ? errno : 0;
1500
1506
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1501
1507
      explicit_bzero(send_buffer, send_buffer_length);
1502
1508
#else
1520
1526
          /* Retry, below */
1521
1527
          break;
1522
1528
        case EMSGSIZE:
1523
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1524
 
                (uintmax_t)password->length);
 
1529
          error(0, saved_errno, "Password of size %" PRIuMAX
 
1530
                " is too big", (uintmax_t)password->length);
1525
1531
#if __GNUC__ < 7
1526
1532
          /* FALLTHROUGH */
1527
1533
#else
1529
1535
#endif
1530
1536
        case 0:
1531
1537
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1532
 
            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);
1533
1541
          }
1534
1542
#if __GNUC__ < 7
1535
1543
          /* FALLTHROUGH */
2190
2198
    }
2191
2199
    exit(EXIT_SUCCESS);
2192
2200
  }
 
2201
  if(pid == -1){
 
2202
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2203
  }
 
2204
 
2193
2205
  int status;
2194
2206
  waitpid(pid, &status, 0);
2195
2207
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
5797
5809
  char write_data[PIPE_BUF];
5798
5810
  {
5799
5811
    /* Construct test password buffer */
5800
 
    /* Start with + since that is what the real procotol uses */
 
5812
    /* Start with + since that is what the real protocol uses */
5801
5813
    write_data[0] = '+';
5802
5814
    /* Set a special character at string end just to mark the end */
5803
5815
    write_data[sizeof(write_data)-2] = 'y';
5955
5967
  char *const filename = strdup("/nonexistent/socket");
5956
5968
  __attribute__((cleanup(string_set_clear)))
5957
5969
    string_set cancelled_filenames = {};
5958
 
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5959
 
  __attribute__((cleanup(cleanup_buffer)))
5960
 
    buffer password = {
5961
 
    .data=malloc(oversized),
5962
 
    .length=oversized,
5963
 
    .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
    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
      break;
 
6010
    } else if(ssret != (ssize_t)message_size){
 
6011
      g_test_skip("Skipping EMSGSIZE test");
 
6012
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
 
6013
                     " bytes", (uintmax_t)ssret,
 
6014
                     (intmax_t)message_size);
 
6015
      return;
 
6016
    }
 
6017
  }
 
6018
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
 
6019
                 (intmax_t)message_size);
 
6020
 
 
6021
  buffer password = {
 
6022
    .data=message_buffer,
 
6023
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
 
6024
    .allocated=message_size,
5964
6025
  };
5965
 
  g_assert_nonnull(password.data);
5966
6026
  if(mlock(password.data, password.allocated) != 0){
5967
6027
    g_assert_true(errno == EPERM or errno == ENOMEM);
5968
6028
  }
5969
 
  /* Construct test password buffer */
5970
 
  /* Start with + since that is what the real procotol uses */
5971
 
  password.data[0] = '+';
5972
 
  /* Set a special character at string end just to mark the end */
5973
 
  password.data[oversized-3] = 'y';
5974
 
  /* Set NUL at buffer end, as suggested by the protocol */
5975
 
  password.data[oversized-2] = '\0';
5976
 
  /* Fill rest of password with 'x' */
5977
 
  memset(password.data+1, 'x', oversized-3);
5978
6029
 
5979
6030
  __attribute__((cleanup(cleanup_queue)))
5980
6031
    task_queue *queue = create_queue();
5981
6032
  g_assert_nonnull(queue);
5982
 
  int socketfds[2];
5983
6033
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5984
6034
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5985
6035
                             socketfds), ==, 0);