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