OpenDNSSEC-enforcer  1.3.15
daemon_util.c
Go to the documentation of this file.
1 /*
2  * $Id: daemon_util.c 6482 2012-07-16 06:46:13Z jerry $
3  *
4  * Copyright (c) 2008-2009 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 
29 /*
30  * daemon_util.c code needed to get a daemon up and running
31  *
32  * edit the DAEMONCONFIG and cmlParse function
33  * in daemon_util.[c|h] to add options specific
34  * to your app
35  *
36  * gcc -o daemon daemon_util.c daemon.c
37  *
38  * Most of this is based on stuff I have seen in NSD
39  */
40 #include "config.h"
41 
42 #ifndef _GNU_SOURCE
43 #define _GNU_SOURCE
44 #endif
45 #include <stdlib.h>
46 #include <stdio.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <syslog.h>
50 #include <stdarg.h>
51 #include <errno.h>
52 #include <pwd.h>
53 #include <grp.h>
54 #include <ctype.h>
55 #include <signal.h>
56 #include <fcntl.h>
57 #include <syslog.h>
58 
59 #include <sys/select.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 
63 #include <libxml/tree.h>
64 #include <libxml/parser.h>
65 #include <libxml/xpath.h>
66 #include <libxml/xpathInternals.h>
67 #include <libxml/relaxng.h>
68 
69 #include "daemon.h"
70 #include "daemon_util.h"
71 
72 #include "ksm/database.h"
73 #include "ksm/datetime.h"
74 #include "ksm/string_util.h"
75 #include "ksm/string_util2.h"
76 
82 #if defined(HAVE_SYSLOG_R) && defined(HAVE_OPENLOG_R) && defined(HAVE_CLOSELOG_R) && defined(HAVE_VSYSLOG_R)
83 struct syslog_data sdata = SYSLOG_DATA_INIT;
84 #else
85 #undef HAVE_SYSLOG_R
86 #undef HAVE_OPENLOG_R
87 #undef HAVE_CLOSELOG_R
88 #undef HAVE_VSYSLOG_R
89 #endif
90 
91  int
93 {
94  int status = 0;
95 
96  xmlDocPtr doc = NULL;
97  xmlDocPtr rngdoc = NULL;
98  xmlXPathContextPtr xpathCtx = NULL;
99  xmlXPathObjectPtr xpathObj = NULL;
100  xmlRelaxNGParserCtxtPtr rngpctx = NULL;
101  xmlRelaxNGValidCtxtPtr rngctx = NULL;
102  xmlRelaxNGPtr schema = NULL;
103  xmlChar *user_expr = (unsigned char*) "//Configuration/Enforcer/Privileges/User";
104  xmlChar *group_expr = (unsigned char*) "//Configuration/Enforcer/Privileges/Group";
105 
106  char* filename = NULL;
107  char* rngfilename = OPENDNSSEC_SCHEMA_DIR "/conf.rng";
108  char* temp_char = NULL;
109 
110  struct passwd *pwd;
111  struct group *grp;
112 
113  FILE *file;
114 
115  if (config->configfile != NULL) {
116  filename = StrStrdup(config->configfile);
117  } else {
118  filename = StrStrdup(OPENDNSSEC_CONFIG_FILE);
119  }
120 
121  /* Load XML document */
122  doc = xmlParseFile(filename);
123  if (doc == NULL) {
124  /* To get a better error message try to open the file */
125  file = fopen(filename, "r");
126  if (file == NULL) {
127  log_msg(config, LOG_ERR, "Error: unable to open file \"%s\"", filename);
128  } else {
129  log_msg(config, LOG_ERR, "Error: unable to parse file \"%s\"", filename);
130  fclose(file);
131  }
132  return(-1);
133  }
134 
135  /* Load rng document */
136  rngdoc = xmlParseFile(rngfilename);
137  if (rngdoc == NULL) {
138  /* To get a better error message try to open the file */
139  file = fopen(rngfilename, "r");
140  if (file == NULL) {
141  log_msg(config, LOG_ERR, "Error: unable to open file \"%s\"", rngfilename);
142  } else {
143  log_msg(config, LOG_ERR, "Error: unable to parse file \"%s\"", rngfilename);
144  fclose(file);
145  }
146  return(-1);
147  }
148 
149  /* Create an XML RelaxNGs parser context for the relax-ng document. */
150  rngpctx = xmlRelaxNGNewDocParserCtxt(rngdoc);
151  if (rngpctx == NULL) {
152  log_msg(config, LOG_ERR, "Error: unable to create XML RelaxNGs parser context");
153  return(-1);
154  }
155 
156  /* parse a schema definition resource and build an internal XML Shema struture which can be used to validate instances. */
157  schema = xmlRelaxNGParse(rngpctx);
158  if (schema == NULL) {
159  log_msg(config, LOG_ERR, "Error: unable to parse a schema definition resource");
160  return(-1);
161  }
162 
163  /* Create an XML RelaxNGs validation context based on the given schema */
164  rngctx = xmlRelaxNGNewValidCtxt(schema);
165  if (rngctx == NULL) {
166  log_msg(config, LOG_ERR, "Error: unable to create RelaxNGs validation context based on the schema");
167  return(-1);
168  }
169 
170  xmlRelaxNGSetValidErrors(rngctx,
171  (xmlRelaxNGValidityErrorFunc) log_xml_error,
172  (xmlRelaxNGValidityWarningFunc) log_xml_warn,
173  NULL);
174 
175  /* Validate a document tree in memory. */
176  status = xmlRelaxNGValidateDoc(rngctx,doc);
177  if (status != 0) {
178  log_msg(config, LOG_ERR, "Error validating file \"%s\"", filename);
179  return(-1);
180  }
181 
182  /* Now parse a value out of the conf */
183  /* Create xpath evaluation context */
184  xpathCtx = xmlXPathNewContext(doc);
185  if(xpathCtx == NULL) {
186  log_msg(config, LOG_ERR,"Error: unable to create new XPath context");
187  xmlFreeDoc(doc);
188  return(-1);
189  }
190 
191  /* Set the group if specified */
192  xpathObj = xmlXPathEvalExpression(group_expr, xpathCtx);
193  if(xpathObj == NULL) {
194  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", group_expr);
195  xmlXPathFreeContext(xpathCtx);
196  xmlFreeDoc(doc);
197  return(-1);
198  }
199  if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
200  temp_char = (char*) xmlXPathCastToString(xpathObj);
201  StrAppend(&config->groupname, temp_char);
202  StrFree(temp_char);
203  xmlXPathFreeObject(xpathObj);
204  } else {
205  config->groupname = NULL;
206  }
207 
208  /* Set the user to drop to if specified */
209  xpathObj = xmlXPathEvalExpression(user_expr, xpathCtx);
210  if(xpathObj == NULL) {
211  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", user_expr);
212  xmlXPathFreeContext(xpathCtx);
213  xmlFreeDoc(doc);
214  return(-1);
215  }
216  if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
217  temp_char = (char*) xmlXPathCastToString(xpathObj);
218  StrAppend(&config->username, temp_char);
219  StrFree(temp_char);
220  xmlXPathFreeObject(xpathObj);
221  } else {
222  config->username = NULL;
223  }
224 
225  /* Set uid and gid if required */
226  if (config->username != NULL) {
227  /* Lookup the user id in /etc/passwd */
228  if ((pwd = getpwnam(config->username)) == NULL) {
229 #ifdef HAVE_SYSLOG_R
230  syslog_r(LOG_ERR, &sdata, "user '%s' does not exist. exiting...\n", config->username);
231 #else
232  syslog(LOG_ERR, "user '%s' does not exist. exiting...\n", config->username);
233 #endif
234  exit(1);
235  } else {
236  config->uid = pwd->pw_uid;
237  }
238  endpwent();
239  }
240  if (config->groupname) {
241  /* Lookup the group id in /etc/groups */
242  if ((grp = getgrnam(config->groupname)) == NULL) {
243 #ifdef HAVE_SYSLOG_R
244  syslog_r(LOG_ERR, &sdata, "group '%s' does not exist. exiting...\n", config->groupname);
245 #else
246  syslog(LOG_ERR, "group '%s' does not exist. exiting...\n", config->groupname);
247 #endif
248  exit(1);
249  } else {
250  config->gid = grp->gr_gid;
251  }
252  endgrent();
253  }
254 
255  xmlXPathFreeContext(xpathCtx);
256  xmlRelaxNGFree(schema);
257  xmlRelaxNGFreeValidCtxt(rngctx);
258  xmlRelaxNGFreeParserCtxt(rngpctx);
259  xmlFreeDoc(doc);
260  xmlFreeDoc(rngdoc);
261  StrFree(filename);
262 
263  return 0;
264 }
265 
266 /* Set up logging as per default (facility may be switched based on config file) */
267 void log_init(int facility, const char *program_name)
268 {
269 #ifdef HAVE_OPENLOG_R
270  openlog_r(program_name, 0, facility, &sdata);
271 #else
272  openlog(program_name, 0, facility);
273 #endif
274 }
275 
276 /* Switch log to new facility */
277 void log_switch(int facility, const char *facility_name, const char *program_name, int verbose)
278 {
279 #ifdef HAVE_CLOSELOG_R
280  closelog_r(&sdata);
281 #else
282  closelog();
283 #endif
284 #ifdef HAVE_OPENLOG_R
285  openlog_r(program_name, 0, facility, &sdata);
286 #else
287  openlog(program_name, 0, facility);
288 #endif
289  if (verbose) {
290  log_msg(NULL, LOG_INFO, "Switched log facility to: %s", facility_name);
291  }
292 }
293 
294 
295  void
296 log_msg(DAEMONCONFIG *config, int priority, const char *format, ...)
297 {
298  /* If the variable arg list is bad then random errors can occur */
299  va_list args;
300  if (config && config->debug) priority = LOG_ERR;
301  va_start(args, format);
302 #ifdef HAVE_VSYSLOG_R
303  vsyslog_r(priority, &sdata, format, args);
304 #else
305  vsyslog(priority, format, args);
306 #endif
307  va_end(args);
308 }
309 
310 /*
311  * log function suitable for libksm callback
312  */
313  void
314 ksm_log_msg(const char *format)
315 {
316  if (strncmp(format, "ERROR:", 6) == 0) {
317 #ifdef HAVE_SYSLOG_R
318  syslog_r(LOG_ERR, &sdata, "%s", format);
319 #else
320  syslog(LOG_ERR, "%s", format);
321 #endif
322  }
323  else if (strncmp(format, "INFO:", 5) == 0) {
324 #ifdef HAVE_SYSLOG_R
325  syslog_r(LOG_INFO, &sdata, "%s", format);
326 #else
327  syslog(LOG_INFO, "%s", format);
328 #endif
329  }
330  else if (strncmp(format, "WARNING:", 8) == 0) {
331 #ifdef HAVE_SYSLOG_R
332  syslog_r(LOG_WARNING, &sdata, "%s", format);
333 #else
334  syslog(LOG_WARNING, "%s", format);
335 #endif
336  }
337  else if (strncmp(format, "DEBUG:", 6) == 0) {
338 #ifdef HAVE_SYSLOG_R
339  syslog_r(LOG_DEBUG, &sdata, "%s", format);
340 #else
341  syslog(LOG_DEBUG, "%s", format);
342 #endif
343  }
344  else {
345 #ifdef HAVE_SYSLOG_R
346  syslog_r(LOG_ERR, &sdata, "%s", format);
347 #else
348  syslog(LOG_ERR, "%s", format);
349 #endif
350  }
351 }
352 
353 /* XML Error Message */
354  void
355 log_xml_error(void *ignore, const char *format, ...)
356 {
357  va_list args;
358 
359  (void) ignore;
360 
361  /* If the variable arg list is bad then random errors can occur */
362  va_start(args, format);
363 #ifdef HAVE_VSYSLOG_R
364  vsyslog_r(LOG_ERR, &sdata, format, args);
365 #else
366  vsyslog(LOG_ERR, format, args);
367 #endif
368  va_end(args);
369 }
370 
371 /* XML Warning Message */
372  void
373 log_xml_warn(void *ignore, const char *format, ...)
374 {
375  va_list args;
376 
377  (void) ignore;
378 
379  /* If the variable arg list is bad then random errors can occur */
380  va_start(args, format);
381 #ifdef HAVE_VSYSLOG_R
382  vsyslog_r(LOG_INFO, &sdata, format, args);
383 #else
384  vsyslog(LOG_INFO, format, args);
385 #endif
386  va_end(args);
387 }
388 
389  static void
390 usage(const char* prog)
391 {
392  fprintf(stderr, "Usage: %s [OPTION]...\n", prog);
393  fprintf(stderr, "OpenDNSSEC Enforcer version %s\n\n", VERSION);
394  fprintf(stderr, "Supported options:\n");
395  fprintf(stderr, " -c <file> Use alternate conf.xml.\n");
396  fprintf(stderr, " -d Debug.\n");
397  fprintf(stderr, " -1 Run once, then exit.\n");
398 /* fprintf(stderr, " -u user Change effective uid to the specified user.\n");*/
399  fprintf(stderr, " -P pidfile Specify the PID file to write.\n");
400 
401  fprintf(stderr, " -V Print version.\n");
402  fprintf(stderr, " -[?|h] This help.\n");
403 }
404 
405  static void
406 version(void)
407 {
408  fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
409  fprintf(stderr, "Written by %s.\n\n", AUTHOR_NAME);
410  fprintf(stderr, "%s. This is free software.\n", COPYRIGHT_STR);
411  fprintf(stderr, "See source files for more license information\n");
412  exit(0);
413 }
414 
415  int
416 write_data(DAEMONCONFIG *config, FILE *file, const void *data, size_t size)
417 {
418  size_t result;
419 
420  if (size == 0)
421  return 1;
422 
423  result = fwrite(data, 1, size, file);
424 
425  if (result == 0) {
426  log_msg(config, LOG_ERR, "write failed: %s", strerror(errno));
427  return 0;
428  } else if (result < size) {
429  log_msg(config, LOG_ERR, "short write (disk full?)");
430  return 0;
431  } else {
432  return 1;
433  }
434 }
435 
436  static pid_t
437 readpid(const char *file)
438 {
439  int fd;
440  pid_t pid;
441  char pidbuf[32];
442  char *t;
443  int l;
444 
445  if ((fd = open(file, O_RDONLY)) == -1) {
446  return -1;
447  }
448  if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) {
449  close(fd);
450  return -1;
451  }
452  close(fd);
453  /* Empty pidfile means no pidfile... */
454  if (l == 0) {
455  errno = ENOENT;
456  return -1;
457  }
458  pid = strtol(pidbuf, &t, 10);
459 
460  if (*t && *t != '\n') {
461  return -1;
462  }
463  return pid;
464 }
465 
466  int
468 {
469  FILE * fd;
470  char pidbuf[32];
471  struct stat stat_ret;
472  pid_t oldpid;
473 
474  /* If the file exists then either we didn't shutdown cleanly or an enforcer is
475  * already running; in either case shutdown */
476  if (stat(config->pidfile, &stat_ret) != 0) {
477 
478  if (errno != ENOENT) {
479  log_msg(config, LOG_ERR, "cannot stat pidfile %s: %s",
480  config->pidfile, strerror(errno));
481  return -1;
482  }
483  } else {
484  if (S_ISREG(stat_ret.st_mode)) {
485  /* The file exists already */
486  if ((oldpid = readpid(config->pidfile)) == -1) {
487  /* consider stale pidfile */
488  if (errno != ENOENT) {
489  log_msg(config, LOG_ERR, "cannot read pidfile %s: %s",
490  config->pidfile, strerror(errno));
491  }
492  } else {
493  if (kill(oldpid, 0) == 0 || errno == EPERM) {
494  log_msg(config, LOG_ERR, "pidfile %s already exists, "
495  "a process with pid %u is already running. "
496  "If no ods-enforcerd process is running, a previous "
497  "instance didn't shutdown cleanly, please remove this "
498  "file and try again.", config->pidfile, oldpid);
499  exit(1);
500  } else {
501  log_msg(config, LOG_WARNING, "pidfile %s already exists, "
502  "but no process with pid %u is running. "
503  "A previous instance didn't shutdown cleanly, this "
504  "pidfile is stale.", config->pidfile, oldpid);
505  }
506  }
507  }
508  }
509 
510  /* All good, carry on */
511  snprintf(pidbuf, sizeof(pidbuf), "%lu\n", (unsigned long) config->pid);
512 
513  if ((fd = fopen(config->pidfile, "w")) == NULL ) {
514  return -1;
515  }
516 
517  if (!write_data(config, fd, pidbuf, strlen(pidbuf))) {
518  fclose(fd);
519  return -1;
520  }
521  fclose(fd);
522 
523  if (chown(config->pidfile, config->uid, config->gid) == -1) {
524  log_msg(config, LOG_ERR, "cannot chown(%u,%u) %s: %s",
525  (unsigned) config->uid, (unsigned) config->gid,
526  config->pidfile, strerror(errno));
527  return -1;
528  }
529 
530  /* Mark this our pidfile so exit_function unlink's it */
531  daemon_our_pidfile = 1;
532  return 0;
533 }
534 
535  int
537 {
538  char* directory = NULL;
539  char* slash;
540  struct stat stat_ret;
541  char *path = getenv("PWD");
542 
543  /* Find the directory part of the (fully qualified) pidfile */
544  if (*config->pidfile != '/') {
545  StrAppend(&directory, path);
546  StrAppend(&directory, "/");
547  StrAppend(&directory, config->pidfile);
548  } else {
549  directory = StrStrdup(config->pidfile);
550  }
551  slash = strrchr(directory, '/');
552  *slash = 0;
553 
554  /* Check that it exists */
555  if (stat(directory, &stat_ret) != 0) {
556 
557  if (errno != ENOENT) {
558  log_msg(config, LOG_ERR, "cannot stat directory %s: %s",
559  directory, strerror(errno));
560  return -1;
561  }
562  }
563 
564  if (S_ISDIR(stat_ret.st_mode)) {
565  /* Do nothing, the directory exists already */
566  } else {
567  /* try to create it */
568  if (make_directory(config, directory) != 0) {
569  StrFree(directory);
570  return -1;
571  }
572  }
573  StrFree(directory);
574 
575  return 0;
576 }
577 
578 int make_directory(DAEMONCONFIG* config, const char* path) {
579 
580  char* parent;
581  char* slash;
582  struct stat stat_ret;
583 
584  parent = StrStrdup(path);
585  slash = strrchr(parent, '/');
586 
587  *slash = 0;
588 
589  stat(parent, &stat_ret);
590 
591  if (!S_ISDIR(stat_ret.st_mode)) {
592 
593  make_directory(config, parent);
594 
595  }
596 
597  StrFree(parent);
598 
599  if (mkdir(path, (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0) {
600  log_msg(NULL, LOG_ERR, "cannot create directory %s: %s\n",
601  path, strerror(errno));
602  return 1;
603  }
604 
605 
606  if (chown(path, config->uid, config->gid) == -1) {
607  log_msg(config, LOG_ERR, "cannot chown(%u,%u) %s: %s",
608  (unsigned) config->uid, (unsigned) config->gid,
609  path, strerror(errno));
610  return 1;
611  }
612 
613  return 0;
614 
615 }
616 
617  void
618 cmdlParse(DAEMONCONFIG* config, int *argc, char **argv)
619 {
620  int c;
621 
622  /*
623  * Read the command line
624  */
625  while ((c = getopt(*argc, argv, "1c:hdV?u:P:")) != -1) {
626  switch (c) {
627  case '1':
628  config->once = true;
629  break;
630  case 'c':
631  config->configfile = optarg;
632  break;
633  case 'd':
634  config->debug = true;
635  break;
636  case 'P':
637  config->pidfile = optarg;
638  break;
639  case 'u':
640  break; /* disable this feature */
641  config->username = optarg;
642  /* Parse the username into uid and gid */
643  config->gid = getgid();
644  config->uid = getuid();
645  if (*config->username) {
646  struct passwd *pwd;
647  if (isdigit(*config->username)) {
648  char *t;
649  config->uid = strtol(config->username, &t, 10);
650  if (*t != 0) {
651  if (*t != '.' || !isdigit(*++t)) {
652  log_msg(config, LOG_ERR, "-u user or -u uid or -u uid.gid. exiting...");
653  exit(1);
654  }
655  config->gid = strtol(t, &t, 10);
656  } else {
657  /* Lookup the group id in /etc/passwd */
658  if ((pwd = getpwuid(config->uid)) == NULL) {
659  log_msg(config, LOG_ERR, "user id %u does not exist. exiting...", (unsigned) config->uid);
660  exit(1);
661  } else {
662  config->gid = pwd->pw_gid;
663  }
664  endpwent();
665  }
666  } else {
667  /* Lookup the user id in /etc/passwd */
668  if ((pwd = getpwnam(config->username)) == NULL) {
669  log_msg(config, LOG_ERR, "user '%s' does not exist. exiting...", config->username);
670  exit(1);
671  } else {
672  config->uid = pwd->pw_uid;
673  config->gid = pwd->pw_gid;
674  }
675  endpwent();
676  }
677  }
678  break;
679  case 'h':
680  usage(config->program);
681  exit(0);
682  case '?':
683  usage(config->program);
684  exit(0);
685  case 'V':
686  version();
687  exit(0);
688  default:
689  usage(config->program);
690  exit(0);
691  }
692  }
693 }
694 
695 /*
696  * Returns 0 if the the config file could be read and non-zero if it could not.
697  *
698  * Any function calling this should exit on a non-zero return.
699  */
700 int
702 {
703  xmlDocPtr doc = NULL;
704  xmlDocPtr rngdoc = NULL;
705  xmlXPathContextPtr xpathCtx = NULL;
706  xmlXPathObjectPtr xpathObj = NULL;
707  xmlRelaxNGParserCtxtPtr rngpctx = NULL;
708  xmlRelaxNGValidCtxtPtr rngctx = NULL;
709  xmlRelaxNGPtr schema = NULL;
710  xmlChar *iv_expr = (unsigned char*) "//Configuration/Enforcer/Interval";
711  xmlChar *mk_expr = (unsigned char*) "//Configuration/Enforcer/ManualKeyGeneration";
712  xmlChar *rn_expr = (unsigned char*) "//Configuration/Enforcer/RolloverNotification";
713  xmlChar *ds_expr = (unsigned char*) "//Configuration/Enforcer/DelegationSignerSubmitCommand";
714  xmlChar *litexpr = (unsigned char*) "//Configuration/Enforcer/Datastore/SQLite";
715  xmlChar *mysql_host = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Host";
716  xmlChar *mysql_port = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Host/@port";
717  xmlChar *mysql_db = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Database";
718  xmlChar *mysql_user = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Username";
719  xmlChar *mysql_pass = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Password";
720  xmlChar *log_user_expr = (unsigned char*) "//Configuration/Common/Logging/Syslog/Facility";
721 
722  int mysec = 0;
723  char *logFacilityName;
724  int my_log_user = DEFAULT_LOG_FACILITY;
725  int status;
726  int db_found = 0;
727  char* filename = NULL;
728  char* rngfilename = OPENDNSSEC_SCHEMA_DIR "/conf.rng";
729 
730  char* temp_char = NULL;
731 
732  FILE *file;
733 
734  /* Change the config file location if one was provided on the command line */
735  if (config->configfile != NULL) {
736  filename = StrStrdup(config->configfile);
737  } else {
738  filename = StrStrdup(OPENDNSSEC_CONFIG_FILE);
739  }
740 
741  if (verbose) {
742  log_msg(config, LOG_INFO, "Reading config \"%s\"", filename);
743  }
744 
745  /* Load XML document */
746  doc = xmlParseFile(filename);
747  if (doc == NULL) {
748  /* To get a better error message try to open the file */
749  file = fopen(filename, "r");
750  if (file == NULL) {
751  log_msg(config, LOG_ERR, "Error: unable to open file \"%s\"", filename);
752  } else {
753  log_msg(config, LOG_ERR, "Error: unable to parse file \"%s\"", filename);
754  fclose(file);
755  }
756  return(-1);
757  }
758 
759  /* Load rng document */
760  if (verbose) {
761  log_msg(config, LOG_INFO, "Reading config schema \"%s\"", rngfilename);
762  }
763  rngdoc = xmlParseFile(rngfilename);
764  if (rngdoc == NULL) {
765  /* To get a better error message try to open the file */
766  file = fopen(rngfilename, "r");
767  if (file == NULL) {
768  log_msg(config, LOG_ERR, "Error: unable to open file \"%s\"", rngfilename);
769  } else {
770  log_msg(config, LOG_ERR, "Error: unable to parse file \"%s\"", rngfilename);
771  fclose(file);
772  }
773  return(-1);
774  }
775 
776  /* Create an XML RelaxNGs parser context for the relax-ng document. */
777  rngpctx = xmlRelaxNGNewDocParserCtxt(rngdoc);
778  if (rngpctx == NULL) {
779  log_msg(config, LOG_ERR, "Error: unable to create XML RelaxNGs parser context");
780  return(-1);
781  }
782 
783  /* parse a schema definition resource and build an internal XML Shema struture which can be used to validate instances. */
784  schema = xmlRelaxNGParse(rngpctx);
785  if (schema == NULL) {
786  log_msg(config, LOG_ERR, "Error: unable to parse a schema definition resource");
787  return(-1);
788  }
789 
790  /* Create an XML RelaxNGs validation context based on the given schema */
791  rngctx = xmlRelaxNGNewValidCtxt(schema);
792  if (rngctx == NULL) {
793  log_msg(config, LOG_ERR, "Error: unable to create RelaxNGs validation context based on the schema");
794  return(-1);
795  }
796 
797  xmlRelaxNGSetValidErrors(rngctx,
798  (xmlRelaxNGValidityErrorFunc) log_xml_error,
799  (xmlRelaxNGValidityWarningFunc) log_xml_warn,
800  NULL);
801 
802  /* Validate a document tree in memory. */
803  status = xmlRelaxNGValidateDoc(rngctx,doc);
804  if (status != 0) {
805  log_msg(config, LOG_ERR, "Error validating file \"%s\"", filename);
806  return(-1);
807  }
808  xmlRelaxNGFreeValidCtxt(rngctx);
809  xmlRelaxNGFree(schema);
810  xmlRelaxNGFreeParserCtxt(rngpctx);
811  xmlFreeDoc(rngdoc);
812 
813  /* Now parse a value out of the conf */
814  /* Create xpath evaluation context */
815  xpathCtx = xmlXPathNewContext(doc);
816  if(xpathCtx == NULL) {
817  log_msg(config, LOG_ERR,"Error: unable to create new XPath context");
818  xmlFreeDoc(doc);
819  return(-1);
820  }
821 
822  /* Evaluate xpath expression for interval */
823  xpathObj = xmlXPathEvalExpression(iv_expr, xpathCtx);
824  if(xpathObj == NULL) {
825  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", iv_expr);
826  xmlXPathFreeContext(xpathCtx);
827  xmlFreeDoc(doc);
828  return(-1);
829  }
830 
831  temp_char = (char *)xmlXPathCastToString(xpathObj);
832  status = DtXMLIntervalSeconds(temp_char, &mysec);
833  if (status > 0) {
834  log_msg(config, LOG_ERR, "Error: unable to convert Interval %s to seconds, error: %i", temp_char, status);
835  StrFree(temp_char);
836  return status;
837  }
838  else if (status == -1) {
839  log_msg(config, LOG_INFO, "Info: converting %s to seconds; M interpreted as 31 days, Y interpreted as 365 days", temp_char);
840  }
841  config->interval = mysec;
842  if (verbose) {
843  log_msg(config, LOG_INFO, "Communication Interval: %i", config->interval);
844  }
845  StrFree(temp_char);
846  xmlXPathFreeObject(xpathObj);
847 
848  /* Evaluate xpath expression for Manual key generation */
849  xpathObj = xmlXPathEvalExpression(mk_expr, xpathCtx);
850  if(xpathObj == NULL) {
851  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mk_expr);
852  xmlXPathFreeContext(xpathCtx);
853  xmlFreeDoc(doc);
854  return(-1);
855  }
856 
857  if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
858  /* Manual key generation tag is present */
859  config->manualKeyGeneration = 1;
860  }
861  else {
862  /* Tag absent */
863  config->manualKeyGeneration = 0;
864  }
865  xmlXPathFreeObject(xpathObj);
866 
867  /* Evaluate xpath expression for rollover notification interval */
868  xpathObj = xmlXPathEvalExpression(rn_expr, xpathCtx);
869  if(xpathObj == NULL) {
870  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", rn_expr);
871  xmlXPathFreeContext(xpathCtx);
872  xmlFreeDoc(doc);
873  return(-1);
874  }
875 
876  if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
877  /* Tag RolloverNotification is present; set rolloverNotify */
878  temp_char = (char *)xmlXPathCastToString(xpathObj);
879  status = DtXMLIntervalSeconds(temp_char, &mysec);
880  if (status > 0) {
881  log_msg(config, LOG_ERR, "Error: unable to convert RolloverNotification %s to seconds, error: %i", temp_char, status);
882  StrFree(temp_char);
883  return status;
884  }
885  else if (status == -1) {
886  log_msg(config, LOG_INFO, "Info: converting %s to seconds; M interpreted as 31 days, Y interpreted as 365 days", temp_char);
887  }
888  config->rolloverNotify = mysec;
889  if (verbose) {
890  log_msg(config, LOG_INFO, "Rollover Notification Interval: %i", config->rolloverNotify);
891  }
892  StrFree(temp_char);
893  xmlXPathFreeObject(xpathObj);
894  }
895  else {
896  /* Tag RolloverNotification absent, set rolloverNotify to -1 */
897  config->rolloverNotify = -1;
898  }
899 
900  /* Evaluate xpath expression for DelegationSignerSubmitCommand */
901  xpathObj = xmlXPathEvalExpression(ds_expr, xpathCtx);
902  if(xpathObj == NULL) {
903  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", ds_expr);
904  xmlXPathFreeContext(xpathCtx);
905  xmlFreeDoc(doc);
906  return(-1);
907  }
908  if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
909  /* Tag DelegationSignerSubmitCommand is present; set DSSubmitCmd */
910  if (config->DSSubmitCmd != NULL) {
911  StrFree(config->DSSubmitCmd);
912  }
913  config->DSSubmitCmd = (char *)xmlXPathCastToString(xpathObj);
914 
915  if (verbose) {
916  log_msg(config, LOG_INFO, "Using command: %s to submit DS records", config->DSSubmitCmd);
917  }
918  xmlXPathFreeObject(xpathObj);
919  } else {
920  if (verbose) {
921  log_msg(config, LOG_INFO, "No DS Submit command supplied");
922  }
923  config->DSSubmitCmd[0] = '\0';
924  }
925 
926  /* Evaluate xpath expression for SQLite file location */
927 
928  xpathObj = xmlXPathEvalExpression(litexpr, xpathCtx);
929  if(xpathObj == NULL) {
930  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", litexpr);
931  xmlXPathFreeContext(xpathCtx);
932  xmlFreeDoc(doc);
933  return(-1);
934  }
935  if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
936  db_found = SQLITE_DB;
937  if (config->schema != NULL) {
938  StrFree(config->schema);
939  }
940  config->schema = xmlXPathCastToString(xpathObj);
941  if (verbose) {
942  log_msg(config, LOG_INFO, "SQLite database set to: %s", config->schema);
943  }
944  }
945  xmlXPathFreeObject(xpathObj);
946 
947  if (db_found == 0) {
948  db_found = MYSQL_DB;
949 
950  /* Get all of the MySQL stuff read in too */
951  /* HOST */
952  xpathObj = xmlXPathEvalExpression(mysql_host, xpathCtx);
953  if(xpathObj == NULL) {
954  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_host);
955  xmlXPathFreeContext(xpathCtx);
956  xmlFreeDoc(doc);
957  return(-1);
958  }
959  if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
960  if (config->host != NULL) {
961  StrFree(config->host);
962  }
963  config->host = xmlXPathCastToString(xpathObj);
964  if (verbose) {
965  log_msg(config, LOG_INFO, "MySQL database host set to: %s", config->host);
966  }
967  }
968  xmlXPathFreeObject(xpathObj);
969 
970  /* PORT */
971  xpathObj = xmlXPathEvalExpression(mysql_port, xpathCtx);
972  if(xpathObj == NULL) {
973  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_port);
974  xmlXPathFreeContext(xpathCtx);
975  xmlFreeDoc(doc);
976  return(-1);
977  }
978  if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
979  if (config->port != NULL) {
980  StrFree(config->port);
981  }
982  config->port = xmlXPathCastToString(xpathObj);
983  if (verbose) {
984  log_msg(config, LOG_INFO, "MySQL database port set to: %s", config->port);
985  }
986  }
987  xmlXPathFreeObject(xpathObj);
988 
989  /* SCHEMA */
990  xpathObj = xmlXPathEvalExpression(mysql_db, xpathCtx);
991  if(xpathObj == NULL) {
992  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_db);
993  xmlXPathFreeContext(xpathCtx);
994  xmlFreeDoc(doc);
995  return(-1);
996  }
997  if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
998  if (config->schema != NULL) {
999  StrFree(config->schema);
1000  }
1001  config->schema = xmlXPathCastToString(xpathObj);
1002  if (verbose) {
1003  log_msg(config, LOG_INFO, "MySQL database schema set to: %s", config->schema);
1004  }
1005  } else {
1006  db_found = 0;
1007  }
1008  xmlXPathFreeObject(xpathObj);
1009 
1010  /* DB USER */
1011  xpathObj = xmlXPathEvalExpression(mysql_user, xpathCtx);
1012  if(xpathObj == NULL) {
1013  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_user);
1014  xmlXPathFreeContext(xpathCtx);
1015  xmlFreeDoc(doc);
1016  return(-1);
1017  }
1018  if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
1019  if (config->user != NULL) {
1020  StrFree(config->user);
1021  }
1022  config->user = xmlXPathCastToString(xpathObj);
1023  if (verbose) {
1024  log_msg(config, LOG_INFO, "MySQL database user set to: %s", config->user);
1025  }
1026  } else {
1027  db_found = 0;
1028  }
1029  xmlXPathFreeObject(xpathObj);
1030 
1031  /* DB PASSWORD */
1032  xpathObj = xmlXPathEvalExpression(mysql_pass, xpathCtx);
1033  if(xpathObj == NULL) {
1034  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_pass);
1035  xmlXPathFreeContext(xpathCtx);
1036  xmlFreeDoc(doc);
1037  return(-1);
1038  }
1039  /* password may be blank */
1040 
1041  if (config->password != NULL) {
1042  StrFree(config->password);
1043  }
1044  config->password = xmlXPathCastToString(xpathObj);
1045  if (verbose) {
1046  log_msg(config, LOG_INFO, "MySQL database password set");
1047  }
1048  xmlXPathFreeObject(xpathObj);
1049 
1050  }
1051 
1052  /* Check that we found one or the other database */
1053  if(db_found == 0) {
1054  log_msg(config, LOG_ERR, "Error: unable to find complete database connection expression in %s", filename);
1055  xmlXPathFreeContext(xpathCtx);
1056  xmlFreeDoc(doc);
1057  return(-1);
1058  }
1059 
1060  /* Check that we found the right database type */
1061  if (db_found != DbFlavour()) {
1062  log_msg(config, LOG_ERR, "Error: database in config file %s does not match libksm", filename);
1063  xmlXPathFreeContext(xpathCtx);
1064  xmlFreeDoc(doc);
1065  return(-1);
1066  }
1067 
1068  /* Evaluate xpath expression for log facility (user) */
1069  xpathObj = xmlXPathEvalExpression(log_user_expr, xpathCtx);
1070  if(xpathObj == NULL) {
1071  log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", log_user_expr);
1072  xmlXPathFreeContext(xpathCtx);
1073  xmlFreeDoc(doc);
1074  return(-1);
1075  }
1076 
1077  if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
1078  /* tag present */
1079  logFacilityName = (char *)xmlXPathCastToString(xpathObj);
1080 
1081  status = get_log_user(logFacilityName, &my_log_user);
1082  if (status > 0) {
1083  log_msg(config, LOG_ERR, "Error: unable to set log user: %s, error: %i", logFacilityName, status);
1084  StrFree(logFacilityName);
1085  return status;
1086  }
1087  config->log_user = my_log_user;
1088  if (verbose) {
1089  log_msg(config, LOG_INFO, "Log User set to: %s", logFacilityName);
1090  }
1091 
1092  } else {
1093  /* tag _not_ present, use default */
1094  logFacilityName = StrStrdup( (char *)DEFAULT_LOG_FACILITY_STRING );
1095  config->log_user = DEFAULT_LOG_FACILITY;
1096  if (verbose) {
1097  log_msg(config, LOG_INFO, "Using default log user: %s", logFacilityName);
1098  }
1099  }
1100 
1101  xmlXPathFreeObject(xpathObj);
1102 
1103  log_switch(my_log_user, logFacilityName, config->program, verbose);
1104 
1105  /* Cleanup */
1106  /* TODO: some other frees are needed */
1107  xmlXPathFreeContext(xpathCtx);
1108  xmlFreeDoc(doc);
1109  StrFree(logFacilityName);
1110  StrFree(filename);
1111 
1112  return(0);
1113 
1114 }
1115 
1116 /* To overcome the potential differences in sqlite compile flags assume that it is not
1117  happy with multiple connections.
1118 
1119  The following 2 functions take out a lock and release it
1120 */
1121 
1122 int get_lite_lock(char *lock_filename, FILE* lock_fd)
1123 {
1124  struct flock fl;
1125  struct timeval tv;
1126 
1127  if (lock_fd == NULL) {
1128  log_msg(NULL, LOG_ERR, "%s could not be opened", lock_filename);
1129  return 1;
1130  }
1131 
1132  memset(&fl, 0, sizeof(struct flock));
1133  fl.l_type = F_WRLCK;
1134  fl.l_whence = SEEK_SET;
1135  fl.l_pid = getpid();
1136 
1137  while (fcntl(fileno(lock_fd), F_SETLK, &fl) == -1) {
1138  if (errno == EACCES || errno == EAGAIN) {
1139  log_msg(NULL, LOG_INFO, "%s already locked, sleep", lock_filename);
1140 
1141  /* sleep for 10 seconds TODO make this configurable? */
1142  tv.tv_sec = 10;
1143  tv.tv_usec = 0;
1144  select(0, NULL, NULL, NULL, &tv);
1145 
1146  } else {
1147  log_msg(NULL, LOG_INFO, "couldn't get lock on %s, %s", lock_filename, strerror(errno));
1148  return 1;
1149  }
1150  }
1151 
1152  return 0;
1153 
1154 }
1155 
1156 int release_lite_lock(FILE* lock_fd)
1157 {
1158  struct flock fl;
1159 
1160  if (lock_fd == NULL) {
1161  return 1;
1162  }
1163 
1164  memset(&fl, 0, sizeof(struct flock));
1165  fl.l_type = F_UNLCK;
1166  fl.l_whence = SEEK_SET;
1167 
1168  if (fcntl(fileno(lock_fd), F_SETLK, &fl) == -1) {
1169  return 1;
1170  }
1171 
1172  return 0;
1173 }
1174 
1175 /* convert the name of a log facility (user) into a number */
1176 int get_log_user(const char* username, int* usernumber)
1177 {
1178  char* case_username = NULL;
1179 
1180  if (username == NULL) {
1181  return 1;
1182  }
1183  /* Start with our default */
1184  *usernumber = DEFAULT_LOG_FACILITY;
1185 
1186  case_username = StrStrdup(username);
1187  (void) StrToUpper(case_username);
1188 
1189  /* POSIX only specifies LOG_USER and LOG_LOCAL[0 .. 7] */
1190  if (strncmp(case_username, "USER", 4) == 0) {
1191  *usernumber = LOG_USER;
1192  }
1193 #ifdef LOG_KERN
1194  else if (strncmp(case_username, "KERN", 4) == 0) {
1195  *usernumber = LOG_KERN;
1196  }
1197 #endif /* LOG_KERN */
1198 #ifdef LOG_MAIL
1199  else if (strncmp(case_username, "MAIL", 4) == 0) {
1200  *usernumber = LOG_MAIL;
1201  }
1202 #endif /* LOG_MAIL */
1203 #ifdef LOG_DAEMON
1204  else if (strncmp(case_username, "DAEMON", 6) == 0) {
1205  *usernumber = LOG_DAEMON;
1206  }
1207 #endif /* LOG_DAEMON */
1208 #ifdef LOG_AUTH
1209  else if (strncmp(case_username, "AUTH", 4) == 0) {
1210  *usernumber = LOG_AUTH;
1211  }
1212 #endif /* LOG_AUTH */
1213 #ifdef LOG_SYSLOG
1214  else if (strncmp(case_username, "SYSLOG", 6) == 0) {
1215  *usernumber = LOG_SYSLOG;
1216  }
1217 #endif /* LOG_SYSLOG */
1218 #ifdef LOG_LPR
1219  else if (strncmp(case_username, "LPR", 3) == 0) {
1220  *usernumber = LOG_LPR;
1221  }
1222 #endif /* LOG_LPR */
1223 #ifdef LOG_NEWS
1224  else if (strncmp(case_username, "NEWS", 4) == 0) {
1225  *usernumber = LOG_NEWS;
1226  }
1227 #endif /* LOG_NEWS */
1228 #ifdef LOG_UUCP
1229  else if (strncmp(case_username, "UUCP", 4) == 0) {
1230  *usernumber = LOG_UUCP;
1231  }
1232 #endif /* LOG_UUCP */
1233 #ifdef LOG_AUDIT /* Ubuntu at least doesn't want us to use LOG_AUDIT */
1234  else if (strncmp(case_username, "AUDIT", 5) == 0) {
1235  *usernumber = LOG_AUDIT;
1236  }
1237 #endif /* LOG_AUDIT */
1238 #ifdef LOG_CRON
1239  else if (strncmp(case_username, "CRON", 4) == 0) {
1240  *usernumber = LOG_CRON;
1241  }
1242 #endif /* LOG_CRON */
1243  else if (strncmp(case_username, "LOCAL0", 6) == 0) {
1244  *usernumber = LOG_LOCAL0;
1245  }
1246  else if (strncmp(case_username, "LOCAL1", 6) == 0) {
1247  *usernumber = LOG_LOCAL1;
1248  }
1249  else if (strncmp(case_username, "LOCAL2", 6) == 0) {
1250  *usernumber = LOG_LOCAL2;
1251  }
1252  else if (strncmp(case_username, "LOCAL3", 6) == 0) {
1253  *usernumber = LOG_LOCAL3;
1254  }
1255  else if (strncmp(case_username, "LOCAL4", 6) == 0) {
1256  *usernumber = LOG_LOCAL4;
1257  }
1258  else if (strncmp(case_username, "LOCAL5", 6) == 0) {
1259  *usernumber = LOG_LOCAL5;
1260  }
1261  else if (strncmp(case_username, "LOCAL6", 6) == 0) {
1262  *usernumber = LOG_LOCAL6;
1263  }
1264  else if (strncmp(case_username, "LOCAL7", 6) == 0) {
1265  *usernumber = LOG_LOCAL7;
1266  }
1267 
1268  StrFree(case_username);
1269 
1270  return 0;
1271 
1272 }
1273