]> git.ipfire.org Git - ipfire-2.x.git/blame - src/patches/dnsmasq/0054-Add-dnssec-timestamp-option-and-facility.patch
dnsmasq: Import more upstream fixes
[ipfire-2.x.git] / src / patches / dnsmasq / 0054-Add-dnssec-timestamp-option-and-facility.patch
CommitLineData
6644c1c7
MT
1From f6e62e2af96f5fa0d1e3d93167a93a8f09bf6e61 Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Sun, 1 Mar 2015 18:17:54 +0000
5f206778 4Subject: [PATCH 54/87] Add --dnssec-timestamp option and facility.
6644c1c7
MT
5
6---
7 CHANGELOG | 6 +++++
8 man/dnsmasq.8 | 6 +++++
9 src/dnsmasq.c | 11 +++++++-
10 src/dnsmasq.h | 2 ++
11 src/dnssec.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
12 src/option.c | 7 +++++
13 6 files changed, 108 insertions(+), 6 deletions(-)
14
15diff --git a/CHANGELOG b/CHANGELOG
16index c80dc0fdbe9e..4f4fa305deaa 100644
17--- a/CHANGELOG
18+++ b/CHANGELOG
19@@ -66,6 +66,12 @@ version 2.73
20 for the patch.
21
22 Fix broken DNSSEC validation of ECDSA signatures.
23+
24+ Add --dnssec-timestamp option, which provides an automatic
25+ way to detect when the system time becomes valid after boot
26+ on systems without an RTC, whilst allowing DNS queries before the
27+ clock is valid so that NTP can run. Thanks to
28+ Kevin Darbyshire-Bryant for developing this idea.
29
30
31 version 2.72
32diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
33index 5cdd186afaa0..097e7d75145c 100644
34--- a/man/dnsmasq.8
35+++ b/man/dnsmasq.8
36@@ -674,6 +674,12 @@ that dnsmasq should be started with this flag when the platform determines that
37 reliable time is established, a SIGHUP should be sent to dnsmasq, which enables time checking, and purges the cache of DNS records
38 which have not been throughly checked.
39 .TP
40+.B --dnssec-timestamp=<path>
41+Enables an alternative way of checking the validity of the system time for DNSSEC (see --dnssec-no-timecheck). In this case, the
42+system time is considered to be valid once it becomes later than the timestamp on the specified file. The file is created and
43+its timestamp set automatically by dnsmasq. The file must be stored on a persistent filesystem, so that it and its mtime are carried
44+over system restarts.
45+.TP
46 .B --proxy-dnssec
47 Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
48 alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between
49diff --git a/src/dnsmasq.c b/src/dnsmasq.c
50index e6dabbf556f7..769a19afe6c5 100644
51--- a/src/dnsmasq.c
52+++ b/src/dnsmasq.c
53@@ -58,6 +58,9 @@ int main (int argc, char **argv)
54 struct dhcp_context *context;
55 struct dhcp_relay *relay;
56 #endif
57+#ifdef HAVE_DNSSEC
58+ int badtime;
59+#endif
60
61 #ifdef LOCALEDIR
62 setlocale(LC_ALL, "");
63@@ -369,7 +372,11 @@ int main (int argc, char **argv)
64
65 if (baduser)
66 die(_("unknown user or group: %s"), baduser, EC_BADCONF);
67-
68+
69+#ifdef HAVE_DNSSEC
70+ badtime = setup_timestamp(ent_pw->pw_uid);
71+#endif
72+
73 /* implement group defaults, "dip" if available, or group associated with uid */
74 if (!daemon->group_set && !gp)
75 {
76@@ -689,6 +696,8 @@ int main (int argc, char **argv)
77 my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
78 if (option_bool(OPT_DNSSEC_TIME))
79 my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload"));
80+ if (badtime)
81+ my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
82 }
83 #endif
84
85diff --git a/src/dnsmasq.h b/src/dnsmasq.h
86index 89e758b56a0a..b2f02dda63f0 100644
87--- a/src/dnsmasq.h
88+++ b/src/dnsmasq.h
89@@ -986,6 +986,7 @@ extern struct daemon {
90 #endif
91 #ifdef HAVE_DNSSEC
92 struct ds_config *ds;
93+ char *timestamp_file;
94 #endif
95
96 /* globally used stuff for DNS */
97@@ -1151,6 +1152,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
98 int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
99 size_t filter_rrsigs(struct dns_header *header, size_t plen);
100 unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
101+int setup_timestamp(uid_t uid);
102
103 /* util.c */
104 void rand_init(void);
105diff --git a/src/dnssec.c b/src/dnssec.c
106index 26932373cd3e..bf4406469de0 100644
107--- a/src/dnssec.c
108+++ b/src/dnssec.c
109@@ -34,6 +34,7 @@
110 #include <nettle/dsa-compat.h>
111 #endif
112
113+#include <utime.h>
114
115 #define SERIAL_UNDEF -100
116 #define SERIAL_EQ 0
117@@ -394,17 +395,88 @@ static int serial_compare_32(unsigned long s1, unsigned long s2)
118 return SERIAL_UNDEF;
119 }
120
121+/* Called at startup. If the timestamp file is configured and exists, put its mtime on
122+ timestamp_time. If it doesn't exist, create it, and set the mtime to 1-1-2015.
123+ Change the ownership to the user we'll be running as, so that we can update the mtime.
124+*/
125+static time_t timestamp_time;
126+static int back_to_the_future;
127+
128+int setup_timestamp(uid_t uid)
129+{
130+ struct stat statbuf;
131+
132+ back_to_the_future = 0;
133+
134+ if (!option_bool(OPT_DNSSEC_VALID) || !daemon->timestamp_file)
135+ return 0;
136+
137+ if (stat(daemon->timestamp_file, &statbuf) != -1)
138+ {
139+ timestamp_time = statbuf.st_mtime;
140+ check_and_exit:
141+ if (difftime(timestamp_time, time(0)) <= 0)
142+ {
143+ /* time already OK, update timestamp, and do key checking from the start. */
144+ if (utime(daemon->timestamp_file, NULL) == -1)
145+ my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
146+ back_to_the_future = 1;
147+ return 0;
148+ }
149+ return 1;
150+ }
151+
152+ if (errno == ENOENT)
153+ {
154+ int fd = open(daemon->timestamp_file, O_WRONLY | O_CREAT | O_NONBLOCK, 0666);
155+ if (fd != -1)
156+ {
157+ struct utimbuf timbuf;
158+
159+ close(fd);
160+
161+ timestamp_time = timbuf.actime = timbuf.modtime = 1420070400; /* 1-1-2015 */
162+ if (utime(daemon->timestamp_file, &timbuf) == 0 &&
163+ (getuid() != 0 || chown(daemon->timestamp_file, uid, -1) == 0))
164+ goto check_and_exit;
165+ }
166+ }
167+
168+ die(_("Cannot create timestamp file %s: %s" ), daemon->timestamp_file, EC_BADCONF);
169+ return 0;
170+}
171+
172 /* Check whether today/now is between date_start and date_end */
173 static int check_date_range(unsigned long date_start, unsigned long date_end)
174 {
175- unsigned long curtime;
176-
177+ unsigned long curtime = time(0);
178+
179 /* Checking timestamps may be temporarily disabled */
180- if (option_bool(OPT_DNSSEC_TIME))
181+
182+ /* If the current time if _before_ the timestamp
183+ on our persistent timestamp file, then assume the
184+ time if not yet correct, and don't check the
185+ key timestamps. As soon as the current time is
186+ later then the timestamp, update the timestamp
187+ and start checking keys */
188+ if (daemon->timestamp_file)
189+ {
190+ if (back_to_the_future == 0 && difftime(timestamp_time, curtime) <= 0)
191+ {
192+ if (utime(daemon->timestamp_file, NULL) != 0)
193+ my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
194+
195+ back_to_the_future = 1;
196+ set_option_bool(OPT_DNSSEC_TIME);
197+ queue_event(EVENT_RELOAD); /* purge cache */
198+ }
199+
200+ if (back_to_the_future == 0)
201+ return 1;
202+ }
203+ else if (option_bool(OPT_DNSSEC_TIME))
204 return 1;
205
206- curtime = time(0);
207-
208 /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
209 return serial_compare_32(curtime, date_start) == SERIAL_GT
210 && serial_compare_32(curtime, date_end) == SERIAL_LT;
211diff --git a/src/option.c b/src/option.c
212index ae0ad002d8b8..eace40bb566c 100644
213--- a/src/option.c
214+++ b/src/option.c
215@@ -152,6 +152,7 @@ struct myoption {
216 #define LOPT_DHCP_INOTIFY 340
217 #define LOPT_DHOPT_INOTIFY 341
218 #define LOPT_HOST_INOTIFY 342
219+#define LOPT_DNSSEC_STAMP 343
220
221 #ifdef HAVE_GETOPT_LONG
222 static const struct option opts[] =
223@@ -300,6 +301,7 @@ static const struct myoption opts[] =
224 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
225 { "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
226 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
227+ { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
228 #ifdef OPTION6_PREFIX_CLASS
229 { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
230 #endif
231@@ -463,6 +465,7 @@ static struct {
232 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
233 { LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
234 { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
235+ { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
236 #ifdef OPTION6_PREFIX_CLASS
237 { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
238 #endif
239@@ -3867,6 +3870,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
240 }
241
242 #ifdef HAVE_DNSSEC
243+ case LOPT_DNSSEC_STAMP:
244+ daemon->timestamp_file = opt_string_alloc(arg);
245+ break;
246+
247 case LOPT_TRUST_ANCHOR:
248 {
249 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
250--
2512.1.0
252