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