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
save_timestamp_to_fd(int fd,time_t t)32 save_timestamp_to_fd (int fd, time_t t)
33 {
34 return platform->file_write(fd, &t, sizeof (t));
35 }
36
37 void
report_setter_error(siginfo_t * info)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
time_setter_coprocess(int time_fd,int notify_fd,struct state * state)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