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