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