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