systime-fix: Use plugin features to register validator
[strongswan.git] / src / libcharon / plugins / systime_fix / systime_fix_plugin.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2013 Martin Willi
6 * Copyright (C) 2013 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "systime_fix_plugin.h"
20 #include "systime_fix_validator.h"
21
22 #include <daemon.h>
23 #include <processing/jobs/callback_job.h>
24 #include <processing/jobs/delete_ike_sa_job.h>
25 #include <processing/jobs/rekey_ike_sa_job.h>
26
27 #include <time.h>
28
29 /**
30 * Defining _XOPEN_SOURCE is difficult with libstrongswan includes,
31 * declare function explicitly.
32 */
33 char *strptime(const char *s, const char *format, struct tm *tm);
34
35 typedef struct private_systime_fix_plugin_t private_systime_fix_plugin_t;
36
37 /**
38 * Private data of systime_fix plugin
39 */
40 struct private_systime_fix_plugin_t {
41
42 /**
43 * Implements plugin interface
44 */
45 systime_fix_plugin_t public;
46
47 /**
48 * Certificate lifetime validator
49 */
50 systime_fix_validator_t *validator;
51
52 /**
53 * Interval we check for a now-valid system time, in seconds. 0 if disabled
54 */
55 u_int interval;
56
57 /**
58 * Timestamp where we start considering system time valid
59 */
60 time_t threshold;
61
62 /**
63 * Do we trigger reauth or delete when finding expired certificates?
64 */
65 bool reauth;
66 };
67
68 METHOD(plugin_t, get_name, char*,
69 private_systime_fix_plugin_t *this)
70 {
71 return "systime-fix";
72 }
73
74 /**
75 * Check if all certificates associated to an IKE_SA have valid lifetimes
76 */
77 static bool has_invalid_certs(ike_sa_t *ike_sa)
78 {
79 enumerator_t *cfgs, *items;
80 certificate_t *cert;
81 auth_rule_t type;
82 auth_cfg_t *auth;
83 time_t not_before, not_after;
84 bool valid = TRUE;
85
86 cfgs = ike_sa->create_auth_cfg_enumerator(ike_sa, FALSE);
87 while (valid && cfgs->enumerate(cfgs, &auth))
88 {
89 items = auth->create_enumerator(auth);
90 while (valid && items->enumerate(items, &type, &cert))
91 {
92 switch (type)
93 {
94 case AUTH_RULE_SUBJECT_CERT:
95 case AUTH_RULE_IM_CERT:
96 case AUTH_RULE_CA_CERT:
97 if (!cert->get_validity(cert, NULL, &not_before, &not_after))
98 {
99 DBG1(DBG_CFG, "certificate '%Y' invalid "
100 "(valid from %T to %T)", cert->get_subject(cert),
101 &not_before, FALSE, &not_after, FALSE);
102 valid = FALSE;
103 }
104 break;
105 default:
106 break;
107 }
108 }
109 items->destroy(items);
110 }
111 cfgs->destroy(cfgs);
112
113 if (valid)
114 {
115 DBG1(DBG_CFG, "all certificates have valid lifetimes");
116 }
117 return !valid;
118 }
119
120 /**
121 * Check system time, reevaluate certificates
122 */
123 static job_requeue_t check_systime(private_systime_fix_plugin_t *this)
124 {
125 enumerator_t *enumerator;
126 ike_sa_t *ike_sa;
127 char *action;
128 job_t *job;
129
130 if (time(NULL) < this->threshold)
131 {
132 DBG2(DBG_CFG, "systime not valid, rechecking in %ds", this->interval);
133 lib->scheduler->schedule_job(lib->scheduler, (job_t*)
134 callback_job_create((callback_job_cb_t)check_systime, this,
135 NULL, NULL), this->interval);
136 return JOB_REQUEUE_NONE;
137 }
138
139 DBG1(DBG_CFG, "system time got valid, rechecking certificates");
140
141 enumerator = charon->ike_sa_manager->create_enumerator(
142 charon->ike_sa_manager, TRUE);
143 while (enumerator->enumerate(enumerator, &ike_sa))
144 {
145 if (has_invalid_certs(ike_sa))
146 {
147 if (this->reauth)
148 {
149 action = "reauthenticating";
150 job = &rekey_ike_sa_job_create(ike_sa->get_id(ike_sa),
151 TRUE)->job_interface;
152 }
153 else
154 {
155 action = "deleting";
156 job = &delete_ike_sa_job_create(ike_sa->get_id(ike_sa),
157 TRUE)->job_interface;
158 }
159 DBG1(DBG_CFG, "%s[%d] has certificates not valid, %s IKE_SA",
160 ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa),
161 action);
162 lib->processor->queue_job(lib->processor, job);
163 }
164 }
165 enumerator->destroy(enumerator);
166
167 return JOB_REQUEUE_NONE;
168 }
169
170 /**
171 * Load cert lifetime validator configuration
172 */
173 static bool load_validator(private_systime_fix_plugin_t *this)
174 {
175 struct tm tm = {
176 .tm_mday = 1,
177 };
178 char *str, *fmt;
179
180 fmt = lib->settings->get_str(lib->settings,
181 "%s.plugins.%s.threshold_format", "%Y", charon->name, get_name(this));
182 str = lib->settings->get_str(lib->settings,
183 "%s.plugins.%s.threshold", NULL, charon->name, get_name(this));
184 if (!str)
185 {
186 DBG1(DBG_CFG, "no threshold configured for %s, disabled",
187 get_name(this));
188 return FALSE;
189 }
190 if (strptime(str, fmt, &tm) == NULL)
191 {
192 DBG1(DBG_CFG, "threshold for %s invalid, disabled", get_name(this));
193 return FALSE;
194 }
195 this->threshold = mktime(&tm);
196 if (this->threshold == -1)
197 {
198 DBG1(DBG_CFG, "converting threshold for %s failed, disabled",
199 get_name(this));
200 return FALSE;
201 }
202 if (time(NULL) >= this->threshold)
203 {
204 DBG1(DBG_CFG, "system time looks good, disabling %s", get_name(this));
205 return FALSE;
206 }
207
208 DBG1(DBG_CFG, "enabling %s, threshold: %s", get_name(this), asctime(&tm));
209 this->validator = systime_fix_validator_create(this->threshold);
210 return TRUE;
211 }
212
213 /**
214 * Load validator
215 */
216 static bool plugin_cb(private_systime_fix_plugin_t *this,
217 plugin_feature_t *feature, bool reg, void *cb_data)
218 {
219 if (reg)
220 {
221 if (!load_validator(this))
222 {
223 return FALSE;
224 }
225 lib->credmgr->add_validator(lib->credmgr, &this->validator->validator);
226 if (this->interval != 0)
227 {
228 DBG1(DBG_CFG, "starting systime check, interval: %ds",
229 this->interval);
230 lib->scheduler->schedule_job(lib->scheduler, (job_t*)
231 callback_job_create((callback_job_cb_t)check_systime,
232 this, NULL, NULL), this->interval);
233 }
234 }
235 else
236 {
237 lib->credmgr->remove_validator(lib->credmgr,
238 &this->validator->validator);
239 this->validator->destroy(this->validator);
240 }
241 return TRUE;
242 }
243
244 METHOD(plugin_t, get_features, int,
245 private_systime_fix_plugin_t *this, plugin_feature_t *features[])
246 {
247 static plugin_feature_t f[] = {
248 PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
249 PLUGIN_PROVIDE(CUSTOM, "systime-fix"),
250 };
251 *features = f;
252 return countof(f);
253 }
254
255 METHOD(plugin_t, destroy, void,
256 private_systime_fix_plugin_t *this)
257 {
258 free(this);
259 }
260
261 /**
262 * Plugin constructor
263 */
264 plugin_t *systime_fix_plugin_create()
265 {
266 private_systime_fix_plugin_t *this;
267
268 INIT(this,
269 .public = {
270 .plugin = {
271 .get_name = _get_name,
272 .get_features = _get_features,
273 .destroy = _destroy,
274 },
275 },
276 .interval = lib->settings->get_int(lib->settings,
277 "%s.plugins.%s.interval", 0, charon->name, get_name(this)),
278 .reauth = lib->settings->get_bool(lib->settings,
279 "%s.plugins.%s.reauth", FALSE, charon->name, get_name(this)),
280 );
281
282 return &this->public.plugin;
283 }