OpenDNSSEC-enforcer  1.4.10
kaspcheck.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 <stdio.h>
28 #include <getopt.h>
29 #include <string.h>
30 #include <syslog.h>
31 
32 #include "config.h"
33 
34 #include "kaspcheck.h"
35 #include "kc_helper.h"
36 
37 #include "ksm/database.h"
38 
39 #include <libxml/tree.h>
40 #include <libxml/parser.h>
41 #include <libxml/xpath.h>
42 #include <libxml/xpathInternals.h>
43 #include <libxml/relaxng.h>
44 
45 const char *progname = NULL;
46 
47 char *config = (char *) OPENDNSSEC_CONFIG_FILE;
48 char *kasp = NULL;
49 int verbose = 0;
50 char **repo_list = NULL;
51 int repo_count = 0;
52 
53 #define StrFree(ptr) {if(ptr != NULL) {free(ptr); (ptr) = NULL;}}
54 
55 /*
56  * Display usage
57  */
58 void usage ()
59 {
60  fprintf(stderr,
61  "usage: %s [options]\n\n"
62  "Options:\n"
63  " -c, --conf [PATH_TO_CONF_FILE] Path to OpenDNSSEC configuration file\n"
64  " (defaults to %s)\n"
65  " -k, --kasp [PATH_TO_KASP_FILE] Path to KASP policy file\n"
66  " (defaults to the path from the conf.xml file)\n"
67  " -V, --version Display the version information\n"
68  " -v, --verbose Print extra DEBUG messages\n"
69  " -h, --help Show this message\n", progname, OPENDNSSEC_CONFIG_FILE);
70 }
71 
72 /*
73  * Fairly basic main.
74  */
75 int main (int argc, char *argv[])
76 {
77  int status = 0; /* Will be non-zero on error (NOT warning) */
78  int ch;
79  int option_index = 0;
80  int i = 0;
81  int free_config = 0;
82  static struct option long_options[] =
83  {
84  {"config", required_argument, 0, 'c'},
85  {"help", no_argument, 0, 'h'},
86  {"kasp", required_argument, 0, 'k'},
87  {"version", no_argument, 0, 'V'},
88  {"verbose", no_argument, 0, 'v'},
89  {0,0,0,0}
90  };
91 
92  /* The program name is the last component of the program file name */
93  if ((progname = strrchr(argv[0], '/'))) { /* EQUALS */
94  ++progname; /* Point to character after last "/" */
95  }
96  else {
97  progname = argv[0];
98  }
99 
100  while ((ch = getopt_long(argc, argv, "c:hk:Vv", long_options, &option_index)) != -1) {
101  switch (ch) {
102  case 'c':
104  free_config = 1;
105  break;
106  case 'h':
107  usage();
108  exit(0);
109  break;
110  case 'k':
111  kasp = StrStrdup(optarg);
112  break;
113  case 'V':
114  printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
115  exit(0);
116  break;
117  case 'v':
118  verbose = 1;
119  break;
120  }
121  }
122 
123  /* 0) Some basic setup */
125 
126  /* 1) Check on conf.xml - set kasp.xml (if -k flag not given) */
127  status = check_conf(&kasp);
128 
129  /* 2) Checks on kasp.xml */
130  status += check_kasp();
131 
132  if (verbose) {
133  dual_log("DEBUG: finished %d", status);
134  }
135 
136  xmlCleanupParser();
137 
138  for (i = 0; i < repo_count; i++) {
139  StrFree(repo_list[i]);
140  }
142  if (free_config) {
143  StrFree(config);
144  }
145  StrFree(kasp);
146 
147  return status;
148 }
149 
150 /*
151  * Check the conf.xml file
152  * Set kasp.xml from file (unless -k flag was given)
153  * Return status (0 == success; 1 == error)
154  */
155 
156 int check_conf(char** kasp) {
157  int status = 0;
158  int i = 0;
159  int j = 0;
160  int temp_status = 0;
161 
162  xmlDocPtr doc;
163  xmlXPathContextPtr xpath_ctx;
164  xmlXPathObjectPtr xpath_obj;
165  xmlNode *curNode;
166  xmlChar *xexpr;
167  char* temp_char = NULL;
168 
169  KC_REPO* repo = NULL;
170  int* repo_mods = NULL; /* To see if we have looked at this module before */
171 
172  const char* rngfilename = OPENDNSSEC_SCHEMA_DIR "/conf.rng";
173  const char* zonerngfilename = OPENDNSSEC_SCHEMA_DIR "/zonelist.rng";
174 
175  /* Check that the file is well-formed */
176  status = check_rng(config, rngfilename);
177 
178  if (status == 0) {
179  dual_log("INFO: The XML in %s is valid", config);
180  } else {
181  return status; /* Don't try to read the file if it is invalid */
182  }
183 
184  /* Load XML document */
185  doc = xmlParseFile(config);
186  if (doc == NULL) {
187  return 1;
188  }
189 
190  /* Create xpath evaluation context */
191  xpath_ctx = xmlXPathNewContext(doc);
192  if(xpath_ctx == NULL) {
193  xmlFreeDoc(doc);
194  return 1;
195  }
196 
197  /* REPOSITORY section */
198  xexpr = (xmlChar *)"//Configuration/RepositoryList/Repository";
199  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
200  if(xpath_obj == NULL) {
201  xmlXPathFreeContext(xpath_ctx);
202  xmlFreeDoc(doc);
203  return 1;
204  }
205 
206  if (xpath_obj->nodesetval) {
207  repo_count = xpath_obj->nodesetval->nodeNr;
208 
209  repo = (KC_REPO*)malloc(sizeof(KC_REPO) * repo_count);
210  repo_mods = (int*)malloc(sizeof(int) * repo_count);
211  repo_list = (char**)malloc(sizeof(char*) * repo_count);
212 
213  if (repo == NULL || repo_mods == NULL || repo_list == NULL) {
214  dual_log("ERROR: malloc for repo information failed");
215  exit(1);
216  }
217 
218  for (i = 0; i < repo_count; i++) {
219  repo_mods[i] = 0;
220 
221  curNode = xpath_obj->nodesetval->nodeTab[i]->xmlChildrenNode;
222  /* Default for capacity */
223 
224  repo[i].name = (char *) xmlGetProp(xpath_obj->nodesetval->nodeTab[i],
225  (const xmlChar *)"name");
226  repo_list[i] = StrStrdup(repo[i].name);
227 
228  while (curNode) {
229  if (xmlStrEqual(curNode->name, (const xmlChar *)"TokenLabel"))
230  repo[i].TokenLabel = (char *) xmlNodeGetContent(curNode);
231  if (xmlStrEqual(curNode->name, (const xmlChar *)"Module"))
232  repo[i].module = (char *) xmlNodeGetContent(curNode);
233  curNode = curNode->next;
234  }
235  }
236  }
237  xmlXPathFreeObject(xpath_obj);
238 
239  /* Now we have all the information we need do the checks */
240  for (i = 0; i < repo_count; i++) {
241 
242  if (repo_mods[i] == 0) {
243 
244  /* 1) Check that the module exists */
245  status += check_file(repo[i].module, "Module");
246 
247  repo_mods[i] = 1; /* Done this module */
248 
249  /* 2) Check repos on the same modules have different TokenLabels */
250  for (j = i+1; j < repo_count; j++) {
251  if ( repo_mods[j] == 0 &&
252  (strcmp(repo[i].module, repo[j].module) == 0) ) {
253  repo_mods[j] = 1; /* done */
254 
255  if (strcmp(repo[i].TokenLabel, repo[j].TokenLabel) == 0) {
256  dual_log("ERROR: Multiple Repositories (%s and %s) in %s have the same Module (%s) and TokenLabel (%s)", repo[i].name, repo[j].name, config, repo[i].module, repo[i].TokenLabel);
257  status += 1;
258  }
259  }
260  }
261  }
262 
263  /* 3) Check that the name is unique */
264  for (j = i+1; j < repo_count; j++) {
265  if (strcmp(repo[i].name, repo[j].name) == 0) {
266  dual_log("ERROR: Two repositories exist with the same name (%s)", repo[i].name);
267  status += 1;
268  }
269  }
270  }
271 
272  /* COMMON section */
273  /* PolicyFile (aka KASP); we will validate it later */
274  if (*kasp == NULL) {
275  xexpr = (xmlChar *)"//Configuration/Common/PolicyFile";
276  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
277  if(xpath_obj == NULL) {
278  xmlXPathFreeContext(xpath_ctx);
279  xmlFreeDoc(doc);
280 
281  for (i = 0; i < repo_count; i++) {
282  free(repo[i].name);
283  free(repo[i].module);
284  free(repo[i].TokenLabel);
285  }
286  free(repo);
287  free(repo_mods);
288 
289  return -1;
290  }
291  temp_char = (char*) xmlXPathCastToString(xpath_obj);
292  StrAppend(kasp, temp_char);
293  StrFree(temp_char);
294  xmlXPathFreeObject(xpath_obj);
295  }
296 
297 
298  /* Check that the Zonelist file is well-formed */
299  xexpr = (xmlChar *)"//Configuration/Common/ZoneListFile";
300  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
301  if(xpath_obj == NULL) {
302  xmlXPathFreeContext(xpath_ctx);
303  xmlFreeDoc(doc);
304 
305  for (i = 0; i < repo_count; i++) {
306  free(repo[i].name);
307  free(repo[i].module);
308  free(repo[i].TokenLabel);
309  }
310  free(repo);
311  free(repo_mods);
312 
313  return -1;
314  }
315  temp_char = (char*) xmlXPathCastToString(xpath_obj);
316 
317  if (check_rng(temp_char, zonerngfilename) == 0) {
318  dual_log("INFO: The XML in %s is valid", temp_char);
319  } else {
320  status += 1;
321  }
322 
323  xmlXPathFreeObject(xpath_obj);
324  StrFree(temp_char);
325 
326  /* ENFORCER section */
327 
328  /* Check defined user/group */
329  status += check_user_group(xpath_ctx,
330  (xmlChar *)"//Configuration/Enforcer/Privileges/User",
331  (xmlChar *)"//Configuration/Enforcer/Privileges/Group");
332 
333  /* Check datastore exists (if sqlite) */
334  /* TODO check datastore matches libksm without building against libksm */
335  temp_status = check_file_from_xpath(xpath_ctx, "SQLite datastore",
336  (xmlChar *)"//Configuration/Enforcer/Datastore/SQLite");
337  if (temp_status == -1) {
338  /* Configured for Mysql DB */
339  /*if (DbFlavour() != MYSQL_DB) {
340  dual_log("ERROR: libksm compiled for sqlite3 but conf.xml configured for MySQL");
341  }*/
342  } else {
343  status += temp_status;
344  /* Configured for sqlite DB */
345  /*if (DbFlavour() != SQLITE_DB) {
346  dual_log("ERROR: libksm compiled for MySQL but conf.xml configured for sqlite3");
347  }*/
348  }
349 
350  /* Warn if Interval is M or Y */
351  status += check_time_def_from_xpath(xpath_ctx, (xmlChar *)"//Configuration/Enforcer/Interval", "Configuration", "Enforcer/Interval", config);
352 
353  /* Warn if RolloverNotification is M or Y */
354  status += check_time_def_from_xpath(xpath_ctx, (xmlChar *)"//Configuration/Enforcer/RolloverNotification", "Configuration", "Enforcer/RolloverNotification", config);
355 
356  /* Check DelegationSignerSubmitCommand exists (if set) */
357  temp_status = check_file_from_xpath(xpath_ctx, "DelegationSignerSubmitCommand",
358  (xmlChar *)"//Configuration/Enforcer/DelegationSignerSubmitCommand");
359  if (temp_status > 0) {
360  status += temp_status;
361  }
362 
363  /* SIGNER section */
364  /* Check defined user/group */
365  status += check_user_group(xpath_ctx,
366  (xmlChar *)"//Configuration/Signer/Privileges/User",
367  (xmlChar *)"//Configuration/Signer/Privileges/Group");
368 
369  /* Check WorkingDirectory exists (or default) */
370  temp_status = check_path_from_xpath(xpath_ctx, "WorkingDirectory",
371  (xmlChar *)"//Configuration/Signer/WorkingDirectory");
372  if (temp_status == -1) {
373  /* Check the default location */
374  status += check_path(OPENDNSSEC_STATE_DIR "/tmp", "default WorkingDirectory");
375  } else {
376  status += temp_status;
377  }
378 
379  xmlXPathFreeContext(xpath_ctx);
380  xmlFreeDoc(doc);
381 
382  for (i = 0; i < repo_count; i++) {
383  free(repo[i].name);
384  free(repo[i].module);
385  free(repo[i].TokenLabel);
386  }
387  free(repo);
388  free(repo_mods);
389 
390  return status;
391 }
392 
393 /*
394  * Check the kasp.xml file
395  * Return status (0 == success; 1 == error)
396  */
397 
398 int check_kasp() {
399  int status = 0;
400  int i = 0;
401  int j = 0;
402  const char* rngfilename = OPENDNSSEC_SCHEMA_DIR "/kasp.rng";
403  xmlDocPtr doc;
404  xmlXPathContextPtr xpath_ctx;
405  xmlXPathObjectPtr xpath_obj;
406  xmlNode *curNode;
407  xmlChar *xexpr;
408 
409  int policy_count = 0;
410  char **policy_names = NULL;
411  int default_found = 0;
412 
413  if (kasp == NULL) {
414  dual_log("ERROR: No location for kasp.xml set");
415  return 1;
416  }
417 
418 /* Check that the file is well-formed */
419  status = check_rng(kasp, rngfilename);
420 
421  if (status ==0) {
422  dual_log("INFO: The XML in %s is valid", kasp);
423  } else {
424  return 1;
425  }
426 
427  /* Load XML document */
428  doc = xmlParseFile(kasp);
429  if (doc == NULL) {
430  return 1;
431  }
432 
433  /* Create xpath evaluation context */
434  xpath_ctx = xmlXPathNewContext(doc);
435  if(xpath_ctx == NULL) {
436  xmlFreeDoc(doc);
437  return 1;
438  }
439 
440  /* First pass through the whole document to test for a policy called "default" and no duplicate names */
441 
442  xexpr = (xmlChar *)"//KASP/Policy";
443  xpath_obj = xmlXPathEvalExpression(xexpr, xpath_ctx);
444  if(xpath_obj == NULL) {
445  xmlXPathFreeContext(xpath_ctx);
446  xmlFreeDoc(doc);
447  return 1;
448  }
449 
450  if (xpath_obj->nodesetval) {
451  policy_count = xpath_obj->nodesetval->nodeNr;
452 
453  policy_names = (char**)malloc(sizeof(char*) * policy_count);
454  if (policy_names == NULL) {
455  dual_log("ERROR: Malloc for policy names failed");
456  exit(1);
457  }
458 
459  for (i = 0; i < policy_count; i++) {
460 
461  policy_names[i] = (char *) xmlGetProp(xpath_obj->nodesetval->nodeTab[i],
462  (const xmlChar *)"name");
463  }
464  }
465 
466  /* Now we have all the information we need do the checks */
467  for (i = 0; i < policy_count; i++) {
468  if (strcmp(policy_names[i], "default") == 0) {
469  default_found = 1;
470  }
471  for (j = i+1; j < policy_count; j++) {
472  if ( (strcmp(policy_names[i], policy_names[j]) == 0) ) {
473  dual_log("ERROR: Two policies exist with the same name (%s)", policy_names[i]);
474  status += 1;
475  }
476  }
477  }
478  if (default_found == 0) {
479  dual_log("WARNING: No policy named 'default' in %s. This means you will need to refer explicitly to the policy for each zone", kasp);
480  }
481 
482  /* Go again; this time check each policy */
483  for (i = 0; i < policy_count; i++) {
484  curNode = xpath_obj->nodesetval->nodeTab[i]->xmlChildrenNode;
485 
486  status += check_policy(curNode, policy_names[i], repo_list, repo_count, kasp);
487  }
488 
489  for (i = 0; i < policy_count; i++) {
490  free(policy_names[i]);
491  }
492  free(policy_names);
493 
494  xmlXPathFreeObject(xpath_obj);
495  xmlXPathFreeContext(xpath_ctx);
496  xmlFreeDoc(doc);
497 
498  return status;
499 }
#define DEFAULT_LOG_FACILITY
Definition: daemon.h:79
int check_path(const char *pathname, const char *log_string)
Definition: kc_helper.c:251
char * name
Definition: kaspcheck.h:30
char * optarg
int check_policy(xmlNode *curNode, const char *policy_name, char **repo_list, int repo_count, const char *kasp)
Definition: kc_helper.c:410
int check_conf(char **kasp)
Definition: kaspcheck.c:156
char * kasp
Definition: kaspcheck.c:48
void log_init(int facility, const char *program_name)
Definition: daemon_util.c:265
int check_rng(const char *filename, const char *rngfilename)
Definition: kc_helper.c:92
char * StrStrdup(const char *string)
Definition: string_util.c:124
char ** repo_list
Definition: kaspcheck.c:50
int check_kasp()
Definition: kaspcheck.c:398
int main(int argc, char *argv[])
Definition: kaspcheck.c:75
int check_path_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *path_xexpr)
Definition: kc_helper.c:274
int repo_count
Definition: kaspcheck.c:51
int check_file(const char *filename, const char *log_string)
Definition: kc_helper.c:194
void StrAppend(char **str1, const char *str2)
Definition: string_util2.c:76
int verbose
Definition: kaspcheck.c:49
const char * progname
Definition: kaspcheck.c:45
char * TokenLabel
Definition: kaspcheck.h:32
void dual_log(const char *format,...)
Definition: kc_helper.c:63
char * config
Definition: kaspcheck.c:47
#define StrFree(ptr)
Definition: kaspcheck.c:53
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:387
int check_user_group(xmlXPathContextPtr xpath_ctx, const xmlChar *user_xexpr, const xmlChar *group_xexpr)
Definition: kc_helper.c:300
int check_file_from_xpath(xmlXPathContextPtr xpath_ctx, const char *log_string, const xmlChar *file_xexpr)
Definition: kc_helper.c:218
void usage()
Definition: kaspcheck.c:58