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