/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-03 11:39:38 UTC
  • mto: This revision was merged to the branch mainline in revision 386.
  • Revision ID: teddy@recompile.se-20190803113938-httj8rqsnct08txx
dracut-module/password-agent.c: Bug fix: Ignore deleted files

If a question file ("ask.*") is opened, deleted, and then closed, we
still get an IN_CLOSE_WRITE ievent after the IN_DELETE ievent.  We
should pass the IN_EXCL_UNLINK flag to inotify_add_watch() to avoid
this.

* dracut-module/password-agent.c (add_inotify_dir_watch): Add
  "IN_EXCL_UNLINK" flag to inotify_add_watch().
  (test_add_inotify_dir_watch_IN_EXCL_UNLINK): New test.
  (run_tests): Add new test.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1017
1017
    return false;
1018
1018
  }
1019
1019
 
1020
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
1021
 
                       | IN_MOVED_TO | IN_DELETE)
 
1020
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
 
1021
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK)
1022
1022
     == -1){
1023
1023
    error(0, errno, "Failed to create inotify watch on %s", dir);
1024
1024
    return false;
1119
1119
             immediately */
1120
1120
          queue->next_run = 1;
1121
1121
        }
1122
 
      } else if(ievent->mask & IN_DELETE){
 
1122
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1123
1123
        if(not string_set_add(cancelled_filenames,
1124
1124
                              question_filename)){
1125
1125
          error(0, errno, "Could not add question %s to"
3672
3672
}
3673
3673
 
3674
3674
static
 
3675
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
 
3676
                                              test_fixture *fixture,
 
3677
                                              __attribute__((unused))
 
3678
                                              gconstpointer
 
3679
                                              user_data){
 
3680
  __attribute__((cleanup(cleanup_close)))
 
3681
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3682
  g_assert_cmpint(epoll_fd, >=, 0);
 
3683
  __attribute__((cleanup(cleanup_queue)))
 
3684
    task_queue *queue = create_queue();
 
3685
  g_assert_nonnull(queue);
 
3686
  __attribute__((cleanup(string_set_clear)))
 
3687
    string_set cancelled_filenames = {};
 
3688
  const mono_microsecs current_time = 0;
 
3689
 
 
3690
  bool quit_now = false;
 
3691
  buffer password = {};
 
3692
  bool mandos_client_exited = false;
 
3693
  bool password_is_read = false;
 
3694
 
 
3695
  __attribute__((cleanup(cleanup_string)))
 
3696
    char *tempdir = make_temporary_directory();
 
3697
  g_assert_nonnull(tempdir);
 
3698
 
 
3699
  __attribute__((cleanup(cleanup_string)))
 
3700
    char *tempfilename = make_temporary_file_in_directory(tempdir);
 
3701
  g_assert_nonnull(tempfilename);
 
3702
 
 
3703
  __attribute__((cleanup(cleanup_string)))
 
3704
    char *targetdir = make_temporary_directory();
 
3705
  g_assert_nonnull(targetdir);
 
3706
 
 
3707
  __attribute__((cleanup(cleanup_string)))
 
3708
    char *targetfilename = NULL;
 
3709
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
 
3710
                           basename(tempfilename)), >, 0);
 
3711
  g_assert_nonnull(targetfilename);
 
3712
 
 
3713
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3714
                                      &password, tempdir,
 
3715
                                      &cancelled_filenames,
 
3716
                                      &current_time,
 
3717
                                      &mandos_client_exited,
 
3718
                                      &password_is_read));
 
3719
 
 
3720
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
 
3721
 
 
3722
  const task_context *const added_read_task
 
3723
    = find_matching_task(queue,
 
3724
                         (task_context){ .func=read_inotify_event });
 
3725
  g_assert_nonnull(added_read_task);
 
3726
 
 
3727
  /* "sufficient to read at least one event." - inotify(7) */
 
3728
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3729
                              + NAME_MAX + 1);
 
3730
  struct inotify_event *ievent = malloc(ievent_size);
 
3731
  g_assert_nonnull(ievent);
 
3732
 
 
3733
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
3734
 
 
3735
  g_assert_cmpint((int)read_size, >, 0);
 
3736
  g_assert_true(ievent->mask & IN_MOVED_FROM);
 
3737
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
 
3738
 
 
3739
  free(ievent);
 
3740
 
 
3741
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
3742
  g_assert_cmpint(rmdir(targetdir), ==, 0);
 
3743
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3744
}
 
3745
 
 
3746
static
3675
3747
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3676
3748
                                          test_fixture *fixture,
3677
3749
                                          __attribute__((unused))
3735
3807
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3736
3808
}
3737
3809
 
 
3810
static
 
3811
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
 
3812
                                               test_fixture *fixture,
 
3813
                                               __attribute__((unused))
 
3814
                                               gconstpointer
 
3815
                                               user_data){
 
3816
  __attribute__((cleanup(cleanup_close)))
 
3817
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3818
  g_assert_cmpint(epoll_fd, >=, 0);
 
3819
  __attribute__((cleanup(cleanup_queue)))
 
3820
    task_queue *queue = create_queue();
 
3821
  g_assert_nonnull(queue);
 
3822
  __attribute__((cleanup(string_set_clear)))
 
3823
    string_set cancelled_filenames = {};
 
3824
  const mono_microsecs current_time = 0;
 
3825
 
 
3826
  bool quit_now = false;
 
3827
  buffer password = {};
 
3828
  bool mandos_client_exited = false;
 
3829
  bool password_is_read = false;
 
3830
 
 
3831
  __attribute__((cleanup(cleanup_string)))
 
3832
    char *tempdir = make_temporary_directory();
 
3833
  g_assert_nonnull(tempdir);
 
3834
 
 
3835
  __attribute__((cleanup(cleanup_string)))
 
3836
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
3837
  g_assert_nonnull(tempfile);
 
3838
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
 
3839
                         | O_NOFOLLOW);
 
3840
  g_assert_cmpint(tempfile_fd, >, 2);
 
3841
 
 
3842
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3843
                                      &password, tempdir,
 
3844
                                      &cancelled_filenames,
 
3845
                                      &current_time,
 
3846
                                      &mandos_client_exited,
 
3847
                                      &password_is_read));
 
3848
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
3849
 
 
3850
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3851
 
 
3852
  const task_context *const added_read_task
 
3853
    = find_matching_task(queue,
 
3854
                         (task_context){ .func=read_inotify_event });
 
3855
  g_assert_nonnull(added_read_task);
 
3856
 
 
3857
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3858
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3859
 
 
3860
  /* "sufficient to read at least one event." - inotify(7) */
 
3861
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3862
                              + NAME_MAX + 1);
 
3863
  struct inotify_event *ievent = malloc(ievent_size);
 
3864
  g_assert_nonnull(ievent);
 
3865
 
 
3866
  ssize_t read_size = 0;
 
3867
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3868
 
 
3869
  g_assert_cmpint((int)read_size, >, 0);
 
3870
  g_assert_true(ievent->mask & IN_DELETE);
 
3871
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
3872
 
 
3873
  g_assert_cmpint(close(tempfile_fd), ==, 0);
 
3874
 
 
3875
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
 
3876
     file not appear as an ievent, so we should not see it now. */
 
3877
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3878
  g_assert_cmpint((int)read_size, ==, -1);
 
3879
  g_assert_true(errno == EAGAIN);
 
3880
 
 
3881
  free(ievent);
 
3882
 
 
3883
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3884
}
 
3885
 
3738
3886
static void test_read_inotify_event_readerror(__attribute__((unused))
3739
3887
                                              test_fixture *fixture,
3740
3888
                                              __attribute__((unused))
4104
4252
      }));
4105
4253
}
4106
4254
 
 
4255
static
 
4256
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
 
4257
                                           test_fixture *fixture,
 
4258
                                           __attribute__((unused))
 
4259
                                           gconstpointer user_data){
 
4260
  __attribute__((cleanup(cleanup_close)))
 
4261
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4262
  g_assert_cmpint(epoll_fd, >=, 0);
 
4263
  __attribute__((cleanup(string_set_clear)))
 
4264
    string_set cancelled_filenames = {};
 
4265
  const mono_microsecs current_time = 0;
 
4266
 
 
4267
  int pipefds[2];
 
4268
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4269
 
 
4270
  /* "sufficient to read at least one event." - inotify(7) */
 
4271
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4272
                                  + NAME_MAX + 1);
 
4273
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4274
  struct {
 
4275
    struct inotify_event event;
 
4276
    char name_buffer[NAME_MAX + 1];
 
4277
  } ievent_buffer;
 
4278
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4279
 
 
4280
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4281
  ievent->mask = IN_MOVED_FROM;
 
4282
  ievent->len = sizeof(dummy_file_name);
 
4283
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4284
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4285
                              + sizeof(dummy_file_name));
 
4286
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4287
                  ==, ievent_size);
 
4288
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4289
 
 
4290
  bool quit_now = false;
 
4291
  buffer password = {};
 
4292
  bool mandos_client_exited = false;
 
4293
  bool password_is_read = false;
 
4294
  __attribute__((cleanup(cleanup_queue)))
 
4295
    task_queue *queue = create_queue();
 
4296
  g_assert_nonnull(queue);
 
4297
 
 
4298
  task_context task = {
 
4299
    .func=read_inotify_event,
 
4300
    .epoll_fd=epoll_fd,
 
4301
    .fd=pipefds[0],
 
4302
    .quit_now=&quit_now,
 
4303
    .password=&password,
 
4304
    .filename=strdup("/nonexistent"),
 
4305
    .cancelled_filenames=&cancelled_filenames,
 
4306
    .current_time=&current_time,
 
4307
    .mandos_client_exited=&mandos_client_exited,
 
4308
    .password_is_read=&password_is_read,
 
4309
  };
 
4310
  task.func(task, queue);
 
4311
  g_assert_false(quit_now);
 
4312
  g_assert_true(queue->next_run == 0);
 
4313
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4314
 
 
4315
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4316
        .func=read_inotify_event,
 
4317
        .epoll_fd=epoll_fd,
 
4318
        .fd=pipefds[0],
 
4319
        .quit_now=&quit_now,
 
4320
        .password=&password,
 
4321
        .filename=task.filename,
 
4322
        .cancelled_filenames=&cancelled_filenames,
 
4323
        .current_time=&current_time,
 
4324
        .mandos_client_exited=&mandos_client_exited,
 
4325
        .password_is_read=&password_is_read,
 
4326
      }));
 
4327
 
 
4328
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4329
                                   EPOLLIN | EPOLLRDHUP));
 
4330
 
 
4331
  __attribute__((cleanup(cleanup_string)))
 
4332
    char *filename = NULL;
 
4333
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4334
                           dummy_file_name), >, 0);
 
4335
  g_assert_nonnull(filename);
 
4336
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
4337
                                    filename));
 
4338
}
 
4339
 
4107
4340
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4108
4341
                                              test_fixture *fixture,
4109
4342
                                              __attribute__((unused))
4342
4575
                                   EPOLLIN | EPOLLRDHUP));
4343
4576
}
4344
4577
 
 
4578
static void
 
4579
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
 
4580
                                              test_fixture *fixture,
 
4581
                                              __attribute__((unused))
 
4582
                                              gconstpointer
 
4583
                                              user_data){
 
4584
  __attribute__((cleanup(cleanup_close)))
 
4585
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4586
  g_assert_cmpint(epoll_fd, >=, 0);
 
4587
  __attribute__((cleanup(string_set_clear)))
 
4588
    string_set cancelled_filenames = {};
 
4589
  const mono_microsecs current_time = 0;
 
4590
 
 
4591
  int pipefds[2];
 
4592
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4593
 
 
4594
  /* "sufficient to read at least one event." - inotify(7) */
 
4595
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4596
                                  + NAME_MAX + 1);
 
4597
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4598
  struct {
 
4599
    struct inotify_event event;
 
4600
    char name_buffer[NAME_MAX + 1];
 
4601
  } ievent_buffer;
 
4602
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4603
 
 
4604
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4605
  ievent->mask = IN_MOVED_FROM;
 
4606
  ievent->len = sizeof(dummy_file_name);
 
4607
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4608
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4609
                              + sizeof(dummy_file_name));
 
4610
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4611
                  ==, ievent_size);
 
4612
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4613
 
 
4614
  bool quit_now = false;
 
4615
  buffer password = {};
 
4616
  bool mandos_client_exited = false;
 
4617
  bool password_is_read = false;
 
4618
  __attribute__((cleanup(cleanup_queue)))
 
4619
    task_queue *queue = create_queue();
 
4620
  g_assert_nonnull(queue);
 
4621
 
 
4622
  task_context task = {
 
4623
    .func=read_inotify_event,
 
4624
    .epoll_fd=epoll_fd,
 
4625
    .fd=pipefds[0],
 
4626
    .quit_now=&quit_now,
 
4627
    .password=&password,
 
4628
    .filename=strdup("/nonexistent"),
 
4629
    .cancelled_filenames=&cancelled_filenames,
 
4630
    .current_time=&current_time,
 
4631
    .mandos_client_exited=&mandos_client_exited,
 
4632
    .password_is_read=&password_is_read,
 
4633
  };
 
4634
  task.func(task, queue);
 
4635
  g_assert_false(quit_now);
 
4636
  g_assert_true(queue->next_run == 0);
 
4637
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4638
 
 
4639
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4640
        .func=read_inotify_event,
 
4641
        .epoll_fd=epoll_fd,
 
4642
        .fd=pipefds[0],
 
4643
        .quit_now=&quit_now,
 
4644
        .password=&password,
 
4645
        .filename=task.filename,
 
4646
        .cancelled_filenames=&cancelled_filenames,
 
4647
        .current_time=&current_time,
 
4648
        .mandos_client_exited=&mandos_client_exited,
 
4649
        .password_is_read=&password_is_read,
 
4650
      }));
 
4651
 
 
4652
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4653
                                   EPOLLIN | EPOLLRDHUP));
 
4654
 
 
4655
  __attribute__((cleanup(cleanup_string)))
 
4656
    char *filename = NULL;
 
4657
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4658
                           dummy_file_name), >, 0);
 
4659
  g_assert_nonnull(filename);
 
4660
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
4661
}
 
4662
 
4345
4663
static
4346
4664
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4347
4665
                                               test_fixture *fixture,
7593
7911
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7594
7912
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7595
7913
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
7914
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
 
7915
              test_add_inotify_dir_watch_IN_MOVED_FROM);
 
7916
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
 
7917
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7596
7918
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7597
7919
              test_add_inotify_dir_watch_IN_DELETE);
7598
7920
  test_add_st("/task/read_inotify_event/readerror",
7607
7929
              test_read_inotify_event_IN_CLOSE_WRITE);
7608
7930
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7609
7931
              test_read_inotify_event_IN_MOVED_TO);
 
7932
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
 
7933
              test_read_inotify_event_IN_MOVED_FROM);
7610
7934
  test_add_st("/task/read_inotify_event/IN_DELETE",
7611
7935
              test_read_inotify_event_IN_DELETE);
7612
7936
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7613
7937
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
7614
7938
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7615
7939
              test_read_inotify_event_IN_MOVED_TO_badname);
 
7940
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
 
7941
              test_read_inotify_event_IN_MOVED_FROM_badname);
7616
7942
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7617
7943
              test_read_inotify_event_IN_DELETE_badname);
7618
7944
  test_add_st("/task/open_and_parse_question/ENOENT",