OpenDNSSEC-enforcer  1.4.5
ksm_update.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2008-2009 Nominet UK. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
19  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
21  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26 
27 /*+
28  * ksm_update.c - Update Times
29  *
30  * Description:
31  * Given a set of zones, this module updates all the estimated times in
32  * the keys associated with the zone.
33  *
34  * The estimated times are updated using the current state of the key and
35  * the actual time the key entered that state. The key is updated using
36  * the values of the various parameters.
37  *
38  * SO FAR, THIS ONLY APPLIES TO ZSKS
39 -*/
40 
41 #include <stdio.h>
42 #include <limits.h>
43 
44 #include "ksm/database.h"
45 #include "ksm/db_fields.h"
46 #include "ksm/debug.h"
47 #include "ksm/ksm.h"
48 #include "ksm/kmedef.h"
49 #include "ksm/ksmdef.h"
50 #include "ksm/message.h"
51 
52 #define MAX(a, b) ((a) > (b) ? (a) : (b))
53 #define MIN(a, b) ((a) < (b) ? (a) : (b))
54 
55 
56 /*+
57  * KsmUpdate - Update Times for Keys
58  *
59  * Description:
60  * Obtains the times for the specified zone and process each key in it.
61  *
62  * Arguments:
63  * None.
64  *
65  * Returns:
66  * int
67  * Always 0.
68 -*/
69 
70 int KsmUpdate(int policy_id, int zone_id)
71 {
72  KSM_PARCOLL collection; /* Collection of parameters for zone */
73  KSM_KEYDATA data; /* Data about the key */
74  DB_RESULT result; /* For iterating through keys */
75  int status = 0; /* Status return */
76  DQS_QUERY_CONDITION condition[2]; /* Condition codes */
77 
78  /* Set collection defaults */
79  KsmCollectionInit(&collection);
80 
81  /* Get the values of the parameters */
82  status = KsmParameterCollection(&collection, policy_id);
83  if (status == 0) {
84 
85  /*
86  * Iterate round, updating each key. As always, an error causes a
87  * message to be output, so we don't need to handle error conditions.
88  * Abandon updates if the update of a single key fails.
89  */
90 
91  /* zone_id of -1 means all zones */
92  if (zone_id == -1) {
93  status = KsmKeyInit(&result, NULL);
94  }
95  else {
96  condition[0].code = DB_KEYDATA_ZONE_ID;
97  condition[0].data.number = zone_id;
98  condition[0].compare = DQS_COMPARE_EQ;
99 
100  condition[1].compare = DQS_END_OF_LIST;
101 
102  status = KsmKeyInit(&result, condition);
103  }
104 
105  if (status == 0) {
106  /* Transaction handling is one level up (in KsmRequestKeys) */
107  status = KsmKey(result, &data);
108  while (status == 0) {
109  (void) KsmUpdateKey(&data, &collection, zone_id);
110  status = KsmKey(result, &data);
111  }
112  (void) KsmKeyEnd(result);
113 
114  /* Change end of list status to a success */
115 
116  if (status == -1) {
117  status = 0;
118  }
119  }
120  }
121  /*
122  * else {
123  * Unable to get parameter collection information. If we can't do
124  * this, something must be seriously wrong.
125  * }
126  */
127 
128  return status;
129 }
130 
131 
132 /*+
133  * KsmUpdateKey - Update Key Times
134  *
135  * Description:
136  * Updates the estimated times in a key based on the current state and the
137  * parameters.
138  *
139  * Arguments:
140  * KSM_KEYDATA* data
141  * Key to update.
142  *
143  * KSM_PARCOLL* collection
144  * Parameter collection.
145  *
146  * int zone_id
147  * zone we are looking at
148 -*/
149 
150 void KsmUpdateKey(KSM_KEYDATA* data, KSM_PARCOLL* collection, int zone_id)
151 {
152  /* check the argument */
153  if (data == NULL) {
154  MsgLog(KSM_INVARG, "NULL data");
155  return;
156  }
157 
158  switch (data->state) {
159  case KSM_STATE_GENERATE:
161  break;
162 
163  case KSM_STATE_PUBLISH:
164  KsmUpdatePublishKeyTime(data, collection, zone_id);
165  break;
166 
167  case KSM_STATE_READY:
168  KsmUpdateReadyKeyTime(data);
169  break;
170 
171  case KSM_STATE_ACTIVE:
172  KsmUpdateActiveKeyTime(data, collection, zone_id);
173  break;
174 
175  case KSM_STATE_RETIRE:
176  KsmUpdateRetireKeyTime(data, collection, zone_id);
177  break;
178 
179  case KSM_STATE_DEAD:
180  KsmUpdateDeadKeyTime(data);
181  break;
182 
183  case KSM_STATE_DSSUB:
184  /* Do nothing, wait for ds-seen before moving to DSPUBLISH */
185  break;
186 
187  case KSM_STATE_DSPUBLISH:
188  KsmUpdateDSPublishKeyTime(data, collection, zone_id);
189  break;
190 
191  case KSM_STATE_DSREADY:
192  /* Do nothing, hold the standby key in this state */
193  break;
194 
196  KsmUpdateKEYPublishKeyTime(data, collection, zone_id);
197  break;
198  default:
199 
200  /* Should not have a key in an unknown state */
201 
202  MsgLog(KME_UNRKEYSTA, (int) data->keypair_id, data->state);
203  }
204 
205  return;
206 }
207 
208 
209 /*+
210  * KsmUpdateXxxxKeyTime - Update Key Time for Key In Xxxx State
211  *
212  * Description:
213  * Handles the update of a key in the specified state.
214  *
215  * Arguments:id
216  * KSM_KEYDATA* data
217  * Key to update.
218  *
219 -*/
220 
222 {
223  /*
224  * Keys in the generated state don't automatically change their state -
225  * they wait until a request is made to publish them.
226  */
227 
228  /* check the argument */
229  if (data == NULL) {
230  MsgLog(KSM_INVARG, "NULL data");
231  return;
232  }
233  DbgOutput(DBG_M_UPDATE, "Key ID %d in state 'generate' - not updated\n",
234  (int) data->keypair_id);
235 
236  return;
237 }
238 
239 void KsmUpdatePublishKeyTime(KSM_KEYDATA* data, KSM_PARCOLL* collection, int zone_id)
240 {
241  int deltat = 0; /* Time interval */
242  int Ipc; /* Child zone publication interval */
243 
244  /* check the argument */
245  if (data == NULL || collection == NULL) {
246  MsgLog(KSM_INVARG, "NULL argument");
247  return;
248  }
249  DbgOutput(DBG_M_UPDATE, "Key ID %d in state 'publish' - updating\n",
250  (int) data->keypair_id);
251 
252  Ipc = collection->zskttl +
253  collection->propdelay + collection->pub_safety;
254  if (data->keytype == KSM_TYPE_ZSK) {
255  /*
256  * A key in the "publish" state moves into the "ready" state when it has
257  * been published for at least:
258  *
259  * Ipc = TTLkeyc + Dpc +Sp
260  *
261  * ... where:
262  *
263  * TTLkeyc = TTL of the ZSK DNSKEY record
264  * Dpc = Propagation delay
265  * Sp = Publish Safety Margin
266  *
267  */
268 
269  deltat = Ipc;
270  }
271  else if (data->keytype == KSM_TYPE_KSK) {
272  /*
273  * A key in the "publish" state moves into the "ready" state when it has
274  * been published for either:
275  *
276  * Ipc or Ipp, depending on the rollover scheme
277  * where
278  * Ipp = TTLdsp + Dpp + Dr +Sp
279  *
280  * ... where:
281  *
282  * TTLdsp = TTL of the DS record in the parent
283  * Dpp = Propagation delay
284  * Dr = Registration delay (Currently unused)
285  * Sp = Publish Safety Margin
286  *
287  */
288  if (collection->kskroll == KSM_ROLL_DNSKEY) {
289  deltat = Ipc;
290  }
291  else if (collection->kskroll == KSM_ROLL_DS) {
292  deltat = collection->kskttl + collection->kskpropdelay +
293  collection->pub_safety; /* Ipp */
294  }
295  }
296  else {
297  return;
298  }
299 
300  (void) KsmUpdateKeyTime(data, "PUBLISH", "READY", deltat, zone_id);
301 
302  return;
303 }
304 
306 {
307  /*
308  * Keys in the ready state don't automatically move into the active state.
309  * They need to be explicitly activated.
310  */
311 
312  /* check the argument */
313  if (data == NULL) {
314  MsgLog(KSM_INVARG, "NULL data");
315  return;
316  }
317  DbgOutput(DBG_M_UPDATE, "Key ID %d in state 'ready' - not updated\n",
318  (int) data->keypair_id);
319 
320  return;
321 }
322 
323 void KsmUpdateActiveKeyTime(KSM_KEYDATA* data, KSM_PARCOLL* collection, int zone_id)
324 {
325  int deltat; /* Time interval */
326 
327  /* check the argument */
328  if (data == NULL || collection == NULL) {
329  MsgLog(KSM_INVARG, "NULL argument");
330  return;
331  }
332  DbgOutput(DBG_M_UPDATE, "Key ID %d in state 'active' - updating\n",
333  (int) data->keypair_id);
334 
335  /*
336  * A key in the "active" state moves into the "retire" state when it has
337  * been active for at least:
338  *
339  * Lz
340  *
341  * ... where
342  *
343  * Lz = Life time of a ZSK (i.e. how long it is used for)
344  */
345 
346  if (data->keytype == KSM_TYPE_ZSK) {
347  deltat = collection->zsklife;
348  }
349  else if (data->keytype == KSM_TYPE_KSK) {
350  deltat = collection->ksklife;
351  }
352  else {
353  return;
354  }
355 
356  /* "Infinite" lifetime */
357  if (deltat == 0) {
358  deltat = INT_MAX -1;
359  }
360 
361  /*
362  * Update the retire time if the key is not marked as fixedDate.
363  * If we asked for a rollover, but no keys were ready then a compromised key
364  * may still be active.
365  */
366  if (!data->fixedDate) {
367  (void) KsmUpdateKeyTime(data, "ACTIVE", "RETIRE", deltat, zone_id);
368  }
369 
370  return;
371 }
372 
373 void KsmUpdateRetireKeyTime(KSM_KEYDATA* data, KSM_PARCOLL* collection, int zone_id)
374 {
375  int deltat = 0; /* Time interval */
376 
377  /* check the argument */
378  if (data == NULL || collection == NULL) {
379  MsgLog(KSM_INVARG, "NULL argument");
380  return;
381  }
382  DbgOutput(DBG_M_UPDATE, "Key ID %d in state 'retire' - updating\n",
383  (int) data->keypair_id);
384 
385  /*
386  * A key in the "retire" state moves into the "dead" state after a period
387  * of:
388  *
389  * TTLsig + Dp + St
390  *
391  * ... where
392  *
393  * TTLsig = Signature lifetime (how long a signature is valid for)
394  * Dp = Propagation delay
395  * St = Retire safety margin
396  */
397 
398  if (data->keytype == KSM_TYPE_ZSK) {
399  deltat = collection->zsksiglife + collection->propdelay + collection->ret_safety;
400  }
401  else if (data->keytype == KSM_TYPE_KSK) {
402  /*
403  * for a KSK this can be 0 (from the timings draft); are we happy with that?
404  * Might revisit this in the future as it might be a surprise for people
405  *
406  * Change of heart... make this as large as we can so that keys stay retired
407  * until some manual process tells us that its DS record has been removed.
408  *
409  * second change of heart:
410  * Don't do anything here, this time is set when the ksk-roll command is issued.
411  *
412  * Third change
413  */
414  deltat = collection->dsttl + collection->kskpropdelay +
415  collection->ret_safety; /* Ipp */
416  }
417  else {
418  return;
419  }
420 
421  (void) KsmUpdateKeyTime(data, "RETIRE", "DEAD", deltat, zone_id);
422 
423  return;
424 }
425 
427 {
428  /*
429  * Keys in the dead state don't automatically change their state - they
430  * are retained in the database for historical reasons or until they are
431  * explicitly deleted.
432  */
433 
434  /* check the argument */
435  if (data == NULL) {
436  MsgLog(KSM_INVARG, "NULL data");
437  return;
438  }
439  DbgOutput(DBG_M_UPDATE, "Key ID %d in state 'dead' - not updated\n",
440  (int) data->keypair_id);
441 
442  return;
443 }
444 
445 void KsmUpdateDSPublishKeyTime(KSM_KEYDATA* data, KSM_PARCOLL* collection, int zone_id)
446 {
447  int deltat = 0; /* Time interval */
448 
449  /* check the argument */
450  if (data == NULL || collection == NULL) {
451  MsgLog(KSM_INVARG, "NULL argument");
452  return;
453  }
454  DbgOutput(DBG_M_UPDATE, "Key ID %d in state 'publish' - updating\n",
455  (int) data->keypair_id);
456 
457  if (data->keytype == KSM_TYPE_ZSK) {
458  /*
459  * This state should only be used by KSKs
460  */
461 
462  return;
463  }
464  else if (data->keytype == KSM_TYPE_KSK) {
465  /*
466  * A key in the "dspublish" state moves into the "dsready" state when it has
467  * been published for either:
468  *
469  * Ipp = TTLdsp + Dpp + Dr +Sp
470  *
471  * ... where:
472  *
473  * TTLdsp = TTL of the DS record in the parent
474  * Dpp = Propagation delay
475  * Dr = Registration delay (Currently unused)
476  * Sp = Publish Safety Margin
477  *
478  */
479  deltat = collection->kskttl + collection->kskpropdelay +
480  collection->pub_safety;
481  }
482  else {
483  return;
484  }
485 
486  (void) KsmUpdateKeyTime(data, "PUBLISH", "READY", deltat, zone_id);
487 
488  return;
489 }
490 
491 void KsmUpdateKEYPublishKeyTime(KSM_KEYDATA* data, KSM_PARCOLL* collection, int zone_id)
492 {
493  int deltat = 0; /* Time interval */
494 
495  /* check the argument */
496  if (data == NULL || collection == NULL) {
497  MsgLog(KSM_INVARG, "NULL argument");
498  return;
499  }
500  DbgOutput(DBG_M_UPDATE, "Key ID %d in state 'KEYpublish' - updating\n",
501  (int) data->keypair_id);
502 
503  /*
504  * A key in the "KEYpublish" state moves into the "active" state when it has
505  * been published for at least:
506  *
507  * Ipc = TTLkeyc + Dpc +Sp
508  *
509  * ... where:
510  *
511  * TTLkeyc = TTL of the ZSK DNSKEY record
512  * Dpc = Propagation delay
513  * Sp = Publish Safety Margin
514  *
515  */
516  deltat = collection->zskttl +
517  collection->propdelay + collection->pub_safety;
518 
519  (void) KsmUpdateKeyTime(data, "PUBLISH", "ACTIVE", deltat, zone_id);
520 
521  return;
522 }
523 
524 /*+
525  * KsmUpdateKeyTime - Update Key Time
526  *
527  * Description:
528  * Actually performs the update of the database. The update is
529  *
530  * destination_time = source_time + interval
531  *
532  * Arguments:
533  * const KSM_KEYDATA* data
534  * Data about the key to be updated. Note that this is NOT updated
535  * by the update.
536  *
537  * const char* source
538  * Source field.
539  *
540  * const char* destination
541  * Source field.
542  *
543  * int interval
544  * Interval (seconds) to update the source field with.
545  *
546  * int zone_id
547  * zone we are looking at
548  *
549  * Returns:
550  * int
551  * 0 Update successful
552  * Other Error. A message will have beeen output.
553 -*/
554 
555 int KsmUpdateKeyTime(const KSM_KEYDATA* data, const char* source,
556  const char* destination, int interval, int zone_id)
557 {
558  char buffer[KSM_SQL_SIZE]; /* Long enough for any statement */
559  unsigned int nchar; /* Number of characters converted */
560  int status; /* Status return */
561 
562  /* check the argument */
563  if (data == NULL || source == NULL || destination == NULL) {
564  return MsgLog(KSM_INVARG, "NULL argument");
565  }
566 
567 #ifdef USE_MYSQL
568  nchar = snprintf(buffer, sizeof(buffer),
569  "UPDATE dnsseckeys SET %s = DATE_ADD(%s, INTERVAL %d SECOND) WHERE KEYPAIR_ID = %lu and zone_id = %d",
570  destination, source, interval, (unsigned long) data->keypair_id, zone_id);
571 #else
572  nchar = snprintf(buffer, sizeof(buffer),
573  "UPDATE dnsseckeys SET %s = DATETIME(%s, '+%d SECONDS') WHERE KEYPAIR_ID = %lu and zone_id = %d",
574  destination, source, interval, (unsigned long) data->keypair_id, zone_id);
575 #endif /* USE_MYSQL */
576 
577  if (nchar < sizeof(buffer)) {
578 
579  /* All OK, execute the statement */
580 
581  status = DbExecuteSqlNoResult(DbHandle(), buffer);
582  }
583  else {
584 
585  /* Unable to create update statement */
586 
587  status = MsgLog(KME_BUFFEROVF, "KsmUpdateKeyTime");
588  }
589 
590  return status;
591 }