OpenDNSSEC-enforcer  2.0.3
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 "config.h"
39 #include "kc_helper.h"
40 
41 #include <libxml/tree.h>
42 #include <libxml/parser.h>
43 #include <libxml/xpath.h>
44 #include <libxml/xpathInternals.h>
45 #include <libxml/relaxng.h>
46 
47 #define StrFree(ptr) {if(ptr != NULL) {free(ptr); (ptr) = NULL;}}
48 
50 
51 void log_init(int facility, const char *program_name)
52 {
53  openlog(program_name, 0, facility);
54 }
55 
56 /* As far as possible we send messages both to syslog and STDOUT */
57 #pragma GCC diagnostic push
58 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
59 void dual_log(const char *format, ...) {
60 
61  /* If the variable arg list is bad then random errors can occur */
62  va_list args;
63  va_list args2;
64  va_start(args, format);
65  va_copy(args2, args);
66 
67  if (strncmp(format, "ERROR:", 6) == 0) {
68  vsyslog(LOG_ERR, format, args);
69  } else if (strncmp(format, "WARNING:", 8) == 0) {
70  vsyslog(LOG_WARNING, format, args);
71  } else if (strncmp(format, "DEBUG:", 6) == 0) {
72  vsyslog(LOG_DEBUG, format, args);
73  } else {
74  vsyslog(LOG_INFO, format, args);
75  }
76 
78  vprintf(format, args2);
79  printf("\n");
80  }
81 
82  va_end(args);
83  va_end(args2);
84 }
85 #pragma GCC diagnostic pop
86 
87 /* Check an XML file against its rng */
88 int check_rng(const char *filename, const char *rngfilename, int verbose)
89 {
90  xmlDocPtr doc = NULL;
91  xmlDocPtr rngdoc = NULL;
92  xmlRelaxNGParserCtxtPtr rngpctx = NULL;
93  xmlRelaxNGValidCtxtPtr rngctx = NULL;
94  xmlRelaxNGPtr schema = NULL;
95 
96  if (verbose) {
97  dual_log("DEBUG: About to check XML validity in %s with %s",
98  filename, rngfilename);
99  }
100 
101  /* Load XML document */
102  doc = xmlParseFile(filename);
103  if (doc == NULL) {
104  dual_log("ERROR: unable to parse file \"%s\"", filename);
105  /* Maybe the file doesn't exist? */
106  check_file(filename, "Configuration file");
107 
108  return(1);
109  }
110 
111  /* Load rng document */
112  rngdoc = xmlParseFile(rngfilename);
113  if (rngdoc == NULL) {
114  dual_log("ERROR: unable to parse file \"%s\"", rngfilename);
115  /* Maybe the file doesn't exist? */
116  check_file(rngfilename, "RNG file");
117 
118  xmlFreeDoc(doc);
119 
120  return(1);
121  }
122 
123  /* Create an XML RelaxNGs parser context for the relax-ng document. */
124  rngpctx = xmlRelaxNGNewDocParserCtxt(rngdoc);
125  if (rngpctx == NULL) {
126  dual_log("ERROR: unable to create XML RelaxNGs parser context");
127 
128  xmlFreeDoc(doc);
129  xmlFreeDoc(rngdoc);
130 
131  return(1);
132  }
133 
134  xmlRelaxNGSetParserErrors(rngpctx,
135  (xmlRelaxNGValidityErrorFunc) fprintf,
136  (xmlRelaxNGValidityWarningFunc) fprintf,
137  stderr);
138 
139  /* parse a schema definition resource and build an internal XML
140  * Shema struture which can be used to validate instances. */
141  schema = xmlRelaxNGParse(rngpctx);
142  if (schema == NULL) {
143  dual_log("ERROR: unable to parse a schema definition resource");
144 
145  xmlRelaxNGFreeParserCtxt(rngpctx);
146  xmlFreeDoc(doc);
147  xmlFreeDoc(rngdoc);
148 
149  return(1);
150  }
151 
152  /* Create an XML RelaxNGs validation context based on the given schema */
153  rngctx = xmlRelaxNGNewValidCtxt(schema);
154  if (rngctx == NULL) {
155  dual_log("ERROR: unable to create RelaxNGs validation context based on the schema");
156 
157  xmlRelaxNGFree(schema);
158  xmlRelaxNGFreeParserCtxt(rngpctx);
159  xmlFreeDoc(doc);
160  xmlFreeDoc(rngdoc);
161 
162  return(1);
163  }
164 
165  xmlRelaxNGSetValidErrors(rngctx,
166  (xmlRelaxNGValidityErrorFunc) fprintf,
167  (xmlRelaxNGValidityWarningFunc) fprintf,
168  stderr);
169 
170  /* Validate a document tree in memory. */
171  if (xmlRelaxNGValidateDoc(rngctx,doc) != 0) {
172  dual_log("ERROR: %s fails to validate", filename);
173 
174  xmlRelaxNGFreeValidCtxt(rngctx);
175  xmlRelaxNGFree(schema);
176  xmlRelaxNGFreeParserCtxt(rngpctx);
177  xmlFreeDoc(doc);
178  xmlFreeDoc(rngdoc);
179 
180  return(1);
181  }
182 
183  xmlRelaxNGFreeValidCtxt(rngctx);
184  xmlRelaxNGFree(schema);
185  xmlRelaxNGFreeParserCtxt(rngpctx);
186  xmlFreeDoc(doc);
187  xmlFreeDoc(rngdoc);
188 
189  return 0;
190 }
191 
192 int check_file(const char *filename, const char *log_string) {
193  struct stat stat_ret;
194 
195  if (stat(filename, &stat_ret) != 0) {
196 
197  if (errno != ENOENT) {
198  dual_log("ERROR: cannot stat file %s: %s",
199  filename, strerror(errno));
200  return 1;
201  }
202 
203  dual_log("ERROR: %s (%s) does not exist", log_string, filename);
204  return 1;
205  }
206 
207  if (S_ISREG(stat_ret.st_mode)) {
208  /* The file exists */
209  return 0;
210  }
211 
212  dual_log("ERROR: %s (%s) does not exist", log_string, filename);
213  return 1;
214 }
215 
216 int check_file_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *file_xexpr) {
217  int status = 0;
218  xmlXPathObjectPtr xpath_obj;
219  char* temp_char = NULL;
220  char* str = NULL;
221 
222  xpath_obj = xmlXPathEvalExpression(file_xexpr, xpath_ctx);
223  if(xpath_obj == NULL) {
224  dual_log("ERROR: unable to evaluate xpath expression: %s", file_xexpr);
225  return 1;
226  }
227  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
228  temp_char = (char*) xmlXPathCastToString(xpath_obj);
229 
230  /* strip off any trailing characters (needed for DSSub with cks_id) */
231  str = strrchr(temp_char, ' ');
232  if (str) {
233  *str = 0;
234  }
235 
236  status = check_file(temp_char, log_string);
237 
238  StrFree(temp_char);
239  } else {
240  /* Not set; return -1 so that we can test the default path */
241  xmlXPathFreeObject(xpath_obj);
242  return -1;
243  }
244 
245  xmlXPathFreeObject(xpath_obj);
246  return status;
247 }
248 
249 int check_path(const char *pathname, const char *log_string) {
250  struct stat stat_ret;
251 
252  if (stat(pathname, &stat_ret) != 0) {
253  if (errno != ENOENT) {
254  dual_log("ERROR: cannot stat directory %s: %s",
255  pathname, strerror(errno));
256  return 1;
257  }
258 
259  dual_log("ERROR: %s (%s) does not exist", log_string, pathname);
260  return 1;
261  }
262 
263  if (S_ISDIR(stat_ret.st_mode)) {
264  /* The directory exists */
265  return 0;
266  }
267 
268  dual_log("ERROR: %s (%s) is not a directory", log_string, pathname);
269  return 1;
270 }
271 
272 int check_path_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *path_xexpr) {
273  int status = 0;
274  xmlXPathObjectPtr xpath_obj;
275  char* temp_char = NULL;
276 
277  xpath_obj = xmlXPathEvalExpression(path_xexpr, xpath_ctx);
278  if(xpath_obj == NULL) {
279  dual_log("ERROR: unable to evaluate xpath expression: %s", path_xexpr);
280  return 1;
281  }
282  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
283  temp_char = (char*) xmlXPathCastToString(xpath_obj);
284 
285  status = check_path(temp_char, log_string);
286 
287  StrFree(temp_char);
288  } else {
289  /* Not set; return -1 so that we can test the default path */
290  xmlXPathFreeObject(xpath_obj);
291  return -1;
292  }
293 
294  xmlXPathFreeObject(xpath_obj);
295  return status;
296 }
297 
298 int check_user_group(xmlXPathContextPtr xpath_ctx, const xmlChar *user_xexpr, const xmlChar *group_xexpr) {
299  int status = 0;
300  xmlXPathObjectPtr xpath_obj;
301  char* temp_char = NULL;
302 
303  struct passwd *pwd;
304  struct group *grp;
305 
306  /* Group if specified */
307  xpath_obj = xmlXPathEvalExpression(group_xexpr, xpath_ctx);
308  if(xpath_obj == NULL) {
309  dual_log("ERROR: unable to evaluate xpath expression: %s", group_xexpr);
310  return(1);
311  }
312  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
313  temp_char = (char*) xmlXPathCastToString(xpath_obj);
314 
315  if ((grp = getgrnam(temp_char)) == NULL) {
316  dual_log("ERROR: Group '%s' does not exist", temp_char);
317  status += 1;
318  }
319  endgrent();
320 
321  StrFree(temp_char);
322  }
323  xmlXPathFreeObject(xpath_obj);
324 
325  /* User if specified */
326  xpath_obj = xmlXPathEvalExpression(user_xexpr, xpath_ctx);
327  if(xpath_obj == NULL) {
328  dual_log("ERROR: unable to evaluate xpath expression: %s", user_xexpr);
329  return(1);
330  }
331  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
332  temp_char = (char*) xmlXPathCastToString(xpath_obj);
333 
334  if ((pwd = getpwnam(temp_char)) == NULL) {
335  dual_log("ERROR: User '%s' does not exist", temp_char);
336  status += 1;
337  }
338  endpwent();
339 
340  StrFree(temp_char);
341  }
342 
343  xmlXPathFreeObject(xpath_obj);
344 
345  return status;
346 }
347 
348 int check_time_def(const char *time_expr, const char *location, const char *field, const char *filename, int* interval) {
349 
350  int status = DtXMLIntervalSeconds(time_expr, interval);
351 
352  if (status != 0) {
353  switch (status) {
354  case -1:
355  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);
356  break;
357  case -2:
358  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);
359  break;
360  case -3:
361  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);
362  break;
363  case 2:
364  dual_log("ERROR: unable to translate %s (%s) to seconds.", field, time_expr);
365  break;
366  case 3:
367  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);
368  break;
369  case 4:
370  dual_log("ERROR: invalid pointers or text string NULL in %s (%s).", field, time_expr);
371  break;
372  default:
373  dual_log("ERROR: unknown error converting %s (%s) to seconds", field, time_expr);
374  }
375  }
376 
377  if (status > 0) {
378  *interval = 0;
379  return 1;
380  }
381 
382  return 0;
383 }
384 
385 int check_time_def_from_xpath(xmlXPathContextPtr xpath_ctx, const xmlChar *time_xexpr, const char *location, const char *field, const char *filename) {
386 
387  xmlXPathObjectPtr xpath_obj;
388  char* temp_char = NULL;
389  int status = 0;
390  int ignore = 0;
391 
392  xpath_obj = xmlXPathEvalExpression(time_xexpr, xpath_ctx);
393  if(xpath_obj == NULL) {
394  dual_log("ERROR: unable to evaluate xpath expression: %s", time_xexpr);
395  return 1;
396  }
397  if (xpath_obj->nodesetval != NULL && xpath_obj->nodesetval->nodeNr > 0) {
398  temp_char = (char *)xmlXPathCastToString(xpath_obj);
399  status += check_time_def(temp_char, location, field, filename, &ignore);
400  StrFree(temp_char);
401  }
402 
403  xmlXPathFreeObject(xpath_obj);
404 
405  return status;
406 }
407 
408 int check_interval(xmlXPathContextPtr xpath_ctx,
409  const xmlChar *interval_xexpr, const char *filename)
410 {
411  char *temp_char;
412  xmlXPathObjectPtr xpath_obj;
413  xpath_obj = xmlXPathEvalExpression(interval_xexpr, xpath_ctx);
414 
415  if(!xpath_obj) {
416  dual_log("ERROR: unable to evaluate xpath expression: %s", interval_xexpr);
417  return 1;
418  }
419  temp_char = (char*) xmlXPathCastToString(xpath_obj);
420  xmlXPathFreeObject(xpath_obj);
421  if ( strlen(temp_char) != 0) {
422  dual_log("WARNING: Deprecated tag %s found in %s.", interval_xexpr, filename);
423  return 0;
424  }
425  return 0;
426 }
427 
428 
429 
430 int check_policy(xmlNode *curNode, const char *policy_name, char **repo_list, int repo_count, const char *kasp) {
431  int status = 0;
432  int i = 0;
433  char* temp_char = NULL;
434  xmlNode *childNode;
435  xmlNode *childNode2;
436  xmlNode *childNode3;
437  char my_policy[KC_NAME_LENGTH];
438  int resign = 0;
439  int resigns_per_day = 0;
440  int refresh = 0;
441  int defalt = 0; /* default is not a suitable variable name */
442  int denial = 0;
443  int jitter = 0;
444  int inception = 0;
445  int ttl = 0;
446  int ds_ttl = 0;
447  int maxzone_ttl = 0;
448  int retire = 0;
449  int publish = 0;
450  int nsec = 0;
451  int resalt = 0;
452  int hash_algo = 0;
453 
454  enum {KSK = 1, ZSK, CSK};
455  struct key {
456  int type;
457  int algo;
458  int length;
459  int life;
460  char *repo;
461  struct key *next;
462  };
463  struct key *tmpkey, *firstkey = NULL, *curkey = NULL;
464  char *serial = NULL;
465 
466  snprintf(my_policy, KC_NAME_LENGTH, "policy %s,", policy_name);
467 
468  while (curNode) {
469  if (xmlStrEqual(curNode->name, (const xmlChar *)"Signatures")) {
470  childNode = curNode->children;
471  while (childNode){
472  if (xmlStrEqual(childNode->name, (const xmlChar *)"Resign")) {
473  temp_char = (char *) xmlNodeGetContent(childNode);
474  status += check_time_def(temp_char, my_policy, "Signatures/Resign", kasp, &resign);
475  StrFree(temp_char);
476  }
477  else if (xmlStrEqual(childNode->name, (const xmlChar *)"Refresh")) {
478  temp_char = (char *) xmlNodeGetContent(childNode);
479  status += check_time_def(temp_char, my_policy, "Signatures/Refresh", kasp, &refresh);
480  StrFree(temp_char);
481  }
482  else if (xmlStrEqual(childNode->name, (const xmlChar *)"Validity")) {
483  childNode2 = childNode->children;
484  while (childNode2){
485  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Default")) {
486  temp_char = (char *) xmlNodeGetContent(childNode2);
487  status += check_time_def(temp_char, my_policy, "Signatures/Validity/Default", kasp, &defalt);
488  StrFree(temp_char);
489  }
490  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Denial")) {
491  temp_char = (char *) xmlNodeGetContent(childNode2);
492  status += check_time_def(temp_char, my_policy, "Signatures/Validity/Denial", kasp, &denial);
493  StrFree(temp_char);
494  }
495  childNode2 = childNode2->next;
496  }
497  }
498  else if (xmlStrEqual(childNode->name, (const xmlChar *)"Jitter")) {
499  temp_char = (char *) xmlNodeGetContent(childNode);
500  status += check_time_def(temp_char, my_policy, "Signatures/Jitter", kasp, &jitter);
501  StrFree(temp_char);
502  }
503  else if (xmlStrEqual(childNode->name, (const xmlChar *)"InceptionOffset")) {
504  temp_char = (char *) xmlNodeGetContent(childNode);
505  status += check_time_def(temp_char, my_policy, "Signatures/InceptionOffset", kasp, &inception);
506  StrFree(temp_char);
507  }
508  else if (xmlStrEqual(childNode->name, (const xmlChar *)"MaxZoneTTL")) {
509  temp_char = (char *) xmlNodeGetContent(childNode);
510  status += check_time_def(temp_char, my_policy, "Signatures/MaxZoneTTL", kasp, &maxzone_ttl);
511  StrFree(temp_char);
512  }
513 
514  childNode = childNode->next;
515  }
516  }
517  else if (xmlStrEqual(curNode->name, (const xmlChar *)"Denial")) {
518  childNode = curNode->children;
519  while (childNode) {
520 
521  if (xmlStrEqual(childNode->name, (const xmlChar *)"NSEC")) {
522  nsec = 1;
523  }
524  else if (xmlStrEqual(childNode->name, (const xmlChar *)"NSEC3")) {
525  nsec = 3;
526  childNode2 = childNode->children;
527  while (childNode2){
528 
529  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Resalt")) {
530  temp_char = (char *) xmlNodeGetContent(childNode2);
531  status += check_time_def(temp_char, my_policy, "Denial/NSEC3/Resalt", kasp, &resalt);
532  StrFree(temp_char);
533  } else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Hash")) {
534  childNode3 = childNode2->children;
535  while (childNode3) {
536  if (xmlStrEqual(childNode3->name, (const xmlChar *)"Algorithm")) {
537  temp_char = (char *) xmlNodeGetContent(childNode3);
538  /* we know temp_char is a number */
539  hash_algo = atoi(temp_char);
540  if (hash_algo != 1) {
541  dual_log("ERROR: NSEC3 Hash algorithm for %s Policy "
542  "in %s is %d but should be 1", policy_name,
543  kasp, hash_algo);
544  status++;
545  }
546  StrFree(temp_char);
547  }
548  childNode3 = childNode3->next;
549  }
550  }
551 
552  childNode2 = childNode2->next;
553  }
554  }
555 
556  childNode = childNode->next;
557  }
558  }
559  else if (xmlStrEqual(curNode->name, (const xmlChar *)"Keys")) {
560  childNode = curNode->children;
561  while (childNode) {
562 
563  if (xmlStrEqual(childNode->name, (const xmlChar *)"TTL")) {
564  temp_char = (char *) xmlNodeGetContent(childNode);
565  status += check_time_def(temp_char, my_policy, "Keys/TTL", kasp, &ttl);
566  StrFree(temp_char);
567  }
568  else if (xmlStrEqual(childNode->name, (const xmlChar *)"RetireSafety")) {
569  temp_char = (char *) xmlNodeGetContent(childNode);
570  status += check_time_def(temp_char, my_policy, "Keys/RetireSafety", kasp, &retire);
571  StrFree(temp_char);
572  }
573  else if (xmlStrEqual(childNode->name, (const xmlChar *)"PublishSafety")) {
574  temp_char = (char *) xmlNodeGetContent(childNode);
575  status += check_time_def(temp_char, my_policy, "Keys/PublishSafety", kasp, &publish);
576  StrFree(temp_char);
577  }
578  else if (xmlStrEqual(childNode->name, (const xmlChar *)"KSK")) {
579  childNode2 = childNode->children;
580  if (!curkey) {
581  firstkey = curkey = (struct key*) malloc(sizeof *curkey);
582  } else {
583  curkey->next = (struct key*) malloc(sizeof *curkey);
584  curkey = curkey->next;
585  }
586  memset(curkey, 0, sizeof *curkey);
587  curkey->type = KSK;
588 
589  while (childNode2){
590 
591  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Algorithm")) {
592  temp_char = (char *) xmlNodeGetContent(childNode2);
593  StrStrtoi(temp_char, &curkey->algo);
594  StrFree(temp_char);
595 
596  temp_char = (char *)xmlGetProp(childNode2, (const xmlChar *)"length");
597  StrStrtoi(temp_char, &curkey->length);
598  StrFree(temp_char);
599  }
600  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Lifetime")) {
601  temp_char = (char *) xmlNodeGetContent(childNode2);
602  status += check_time_def(temp_char, my_policy, "Keys/KSK Lifetime", kasp, &curkey->life);
603  StrFree(temp_char);
604  }
605  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Repository")) {
606  curkey->repo = (char *) xmlNodeGetContent(childNode2);
607  }
608 
609  childNode2 = childNode2->next;
610  }
611  }
612  else if (xmlStrEqual(childNode->name, (const xmlChar *)"ZSK")) {
613  childNode2 = childNode->children;
614  if (!curkey) {
615  firstkey = curkey = (struct key*) malloc(sizeof *curkey);
616  } else {
617  curkey->next = (struct key*) malloc(sizeof *curkey);
618  curkey = curkey->next;
619  }
620  memset(curkey, 0, sizeof *curkey);
621  curkey->type = ZSK;
622 
623  while (childNode2){
624 
625  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Algorithm")) {
626  temp_char = (char *) xmlNodeGetContent(childNode2);
627  StrStrtoi(temp_char, &curkey->algo);
628  StrFree(temp_char);
629 
630  temp_char = (char *)xmlGetProp(childNode2, (const xmlChar *)"length");
631  StrStrtoi(temp_char, &curkey->length);
632  StrFree(temp_char);
633 
634  }
635  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Lifetime")) {
636  temp_char = (char *) xmlNodeGetContent(childNode2);
637  status += check_time_def(temp_char, my_policy, "Keys/ZSK Lifetime", kasp, &curkey->life);
638  StrFree(temp_char);
639  }
640  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Repository")) {
641  curkey->repo = (char *) xmlNodeGetContent(childNode2);
642  }
643 
644  childNode2 = childNode2->next;
645  }
646  }
647  else if (xmlStrEqual(childNode->name, (const xmlChar *)"CSK")) {
648  childNode2 = childNode->children;
649  if (!curkey) {
650  firstkey = curkey = (struct key*) malloc(sizeof *curkey);
651  } else {
652  curkey->next = (struct key*) malloc(sizeof *curkey);
653  curkey = curkey->next;
654  }
655  memset(curkey, 0, sizeof *curkey);
656  curkey->type = CSK;
657 
658  while (childNode2){
659 
660  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Algorithm")) {
661  temp_char = (char *) xmlNodeGetContent(childNode2);
662  StrStrtoi(temp_char, &curkey->algo);
663  StrFree(temp_char);
664 
665  temp_char = (char *)xmlGetProp(childNode2, (const xmlChar *)"length");
666  StrStrtoi(temp_char, &curkey->length);
667  StrFree(temp_char);
668 
669  }
670  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Lifetime")) {
671  temp_char = (char *) xmlNodeGetContent(childNode2);
672  status += check_time_def(temp_char, my_policy, "Keys/CSK Lifetime", kasp, &curkey->life);
673  StrFree(temp_char);
674  }
675  else if (xmlStrEqual(childNode2->name, (const xmlChar *)"Repository")) {
676  curkey->repo = (char *) xmlNodeGetContent(childNode2);
677  }
678 
679  childNode2 = childNode2->next;
680  }
681  }
682 
683  childNode = childNode->next;
684  }
685  }
686  else if (xmlStrEqual(curNode->name, (const xmlChar *)"Zone")) {
687  childNode = curNode->children;
688  while (childNode) {
689 
690  if (xmlStrEqual(childNode->name, (const xmlChar *)"SOA")) {
691  childNode2 = childNode->children;
692  while (childNode2){
693 
694  if (xmlStrEqual(childNode2->name, (const xmlChar *)"Serial")) {
695  serial = (char *) xmlNodeGetContent(childNode2);
696  }
697 
698  childNode2 = childNode2->next;
699  }
700  }
701 
702  childNode = childNode->next;
703  }
704  }
705  else if (xmlStrEqual(curNode->name, (const xmlChar *)"Parent")) {
706  childNode = curNode->children;
707  while (childNode) {
708 
709  if (xmlStrEqual(childNode->name, (const xmlChar *)"DS")) {
710  childNode2 = childNode->children;
711  while (childNode2){
712 
713  if (xmlStrEqual(childNode2->name, (const xmlChar *)"TTL")) {
714  temp_char = (char *) xmlNodeGetContent(childNode2);
715  status += check_time_def(temp_char, my_policy, "Parent/DS/TTL", kasp, &ds_ttl);
716  StrFree(temp_char);
717  }
718 
719  childNode2 = childNode2->next;
720  }
721  }
722 
723  childNode = childNode->next;
724  }
725  }
726 
727 
728  curNode = curNode->next;
729  }
730 
731  /* Now for the actual tests, from
732  * https://wiki.opendnssec.org/display/OpenDNSSEC/Configuration+Checker+%28ods-kaspcheck%29 */
733 
734  for (curkey = firstkey; curkey; curkey = curkey->next) {
735  if ((curkey->type & KSK) && ds_ttl + ttl >= curkey->life) {
736  dual_log("ERROR: KSK/Lifetime (%d seconds) for policy '%s' "
737  "must be greater than the DNSKEY record TTL (%d seconds) plus "
738  "the DS record TTL (%d seconds). This time is needed to pass for the "
739  "KSK to be able to reach the ready state.",
740  curkey->life, policy_name, ttl, ds_ttl);
741  status++;
742  }
743 
744  if ((curkey->type & ZSK) && maxzone_ttl + ttl >= curkey->life) {
745  dual_log("ERROR: ZSK/Lifetime (%d seconds) for policy '%s' "
746  "must be greater than the DNSKEY record TTL (%d seconds) plus "
747  "the MaxZoneTTL (%d seconds). This time is needed to pass for the "
748  "ZSK to be able to reach the ready state.",
749  curkey->life, policy_name, ttl, maxzone_ttl);
750  status++;
751  }
752  }
753  /* For all policies, check that the "Re-sign" interval is less
754  * than the "Refresh" interval. */
755  if (refresh <= resign) {
756  dual_log("ERROR: The Refresh interval (%d seconds) for "
757  "%s Policy in %s is less than or equal to the Resign interval "
758  "(%d seconds)", refresh, policy_name, kasp, resign);
759  status++;
760  }
761 
762  /* Ensure that the "Default" and "Denial" validity periods are
763  * greater than the "Refresh" interval. */
764  if (defalt <= refresh) {
765  dual_log("ERROR: Validity/Default (%d seconds) for "
766  "%s policy in %s is less than or equal to the Refresh interval "
767  "(%d seconds)", defalt, policy_name, kasp, refresh);
768  status++;
769  }
770  if (denial <= refresh) {
771  dual_log("ERROR: Validity/Denial (%d seconds) for "
772  "%s policy in %s is less than or equal to the Refresh interval "
773  "(%d seconds)", denial, policy_name, kasp, refresh);
774  status++;
775  }
776 
777  /* Warn if "Jitter" is greater than 50% of the maximum of the "default"
778  * and "Denial" period. (This is a bit arbitrary. The point is to get
779  * the user to realise that there will be a large spread in the signature
780  * lifetimes.) */
781  if (defalt > denial) {
782  if (jitter > (defalt * 0.5)) {
783  dual_log("WARNING: Jitter time (%d seconds) is large "
784  "compared to Validity/Default (%d seconds) "
785  "for %s policy in %s", jitter, defalt, policy_name, kasp);
786  }
787  } else {
788  if (jitter > (denial * 0.5)) {
789  dual_log("WARNING: Jitter time (%d seconds) is large "
790  "compared to Validity/Denial (%d seconds) "
791  "for %s policy in %s", jitter, denial, policy_name, kasp);
792  }
793  }
794 
795 
796  /* Warn if the InceptionOffset is greater than one hour. (Again arbitrary
797  * - but do we really expect the times on two systems to differ by more
798  * than this?) */
799  if (inception > 3600) {
800  dual_log("WARNING: InceptionOffset is higher than expected "
801  "(%d seconds) for %s policy in %s",
802  inception, policy_name, kasp);
803  }
804 
805  /* Warn if the "PublishSafety" and "RetireSafety" margins are less
806  * than 0.1 * TTL or more than 5 * TTL. */
807  if (publish < (ttl * 0.1)) {
808  dual_log("WARNING: Keys/PublishSafety (%d seconds) is less than "
809  "0.1 * TTL (%d seconds) for %s policy in %s",
810  publish, ttl, policy_name, kasp);
811  }
812  else if (publish > (ttl * 5)) {
813  dual_log("WARNING: Keys/PublishSafety (%d seconds) is greater than "
814  "5 * TTL (%d seconds) for %s policy in %s",
815  publish, ttl, policy_name, kasp);
816  }
817 
818  if (retire < (ttl * 0.1)) {
819  dual_log("WARNING: Keys/RetireSafety (%d seconds) is less than "
820  "0.1 * TTL (%d seconds) for %s policy in %s",
821  retire, ttl, policy_name, kasp);
822  }
823  else if (retire > (ttl * 5)) {
824  dual_log("WARNING: Keys/RetireSafety (%d seconds) is greater than "
825  "5 * TTL (%d seconds) for %s policy in %s",
826  retire, ttl, policy_name, kasp);
827  }
828 
829  /* The algorithm should be checked to ensure it is consistent with the
830  * NSEC/NSEC3 choice for the zone. */
831  if (nsec == 1) {
832  }
833  else if (nsec == 3) {
834  for (curkey = firstkey; curkey; curkey = curkey->next) {
835  if ((curkey->type & KSK) && curkey->algo <= 5) {
836  dual_log("ERROR: In policy %s, incompatible algorithm (%d) used for "
837  "KSK NSEC3 in %s.", policy_name, curkey->algo, kasp);
838  status++;
839  }
840  if ((curkey->type & ZSK) && curkey->algo <= 5) {
841  dual_log("ERROR: In policy %s, incompatible algorithm (%d) used for "
842  "ZSK NSEC3 in %s.", policy_name, curkey->algo, kasp);
843  status++;
844  }
845  }
846 
847  /* Warn if resalt is less than resign interval. */
848  if (resalt < resign) {
849  dual_log("WARNING: NSEC3 resalt interval (%d secs) is less than "
850  "signature resign interval (%d secs) for %s Policy",
851  resalt, resign, policy_name);
852  }
853 
854  }
855 
856  /* If datecounter is used for serial, then no more than 99 signings
857  * should be done per day (there are only two digits to play with in the
858  * version number). */
859  if (serial != NULL && strncmp(serial, "datecounter", 11) == 0) {
860  if (resign != 0) {
861  resigns_per_day = (60 * 60 * 24) / resign;
862  if (resigns_per_day > 99) {
863  dual_log("ERROR: In %s, policy %s, serial type datecounter used "
864  "but %d re-signs requested. No more than 99 re-signs per "
865  "day should be used with datecounter as only 2 digits are "
866  "allocated for the version number.",
867  kasp, policy_name, resigns_per_day);
868  status++;
869  }
870  }
871  }
872 
873  /* The key strength should be checked for sanity
874  * - warn if less than 1024 or error if more than 4096.
875  * Only do this check for RSA. */
876  for (curkey = firstkey; curkey; curkey = curkey->next) {
877  if ((curkey->type & KSK) && (curkey->algo == 5 ||
878  curkey->algo == 7 ||curkey->algo == 8 ||
879  curkey->algo == 10)) {
880  if (curkey->length < 1024) {
881  dual_log("WARNING: Key length of %d used for KSK in %s policy in %s. Should "
882  "probably be 1024 or more", curkey->length, policy_name, kasp);
883  }
884  else if (curkey->length > 4096) {
885  dual_log("ERROR: Key length of %d used for KSK in %s policy in %s. Should "
886  "be 4096 or less", curkey->length, policy_name, kasp);
887  status++;
888  }
889  }
890  if ((curkey->type & ZSK) && (curkey->algo == 5 ||
891  curkey->algo == 7 || curkey->algo == 8 ||
892  curkey->algo == 10)) {
893  if (curkey->length < 1024) {
894  dual_log("WARNING: Key length of %d used for ZSK in %s policy in %s. Should "
895  "probably be 1024 or more", curkey->length, policy_name, kasp);
896  }
897  else if (curkey->length > 4096) {
898  dual_log("ERROR: Key length of %d used for ZSK in %s policy in %s. Should "
899  "be 4096 or less", curkey->length, policy_name, kasp);
900  status++;
901  }
902  }
903  }
904 
905  /* Check that repositories listed in the KSK and ZSK sections are defined
906  * in conf.xml. */
907  if (repo_list) {
908  for (curkey = firstkey; curkey; curkey = curkey->next) {
909  if ((curkey->type & KSK) && curkey->repo != NULL) {
910  for (i = 0; i < repo_count; i++) {
911  if (strcmp(curkey->repo, repo_list[i]) == 0) {
912  break;
913  }
914  }
915  if (i >= repo_count) {
916  dual_log("ERROR: Unknown repository (%s) defined for KSK in "
917  "%s policy in %s", curkey->repo, policy_name, kasp);
918  status++;
919  }
920  }
921 
922  if ((curkey->type & ZSK) && curkey->repo != NULL) {
923  for (i = 0; i < repo_count; i++) {
924  if (strcmp(curkey->repo, repo_list[i]) == 0) {
925  break;
926  }
927  }
928  if (i >= repo_count) {
929  dual_log("ERROR: Unknown repository (%s) defined for ZSK in "
930  "%s policy", curkey->repo, policy_name);
931  status++;
932  }
933  }
934  }
935  }
936  /* O(n^2). But this is probably a small set */
937  for (curkey = firstkey; curkey; curkey = curkey->next) {
938  if (!(curkey->type & KSK)) continue;
939  for (tmpkey = firstkey; tmpkey; tmpkey = tmpkey->next) {
940  if (!(tmpkey->type & ZSK)) continue;
941  if (tmpkey->algo != curkey->algo) continue;
942  /* Warn if for any zone, the KSK lifetime is less than the ZSK lifetime. */
943  if (curkey->life < tmpkey->life) {
944  dual_log("WARNING: KSK minimum lifetime (%d seconds) is less than "
945  "ZSK minimum lifetime (%d seconds) for %s Policy in %s",
946  curkey->life, tmpkey->life, policy_name, kasp);
947  }
948  }
949  }
950 
951  /* Check that the value of the "Serial" tag is valid. (Done by rng) */
952 
953  /* Error if Jitter is greater than either the Default or Denial Validity. */
954  if (jitter > defalt) {
955  dual_log("ERROR: Jitter time (%d seconds) is greater than the "
956  "Default Validity (%d seconds) for %s policy in %s",
957  jitter, defalt, policy_name, kasp);
958  status++;
959  }
960  if (jitter > denial) {
961  dual_log("ERROR: Jitter time (%d seconds) is greater than the "
962  "Denial Validity (%d seconds) for %s policy in %s",
963  jitter, denial, policy_name, kasp);
964  status++;
965  }
966  while (curkey) {
967  tmpkey = curkey;
968  curkey = curkey->next;
969  StrFree(tmpkey->repo);
970  free(tmpkey);
971  }
972  StrFree(serial);
973 
974  return status;
975 }
976 
977 /* NOTE: The following are taken from various files within libksm */
978 
979 /*+
980  * DtXMLIntervalSeconds - Parse xsd:durations Interval String
981  *
982  * Description:
983  * Parses an interval string which is of the form:
984  *
985  * P<number>
986  * or P<number><interval-type>
987  * or PT<number><interval-type> (if the interval-type is H, M or S)
988  *
989  * Without an interval type, the interval is assumed to be in seconds.
990  * Otherwise, the following interval types recognised are:
991  *
992  * S Seconds
993  * M Minutes - multiply number by 60 (no. seconds in a minute)
994  * H Hours - multiply number by 3600 (no. seconds in an hour)
995  * D Day - multiply number by 86400 (no. seconds in a day)
996  * W Week - multiply number by 604,800 (no. seconds in a week)
997  * M Month - multiply number by 2,678,400 (no. seconds in 31 days)
998  * Y Year - multiply number by 31,536,000 (no. seconds in 365 days)
999  *
1000  * Lower-case characters are not recognised.
1001  *
1002  * Example: The string P2D would translate to 172,800
1003  *
1004  * Arguments:
1005  * const char* text
1006  * Interval as a string.
1007  *
1008  * long* interval
1009  * Returned interval.
1010  *
1011  * Returns:
1012  * int
1013  * < 0 Success, string translated OK _BUT_ may not be what was expected
1014  * (Year or Month used which gives approximate answer).
1015  * 0 Success, string translated OK
1016  * 2 Error - unable to translate string.
1017  * 3 Error - string too long to be a number.
1018  * 4 Error - invalid pointers or text string NULL.
1019  *
1020  * Known issues:
1021  *
1022  * 1. Years and months are only approximate as it has no concept of "now"
1023  * We use 31 days = 1 month and 365 days = 1 year.
1024  * 2. The "T" only effects the value of "M" (P1S should be illegal as correctly
1025  * it would be PT1S)
1026  *
1027  * NOTE: This is copied from ksm/datatime.c and modified slightly to separate
1028  * "Y" and "M" warnings
1029  *
1030 -*/
1031 
1032 int DtXMLIntervalSeconds(const char* text, int* interval)
1033 {
1034  int length = 0; /* Length of the string */
1035  short is_time = 0; /* Do we have a Time section or not */
1036  short is_neg = 0; /* Do we have a negative number */
1037  short warning = 0; /* Do we need a warning code for duration approximation? */
1038  short got_temp = 0; /* Have we seen a number? */
1039  long temp = 0; /* Number from this section */
1040  const char *ptr = text; /* allow us to read through */
1041  const char *end;
1042  long temp_interval = 0;
1043 
1044  if (!text || !interval || !*text) return 4;
1045  length = strlen(text);
1046  if (length <= 2) return 2;
1047 
1048  if (*ptr == '-') {
1049  is_neg = 1;
1050  ptr++;
1051  }
1052  if (*ptr != 'P') return 2;
1053  ptr++;
1054 
1055  end = text + length;
1056  while (ptr < end) {
1057  switch (*ptr) {
1058  case 'S':
1059  if (!got_temp || !is_time) return 2;
1060  temp_interval += temp;
1061  temp = 0;
1062  got_temp = 0;
1063  break;
1064 
1065  case 'M':
1066  if (!got_temp) return 2;
1067  if (is_time) {
1068  temp_interval += 60 * temp;
1069  } else {
1070  temp_interval += 31 * 24 * 60 * 60 * temp;
1071  warning -= 1; /* month is an ambiguous period */
1072  }
1073  temp = 0;
1074  got_temp = 0;
1075  break;
1076 
1077  case 'H':
1078  if (!got_temp || !is_time) return 2;
1079  temp_interval += 60 * 60 * temp;
1080  temp = 0;
1081  got_temp = 0;
1082  break;
1083 
1084  case 'D':
1085  if (!got_temp || is_time) return 2;
1086  temp_interval += 24 * 60 * 60 * temp;
1087  temp = 0;
1088  got_temp = 0;
1089  break;
1090 
1091  case 'W':
1092  if (!got_temp || is_time) return 2;
1093  temp_interval += 7 * 24 * 60 * 60 * temp;
1094  temp = 0;
1095  got_temp = 0;
1096  break;
1097 
1098  case 'Y':
1099  if (!got_temp || is_time) return 2;
1100  temp_interval += 365 * 24 * 60 * 60 * temp;
1101  temp = 0;
1102  warning -= 2; /* year is an ambiguous period */
1103  got_temp = 0;
1104  break;
1105 
1106  case 'T':
1107  is_time = 1;
1108  break;
1109 
1110  case '0':
1111  case '1':
1112  case '2':
1113  case '3':
1114  case '4':
1115  case '5':
1116  case '6':
1117  case '7':
1118  case '8':
1119  case '9':
1120  if (!temp) {
1121  char *endptr;
1122  temp = strtol(ptr, &endptr, 10);
1123  if (temp == LONG_MIN || temp == LONG_MAX)
1124  return 3;
1125  got_temp = 1;
1126  ptr = endptr-1;
1127  }
1128  break;
1129 
1130  default:
1131  /* encountered unparsable char */
1132  if (ptr != end) return 2;
1133  }
1134  ptr++;
1135  }
1136 
1137  /* If we had no trailing letter then it is an implicit "S"
1138  * But only if is_time is not set.*/
1139  if (temp && !is_time) return 2;
1140  temp_interval += temp;
1141 
1142  if (is_neg) temp_interval *= -1;
1143  *interval = (int) temp_interval;
1144  return warning;
1145 }
1146 
1147 /*+
1148  * StrStrtoi - Convert String to int
1149  *
1150  * Description:
1151  * Converts a string to a "int".
1152  *
1153  * This version strips out tabs and whitespace characters.
1154  *
1155  * Arguments:
1156  * const char* string (input)
1157  * String to convert.
1158  *
1159  * int* value (returned)
1160  * Return value.
1161  *
1162  * Returns:
1163  * int
1164  * 0 Success
1165  * 1 Conversion failed
1166 -*/
1167 
1168 int StrStrtoi(const char* string, int* value)
1169 {
1170  long longval; /* "long" to be passed to StrStrtol */
1171  int status; /* Status return */
1172 
1173  if (value == NULL) {
1174  dual_log("ERROR: NULL value passed to StrStrtoi");
1175  return 1;
1176  }
1177  status = StrStrtol(string, &longval);
1178  if (status == 0) {
1179  if ((longval >= INT_MIN) && (longval <= INT_MAX)) {
1180  *value = (int) longval;
1181  }
1182  else {
1183  status = 1; /* Integer overflow */
1184  }
1185  }
1186 
1187  return status;
1188 }
1189 
1190 /*+
1191  * StrStrtol - Convert String to long
1192  *
1193  * Description:
1194  * Converts a string to a "long". It uses strtol, but also passes
1195  * back a status code to indicate if the conversion was successful.
1196  *
1197  * This version strips out tabs and whitespace characters.
1198  *
1199  * Arguments:
1200  * const char* string (input)
1201  * String to convert.
1202  *
1203  * long* value (returned)
1204  * Return value.
1205  *
1206  * Returns:
1207  * int
1208  * 0 Success
1209  * 1 Conversion failed
1210 -*/
1211 
1212 int StrStrtol(const char* string, long* value)
1213 {
1214  char* endptr; /* End of string pointer */
1215  int status = 1; /* Assume failure */
1216  char* copy; /* Copy of the string */
1217  char* start; /* Start of the trimmed string */
1218 
1219  if (value == NULL) {
1220  dual_log("ERROR: NULL value passed to StrStrtol");
1221  return 1;
1222  }
1223  if (string) {
1224  copy = StrStrdup(string);
1225  StrTrimR(copy); /* Remove trailing spaces */
1226  start = StrTrimL(copy); /* ... and leading ones */
1227  if (*start) {
1228 
1229  /* String is not NULL, so try a conversion */
1230 
1231  errno = 0;
1232  *value = strtol(start, &endptr, 10);
1233 
1234  /* Only success if all characters converted */
1235 
1236  if (errno == 0) {
1237  status = (*endptr == '\0') ? 0 : 1;
1238  }
1239  else {
1240  status = 1;
1241  }
1242  }
1243  StrFree(copy);
1244  }
1245 
1246  return status;
1247 }
1248 
1249 /*+
1250  * StrStrdup - Duplicate String
1251  *
1252  * Description:
1253  * Wrapper for "strdup" that always returns, or exits the program (after
1254  * outputting a message to stderr) if the string duplication fails.
1255  *
1256  * Arguments:
1257  * const char* string (input)
1258  * String to be duplicated.
1259  *
1260  * Returns:
1261  * char*
1262  * Pointer to duplicated string (guaranteed to be non-null). The
1263  * string should be freed with StrFree() - a macro wrapper for "free".
1264 -*/
1265 
1266 char* StrStrdup(const char* string)
1267 {
1268  char* duplicate = NULL; /* Pointer to the duplicated string */
1269 
1270  if (string) {
1271  duplicate = strdup(string);
1272  if (duplicate == NULL) {
1273  dual_log("ERROR: StrStrdup: Call to malloc() returned null - out of swap space?");
1274  exit(1);
1275  }
1276  }
1277  else {
1278  duplicate = MemCalloc(1, 1); /* Allocate a single zeroed byte */
1279  }
1280 
1281  return duplicate;
1282 }
1283 
1284 /*+
1285  * StrAppend - Append String with Reallocation
1286  *
1287  * Description:
1288  * Appends the given string to a dynamically-allocated string, reallocating
1289  * the former as needed.
1290  *
1291  * The function is a no-op if either of its arguments are NULL.
1292  *
1293  * Arguments:
1294  * char** str1
1295  * On input this holds the current string. It is assumed that the
1296  * string has been dynamically allocated (with malloc or the like).
1297  * On output, this holds the concatenation of the two strings.
1298  *
1299  * If, on input, the string is NULL (i.e. *str is NULL, *not* str1 is
1300  * NULL), a new string is allocated and str2 copied to it.
1301  *
1302  * On exit, the string can be freed via a call to StrFree.
1303  *
1304  * const char* str2
1305  * The string to be appended.
1306 -*/
1307 
1308 /*+
1309  * StrTrimR - Trim Right
1310  *
1311  * Description:
1312  * Modifies a string by trimming white-space characters from the right of
1313  * the string. It does this by modifying the string, inserting a null
1314  * character after the last non white-space character.
1315  *
1316  * Arguments:
1317  * char *text (modified)
1318  * Text to modify. If this is NULL, the routine is a no-op.
1319  *
1320  * Returns:
1321  * void
1322 -*/
1323 
1324 void StrTrimR(char *text)
1325 {
1326  if (text) {
1327 
1328  /* Work backwards through the string */
1329 
1330  int textlen = strlen(text);
1331  while (-- textlen >= 0) {
1332  if (! isspace((int) text[textlen])) {
1333  text[textlen + 1] = '\0';
1334  return;
1335  }
1336  }
1337 
1338  /* Get here if the entire string is white space */
1339 
1340  text[0] = '\0';
1341  }
1342 }
1343 
1344 /*+
1345  * StrTrimL - Trim Left
1346  *
1347  * Description:
1348  * Searches a string and returns a pointer to the first non white-space
1349  * character in it.
1350  *
1351  * Arguments:
1352  * char* text (input)
1353  * Text to search.
1354  *
1355  * Returns:
1356  * char*
1357  * Pointer to first non white-space character in the string. If the
1358  * string is NULL, NULL is returned. If the string is all white space,
1359  * a pointer to the trailing null character is returned.
1360 -*/
1361 
1362 char* StrTrimL(char* text)
1363 {
1364  if (text) {
1365  while (*text && isspace((int) *text)) {
1366  ++text;
1367  }
1368  }
1369 
1370  return text;
1371 }
1372 
1373 void* MemCalloc(size_t nmemb, size_t size)
1374 {
1375  void *ptr = calloc(nmemb, size);
1376  if (ptr == NULL) {
1377  dual_log("ERROR: calloc: Out of swap space");
1378  exit(1);
1379  }
1380  return ptr;
1381 }
1382 
1383 /* Used to squelch libxml output when linked in Enforcer */
1384 static void quiet_error_func(void * ctx, const char * msg, ...)
1385 {
1386  (void)ctx; (void)msg;
1387 }
1388 
1395 int check_conf(const char *conf, char **kasp, char **zonelist,
1396  char ***repo_listout, int *repo_countout, int verbose)
1397 {
1398  int status = 0;
1399  int i = 0;
1400  int j = 0;
1401  int temp_status = 0;
1402  char **repo_list;
1403  int repo_count = 0;
1404 
1405  xmlDocPtr doc;
1406  xmlXPathContextPtr xpath_ctx;
1407  xmlXPathObjectPtr xpath_obj;
1408  xmlNode *curNode;
1409  xmlChar *xexpr;
1410  char* signer_dir = NULL;
1411  int signer_dir_default = 0;
1412  char* enforcer_dir = NULL;
1413  int enforcer_dir_default = 0;
1414 
1415  KC_REPO* repo = NULL;
1416  int* repo_mods = NULL; /* To see if we have looked at this module before */
1417 
1419  xmlSetGenericErrorFunc(NULL, quiet_error_func);
1420 
1421  /* Check that the file is well-formed */
1422  status = check_rng(conf, OPENDNSSEC_SCHEMA_DIR "/conf.rng", verbose);
1423 
1424  /* Don't try to read the file if it is invalid */
1425  if (status != 0) return status;
1426  dual_log("INFO: The XML in %s is valid", conf);
1427 
1428  /* Load XML document */
1429  doc = xmlParseFile(conf);
1430  if (doc == NULL) return 1;
1431 
1432  /* Create xpath evaluation context */
1433  xpath_ctx = xmlXPathNewContext(doc);
1434  if(xpath_ctx == NULL) {
1435  xmlFreeDoc(doc);
1436  return 1;
1437  }
1438 
1439  /* REPOSITORY section */
1440  xexpr = (xmlChar *)"//Configuration/RepositoryList/Repository";
1441  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
1442  if(xpath_obj == NULL) {
1443  xmlXPathFreeContext(xpath_ctx);
1444  xmlFreeDoc(doc);
1445  return 1;
1446  }
1447 
1448  if (xpath_obj->nodesetval) {
1449  repo_count = xpath_obj->nodesetval->nodeNr;
1450  *repo_countout = repo_count;
1451 
1452  repo = (KC_REPO*)malloc(sizeof(KC_REPO) * repo_count);
1453  repo_mods = (int*)malloc(sizeof(int) * repo_count);
1454  repo_list = (char**)malloc(sizeof(char*) * repo_count);
1455  *repo_listout = repo_list;
1456 
1457  if (repo == NULL || repo_mods == NULL || repo_list == NULL) {
1458  dual_log("ERROR: malloc for repo information failed");
1459  exit(1);
1460  }
1461 
1462  for (i = 0; i < repo_count; i++) {
1463  repo_mods[i] = 0;
1464 
1465  curNode = xpath_obj->nodesetval->nodeTab[i]->xmlChildrenNode;
1466  /* Default for capacity */
1467 
1468  repo[i].name = (char *) xmlGetProp(xpath_obj->nodesetval->nodeTab[i],
1469  (const xmlChar *)"name");
1470  repo_list[i] = StrStrdup(repo[i].name);
1471 
1472  while (curNode) {
1473  if (xmlStrEqual(curNode->name, (const xmlChar *)"TokenLabel"))
1474  repo[i].TokenLabel = (char *) xmlNodeGetContent(curNode);
1475  if (xmlStrEqual(curNode->name, (const xmlChar *)"Module"))
1476  repo[i].module = (char *) xmlNodeGetContent(curNode);
1477  curNode = curNode->next;
1478  }
1479  }
1480  }
1481  xmlXPathFreeObject(xpath_obj);
1482 
1483  /* Now we have all the information we need do the checks */
1484  for (i = 0; i < repo_count; i++) {
1485 
1486  if (repo_mods[i] == 0) {
1487 
1488  /* 1) Check that the module exists */
1489  status += check_file(repo[i].module, "Module");
1490 
1491  repo_mods[i] = 1; /* Done this module */
1492 
1493  /* 2) Check repos on the same modules have different TokenLabels */
1494  for (j = i+1; j < repo_count; j++) {
1495  if ( repo_mods[j] == 0 &&
1496  (strcmp(repo[i].module, repo[j].module) == 0) ) {
1497  repo_mods[j] = 1; /* done */
1498 
1499  if (strcmp(repo[i].TokenLabel, repo[j].TokenLabel) == 0) {
1500  dual_log("ERROR: Multiple Repositories (%s and %s) in %s have the same Module (%s) and TokenLabel (%s)", repo[i].name, repo[j].name, conf, repo[i].module, repo[i].TokenLabel);
1501  status += 1;
1502  }
1503  }
1504  }
1505  }
1506 
1507  /* 3) Check that the name is unique */
1508  for (j = i+1; j < repo_count; j++) {
1509  if (strcmp(repo[i].name, repo[j].name) == 0) {
1510  dual_log("ERROR: Two repositories exist with the same name (%s)", repo[i].name);
1511  status += 1;
1512  }
1513  }
1514  }
1515 
1516  /* COMMON section */
1517  /* PolicyFile (aka KASP); we will validate it later */
1518  if (*kasp == NULL) {
1519  xexpr = (xmlChar *)"//Configuration/Common/PolicyFile";
1520  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
1521  if(xpath_obj == NULL) {
1522  xmlXPathFreeContext(xpath_ctx);
1523  xmlFreeDoc(doc);
1524 
1525  for (i = 0; i < repo_count; i++) {
1526  free(repo[i].name);
1527  free(repo[i].module);
1528  free(repo[i].TokenLabel);
1529  }
1530  free(repo);
1531  free(repo_mods);
1532 
1533  return -1;
1534  }
1535  *kasp = (char*) xmlXPathCastToString(xpath_obj);
1536  xmlXPathFreeObject(xpath_obj);
1537  }
1538 
1539  if (*zonelist == NULL) {
1540  xexpr = (xmlChar *)"//Configuration/Common/ZoneListFile";
1541  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
1542  if(xpath_obj == NULL) {
1543  xmlXPathFreeContext(xpath_ctx);
1544  xmlFreeDoc(doc);
1545 
1546  for (i = 0; i < repo_count; i++) {
1547  free(repo[i].name);
1548  free(repo[i].module);
1549  free(repo[i].TokenLabel);
1550  }
1551  free(repo);
1552  free(repo_mods);
1553 
1554  return -1;
1555  }
1556  *zonelist = (char*) xmlXPathCastToString(xpath_obj);
1557  xmlXPathFreeObject(xpath_obj);
1558  }
1559 
1560  /* ENFORCER section */
1561 
1562  /* Check defined user/group */
1563  status += check_user_group(xpath_ctx,
1564  (xmlChar *)"//Configuration/Enforcer/Privileges/User",
1565  (xmlChar *)"//Configuration/Enforcer/Privileges/Group");
1566 
1567  /* Check datastore exists (if sqlite) */
1568  /* TODO check datastore matches libksm without building against libksm */
1569  temp_status = check_file_from_xpath(xpath_ctx, "SQLite datastore",
1570  (xmlChar *)"//Configuration/Enforcer/Datastore/SQLite");
1571  if (temp_status == -1) {
1572  /* Configured for Mysql DB */
1573  /*if (DbFlavour() != MYSQL_DB) {
1574  dual_log("ERROR: libksm compiled for sqlite3 but conf.xml configured for MySQL");
1575  }*/
1576  } else {
1577  status += temp_status;
1578  /* Configured for sqlite DB */
1579  /*if (DbFlavour() != SQLITE_DB) {
1580  dual_log("ERROR: libksm compiled for MySQL but conf.xml configured for sqlite3");
1581  }*/
1582  }
1583 
1584  /* Warn if Interval is M or Y */
1585  status += check_time_def_from_xpath(xpath_ctx, (xmlChar *)"//Configuration/Enforcer/Interval", "Configuration", "Enforcer/Interval", conf);
1586 
1587  /* Warn if RolloverNotification is M or Y */
1588  status += check_time_def_from_xpath(xpath_ctx, (xmlChar *)"//Configuration/Enforcer/RolloverNotification", "Configuration", "Enforcer/RolloverNotification", conf);
1589 
1590  status += check_interval(xpath_ctx,
1591  (xmlChar *)"//Configuration/Enforcer/Interval", conf);
1592 
1593  /* Check DelegationSignerSubmitCommand exists (if set) */
1594  temp_status = check_file_from_xpath(xpath_ctx, "DelegationSignerSubmitCommand",
1595  (xmlChar *)"//Configuration/Enforcer/DelegationSignerSubmitCommand");
1596  if (temp_status > 0) {
1597  status += temp_status;
1598  }
1599 
1600  /* Check Enforcer WorkingDirectory exists (or default)*/
1601  temp_status = check_path_from_xpath(xpath_ctx, "Enforcer WorkingDirectory",
1602  (xmlChar *)"//Configuration/Enforcer/WorkingDirectory");
1603  if (temp_status == -1) {
1604  /* Check the default location */
1605  temp_status = check_path(OPENDNSSEC_STATE_DIR "/enforcer",
1606  "default Enforcer WorkingDirectory");
1607  }
1608  if (temp_status > 0) {
1609  status += temp_status;
1610  }
1611 
1612  /* SIGNER section */
1613  /* Check defined user/group */
1614  status += check_user_group(xpath_ctx,
1615  (xmlChar *)"//Configuration/Signer/Privileges/User",
1616  (xmlChar *)"//Configuration/Signer/Privileges/Group");
1617 
1618  /* Check WorkingDirectory exists (or default) */
1619  temp_status = check_path_from_xpath(xpath_ctx, "Signer WorkingDirectory",
1620  (xmlChar *)"//Configuration/Signer/WorkingDirectory");
1621  if (temp_status == -1) {
1622  /* Check the default location */
1623  temp_status = check_path(OPENDNSSEC_STATE_DIR "/signer",
1624  "default Signer WorkingDirectory");
1625  }
1626  if (temp_status > 0) {
1627  status += temp_status;
1628  }
1629 
1630  /* Check signer workdirectory is not as same as the one of enforcer*/
1631  xexpr = (xmlChar *)"//Configuration/Signer/WorkingDirectory";
1632  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
1633  if (NULL == xpath_obj) {
1634  signer_dir = (char*) OPENDNSSEC_STATE_DIR "/signer";
1635  signer_dir_default = 1;
1636  }
1637  else {
1638  signer_dir = (char*) xmlXPathCastToString(xpath_obj);
1639  xmlXPathFreeObject(xpath_obj);
1640  }
1641  xexpr = (xmlChar *)"//Configuration/Enforcer/WorkingDirectory";
1642  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
1643  if (NULL == xpath_obj) {
1644  enforcer_dir = (char*) OPENDNSSEC_STATE_DIR "/enforcer";
1645  enforcer_dir_default = 1;
1646  }
1647  else {
1648  enforcer_dir = (char*) xmlXPathCastToString(xpath_obj);
1649  xmlXPathFreeObject(xpath_obj);
1650  }
1651  temp_status = strcmp(signer_dir, enforcer_dir);
1652  if (0 == temp_status) {
1653  status++;
1654  dual_log("ERROR: signer workingdirectory is the same as the one of enforcer");
1655  }
1656  if (0 == signer_dir_default)
1657  StrFree(signer_dir);
1658  if (0 == enforcer_dir_default)
1659  StrFree(enforcer_dir);
1660 
1661  xmlXPathFreeContext(xpath_ctx);
1662  xmlFreeDoc(doc);
1663 
1664  for (i = 0; i < repo_count; i++) {
1665  free(repo[i].name);
1666  free(repo[i].module);
1667  free(repo[i].TokenLabel);
1668  }
1669  free(repo);
1670  free(repo_mods);
1671 
1672  return status;
1673 }
1674 
1675 /*
1676  * Check the zonelist.xml file
1677  * Return status (0 == success; 1 == error)
1678  */
1679 int check_zonelist(const char *zonelist, int verbose, char **policy_names,
1680  int policy_count)
1681 {
1682  xmlDocPtr doc;
1683  xmlXPathContextPtr xpath_ctx;
1684  xmlXPathObjectPtr xpath_obj;
1685  xmlChar *xexpr;
1686  int i, j, found, status = 0;
1687  char *policy_name;
1688 
1689  if (!zonelist || !strncmp(zonelist, "", 1)) {
1690  dual_log("ERROR: No location for zonelist.xml set");
1691  return 1;
1692  }
1693 
1695  xmlSetGenericErrorFunc(NULL, quiet_error_func);
1696 
1697  /* Check that the Zonelist file is well-formed */
1698  if (check_rng(zonelist, OPENDNSSEC_SCHEMA_DIR "/zonelist.rng", verbose) != 0)
1699  return 1;
1700 
1701  if (policy_names) {
1702  doc = xmlParseFile(zonelist);
1703  if (doc == NULL) {
1704  return 1;
1705  }
1706 
1707  xpath_ctx = xmlXPathNewContext(doc);
1708  if(xpath_ctx == NULL) {
1709  xmlFreeDoc(doc);
1710  return 1;
1711  }
1712 
1713  xexpr = (xmlChar *)"//ZoneList/Zone/Policy";
1714  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
1715  if(xpath_obj == NULL) {
1716  xmlXPathFreeContext(xpath_ctx);
1717  xmlFreeDoc(doc);
1718  return 1;
1719  }
1720 
1721  if (xpath_obj->nodesetval) {
1722  for (i = 0; i < xpath_obj->nodesetval->nodeNr; i++) {
1723  policy_name = (char*)xmlNodeGetContent(xpath_obj->nodesetval->nodeTab[i]);
1724 
1725  found = 0;
1726  if (policy_name) {
1727  for (j = 0; j < policy_count; j++) {
1728  if (!strcmp(policy_name, policy_names[j])) {
1729  found = 1;
1730  break;
1731  }
1732  }
1733  }
1734  if (!found) {
1735  dual_log("ERROR: Policy %s in zonelist does not exist!", policy_name);
1736  status++;
1737  }
1738  if (policy_name) free(policy_name);
1739  }
1740  }
1741 
1742  xmlXPathFreeObject(xpath_obj);
1743  xmlXPathFreeContext(xpath_ctx);
1744  xmlFreeDoc(doc);
1745  }
1746 
1747  if (!status) dual_log("INFO: The XML in %s is valid", zonelist);
1748  return status;
1749 }
1750 
1751 /*
1752  * Check the kasp.xml file
1753  * Return status (0 == success; 1 == error)
1754  */
1755 int check_kasp(const char *kasp, char **repo_list, int repo_count, int verbose,
1756  char ***policy_names_out, int *policy_count_out)
1757 {
1758  int status = 0;
1759  int i = 0;
1760  int j = 0;
1761  xmlDocPtr doc;
1762  xmlXPathContextPtr xpath_ctx;
1763  xmlXPathObjectPtr xpath_obj;
1764  xmlNode *curNode;
1765  xmlChar *xexpr;
1766 
1767  int policy_count = 0;
1768  char **policy_names = NULL;
1769  int default_found = 0;
1770 
1772  xmlSetGenericErrorFunc(NULL, quiet_error_func);
1773 
1774  if (!kasp) {
1775  dual_log("ERROR: No location for kasp.xml set");
1776  return 1;
1777  }
1778 
1779 /* Check that the file is well-formed */
1780  status = check_rng(kasp, OPENDNSSEC_SCHEMA_DIR "/kasp.rng", verbose);
1781 
1782  if (status ==0) {
1783  dual_log("INFO: The XML in %s is valid", kasp);
1784  } else {
1785  return 1;
1786  }
1787 
1788  /* Load XML document */
1789  doc = xmlParseFile(kasp);
1790  if (doc == NULL) {
1791  return 1;
1792  }
1793 
1794  /* Create xpath evaluation context */
1795  xpath_ctx = xmlXPathNewContext(doc);
1796  if(xpath_ctx == NULL) {
1797  xmlFreeDoc(doc);
1798  return 1;
1799  }
1800 
1801  /* First pass through the whole document to test for a policy called "default" and no duplicate names */
1802 
1803  xexpr = (xmlChar *)"//KASP/Policy";
1804  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
1805  if(xpath_obj == NULL) {
1806  xmlXPathFreeContext(xpath_ctx);
1807  xmlFreeDoc(doc);
1808  return 1;
1809  }
1810 
1811  if (xpath_obj->nodesetval) {
1812  policy_count = xpath_obj->nodesetval->nodeNr;
1813 
1814  policy_names = (char**)malloc(sizeof(char*) * policy_count);
1815  if (policy_names == NULL) {
1816  dual_log("ERROR: Malloc for policy names failed");
1817  exit(1);
1818  }
1819 
1820  for (i = 0; i < policy_count; i++) {
1821 
1822  policy_names[i] = (char *) xmlGetProp(xpath_obj->nodesetval->nodeTab[i],
1823  (const xmlChar *)"name");
1824  }
1825  }
1826 
1827  /* Now we have all the information we need do the checks */
1828  for (i = 0; i < policy_count; i++) {
1829  if (strcmp(policy_names[i], "default") == 0) {
1830  default_found = 1;
1831  }
1832  for (j = i+1; j < policy_count; j++) {
1833  if ( (strcmp(policy_names[i], policy_names[j]) == 0) ) {
1834  dual_log("ERROR: Two policies exist with the same name (%s)", policy_names[i]);
1835  status += 1;
1836  }
1837  }
1838  }
1839  if (default_found == 0) {
1840  dual_log("WARNING: No policy named 'default' in %s. This means you will need to refer explicitly to the policy for each zone", kasp);
1841  }
1842 
1843  /* Go again; this time check each policy */
1844  for (i = 0; i < policy_count; i++) {
1845  curNode = xpath_obj->nodesetval->nodeTab[i]->xmlChildrenNode;
1846 
1847  status += check_policy(curNode, policy_names[i], repo_list, repo_count, kasp);
1848  }
1849 
1850  if (!status && policy_names_out && policy_count_out) {
1851  *policy_names_out = policy_names;
1852  *policy_count_out = policy_count;
1853  }
1854  else {
1855  for (i = 0; i < policy_count; i++) {
1856  free(policy_names[i]);
1857  }
1858  free(policy_names);
1859  }
1860 
1861  xmlXPathFreeObject(xpath_obj);
1862  xmlXPathFreeContext(xpath_ctx);
1863  xmlFreeDoc(doc);
1864 
1865  return status;
1866 }
int check_time_def(const char *time_expr, const char *location, const char *field, const char *filename, int *interval)
Definition: kc_helper.c:348
#define StrFree(ptr)
Definition: kc_helper.c:47
char * StrStrdup(const char *string)
Definition: kc_helper.c:1266
int check_path(const char *pathname, const char *log_string)
Definition: kc_helper.c:249
int check_conf(const char *conf, char **kasp, char **zonelist, char ***repo_listout, int *repo_countout, int verbose)
Definition: kc_helper.c:1395
#define KC_NAME_LENGTH
Definition: kc_helper.h:39
int DtXMLIntervalSeconds(const char *text, int *interval)
Definition: kc_helper.c:1032
void StrTrimR(char *text)
Definition: kc_helper.c:1324
char * name
Definition: kc_helper.h:42
const char * policy_name(const policy_t *policy)
Definition: policy.c:813
int check_policy(xmlNode *curNode, const char *policy_name, char **repo_list, int repo_count, const char *kasp)
Definition: kc_helper.c:430
int check_kasp(const char *kasp, char **repo_list, int repo_count, int verbose, char ***policy_names_out, int *policy_count_out)
Definition: kc_helper.c:1755
int check_zonelist(const char *zonelist, int verbose, char **policy_names, int policy_count)
Definition: kc_helper.c:1679
void * MemCalloc(size_t nmemb, size_t size)
Definition: kc_helper.c:1373
int check_rng(const char *filename, const char *rngfilename, int verbose)
Definition: kc_helper.c:88
int check_path_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *path_xexpr)
Definition: kc_helper.c:272
int check_file(const char *filename, const char *log_string)
Definition: kc_helper.c:192
int StrStrtol(const char *string, long *value)
Definition: kc_helper.c:1212
int kc_helper_printto_stdout
Definition: kc_helper.c:49
void log_init(int facility, const char *program_name)
Definition: kc_helper.c:51
char * TokenLabel
Definition: kc_helper.h:44
int StrStrtoi(const char *string, int *value)
Definition: kc_helper.c:1168
int check_interval(xmlXPathContextPtr xpath_ctx, const xmlChar *interval_xexpr, const char *filename)
Definition: kc_helper.c:408
void dual_log(const char *format,...)
Definition: kc_helper.c:59
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:385
int check_user_group(xmlXPathContextPtr xpath_ctx, const xmlChar *user_xexpr, const xmlChar *group_xexpr)
Definition: kc_helper.c:298
int check_file_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *file_xexpr)
Definition: kc_helper.c:216
char * StrTrimL(char *text)
Definition: kc_helper.c:1362