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