]>
Commit | Line | Data |
---|---|---|
6644c1c7 MT |
1 | From f6e62e2af96f5fa0d1e3d93167a93a8f09bf6e61 Mon Sep 17 00:00:00 2001 |
2 | From: Simon Kelley <simon@thekelleys.org.uk> | |
3 | Date: Sun, 1 Mar 2015 18:17:54 +0000 | |
697b4f04 | 4 | Subject: [PATCH 054/113] 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 | ||
15 | diff --git a/CHANGELOG b/CHANGELOG | |
16 | index 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 | |
32 | diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 | |
33 | index 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 | |
49 | diff --git a/src/dnsmasq.c b/src/dnsmasq.c | |
50 | index 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 | ||
85 | diff --git a/src/dnsmasq.h b/src/dnsmasq.h | |
86 | index 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); | |
105 | diff --git a/src/dnssec.c b/src/dnssec.c | |
106 | index 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; | |
211 | diff --git a/src/option.c b/src/option.c | |
212 | index 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 | -- | |
251 | 2.1.0 | |
252 |