OpenDNSSEC-enforcer  1.4.8.2
kc_helper.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 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 #define _GNU_SOURCE
27 #include <syslog.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <errno.h>
33 #include <pwd.h>
34 #include <grp.h>
35 #include <limits.h>
36 #include <ctype.h>
37 
38 #include <libxml/tree.h>
39 #include <libxml/parser.h>
40 #include <libxml/xpath.h>
41 #include <libxml/xpathInternals.h>
42 #include <libxml/relaxng.h>
43 
44 #include "kc_helper.h"
45 
46 extern int verbose;
47 
48 #define StrFree(ptr) {if(ptr != NULL) {free(ptr); (ptr) = NULL;}}
49 
50 void log_init(int facility, const char *program_name)
51 {
52  openlog(program_name, 0, facility);
53 }
54 
55 /* Switch log to new facility */
56 void log_switch(int facility, const char *program_name)
57 {
58  closelog();
59  openlog(program_name, 0, facility);
60 }
61 
62 /* As far as possible we send messages both to syslog and STDOUT */
63 void dual_log(const char *format, ...) {
64 
65  /* If the variable arg list is bad then random errors can occur */
66  va_list args;
67  va_list args2;
68  va_start(args, format);
69  va_copy(args2, args);
70 
71  if (strncmp(format, "ERROR:", 6) == 0) {
72  vsyslog(LOG_ERR, format, args);
73  }
74  else if (strncmp(format, "WARNING:", 8) == 0) {
75  vsyslog(LOG_WARNING, format, args);
76  }
77  else if (strncmp(format, "DEBUG:", 6) == 0) {
78  vsyslog(LOG_DEBUG, format, args);
79  }
80  else {
81  vsyslog(LOG_INFO, format, args);
82  }
83 
84  vprintf(format, args2);
85  printf("\n");
86 
87  va_end(args);
88  va_end(args2);
89 }
90 
91 /* Check an XML file against its rng */
92 int check_rng(const char *filename, const char *rngfilename) {
93 
94  xmlDocPtr doc = NULL;
95  xmlDocPtr rngdoc = NULL;
96  xmlRelaxNGParserCtxtPtr rngpctx = NULL;
97  xmlRelaxNGValidCtxtPtr rngctx = NULL;
98  xmlRelaxNGPtr schema = NULL;
99 
100  if (verbose) {
101  dual_log("DEBUG: About to check XML validity in %s", filename);
102  }
103 
104  /* Load XML document */
105  doc = xmlParseFile(filename);
106  if (doc == NULL) {
107  dual_log("ERROR: unable to parse file \"%s\"", filename);
108  /* Maybe the file doesn't exist? */
109  check_file(filename, "Configuration file");
110 
111  return(1);
112  }
113 
114  /* Load rng document */
115  rngdoc = xmlParseFile(rngfilename);
116  if (rngdoc == NULL) {
117  dual_log("ERROR: unable to parse file \"%s\"", rngfilename);
118  /* Maybe the file doesn't exist? */
119  check_file(rngfilename, "RNG file");
120 
121  xmlFreeDoc(doc);
122 
123  return(1);
124  }
125 
126  /* Create an XML RelaxNGs parser context for the relax-ng document. */
127  rngpctx = xmlRelaxNGNewDocParserCtxt(rngdoc);
128  if (rngpctx == NULL) {
129  dual_log("ERROR: unable to create XML RelaxNGs parser context");
130 
131  xmlFreeDoc(doc);
132  xmlFreeDoc(rngdoc);
133 
134  return(1);
135  }
136 
137  xmlRelaxNGSetParserErrors(rngpctx,
138  (xmlRelaxNGValidityErrorFunc) fprintf,
139  (xmlRelaxNGValidityWarningFunc) fprintf,
140  stderr);
141 
142  /* parse a schema definition resource and build an internal XML Shema struture which can be used to validate instances. */
143  schema = xmlRelaxNGParse(rngpctx);
144  if (schema == NULL) {
145  dual_log("ERROR: unable to parse a schema definition resource");
146 
147  xmlRelaxNGFreeParserCtxt(rngpctx);
148  xmlFreeDoc(doc);
149  xmlFreeDoc(rngdoc);
150 
151  return(1);
152  }
153 
154  /* Create an XML RelaxNGs validation context based on the given schema */
155  rngctx = xmlRelaxNGNewValidCtxt(schema);
156  if (rngctx == NULL) {
157  dual_log("ERROR: unable to create RelaxNGs validation context based on the schema");
158 
159  xmlRelaxNGFree(schema);
160  xmlRelaxNGFreeParserCtxt(rngpctx);
161  xmlFreeDoc(doc);
162  xmlFreeDoc(rngdoc);
163 
164  return(1);
165  }
166 
167  xmlRelaxNGSetValidErrors(rngctx,
168  (xmlRelaxNGValidityErrorFunc) fprintf,
169  (xmlRelaxNGValidityWarningFunc) fprintf,
170  stderr);
171 
172  /* Validate a document tree in memory. */
173  if (xmlRelaxNGValidateDoc(rngctx,doc) != 0) {
174  dual_log("ERROR: %s fails to validate", filename);
175 
176  xmlRelaxNGFreeValidCtxt(rngctx);
177  xmlRelaxNGFree(schema);
178  xmlRelaxNGFreeParserCtxt(rngpctx);
179  xmlFreeDoc(doc);
180  xmlFreeDoc(rngdoc);
181 
182  return(1);
183  }
184 
185  xmlRelaxNGFreeValidCtxt(rngctx);
186  xmlRelaxNGFree(schema);
187  xmlRelaxNGFreeParserCtxt(rngpctx);
188  xmlFreeDoc(doc);
189  xmlFreeDoc(rngdoc);
190 
191  return 0;
192 }
193 
194 int check_file(const char *filename, const char *log_string) {
195  struct stat stat_ret;
196 
197  if (stat(filename, &stat_ret) != 0) {
198 
199  if (errno != ENOENT) {
200  dual_log("ERROR: cannot stat file %s: %s",
201  filename, strerror(errno));
202  return 1;
203  }
204 
205  dual_log("ERROR: %s (%s) does not exist", log_string, filename);
206  return 1;
207  }
208 
209  if (S_ISREG(stat_ret.st_mode)) {
210  /* The file exists */
211  return 0;
212  }
213 
214  dual_log("ERROR: %s (%s) does not exist", log_string, filename);
215  return 1;
216 }
217 
218 int check_file_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *file_xexpr) {
219  int status = 0;
220  xmlXPathObjectPtr xpath_obj;
221  char* temp_char = NULL;
222  char* str = NULL;
223 
224  xpath_obj = xmlXPathEvalExpression(file_xexpr, xpath_ctx);
225  if(xpath_obj == NULL) {
226  dual_log("ERROR: unable to evaluate xpath expression: %s", file_xexpr);
227  return 1;
228  }
229  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
230  temp_char = (char*) xmlXPathCastToString(xpath_obj);
231 
232  /* strip off any trailing characters (needed for DSSub with cks_id) */
233  str = strrchr(temp_char, ' ');
234  if (str) {
235  *str = 0;
236  }
237 
238  status = check_file(temp_char, log_string);
239 
240  StrFree(temp_char);
241  } else {
242  /* Not set; return -1 so that we can test the default path */
243  xmlXPathFreeObject(xpath_obj);
244  return -1;
245  }
246 
247  xmlXPathFreeObject(xpath_obj);
248  return status;
249 }
250 
251 int check_path(const char *pathname, const char *log_string) {
252  struct stat stat_ret;
253 
254  if (stat(pathname, &stat_ret) != 0) {
255  if (errno != ENOENT) {
256  dual_log("ERROR: cannot stat directory %s: %s",
257  pathname, strerror(errno));
258  return 1;
259  }
260 
261  dual_log("ERROR: %s (%s) does not exist", log_string, pathname);
262  return 1;
263  }
264 
265  if (S_ISDIR(stat_ret.st_mode)) {
266  /* The directory exists */
267  return 0;
268  }
269 
270  dual_log("ERROR: %s (%s) is not a directory", log_string, pathname);
271  return 1;
272 }
273 
274 int check_path_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *path_xexpr) {
275  int status = 0;
276  xmlXPathObjectPtr xpath_obj;
277  char* temp_char = NULL;
278 
279  xpath_obj = xmlXPathEvalExpression(path_xexpr, xpath_ctx);
280  if(xpath_obj == NULL) {
281  dual_log("ERROR: unable to evaluate xpath expression: %s", path_xexpr);
282  return 1;
283  }
284  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
285  temp_char = (char*) xmlXPathCastToString(xpath_obj);
286 
287  status = check_path(temp_char, log_string);
288 
289  StrFree(temp_char);
290  } else {
291  /* Not set; return -1 so that we can test the default path */
292  xmlXPathFreeObject(xpath_obj);
293  return -1;
294  }
295 
296  xmlXPathFreeObject(xpath_obj);
297  return status;
298 }
299 
300 int check_user_group(xmlXPathContextPtr xpath_ctx, const xmlChar *user_xexpr, const xmlChar *group_xexpr) {
301  int status = 0;
302  xmlXPathObjectPtr xpath_obj;
303  char* temp_char = NULL;
304 
305  struct passwd *pwd;
306  struct group *grp;
307 
308  /* Group if specified */
309  xpath_obj = xmlXPathEvalExpression(group_xexpr, xpath_ctx);
310  if(xpath_obj == NULL) {
311  dual_log("ERROR: unable to evaluate xpath expression: %s", group_xexpr);
312  return(1);
313  }
314  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
315  temp_char = (char*) xmlXPathCastToString(xpath_obj);
316 
317  if ((grp = getgrnam(temp_char)) == NULL) {
318  dual_log("ERROR: Group '%s' does not exist", temp_char);
319  status += 1;
320  }
321  endgrent();
322 
323  StrFree(temp_char);
324  }
325  xmlXPathFreeObject(xpath_obj);
326 
327  /* User if specified */
328  xpath_obj = xmlXPathEvalExpression(user_xexpr, xpath_ctx);
329  if(xpath_obj == NULL) {
330  dual_log("ERROR: unable to evaluate xpath expression: %s", user_xexpr);
331  return(1);
332  }
333  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
334  temp_char = (char*) xmlXPathCastToString(xpath_obj);
335 
336  if ((pwd = getpwnam(temp_char)) == NULL) {
337  dual_log("ERROR: User '%s' does not exist", temp_char);
338  status += 1;
339  }
340  endpwent();
341 
342  StrFree(temp_char);
343  }
344 
345  xmlXPathFreeObject(xpath_obj);
346 
347  return status;
348 }
349 
350 int check_time_def(const char *time_expr, const char *location, const char *field, const char *filename, int* interval) {
351 
352  int status = DtXMLIntervalSeconds(time_expr, interval);
353 
354  if (status != 0) {
355  switch (status) {
356  case -1:
357  dual_log("WARNING: In %s M used in duration field for %s (%s) in %s - this will be interpreted as 31 days", location, field, time_expr, filename);
358  break;
359  case -2:
360  dual_log("WARNING: In %s Y used in duration field for %s (%s) in %s - this will be interpreted as 365 days", location, field, time_expr, filename);
361  break;
362  case -3:
363  dual_log("WARNING: In %s M & Y used in duration field for %s (%s) in %s - these will be interpreted as 31 and 365 days respectively", location, field, time_expr, filename);
364  break;
365  case 2:
366  dual_log("ERROR: unable to translate %s (%s) to seconds.", field, time_expr);
367  break;
368  case 3:
369  dual_log("ERROR: %s (%s) too long to be an int. E.g. Maximum is ~68 years on a system with 32-bit integers.", field, time_expr);
370  break;
371  case 4:
372  dual_log("ERROR: invalid pointers or text string NULL in %s (%s).", field, time_expr);
373  break;
374  default:
375  dual_log("ERROR: unknown error converting %s (%s) to seconds", field, time_expr);
376  }
377  }
378 
379  if (status > 0) {
380  *interval = 0;
381  return 1;
382  }
383 
384  return 0;
385 }
386 
387 int check_time_def_from_xpath(xmlXPathContextPtr xpath_ctx, const xmlChar *time_xexpr, const char *location, const char *field, const char *filename) {
388 
389  xmlXPathObjectPtr xpath_obj;
390  char* temp_char = NULL;
391  int status = 0;
392  int ignore = 0;
393 
394  xpath_obj = xmlXPathEvalExpression(time_xexpr, xpath_ctx);
395  if(xpath_obj == NULL) {
396  dual_log("ERROR: unable to evaluate xpath expression: %s", time_xexpr);
397  return 1;
398  }
399  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
400  temp_char = (char *)xmlXPathCastToString(xpath_obj);
401  status += check_time_def(temp_char, location, field, filename, &ignore);
402  StrFree(temp_char);
403  }
404 
405  xmlXPathFreeObject(xpath_obj);
406 
407  return status;
408 }
409 
410 int check_policy(xmlNode *curNode, const char *policy_name, char **repo_list, int repo_count, const char *kasp) {
411  int status = 0;
412  int i = 0;
413  char* temp_char = NULL;
414  xmlNode *childNode;
415  xmlNode *childNode2;
416  xmlNode *childNode3;
417  char my_policy[KC_NAME_LENGTH];
418  int resign = 0;
419  int resigns_per_day = 0;
420  int refresh = 0;
421  int defalt = 0; /* default is not a suitable variable name */
422  int denial = 0;
423  int jitter = 0;
424  int inception = 0;
425  int ttl = 0;
426  int retire = 0;
427  int publish = 0;
428  int nsec = 0;
429  int hash_algo = 0;
430  int resalt = 0;
431  int ksk_algo = 0;
432  int ksk_length = 0;
433  int ksk_life = 0;
434  char *ksk_repo = NULL;
435  int zsk_algo = 0;
436  int zsk_length = 0;
437  int zsk_life = 0;
438  char *zsk_repo = NULL;
439  char *serial = NULL;
440 
441  snprintf(my_policy, KC_NAME_LENGTH, "policy %s,", policy_name);
442 
443  while (curNode) {
444  if (xmlStrEqual(curNode->name, (const xmlChar *)"Signatures")) {
445  childNode = curNode->children;
446  while (childNode){
447  if (xmlStrEqual(childNode->name, (const xmlChar *)"Resign")) {
448  temp_char = (char *) xmlNodeGetContent(childNode);
449  status += check_time_def(temp_char, my_policy, "Signatures/Resign", kasp, &resign);
450  StrFree(temp_char);
451  }
452  else if (xmlStrEqual(childNode->name, (const xmlChar *)"Refresh")) {
453  temp_char = (char *) xmlNodeGetContent(childNode);
454  status += check_time_def(temp_char, my_policy, "Signatures/Refresh", kasp, &refresh);
455  StrFree(temp_char);
456  }
457  else if (xmlStrEqual(childNode->name, (const xmlChar *)"Validity")) {
458  childNode2 = childNode->children;
459  while (childNode2){
460  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Default")) {
461  temp_char = (char *) xmlNodeGetContent(childNode2);
462  status += check_time_def(temp_char, my_policy, "Signatures/Validity/Default", kasp, &defalt);
463  StrFree(temp_char);
464  }
465  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Denial")) {
466  temp_char = (char *) xmlNodeGetContent(childNode2);
467  status += check_time_def(temp_char, my_policy, "Signatures/Validity/Denial", kasp, &denial);
468  StrFree(temp_char);
469  }
470  childNode2 = childNode2->next;
471  }
472  }
473  else if (xmlStrEqual(childNode->name, (const xmlChar *)"Jitter")) {
474  temp_char = (char *) xmlNodeGetContent(childNode);
475  status += check_time_def(temp_char, my_policy, "Signatures/Jitter", kasp, &jitter);
476  StrFree(temp_char);
477  }
478  else if (xmlStrEqual(childNode->name, (const xmlChar *)"InceptionOffset")) {
479  temp_char = (char *) xmlNodeGetContent(childNode);
480  status += check_time_def(temp_char, my_policy, "Signatures/InceptionOffset", kasp, &inception);
481  StrFree(temp_char);
482  }
483 
484  childNode = childNode->next;
485  }
486  }
487  else if (xmlStrEqual(curNode->name, (const xmlChar *)"Denial")) {
488  childNode = curNode->children;
489  while (childNode) {
490 
491  if (xmlStrEqual(childNode->name, (const xmlChar *)"NSEC")) {
492  nsec = 1;
493  }
494  else if (xmlStrEqual(childNode->name, (const xmlChar *)"NSEC3")) {
495  nsec = 3;
496  childNode2 = childNode->children;
497  while (childNode2){
498 
499  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Resalt")) {
500  temp_char = (char *) xmlNodeGetContent(childNode2);
501  status += check_time_def(temp_char, my_policy, "Denial/NSEC3/Resalt", kasp, &resalt);
502  StrFree(temp_char);
503  } else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Hash")) {
504  childNode3 = childNode2->children;
505  while (childNode3) {
506 
507  if (xmlStrEqual(childNode3->name, (const xmlChar *)"Algorithm")) {
508  temp_char = (char *) xmlNodeGetContent(childNode3);
509  /* we know temp_char is a number */
510  hash_algo = atoi(temp_char);
511  if (hash_algo != 1) {
512  dual_log("ERROR: NSEC3 Hash algorithm for %s Policy "
513  "in %s is %d but should be 1", policy_name,
514  kasp, hash_algo);
515  status++;
516  }
517  StrFree(temp_char);
518  }
519  childNode3 = childNode3->next;
520 
521  }
522  }
523 
524  childNode2 = childNode2->next;
525  }
526  }
527 
528  childNode = childNode->next;
529  }
530  }
531  else if (xmlStrEqual(curNode->name, (const xmlChar *)"Keys")) {
532  childNode = curNode->children;
533  while (childNode) {
534 
535  if (xmlStrEqual(childNode->name, (const xmlChar *)"TTL")) {
536  temp_char = (char *) xmlNodeGetContent(childNode);
537  status += check_time_def(temp_char, my_policy, "Keys/TTL", kasp, &ttl);
538  StrFree(temp_char);
539  }
540  else if (xmlStrEqual(childNode->name, (const xmlChar *)"RetireSafety")) {
541  temp_char = (char *) xmlNodeGetContent(childNode);
542  status += check_time_def(temp_char, my_policy, "Keys/RetireSafety", kasp, &retire);
543  StrFree(temp_char);
544  }
545  else if (xmlStrEqual(childNode->name, (const xmlChar *)"PublishSafety")) {
546  temp_char = (char *) xmlNodeGetContent(childNode);
547  status += check_time_def(temp_char, my_policy, "Keys/PublishSafety", kasp, &publish);
548  StrFree(temp_char);
549  }
550  else if (xmlStrEqual(childNode->name, (const xmlChar *)"KSK")) {
551  childNode2 = childNode->children;
552  while (childNode2){
553 
554  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Algorithm")) {
555  temp_char = (char *) xmlNodeGetContent(childNode2);
556  StrStrtoi(temp_char, &ksk_algo);
557  StrFree(temp_char);
558 
559  temp_char = (char *)xmlGetProp(childNode2, (const xmlChar *)"length");
560  StrStrtoi(temp_char, &ksk_length);
561  StrFree(temp_char);
562  }
563  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Lifetime")) {
564  temp_char = (char *) xmlNodeGetContent(childNode2);
565  status += check_time_def(temp_char, my_policy, "Keys/KSK Lifetime", kasp, &ksk_life);
566  StrFree(temp_char);
567  }
568  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Repository")) {
569  ksk_repo = (char *) xmlNodeGetContent(childNode2);
570  }
571 
572  childNode2 = childNode2->next;
573  }
574  }
575  else if (xmlStrEqual(childNode->name, (const xmlChar *)"ZSK")) {
576  childNode2 = childNode->children;
577  while (childNode2){
578 
579  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Algorithm")) {
580  temp_char = (char *) xmlNodeGetContent(childNode2);
581  StrStrtoi(temp_char, &zsk_algo);
582  StrFree(temp_char);
583 
584  temp_char = (char *)xmlGetProp(childNode2, (const xmlChar *)"length");
585  StrStrtoi(temp_char, &zsk_length);
586  StrFree(temp_char);
587 
588  }
589  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Lifetime")) {
590  temp_char = (char *) xmlNodeGetContent(childNode2);
591  status += check_time_def(temp_char, my_policy, "Keys/ZSK Lifetime", kasp, &zsk_life);
592  StrFree(temp_char);
593  }
594  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Repository")) {
595  zsk_repo = (char *) xmlNodeGetContent(childNode2);
596  }
597 
598  childNode2 = childNode2->next;
599  }
600  }
601 
602  childNode = childNode->next;
603  }
604  }
605  else if (xmlStrEqual(curNode->name, (const xmlChar *)"Zone")) {
606  childNode = curNode->children;
607  while (childNode) {
608 
609  if (xmlStrEqual(childNode->name, (const xmlChar *)"SOA")) {
610  childNode2 = childNode->children;
611  while (childNode2){
612 
613  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Serial")) {
614  serial = (char *) xmlNodeGetContent(childNode2);
615  }
616 
617  childNode2 = childNode2->next;
618  }
619  }
620 
621  childNode = childNode->next;
622  }
623  }
624 
625 
626  curNode = curNode->next;
627  }
628 
629  /* Now for the actual tests, from
630  * https://wiki.opendnssec.org/display/OpenDNSSEC/Configuration+Checker+%28ods-kaspcheck%29 */
631 
632  /* For all policies, check that the "Re-sign" interval is less
633  * than the "Refresh" interval. */
634  if (refresh && refresh <= resign) {
635  dual_log("ERROR: The Refresh interval (%d seconds) for "
636  "%s Policy in %s is less than or equal to the Resign interval "
637  "(%d seconds)", refresh, policy_name, kasp, resign);
638  status++;
639  }
640 
641  /* Ensure that the "Default" and "Denial" validity periods are
642  * greater than the "Refresh" interval. */
643  if (defalt <= refresh) {
644  dual_log("ERROR: Validity/Default (%d seconds) for "
645  "%s policy in %s is less than or equal to the Refresh interval "
646  "(%d seconds)", defalt, policy_name, kasp, refresh);
647  status++;
648  }
649  if (denial <= refresh) {
650  dual_log("ERROR: Validity/Denial (%d seconds) for "
651  "%s policy in %s is less than or equal to the Refresh interval "
652  "(%d seconds)", denial, policy_name, kasp, refresh);
653  status++;
654  }
655 
656  /* Warn if "Jitter" is greater than 50% of the maximum of the "default"
657  * and "Denial" period. (This is a bit arbitrary. The point is to get
658  * the user to realise that there will be a large spread in the signature
659  * lifetimes.) */
660  if (defalt > denial) {
661  if (jitter > (defalt * 0.5)) {
662  dual_log("WARNING: Jitter time (%d seconds) is large "
663  "compared to Validity/Default (%d seconds) "
664  "for %s policy in %s", jitter, defalt, policy_name, kasp);
665  }
666  } else {
667  if (jitter > (denial * 0.5)) {
668  dual_log("WARNING: Jitter time (%d seconds) is large "
669  "compared to Validity/Denial (%d seconds) "
670  "for %s policy in %s", jitter, denial, policy_name, kasp);
671  }
672  }
673 
674 
675  /* Warn if the InceptionOffset is greater than one hour. (Again arbitrary
676  * - but do we really expect the times on two systems to differ by more
677  * than this?) */
678  if (inception > 3600) {
679  dual_log("WARNING: InceptionOffset is higher than expected "
680  "(%d seconds) for %s policy in %s",
681  inception, policy_name, kasp);
682  }
683 
684  /* Warn if the "PublishSafety" and "RetireSafety" margins are less
685  * than 0.1 * TTL or more than 5 * TTL. */
686  if (publish < (ttl * 0.1)) {
687  dual_log("WARNING: Keys/PublishSafety (%d seconds) is less than "
688  "0.1 * TTL (%d seconds) for %s policy in %s",
689  publish, ttl, policy_name, kasp);
690  }
691  else if (publish > (ttl * 5)) {
692  dual_log("WARNING: Keys/PublishSafety (%d seconds) is greater than "
693  "5 * TTL (%d seconds) for %s policy in %s",
694  publish, ttl, policy_name, kasp);
695  }
696 
697  if (retire < (ttl * 0.1)) {
698  dual_log("WARNING: Keys/RetireSafety (%d seconds) is less than "
699  "0.1 * TTL (%d seconds) for %s policy in %s",
700  retire, ttl, policy_name, kasp);
701  }
702  else if (retire > (ttl * 5)) {
703  dual_log("WARNING: Keys/RetireSafety (%d seconds) is greater than "
704  "5 * TTL (%d seconds) for %s policy in %s",
705  retire, ttl, policy_name, kasp);
706  }
707 
708  /* The algorithm should be checked to ensure it is consistent with the
709  * NSEC/NSEC3 choice for the zone. */
710  if (nsec == 1) {
711  }
712  else if (nsec == 3) {
713  if (ksk_algo <= 5) {
714  dual_log("ERROR: In policy %s, incompatible algorithm (%d) used for "
715  "KSK NSEC3 in %s. Policy must have id greater than 5.", policy_name, ksk_algo, kasp);
716  status++;
717  }
718  if (zsk_algo <= 5) {
719  dual_log("ERROR: In policy %s, incompatible algorithm (%d) used for "
720  "ZSK NSEC3 in %s. Policy must have id greater than 5.", policy_name, zsk_algo, kasp);
721  status++;
722  }
723 
724  /* Warn if resalt is less than resign interval. */
725  if (resalt < resign) {
726  dual_log("WARNING: NSEC3 resalt interval (%d secs) is less than "
727  "signature resign interval (%d secs) for %s Policy",
728  resalt, resign, policy_name);
729  }
730 
731  }
732 
733  /* If datecounter is used for serial, then no more than 99 signings
734  * should be done per day (there are only two digits to play with in the
735  * version number). */
736  if (serial != NULL && strncmp(serial, "datecounter", 11) == 0) {
737  if (resign != 0) {
738  resigns_per_day = (60 * 60 * 24) / resign;
739  if (resigns_per_day > 99) {
740  dual_log("ERROR: In %s, policy %s, serial type datecounter used "
741  "but %d re-signs requested. No more than 99 re-signs per "
742  "day should be used with datecounter as only 2 digits are "
743  "allocated for the version number.",
744  kasp, policy_name, resigns_per_day);
745  status++;
746  }
747  }
748  }
749 
750  /* The key strength should be checked for sanity
751  * - warn if less than 1024 or error if more than 4096.
752  * Only do this check for RSA. */
753  if (ksk_algo == 5 || ksk_algo == 7 || ksk_algo == 8 || ksk_algo == 10) {
754  if (ksk_length < 1024) {
755  dual_log("WARNING: Key length of %d used for KSK in %s policy in %s. Should "
756  "probably be 1024 or more", ksk_length, policy_name, kasp);
757  }
758  else if (ksk_length > 4096) {
759  dual_log("ERROR: Key length of %d used for KSK in %s policy in %s. Should "
760  "be 4096 or less", ksk_length, policy_name, kasp);
761  status++;
762  }
763  }
764  if (zsk_algo == 5 || zsk_algo == 7 || zsk_algo == 8 || zsk_algo == 10) {
765  if (zsk_length < 1024) {
766  dual_log("WARNING: Key length of %d used for ZSK in %s policy in %s. Should "
767  "probably be 1024 or more", zsk_length, policy_name, kasp);
768  }
769  else if (zsk_length > 4096) {
770  dual_log("ERROR: Key length of %d used for ZSK in %s policy in %s. Should "
771  "be 4096 or less", zsk_length, policy_name, kasp);
772  status++;
773  }
774  }
775 
776  /* Check that repositories listed in the KSK and ZSK sections are defined
777  * in conf.xml. */
778  if (ksk_repo != NULL) {
779  for (i = 0; i < repo_count; i++) {
780  if (strcmp(ksk_repo, repo_list[i]) == 0) {
781  break;
782  }
783  }
784  if (i >= repo_count) {
785  dual_log("ERROR: Unknown repository (%s) defined for KSK in "
786  "%s policy in %s", ksk_repo, policy_name, kasp);
787  status++;
788  }
789  }
790 
791  if (zsk_repo != NULL) {
792  for (i = 0; i < repo_count; i++) {
793  if (strcmp(zsk_repo, repo_list[i]) == 0) {
794  break;
795  }
796  }
797  if (i >= repo_count) {
798  dual_log("ERROR: Unknown repository (%s) defined for ZSK in "
799  "%s policy", zsk_repo, policy_name);
800  status++;
801  }
802  }
803 
804  /* Warn if for any zone, the KSK lifetime is less than the ZSK lifetime. */
805  if (ksk_life < zsk_life) {
806  dual_log("WARNING: KSK minimum lifetime (%d seconds) is less than "
807  "ZSK minimum lifetime (%d seconds) for %s Policy in %s",
808  ksk_life, zsk_life, policy_name, kasp);
809  }
810 
811  /* Check that the value of the "Serial" tag is valid. (Done by rng) */
812 
813  /* Error if Jitter is greater than either the Default or Denial Validity. */
814  if (jitter > defalt) {
815  dual_log("ERROR: Jitter time (%d seconds) is greater than the "
816  "Default Validity (%d seconds) for %s policy in %s",
817  jitter, defalt, policy_name, kasp);
818  status++;
819  }
820  if (jitter > denial) {
821  dual_log("ERROR: Jitter time (%d seconds) is greater than the "
822  "Denial Validity (%d seconds) for %s policy in %s",
823  jitter, denial, policy_name, kasp);
824  status++;
825  }
826 
827  StrFree(ksk_repo);
828  StrFree(zsk_repo);
829  StrFree(serial);
830 
831  return status;
832 }
833 
834 /* NOTE: The following are taken from various files within libksm */
835 
836 /*+
837  * DtXMLIntervalSeconds - Parse xsd:durations Interval String
838  *
839  * Description:
840  * Parses an interval string which is of the form:
841  *
842  * P<number>
843  * or P<number><interval-type>
844  * or PT<number><interval-type> (if the interval-type is H, M or S)
845  *
846  * Without an interval type, the interval is assumed to be in seconds.
847  * Otherwise, the following interval types recognised are:
848  *
849  * S Seconds
850  * M Minutes - multiply number by 60 (no. seconds in a minute)
851  * H Hours - multiply number by 3600 (no. seconds in an hour)
852  * D Day - multiply number by 86400 (no. seconds in a day)
853  * W Week - multiply number by 604,800 (no. seconds in a week)
854  * M Month - multiply number by 2,678,400 (no. seconds in 31 days)
855  * Y Year - multiply number by 31,536,000 (no. seconds in 365 days)
856  *
857  * Lower-case characters are not recognised.
858  *
859  * Example: The string P2D would translate to 172,800
860  *
861  * Arguments:
862  * const char* text
863  * Interval as a string.
864  *
865  * long* interval
866  * Returned interval.
867  *
868  * Returns:
869  * int
870  * < 0 Success, string translated OK _BUT_ may not be what was expected
871  * (Year or Month used which gives approximate answer).
872  * 0 Success, string translated OK
873  * 2 Error - unable to translate string.
874  * 3 Error - string too long to be a number.
875  * 4 Error - invalid pointers or text string NULL.
876  *
877  * Known issues:
878  *
879  * 1. Years and months are only approximate as it has no concept of "now"
880  * We use 31 days = 1 month and 365 days = 1 year.
881  * 2. The "T" only effects the value of "M" (P1S should be illegal as correctly
882  * it would be PT1S)
883  *
884  * NOTE: This is copied from ksm/datatime.c and modified slightly to separate
885  * "Y" and "M" warnings
886  *
887 -*/
888 
889 int DtXMLIntervalSeconds(const char* text, int* interval)
890 {
891  int length = 0; /* Length of the string */
892  short is_time = 0; /* Do we have a Time section or not */
893  short is_neg = 0; /* Do we have a negative number */
894  short warning = 0; /* Do we need a warning code for duration approximation? */
895  short got_temp = 0; /* Have we seen a number? */
896  long temp = 0; /* Number from this section */
897  const char *ptr = text; /* allow us to read through */
898  const char *end;
899  long temp_interval = 0;
900 
901  if (!text || !interval || !*text) return 4;
902  length = strlen(text);
903  if (length <= 2) return 2;
904 
905  if (*ptr == '-') {
906  is_neg = 1;
907  ptr++;
908  }
909  if (*ptr != 'P') return 2;
910  ptr++;
911 
912  end = text + length;
913  while (ptr < end) {
914  switch (*ptr) {
915  case 'S':
916  if (!got_temp || !is_time) return 2;
917  temp_interval += temp;
918  temp = 0;
919  got_temp = 0;
920  break;
921 
922  case 'M':
923  if (!got_temp) return 2;
924  if (is_time) {
925  temp_interval += 60 * temp;
926  } else {
927  temp_interval += 31 * 24 * 60 * 60 * temp;
928  warning -= 1; /* month is an ambiguous period */
929  }
930  temp = 0;
931  got_temp = 0;
932  break;
933 
934  case 'H':
935  if (!got_temp || !is_time) return 2;
936  temp_interval += 60 * 60 * temp;
937  temp = 0;
938  got_temp = 0;
939  break;
940 
941  case 'D':
942  if (!got_temp || is_time) return 2;
943  temp_interval += 24 * 60 * 60 * temp;
944  temp = 0;
945  got_temp = 0;
946  break;
947 
948  case 'W':
949  if (!got_temp || is_time) return 2;
950  temp_interval += 7 * 24 * 60 * 60 * temp;
951  temp = 0;
952  got_temp = 0;
953  break;
954 
955  case 'Y':
956  if (!got_temp || is_time) return 2;
957  temp_interval += 365 * 24 * 60 * 60 * temp;
958  temp = 0;
959  warning -= 2; /* year is an ambiguous period */
960  got_temp = 0;
961  break;
962 
963  case 'T':
964  is_time = 1;
965  break;
966 
967  case '0':
968  case '1':
969  case '2':
970  case '3':
971  case '4':
972  case '5':
973  case '6':
974  case '7':
975  case '8':
976  case '9':
977  if (!temp) {
978  char *endptr;
979  temp = strtol(ptr, &endptr, 10);
980  if (temp == LONG_MIN || temp == LONG_MAX)
981  return 3;
982  got_temp = 1;
983  ptr = endptr-1;
984  }
985  break;
986 
987  default:
988  /* encountered unparsable char */
989  if (ptr != end) return 2;
990  }
991  ptr++;
992  }
993 
994  /* If we had no trailing letter then it is an implicit "S"
995  * But only if is_time is not set.*/
996  if (temp && !is_time) return 2;
997  temp_interval += temp;
998 
999  if (is_neg) temp_interval *= -1;
1000  *interval = (int) temp_interval;
1001  return warning;
1002 }
1003 
1004 /*+
1005  * StrStrtoi - Convert String to int
1006  *
1007  * Description:
1008  * Converts a string to a "int".
1009  *
1010  * This version strips out tabs and whitespace characters.
1011  *
1012  * Arguments:
1013  * const char* string (input)
1014  * String to convert.
1015  *
1016  * int* value (returned)
1017  * Return value.
1018  *
1019  * Returns:
1020  * int
1021  * 0 Success
1022  * 1 Conversion failed
1023 -*/
1024 
1025 int StrStrtoi(const char* string, int* value)
1026 {
1027  long longval; /* "long" to be passed to StrStrtol */
1028  int status; /* Status return */
1029 
1030  if (value == NULL) {
1031  dual_log("ERROR: NULL value passed to StrStrtoi");
1032  return 1;
1033  }
1034  status = StrStrtol(string, &longval);
1035  if (status == 0) {
1036  if ((longval >= INT_MIN) && (longval <= INT_MAX)) {
1037  *value = (int) longval;
1038  }
1039  else {
1040  status = 1; /* Integer overflow */
1041  }
1042  }
1043 
1044  return status;
1045 }
1046 
1047 /*+
1048  * StrStrtol - Convert String to long
1049  *
1050  * Description:
1051  * Converts a string to a "long". It uses strtol, but also passes
1052  * back a status code to indicate if the conversion was successful.
1053  *
1054  * This version strips out tabs and whitespace characters.
1055  *
1056  * Arguments:
1057  * const char* string (input)
1058  * String to convert.
1059  *
1060  * long* value (returned)
1061  * Return value.
1062  *
1063  * Returns:
1064  * int
1065  * 0 Success
1066  * 1 Conversion failed
1067 -*/
1068 
1069 int StrStrtol(const char* string, long* value)
1070 {
1071  char* endptr; /* End of string pointer */
1072  int status = 1; /* Assume failure */
1073  char* copy; /* Copy of the string */
1074  char* start; /* Start of the trimmed string */
1075 
1076  if (value == NULL) {
1077  dual_log("ERROR: NULL value passed to StrStrtol");
1078  return 1;
1079  }
1080  if (string) {
1081  copy = StrStrdup(string);
1082  StrTrimR(copy); /* Remove trailing spaces */
1083  start = StrTrimL(copy); /* ... and leading ones */
1084  if (*start) {
1085 
1086  /* String is not NULL, so try a conversion */
1087 
1088  errno = 0;
1089  *value = strtol(start, &endptr, 10);
1090 
1091  /* Only success if all characters converted */
1092 
1093  if (errno == 0) {
1094  status = (*endptr == '\0') ? 0 : 1;
1095  }
1096  else {
1097  status = 1;
1098  }
1099  }
1100  StrFree(copy);
1101  }
1102 
1103  return status;
1104 }
1105 
1106 /*+
1107  * StrStrdup - Duplicate String
1108  *
1109  * Description:
1110  * Wrapper for "strdup" that always returns, or exits the program (after
1111  * outputting a message to stderr) if the string duplication fails.
1112  *
1113  * Arguments:
1114  * const char* string (input)
1115  * String to be duplicated.
1116  *
1117  * Returns:
1118  * char*
1119  * Pointer to duplicated string (guaranteed to be non-null). The
1120  * string should be freed with StrFree() - a macro wrapper for "free".
1121 -*/
1122 
1123 char* StrStrdup(const char* string)
1124 {
1125  char* duplicate = NULL; /* Pointer to the duplicated string */
1126 
1127  if (string) {
1128  duplicate = strdup(string);
1129  if (duplicate == NULL) {
1130  dual_log("ERROR: StrStrdup: Call to malloc() returned null - out of swap space?");
1131  exit(1);
1132  }
1133  }
1134  else {
1135  duplicate = MemCalloc(1, 1); /* Allocate a single zeroed byte */
1136  }
1137 
1138  return duplicate;
1139 }
1140 
1141 /*+
1142  * StrAppend - Append String with Reallocation
1143  *
1144  * Description:
1145  * Appends the given string to a dynamically-allocated string, reallocating
1146  * the former as needed.
1147  *
1148  * The function is a no-op if either of its arguments are NULL.
1149  *
1150  * Arguments:
1151  * char** str1
1152  * On input this holds the current string. It is assumed that the
1153  * string has been dynamically allocated (with malloc or the like).
1154  * On output, this holds the concatenation of the two strings.
1155  *
1156  * If, on input, the string is NULL (i.e. *str is NULL, *not* str1 is
1157  * NULL), a new string is allocated and str2 copied to it.
1158  *
1159  * On exit, the string can be freed via a call to StrFree.
1160  *
1161  * const char* str2
1162  * The string to be appended.
1163 -*/
1164 
1165 void StrAppend(char** str1, const char* str2)
1166 {
1167  int len1; /* Length of string 1 */
1168  int len2; /* Length of string 2 */
1169 
1170  if (str1 && str2) {
1171 
1172  /* Something to append and we can append it */
1173 
1174  len2 = strlen(str2);
1175  if (*str1) {
1176  len1 = strlen(*str1);
1177 
1178  /* Allocate space for combined string and concatenate */
1179 
1180  *str1 = MemRealloc(*str1, (len1 + len2 + 1) * sizeof(char));
1181  strcat(*str1, str2);
1182  }
1183  else {
1184 
1185  /* Nothing in string 1, so just duplicate string 2 */
1186 
1187  *str1 = StrStrdup(str2);
1188  }
1189  }
1190 
1191  return;
1192 }
1193 
1194 /*+
1195  * StrTrimR - Trim Right
1196  *
1197  * Description:
1198  * Modifies a string by trimming white-space characters from the right of
1199  * the string. It does this by modifying the string, inserting a null
1200  * character after the last non white-space character.
1201  *
1202  * Arguments:
1203  * char *text (modified)
1204  * Text to modify. If this is NULL, the routine is a no-op.
1205  *
1206  * Returns:
1207  * void
1208 -*/
1209 
1210 void StrTrimR(char *text)
1211 {
1212  if (text) {
1213 
1214  /* Work backwards through the string */
1215 
1216  int textlen = strlen(text);
1217  while (-- textlen >= 0) {
1218  if (! isspace((int) text[textlen])) {
1219  text[textlen + 1] = '\0';
1220  return;
1221  }
1222  }
1223 
1224  /* Get here if the entire string is white space */
1225 
1226  text[0] = '\0';
1227  }
1228  return;
1229 }
1230 
1231 /*+
1232  * StrTrimL - Trim Left
1233  *
1234  * Description:
1235  * Searches a string and returns a pointer to the first non white-space
1236  * character in it.
1237  *
1238  * Arguments:
1239  * char* text (input)
1240  * Text to search.
1241  *
1242  * Returns:
1243  * char*
1244  * Pointer to first non white-space character in the string. If the
1245  * string is NULL, NULL is returned. If the string is all white space,
1246  * a pointer to the trailing null character is returned.
1247 -*/
1248 
1249 char* StrTrimL(char* text)
1250 {
1251  if (text) {
1252  while (*text && isspace((int) *text)) {
1253  ++text;
1254  }
1255  }
1256 
1257  return text;
1258 }
1259 
1260 void* MemCalloc(size_t nmemb, size_t size)
1261 {
1262  void *ptr = calloc(nmemb, size);
1263  if (ptr == NULL) {
1264  dual_log("ERROR: calloc: Out of swap space");
1265  exit(1);
1266  }
1267  return ptr;
1268 }
1269 
1270 void* MemRealloc(void *ptr, size_t size)
1271 {
1272  void *ptr1 = realloc(ptr, size);
1273  if (ptr1 == NULL) {
1274  dual_log("ERROR: realloc: Out of swap space");
1275  exit(1);
1276  }
1277  return ptr1;
1278 }
int check_time_def(const char *time_expr, const char *location, const char *field, const char *filename, int *interval)
Definition: kc_helper.c:350
#define StrFree(ptr)
Definition: kc_helper.c:48
char * StrStrdup(const char *string)
Definition: kc_helper.c:1123
int check_path(const char *pathname, const char *log_string)
Definition: kc_helper.c:251
void * MemRealloc(void *ptr, size_t size)
Definition: kc_helper.c:1270
#define KC_NAME_LENGTH
Definition: kc_helper.h:39
int DtXMLIntervalSeconds(const char *text, int *interval)
Definition: kc_helper.c:889
void StrTrimR(char *text)
Definition: kc_helper.c:1210
int check_policy(xmlNode *curNode, const char *policy_name, char **repo_list, int repo_count, const char *kasp)
Definition: kc_helper.c:410
char * kasp
Definition: kaspcheck.c:48
void * MemCalloc(size_t nmemb, size_t size)
Definition: kc_helper.c:1260
int check_rng(const char *filename, const char *rngfilename)
Definition: kc_helper.c:92
void log_switch(int facility, const char *program_name)
Definition: kc_helper.c:56
char ** repo_list
Definition: kaspcheck.c:50
int check_path_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *path_xexpr)
Definition: kc_helper.c:274
int repo_count
Definition: kaspcheck.c:51
int check_file(const char *filename, const char *log_string)
Definition: kc_helper.c:194
int StrStrtol(const char *string, long *value)
Definition: kc_helper.c:1069
void log_init(int facility, const char *program_name)
Definition: kc_helper.c:50
int StrStrtoi(const char *string, int *value)
Definition: kc_helper.c:1025
void dual_log(const char *format,...)
Definition: kc_helper.c:63
void StrAppend(char **str1, const char *str2)
Definition: kc_helper.c:1165
int check_time_def_from_xpath(xmlXPathContextPtr xpath_ctx, const xmlChar *time_xexpr, const char *location, const char *field, const char *filename)
Definition: kc_helper.c:387
int check_user_group(xmlXPathContextPtr xpath_ctx, const xmlChar *user_xexpr, const xmlChar *group_xexpr)
Definition: kc_helper.c:300
int check_file_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *file_xexpr)
Definition: kc_helper.c:218
char * StrTrimL(char *text)
Definition: kc_helper.c:1249
int verbose
Definition: kaspcheck.c:49