Home | History | Annotate | Download | only in src
      1 /*
      2  * tlsdate-setter.c - privileged time setter for tlsdated
      3  * Copyright (c) 2013 The Chromium Authors. All rights reserved.
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "config.h"
      9 
     10 #include <errno.h>
     11 #include <fcntl.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <sys/ioctl.h>
     15 #include <sys/prctl.h>
     16 #include <sys/stat.h>
     17 #include <sys/time.h>
     18 #include <sys/types.h>
     19 #include <sys/wait.h>
     20 #include <unistd.h>
     21 
     22 #include <event2/event.h>
     23 
     24 #include "src/conf.h"
     25 #include "src/dbus.h"
     26 #include "src/seccomp.h"
     27 #include "src/tlsdate.h"
     28 #include "src/util.h"
     29 
     30 /* Atomically writes the timestamp to the specified fd. */
     31 int
     32 save_timestamp_to_fd (int fd, time_t t)
     33 {
     34   return platform->file_write(fd, &t, sizeof (t));
     35 }
     36 
     37 void
     38 report_setter_error (siginfo_t *info)
     39 {
     40   const char *code;
     41   int killit = 0;
     42   switch (info->si_code)
     43     {
     44     case CLD_EXITED:
     45       code = "EXITED";
     46       break;
     47     case CLD_KILLED:
     48       code = "KILLED";
     49       break;
     50     case CLD_DUMPED:
     51       code = "DUMPED";
     52       break;
     53     case CLD_STOPPED:
     54       code = "STOPPED";
     55       killit = 1;
     56       break;
     57     case CLD_TRAPPED:
     58       code = "TRAPPED";
     59       killit = 1;
     60       break;
     61     case CLD_CONTINUED:
     62       code = "CONTINUED";
     63       killit = 1;
     64       break;
     65     default:
     66       code = "???";
     67       killit = 1;
     68     }
     69   info ("tlsdate-setter exitting: code:%s status:%d pid:%d uid:%d",
     70         code, info->si_status, info->si_pid, info->si_uid);
     71   if (killit)
     72     kill (info->si_pid, SIGKILL);
     73 }
     74 
     75 void
     76 time_setter_coprocess (int time_fd, int notify_fd, struct state *state)
     77 {
     78   int save_fd = -1;
     79   int status;
     80   prctl (PR_SET_NAME, "tlsdated-setter");
     81   if (state->opts.should_save_disk && !state->opts.dry_run)
     82     {
     83       const mode_t perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
     84       /* TODO(wad) platform->file_open */
     85       if ( (save_fd = open (state->timestamp_path,
     86                             O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC,
     87                             perms)) < 0 ||
     88            fchmod (save_fd, perms) != 0)
     89         {
     90           /* Attempt to unlink the path on the way out. */
     91           unlink (state->timestamp_path);
     92           status = SETTER_NO_SAVE;
     93           goto notify_and_die;
     94         }
     95     }
     96   /* XXX: Drop all privs but CAP_SYS_TIME */
     97 #ifdef HAVE_SECCOMP_FILTER
     98   if (enable_setter_seccomp())
     99     {
    100       status = SETTER_NO_SBOX;
    101       goto notify_and_die;
    102     }
    103 #endif
    104   while (1)
    105     {
    106       struct timeval tv = { 0, 0 };
    107       /* The wire protocol is a time_t, but the caller should
    108        * always be the unprivileged tlsdated process which spawned this
    109        * helper.
    110        * There are two special messages:
    111        * (time_t)   0: requests a clean shutdown
    112        * (time_t) < 0: indicates not to write to disk
    113        * On Linux, time_t is a signed long.  Expanding the protocol
    114        * is easy, but writing one long only is ideal.
    115        */
    116       ssize_t bytes = read (time_fd, &tv.tv_sec, sizeof (tv.tv_sec));
    117       int save = 1;
    118       if (bytes == -1)
    119         {
    120           if (errno == EINTR)
    121             continue;
    122           status = SETTER_READ_ERR;
    123           goto notify_and_die;
    124         }
    125       if (bytes == 0)
    126         {
    127           /* End of pipe */
    128           status = SETTER_READ_ERR;
    129           goto notify_and_die;
    130         }
    131       if (bytes != sizeof (tv.tv_sec))
    132         continue;
    133       if (tv.tv_sec < 0)
    134         {
    135           /* Don't write to disk */
    136           tv.tv_sec = -tv.tv_sec;
    137           save = 0;
    138         }
    139       if (tv.tv_sec == 0)
    140         {
    141           status = SETTER_EXIT;
    142           goto notify_and_die;
    143         }
    144       if (is_sane_time (tv.tv_sec))
    145         {
    146           /* It would be nice if time was only allowed to move forward, but
    147            * if a single time source is wrong, then it could make it impossible
    148            * to recover from once the time is written to disk.
    149            */
    150           status = SETTER_BAD_TIME;
    151           if (!state->opts.dry_run)
    152             {
    153               if (settimeofday (&tv, NULL) < 0)
    154                 {
    155                   status = SETTER_SET_ERR;
    156                   goto notify_and_die;
    157                 }
    158               if (state->opts.should_sync_hwclock &&
    159                   platform->rtc_write(&state->hwclock, &tv))
    160                 {
    161                   status = SETTER_NO_RTC;
    162                   goto notify_and_die;
    163                 }
    164               if (save && save_fd != -1 &&
    165                   save_timestamp_to_fd (save_fd, tv.tv_sec))
    166                 {
    167                   status = SETTER_NO_SAVE;
    168                   goto notify_and_die;
    169                 }
    170             }
    171           status = SETTER_TIME_SET;
    172         }
    173       /* TODO(wad) platform->file_write */
    174       IGNORE_EINTR (write (notify_fd, &status, sizeof(status)));
    175     }
    176 notify_and_die:
    177   IGNORE_EINTR (write (notify_fd, &status, sizeof(status)));
    178   close (notify_fd);
    179   close (save_fd);
    180   _exit (status);
    181 }
    182