1/*
2 * tlsdate_status.c - handles tlsdate-monitor responses
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 <stdbool.h>
13#include <stdlib.h>
14#include <sys/types.h>
15#include <sys/wait.h>
16#include <unistd.h>
17
18#include <event2/event.h>
19
20#include "src/conf.h"
21#include "src/util.h"
22#include "src/tlsdate.h"
23
24/* Returns < 0 on error, > 0 on eagain, and 0 on success */
25int
26read_tlsdate_response (int fd, time_t *t)
27{
28  /* TLS passes time as a 32-bit value. */
29  uint32_t server_time = 0;
30  ssize_t ret = IGNORE_EINTR (read (fd, &server_time, sizeof (server_time)));
31  if (ret == -1 && errno == EAGAIN)
32    {
33      /* Full response isn't ready yet. */
34      return 1;
35    }
36  if (ret != sizeof (server_time))
37    {
38      /* End of pipe (0) or truncated: death probable. */
39      error ("[event:(%s)] invalid time read from tlsdate (rd:%d,ret:%zd).",
40             __func__, server_time, ret);
41      return -1;
42    }
43  /* uint32_t moves to signed long so there is room for silliness. */
44  *t = server_time;
45  return 0;
46}
47
48void
49action_tlsdate_timeout (evutil_socket_t fd, short what, void *arg)
50{
51  struct state *state = arg;
52  info ("[event:%s] tlsdate timed out", __func__);
53  /* Force kill it and let action_sigchld rerun. */
54  if (state->tlsdate_pid)
55    kill (state->tlsdate_pid, SIGKILL);
56}
57
58void
59action_tlsdate_status (evutil_socket_t fd, short what, void *arg)
60{
61  struct state *state = arg;
62  time_t t = 0;
63  int ret = read_tlsdate_response (fd, &t);
64  verb_debug ("[event:%s] fired", __func__);
65  if (ret < 0)
66    {
67      verb_debug ("[event:%s] forcibly timing out tlsdate", __func__);
68      trigger_event (state, E_TLSDATE_TIMEOUT, 0);
69      return;
70    }
71  if (ret)
72    {
73      /* EAGAIN'd: wait for the rest. */
74      trigger_event (state, E_TLSDATE_STATUS, -1);
75      return;
76    }
77  if (is_sane_time (t))
78    {
79      /* Note that last_time is from an online source */
80      state->last_sync_type = SYNC_TYPE_NET;
81      state->last_time = t;
82      trigger_event (state, E_SAVE, -1);
83    }
84  else
85    {
86      error ("[event:%s] invalid time received from tlsdate: %ld",
87             __func__, t);
88    }
89  /* Restore the backoff and tries count on success, insane or not.
90   * On failure, the event handler does it.
91   */
92  state->tries = 0;
93  state->backoff = state->opts.wait_between_tries;
94  return;
95}
96
97/* Returns 0 on success and populates |fds| */
98int
99new_tlsdate_monitor_pipe (int fds[2])
100{
101  if (pipe (fds) < 0)
102    {
103      perror ("pipe failed");
104      return -1;
105    }
106  /* TODO(wad): CLOEXEC, Don't leak these into tlsdate proper. */
107  return 0;
108}
109
110/* Create a fd pair that the tlsdate runner will communicate over */
111int
112setup_tlsdate_status (struct state *state)
113{
114  int fds[2] = { -1, -1 };
115  /* One pair of pipes are reused along with the event. */
116  if (new_tlsdate_monitor_pipe (fds))
117    {
118      return -1;
119    }
120  verb_debug ("[%s] monitor fd pair (%d, %d)", __func__, fds[0], fds[1]);
121  /* The fd that the monitor process will write to */
122  state->tlsdate_monitor_fd = fds[1];
123  /* Make the reader fd non-blocking and not leak into tlsdate. */
124  if (fcntl (fds[0], F_SETFL, O_NONBLOCK|O_CLOEXEC) < 0)
125    {
126      perror ("pipe[0] fcntl(O_NONBLOCK) failed");
127      return 1;
128    }
129  state->events[E_TLSDATE_STATUS] = event_new (state->base, fds[0],
130                                    EV_READ,
131                                    action_tlsdate_status, state);
132  if (!state->events[E_TLSDATE_STATUS])
133    {
134      error ("Failed to allocate tlsdate status event");
135      return 1;
136    }
137  event_priority_set (state->events[E_TLSDATE_STATUS], PRI_NET);
138  state->events[E_TLSDATE_TIMEOUT] = event_new (state->base, -1,
139                                     EV_TIMEOUT,
140                                     action_tlsdate_timeout, state);
141  if (!state->events[E_TLSDATE_TIMEOUT])
142    {
143      error ("Failed to allocate tlsdate timeout event");
144      return 1;
145    }
146  event_priority_set (state->events[E_TLSDATE_TIMEOUT], PRI_SAVE);
147  return 0;
148}
149