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