OpenDNSSEC-enforcer  1.3.8
/build/buildd/opendnssec-1.3.8/enforcer/common/daemon_util.c
Go to the documentation of this file.
00001 /*
00002  * $Id: daemon_util.c 6199 2012-03-07 10:26:59Z matthijs $
00003  *
00004  * Copyright (c) 2008-2009 Nominet UK. All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  *
00015  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00016  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00017  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00018  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00019  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00020  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00021  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00023  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00024  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00025  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026  *
00027  */
00028 
00029 /* 
00030  * daemon_util.c code needed to get a daemon up and running
00031  *
00032  * edit the DAEMONCONFIG and cmlParse function
00033  * in daemon_util.[c|h] to add options specific
00034  * to your app
00035  *
00036  * gcc -o daemon daemon_util.c daemon.c
00037  *
00038  * Most of this is based on stuff I have seen in NSD
00039  */
00040 #include "config.h"
00041 
00042 #ifndef _GNU_SOURCE
00043 #define _GNU_SOURCE
00044 #endif
00045 #include <stdlib.h>
00046 #include <stdio.h>
00047 #include <unistd.h>
00048 #include <string.h>
00049 #include <syslog.h>
00050 #include <stdarg.h>
00051 #include <errno.h>
00052 #include <pwd.h>
00053 #include <grp.h>
00054 #include <ctype.h>
00055 #include <signal.h>
00056 #include <fcntl.h>
00057 #include <syslog.h>
00058 
00059 #include <sys/select.h>
00060 #include <sys/types.h>
00061 #include <sys/stat.h>
00062 
00063 #include <libxml/tree.h>
00064 #include <libxml/parser.h>
00065 #include <libxml/xpath.h>
00066 #include <libxml/xpathInternals.h>
00067 #include <libxml/relaxng.h>
00068 
00069 #include "daemon.h"
00070 #include "daemon_util.h"
00071 
00072 #include "ksm/database.h"
00073 #include "ksm/datetime.h"
00074 #include "ksm/string_util.h"
00075 #include "ksm/string_util2.h"
00076 
00077     int
00078 getPermsForDrop(DAEMONCONFIG* config)
00079 {
00080     int status = 0;
00081 
00082     xmlDocPtr doc = NULL;
00083     xmlDocPtr rngdoc = NULL;
00084     xmlXPathContextPtr xpathCtx = NULL;
00085     xmlXPathObjectPtr xpathObj = NULL;
00086     xmlRelaxNGParserCtxtPtr rngpctx = NULL;
00087     xmlRelaxNGValidCtxtPtr rngctx = NULL;
00088     xmlRelaxNGPtr schema = NULL;
00089     xmlChar *user_expr = (unsigned char*) "//Configuration/Enforcer/Privileges/User";
00090     xmlChar *group_expr = (unsigned char*) "//Configuration/Enforcer/Privileges/Group";
00091 
00092     char* filename = NULL;
00093     char* rngfilename = OPENDNSSEC_SCHEMA_DIR "/conf.rng";
00094     char* temp_char = NULL;
00095 
00096     struct passwd *pwd;
00097     struct group  *grp;
00098 
00099     FILE *file;
00100 
00101     if (config->configfile != NULL) {
00102         filename = StrStrdup(config->configfile);
00103     } else {
00104         filename = StrStrdup(OPENDNSSEC_CONFIG_FILE);
00105     }
00106 
00107     /* Load XML document */
00108     doc = xmlParseFile(filename);
00109     if (doc == NULL) {
00110         /* To get a better error message try to open the file */
00111         file = fopen(filename, "r");
00112         if (file == NULL) {
00113             log_msg(config, LOG_ERR, "Error: unable to open file \"%s\"", filename);
00114         } else {
00115             log_msg(config, LOG_ERR, "Error: unable to parse file \"%s\"", filename);
00116             fclose(file);
00117         }
00118         return(-1);
00119     }
00120 
00121     /* Load rng document */
00122     rngdoc = xmlParseFile(rngfilename);
00123     if (rngdoc == NULL) {
00124         /* To get a better error message try to open the file */
00125         file = fopen(rngfilename, "r");
00126         if (file == NULL) {
00127             log_msg(config, LOG_ERR, "Error: unable to open file \"%s\"", rngfilename);
00128         } else {
00129             log_msg(config, LOG_ERR, "Error: unable to parse file \"%s\"", rngfilename);
00130             fclose(file);
00131         }
00132         return(-1);
00133     }
00134 
00135     /* Create an XML RelaxNGs parser context for the relax-ng document. */
00136     rngpctx = xmlRelaxNGNewDocParserCtxt(rngdoc);
00137     if (rngpctx == NULL) {
00138         log_msg(config, LOG_ERR, "Error: unable to create XML RelaxNGs parser context");
00139         return(-1);
00140     }
00141 
00142     /* parse a schema definition resource and build an internal XML Shema struture which can be used to validate instances. */
00143     schema = xmlRelaxNGParse(rngpctx);
00144     if (schema == NULL) {
00145         log_msg(config, LOG_ERR, "Error: unable to parse a schema definition resource");
00146         return(-1);
00147     }
00148 
00149     /* Create an XML RelaxNGs validation context based on the given schema */
00150     rngctx = xmlRelaxNGNewValidCtxt(schema);
00151     if (rngctx == NULL) {
00152         log_msg(config, LOG_ERR, "Error: unable to create RelaxNGs validation context based on the schema");
00153         return(-1);
00154     }
00155 
00156     xmlRelaxNGSetValidErrors(rngctx,
00157                 (xmlRelaxNGValidityErrorFunc) log_xml_error,
00158                 (xmlRelaxNGValidityWarningFunc) log_xml_warn,
00159                 NULL);
00160 
00161     /* Validate a document tree in memory. */
00162     status = xmlRelaxNGValidateDoc(rngctx,doc);
00163     if (status != 0) {
00164         log_msg(config, LOG_ERR, "Error validating file \"%s\"", filename);
00165         return(-1);
00166     }
00167 
00168     /* Now parse a value out of the conf */
00169     /* Create xpath evaluation context */
00170     xpathCtx = xmlXPathNewContext(doc);
00171     if(xpathCtx == NULL) {
00172         log_msg(config, LOG_ERR,"Error: unable to create new XPath context");
00173         xmlFreeDoc(doc);
00174         return(-1);
00175     }
00176 
00177     /* Set the group if specified */
00178     xpathObj = xmlXPathEvalExpression(group_expr, xpathCtx);
00179     if(xpathObj == NULL) {
00180         log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", group_expr);
00181         xmlXPathFreeContext(xpathCtx);
00182         xmlFreeDoc(doc);
00183         return(-1);
00184     }
00185     if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00186         temp_char = (char*) xmlXPathCastToString(xpathObj);
00187         StrAppend(&config->groupname, temp_char);
00188         StrFree(temp_char);
00189         xmlXPathFreeObject(xpathObj);
00190     } else {
00191         config->groupname = NULL;
00192     }
00193 
00194     /* Set the user to drop to if specified */
00195     xpathObj = xmlXPathEvalExpression(user_expr, xpathCtx);
00196     if(xpathObj == NULL) {
00197         log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", user_expr);
00198         xmlXPathFreeContext(xpathCtx);
00199         xmlFreeDoc(doc);
00200         return(-1);
00201     }
00202     if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00203         temp_char = (char*) xmlXPathCastToString(xpathObj);
00204         StrAppend(&config->username, temp_char);
00205         StrFree(temp_char);
00206         xmlXPathFreeObject(xpathObj);
00207     } else {
00208         config->username = NULL;
00209     }
00210 
00211     /* Set uid and gid if required */
00212     if (config->username != NULL) {
00213         /* Lookup the user id in /etc/passwd */
00214         if ((pwd = getpwnam(config->username)) == NULL) {
00215             syslog(LOG_ERR, "user '%s' does not exist. exiting...\n", config->username);
00216             exit(1);
00217         } else {
00218             config->uid = pwd->pw_uid;
00219         }
00220         endpwent();
00221     }
00222     if (config->groupname) {
00223         /* Lookup the group id in /etc/groups */
00224         if ((grp = getgrnam(config->groupname)) == NULL) {
00225             syslog(LOG_ERR, "group '%s' does not exist. exiting...\n", config->groupname);
00226             exit(1);
00227         } else {
00228             config->gid = grp->gr_gid;
00229         }
00230         endgrent();
00231     }
00232 
00233     xmlXPathFreeContext(xpathCtx);
00234     xmlRelaxNGFree(schema);
00235     xmlRelaxNGFreeValidCtxt(rngctx);
00236     xmlRelaxNGFreeParserCtxt(rngpctx);
00237     xmlFreeDoc(doc);
00238     xmlFreeDoc(rngdoc);
00239     StrFree(filename);
00240 
00241     return 0;
00242 }
00243 
00244 /* Set up logging as per default (facility may be switched based on config file) */
00245 void log_init(int facility, const char *program_name)
00246 {
00247         openlog(program_name, 0, facility);
00248 }
00249 
00250 /* Switch log to new facility */
00251 void log_switch(int facility, const char *facility_name, const char *program_name, int verbose)
00252 {
00253     closelog();
00254         openlog(program_name, 0, facility);
00255     if (verbose) {
00256         log_msg(NULL, LOG_INFO, "Switched log facility to: %s", facility_name);
00257     }
00258 }
00259 
00260 
00261     void
00262 log_msg(DAEMONCONFIG *config, int priority, const char *format, ...)
00263 {
00264     /* If the variable arg list is bad then random errors can occur */ 
00265     va_list args;
00266     if (config && config->debug) priority = LOG_ERR;
00267     va_start(args, format);
00268     vsyslog(priority, format, args);
00269     va_end(args);
00270 }
00271 
00272 /*
00273  * log function suitable for libksm callback
00274  */
00275     void
00276 ksm_log_msg(const char *format)
00277 {
00278     if (strncmp(format, "ERROR:", 6) == 0) {
00279         syslog(LOG_ERR, "%s", format);
00280     }
00281     else if (strncmp(format, "INFO:", 5) == 0) {
00282         syslog(LOG_INFO, "%s", format);
00283     }
00284     else if (strncmp(format, "WARNING:", 8) == 0) {
00285         syslog(LOG_WARNING, "%s", format);
00286     }
00287     else if (strncmp(format, "DEBUG:", 6) == 0) {
00288         syslog(LOG_DEBUG, "%s", format);
00289     }
00290     else {
00291         syslog(LOG_ERR, "%s", format);
00292     }
00293 }
00294 
00295 /* XML Error Message */
00296     void
00297 log_xml_error(void *ignore, const char *format, ...)
00298 {
00299     va_list args;
00300 
00301     (void) ignore;
00302 
00303     /* If the variable arg list is bad then random errors can occur */ 
00304     va_start(args, format);
00305     vsyslog(LOG_ERR, format, args);
00306     va_end(args);
00307 }
00308 
00309 /* XML Warning Message */
00310     void
00311 log_xml_warn(void *ignore, const char *format, ...)
00312 {
00313     va_list args;
00314 
00315     (void) ignore;
00316 
00317     /* If the variable arg list is bad then random errors can occur */ 
00318     va_start(args, format);
00319     vsyslog(LOG_INFO, format, args);
00320     va_end(args);
00321 }
00322 
00323     static void
00324 usage(const char* prog)
00325 {
00326     fprintf(stderr, "Usage: %s [OPTION]...\n", prog);
00327     fprintf(stderr, "OpenDNSSEC Enforcer version %s\n\n", VERSION);
00328     fprintf(stderr, "Supported options:\n");
00329     fprintf(stderr, "  -c <file>   Use alternate conf.xml.\n");
00330     fprintf(stderr, "  -d          Debug.\n");
00331     fprintf(stderr, "  -1          Run once, then exit.\n");
00332 /*    fprintf(stderr, "  -u user     Change effective uid to the specified user.\n");*/
00333     fprintf(stderr, "  -P pidfile  Specify the PID file to write.\n");
00334 
00335     fprintf(stderr, "  -V          Print version.\n");
00336     fprintf(stderr, "  -[?|h]      This help.\n");
00337 }
00338 
00339     static void
00340 version(void)
00341 {
00342     fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
00343     fprintf(stderr, "Written by %s.\n\n", AUTHOR_NAME);
00344     fprintf(stderr, "%s.  This is free software.\n", COPYRIGHT_STR);
00345     fprintf(stderr, "See source files for more license information\n");
00346     exit(0);
00347 }
00348 
00349     int
00350 write_data(DAEMONCONFIG *config, FILE *file, const void *data, size_t size)
00351 {
00352     size_t result;
00353 
00354     if (size == 0)
00355         return 1;
00356 
00357     result = fwrite(data, 1, size, file);
00358 
00359     if (result == 0) {
00360         log_msg(config, LOG_ERR, "write failed: %s", strerror(errno));
00361         return 0;
00362     } else if (result < size) {
00363         log_msg(config, LOG_ERR, "short write (disk full?)");
00364         return 0;
00365     } else {
00366         return 1;
00367     }
00368 }
00369 
00370     static pid_t
00371 readpid(const char *file)
00372 {
00373     int fd;
00374     pid_t pid;
00375     char pidbuf[32];
00376     char *t;
00377     int l;
00378 
00379     if ((fd = open(file, O_RDONLY)) == -1) {
00380         return -1;
00381     }
00382     if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) {
00383         close(fd);
00384         return -1;
00385     }
00386     close(fd);
00387     /* Empty pidfile means no pidfile... */
00388     if (l == 0) {
00389         errno = ENOENT;
00390         return -1;
00391     }
00392     pid = strtol(pidbuf, &t, 10);
00393 
00394     if (*t && *t != '\n') {
00395         return -1;
00396     }
00397     return pid;
00398 }
00399 
00400     int
00401 writepid (DAEMONCONFIG *config)
00402 {
00403     FILE * fd;
00404     char pidbuf[32];
00405     struct stat stat_ret;
00406     pid_t oldpid;
00407 
00408     /* If the file exists then either we didn't shutdown cleanly or an enforcer is
00409      * already running; in either case shutdown */
00410     if (stat(config->pidfile, &stat_ret) != 0) {
00411 
00412         if (errno != ENOENT) {
00413             log_msg(config, LOG_ERR, "cannot stat pidfile %s: %s",
00414                     config->pidfile, strerror(errno));
00415             return -1;
00416         }
00417     } else {
00418         if (S_ISREG(stat_ret.st_mode)) {
00419             /* The file exists already */
00420             if ((oldpid = readpid(config->pidfile)) == -1) {
00421                 /* consider stale pidfile */
00422                 if (errno != ENOENT) {
00423                     log_msg(config, LOG_ERR, "cannot read pidfile %s: %s",
00424                         config->pidfile, strerror(errno));
00425                 }
00426             } else {
00427                 if (kill(oldpid, 0) == 0 || errno == EPERM) {
00428                     log_msg(config, LOG_ERR, "pidfile %s already exists, "
00429                         "a process with pid %u is already running. "
00430                         "If no ods-enforcerd process is running, a previous "
00431                         "instance didn't shutdown cleanly, please remove this "
00432                         "file and try again.", config->pidfile, oldpid);
00433                     exit(1);
00434                 } else {
00435                     log_msg(config, LOG_WARNING, "pidfile %s already exists, "
00436                         "but no process with pid %u is running. "
00437                         "A previous instance didn't shutdown cleanly, this "
00438                         "pidfile is stale.", config->pidfile, oldpid);
00439                 }
00440             }
00441         }
00442     }
00443 
00444     /* All good, carry on */
00445     snprintf(pidbuf, sizeof(pidbuf), "%lu\n", (unsigned long) config->pid);
00446 
00447     if ((fd = fopen(config->pidfile, "w")) ==  NULL ) {
00448         return -1;
00449     }
00450 
00451     if (!write_data(config, fd, pidbuf, strlen(pidbuf))) {
00452         fclose(fd);
00453         return -1;
00454     }
00455     fclose(fd);
00456 
00457     if (chown(config->pidfile, config->uid, config->gid) == -1) {
00458         log_msg(config, LOG_ERR, "cannot chown(%u,%u) %s: %s",
00459                 (unsigned) config->uid, (unsigned) config->gid,
00460                 config->pidfile, strerror(errno));
00461         return -1;
00462     }
00463 
00464     return 0;
00465 }
00466 
00467     int
00468 createPidDir (DAEMONCONFIG *config)
00469 {
00470     char* directory = NULL;
00471     char* slash;
00472     struct stat stat_ret;
00473     char *path = getenv("PWD");
00474 
00475     /* Find the directory part of the (fully qualified) pidfile */
00476     if (*config->pidfile != '/') {
00477         StrAppend(&directory, path);
00478         StrAppend(&directory, "/");
00479         StrAppend(&directory, config->pidfile);
00480     } else {
00481         directory = StrStrdup(config->pidfile);
00482     }
00483     slash = strrchr(directory, '/');
00484     *slash = 0;
00485 
00486     /* Check that it exists */
00487     if (stat(directory, &stat_ret) != 0) {
00488 
00489         if (errno != ENOENT) {
00490             log_msg(config, LOG_ERR, "cannot stat directory %s: %s",
00491                     directory, strerror(errno));
00492             return -1;
00493         }
00494     }
00495 
00496     if (S_ISDIR(stat_ret.st_mode)) {
00497         /* Do nothing, the directory exists already */
00498     } else {
00499         /* try to create it */
00500         if (make_directory(config, directory) != 0) {
00501             StrFree(directory);
00502             return -1;
00503         }
00504     }
00505     StrFree(directory);
00506 
00507     return 0;
00508 }
00509 
00510 int make_directory(DAEMONCONFIG* config, const char* path) {
00511 
00512     char* parent;
00513     char* slash;
00514     struct stat stat_ret;
00515 
00516     parent = StrStrdup(path);
00517     slash = strrchr(parent, '/');
00518 
00519     *slash = 0;
00520 
00521     stat(parent, &stat_ret);
00522 
00523     if (!S_ISDIR(stat_ret.st_mode)) {
00524 
00525         make_directory(config, parent);
00526 
00527     }
00528 
00529     StrFree(parent);
00530 
00531     if (mkdir(path, (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0) {
00532         log_msg(NULL, LOG_ERR, "cannot create directory %s: %s\n",
00533                 path, strerror(errno));
00534         return 1;
00535     }
00536     
00537 
00538     if (chown(path, config->uid, config->gid) == -1) {
00539         log_msg(config, LOG_ERR, "cannot chown(%u,%u) %s: %s",
00540                 (unsigned) config->uid, (unsigned) config->gid,
00541                 path, strerror(errno));
00542         return 1;
00543     }
00544 
00545     return 0;
00546 
00547 }
00548 
00549     void
00550 cmdlParse(DAEMONCONFIG* config, int *argc, char **argv)
00551 {
00552     int c;
00553 
00554     /*
00555      * Read the command line
00556      */
00557     while ((c = getopt(*argc, argv, "1c:hdV?u:P:")) != -1) {
00558         switch (c) {
00559             case '1':
00560                 config->once = true;
00561                 break;
00562             case 'c':
00563                 config->configfile = optarg;
00564                 break;
00565             case 'd':
00566                 config->debug = true;
00567                 break;
00568             case 'P':
00569                 config->pidfile = optarg;
00570                 break;
00571             case 'u':
00572                 break; /* disable this feature */
00573                 config->username = optarg;
00574                 /* Parse the username into uid and gid */
00575                 config->gid = getgid();
00576                 config->uid = getuid();
00577                 if (*config->username) {
00578                     struct passwd *pwd;
00579                     if (isdigit(*config->username)) {
00580                         char *t;
00581                         config->uid = strtol(config->username, &t, 10);
00582                         if (*t != 0) {
00583                             if (*t != '.' || !isdigit(*++t)) {
00584                                 log_msg(config, LOG_ERR, "-u user or -u uid or -u uid.gid. exiting...");
00585                                 exit(1);
00586                             }
00587                             config->gid = strtol(t, &t, 10);
00588                         } else {
00589                             /* Lookup the group id in /etc/passwd */
00590                             if ((pwd = getpwuid(config->uid)) == NULL) {
00591                                 log_msg(config, LOG_ERR, "user id %u does not exist. exiting...", (unsigned) config->uid);
00592                                 exit(1);
00593                             } else {
00594                                 config->gid = pwd->pw_gid;
00595                             }
00596                             endpwent();
00597                         }
00598                     } else {
00599                         /* Lookup the user id in /etc/passwd */
00600                         if ((pwd = getpwnam(config->username)) == NULL) {
00601                             log_msg(config, LOG_ERR, "user '%s' does not exist. exiting...", config->username);
00602                             exit(1);
00603                         } else {
00604                             config->uid = pwd->pw_uid;
00605                             config->gid = pwd->pw_gid;
00606                         }
00607                         endpwent();
00608                     }
00609                 }   
00610                 break;
00611             case 'h':
00612                 usage(config->program);
00613                 exit(0);
00614             case '?':
00615                 usage(config->program);
00616                 exit(0);
00617             case 'V':
00618                 version();
00619                 exit(0);
00620             default:
00621                 usage(config->program);
00622                 exit(0);
00623         }
00624     }
00625 }
00626 
00627 /*
00628  * Returns 0 if the the config file could be read and non-zero if it could not.
00629  *
00630  * Any function calling this should exit on a non-zero return.
00631  */
00632 int
00633 ReadConfig(DAEMONCONFIG *config, int verbose)
00634 {
00635     xmlDocPtr doc = NULL;
00636     xmlDocPtr rngdoc = NULL;
00637     xmlXPathContextPtr xpathCtx = NULL;
00638     xmlXPathObjectPtr xpathObj = NULL;
00639     xmlRelaxNGParserCtxtPtr rngpctx = NULL;
00640     xmlRelaxNGValidCtxtPtr rngctx = NULL;
00641     xmlRelaxNGPtr schema = NULL;
00642     xmlChar *iv_expr = (unsigned char*) "//Configuration/Enforcer/Interval";
00643     xmlChar *mk_expr = (unsigned char*) "//Configuration/Enforcer/ManualKeyGeneration";
00644     xmlChar *rn_expr = (unsigned char*) "//Configuration/Enforcer/RolloverNotification";
00645     xmlChar *ds_expr = (unsigned char*) "//Configuration/Enforcer/DelegationSignerSubmitCommand";
00646     xmlChar *litexpr = (unsigned char*) "//Configuration/Enforcer/Datastore/SQLite";
00647     xmlChar *mysql_host = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Host";
00648     xmlChar *mysql_port = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Host/@port";
00649     xmlChar *mysql_db = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Database";
00650     xmlChar *mysql_user = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Username";
00651     xmlChar *mysql_pass = (unsigned char*) "//Configuration/Enforcer/Datastore/MySQL/Password";
00652     xmlChar *log_user_expr = (unsigned char*) "//Configuration/Common/Logging/Syslog/Facility";
00653 
00654     int mysec = 0;
00655     char *logFacilityName;
00656     int my_log_user = DEFAULT_LOG_FACILITY;
00657     int status;
00658     int db_found = 0;
00659     char* filename = NULL;
00660     char* rngfilename = OPENDNSSEC_SCHEMA_DIR "/conf.rng";
00661 
00662     char* temp_char = NULL;
00663 
00664     FILE *file;
00665 
00666     /* Change the config file location if one was provided on the command line */
00667     if (config->configfile != NULL) {
00668         filename = StrStrdup(config->configfile);
00669     } else {
00670         filename = StrStrdup(OPENDNSSEC_CONFIG_FILE);
00671     }
00672 
00673     if (verbose) {
00674         log_msg(config, LOG_INFO, "Reading config \"%s\"", filename);
00675     }
00676 
00677     /* Load XML document */
00678     doc = xmlParseFile(filename);
00679     if (doc == NULL) {
00680         /* To get a better error message try to open the file */
00681         file = fopen(filename, "r");
00682         if (file == NULL) {
00683             log_msg(config, LOG_ERR, "Error: unable to open file \"%s\"", filename);
00684         } else {
00685             log_msg(config, LOG_ERR, "Error: unable to parse file \"%s\"", filename);
00686             fclose(file);
00687         }
00688         return(-1);
00689     }
00690 
00691     /* Load rng document */
00692     if (verbose) {
00693         log_msg(config, LOG_INFO, "Reading config schema \"%s\"", rngfilename);
00694     }
00695     rngdoc = xmlParseFile(rngfilename);
00696     if (rngdoc == NULL) {
00697         /* To get a better error message try to open the file */
00698         file = fopen(rngfilename, "r");
00699         if (file == NULL) {
00700             log_msg(config, LOG_ERR, "Error: unable to open file \"%s\"", rngfilename);
00701         } else {
00702             log_msg(config, LOG_ERR, "Error: unable to parse file \"%s\"", rngfilename);
00703             fclose(file);
00704         }
00705         return(-1);
00706     }
00707 
00708     /* Create an XML RelaxNGs parser context for the relax-ng document. */
00709     rngpctx = xmlRelaxNGNewDocParserCtxt(rngdoc);
00710     if (rngpctx == NULL) {
00711         log_msg(config, LOG_ERR, "Error: unable to create XML RelaxNGs parser context");
00712         return(-1);
00713     }
00714 
00715     /* parse a schema definition resource and build an internal XML Shema struture which can be used to validate instances. */
00716     schema = xmlRelaxNGParse(rngpctx);
00717     if (schema == NULL) {
00718         log_msg(config, LOG_ERR, "Error: unable to parse a schema definition resource");
00719         return(-1);
00720     }
00721 
00722     /* Create an XML RelaxNGs validation context based on the given schema */
00723     rngctx = xmlRelaxNGNewValidCtxt(schema);
00724     if (rngctx == NULL) {
00725         log_msg(config, LOG_ERR, "Error: unable to create RelaxNGs validation context based on the schema");
00726         return(-1);
00727     }
00728 
00729     xmlRelaxNGSetValidErrors(rngctx,
00730                 (xmlRelaxNGValidityErrorFunc) log_xml_error,
00731                 (xmlRelaxNGValidityWarningFunc) log_xml_warn,
00732                 NULL);
00733 
00734     /* Validate a document tree in memory. */
00735     status = xmlRelaxNGValidateDoc(rngctx,doc);
00736     if (status != 0) {
00737         log_msg(config, LOG_ERR, "Error validating file \"%s\"", filename);
00738         return(-1);
00739     }
00740     xmlRelaxNGFreeValidCtxt(rngctx);
00741     xmlRelaxNGFree(schema);
00742     xmlRelaxNGFreeParserCtxt(rngpctx);
00743     xmlFreeDoc(rngdoc);
00744 
00745     /* Now parse a value out of the conf */
00746     /* Create xpath evaluation context */
00747     xpathCtx = xmlXPathNewContext(doc);
00748     if(xpathCtx == NULL) {
00749         log_msg(config, LOG_ERR,"Error: unable to create new XPath context");
00750         xmlFreeDoc(doc);
00751         return(-1);
00752     }
00753 
00754     /* Evaluate xpath expression for interval */
00755     xpathObj = xmlXPathEvalExpression(iv_expr, xpathCtx);
00756     if(xpathObj == NULL) {
00757         log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", iv_expr);
00758         xmlXPathFreeContext(xpathCtx);
00759         xmlFreeDoc(doc);
00760         return(-1);
00761     }
00762 
00763     temp_char = (char *)xmlXPathCastToString(xpathObj);
00764     status = DtXMLIntervalSeconds(temp_char, &mysec);
00765     if (status > 0) {
00766         log_msg(config, LOG_ERR, "Error: unable to convert Interval %s to seconds, error: %i", temp_char, status);
00767         StrFree(temp_char);
00768         return status;
00769     }
00770     else if (status == -1) {
00771         log_msg(config, LOG_INFO, "Info: converting %s to seconds; M interpreted as 31 days, Y interpreted as 365 days", temp_char);
00772     }
00773     config->interval = mysec;
00774     if (verbose) {
00775         log_msg(config, LOG_INFO, "Communication Interval: %i", config->interval);
00776     }
00777     StrFree(temp_char);
00778     xmlXPathFreeObject(xpathObj);
00779 
00780     /* Evaluate xpath expression for Manual key generation */
00781     xpathObj = xmlXPathEvalExpression(mk_expr, xpathCtx);
00782     if(xpathObj == NULL) {
00783         log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mk_expr);
00784         xmlXPathFreeContext(xpathCtx);
00785         xmlFreeDoc(doc);
00786         return(-1);
00787     }
00788 
00789     if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00790         /* Manual key generation tag is present */
00791         config->manualKeyGeneration = 1;
00792     }
00793     else {
00794         /* Tag absent */
00795         config->manualKeyGeneration = 0;
00796     }
00797     xmlXPathFreeObject(xpathObj);
00798 
00799     /* Evaluate xpath expression for rollover notification interval */
00800     xpathObj = xmlXPathEvalExpression(rn_expr, xpathCtx);
00801     if(xpathObj == NULL) {
00802         log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", rn_expr);
00803         xmlXPathFreeContext(xpathCtx);
00804         xmlFreeDoc(doc);
00805         return(-1);
00806     }
00807 
00808     if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00809         /* Tag RolloverNotification is present; set rolloverNotify */
00810         temp_char = (char *)xmlXPathCastToString(xpathObj);
00811         status = DtXMLIntervalSeconds(temp_char, &mysec);
00812         if (status > 0) {
00813             log_msg(config, LOG_ERR, "Error: unable to convert RolloverNotification %s to seconds, error: %i", temp_char, status);
00814             StrFree(temp_char);
00815             return status;
00816         }
00817         else if (status == -1) {
00818         log_msg(config, LOG_INFO, "Info: converting %s to seconds; M interpreted as 31 days, Y interpreted as 365 days", temp_char);
00819         }
00820         config->rolloverNotify = mysec;
00821         if (verbose) {
00822             log_msg(config, LOG_INFO, "Rollover Notification Interval: %i", config->rolloverNotify);
00823         }
00824         StrFree(temp_char);
00825         xmlXPathFreeObject(xpathObj);
00826     }
00827     else {
00828         /* Tag RolloverNotification absent, set rolloverNotify to -1 */
00829         config->rolloverNotify = -1;
00830     }
00831 
00832     /* Evaluate xpath expression for DelegationSignerSubmitCommand */
00833     xpathObj = xmlXPathEvalExpression(ds_expr, xpathCtx);
00834     if(xpathObj == NULL) {
00835         log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", ds_expr);
00836         xmlXPathFreeContext(xpathCtx);
00837         xmlFreeDoc(doc);
00838         return(-1);
00839     }
00840     if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00841         /* Tag DelegationSignerSubmitCommand is present; set DSSubmitCmd */
00842         if (config->DSSubmitCmd != NULL) {
00843             StrFree(config->DSSubmitCmd);
00844         }
00845         config->DSSubmitCmd = (char *)xmlXPathCastToString(xpathObj);
00846 
00847         if (verbose) {
00848             log_msg(config, LOG_INFO, "Using command: %s to submit DS records", config->DSSubmitCmd);
00849         }
00850         xmlXPathFreeObject(xpathObj);
00851     } else {
00852         if (verbose) {
00853             log_msg(config, LOG_INFO, "No DS Submit command supplied");
00854         }
00855         config->DSSubmitCmd[0] = '\0';
00856     }
00857 
00858     /* Evaluate xpath expression for SQLite file location */
00859                 
00860     xpathObj = xmlXPathEvalExpression(litexpr, xpathCtx);
00861     if(xpathObj == NULL) {
00862         log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", litexpr);
00863         xmlXPathFreeContext(xpathCtx);
00864         xmlFreeDoc(doc);
00865         return(-1);
00866     }
00867     if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00868         db_found = SQLITE_DB;
00869         if (config->schema != NULL) {
00870             StrFree(config->schema);
00871         }
00872         config->schema = xmlXPathCastToString(xpathObj);
00873         if (verbose) {
00874             log_msg(config, LOG_INFO, "SQLite database set to: %s", config->schema);
00875         }
00876     }
00877     xmlXPathFreeObject(xpathObj);
00878 
00879     if (db_found == 0) {
00880         db_found = MYSQL_DB;
00881 
00882         /* Get all of the MySQL stuff read in too */
00883         /* HOST */
00884         xpathObj = xmlXPathEvalExpression(mysql_host, xpathCtx);
00885         if(xpathObj == NULL) {
00886             log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_host);
00887             xmlXPathFreeContext(xpathCtx);
00888             xmlFreeDoc(doc);
00889             return(-1);
00890         }
00891         if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00892             if (config->host != NULL) {
00893                 StrFree(config->host);
00894             }
00895             config->host = xmlXPathCastToString(xpathObj);
00896             if (verbose) {
00897                 log_msg(config, LOG_INFO, "MySQL database host set to: %s", config->host);
00898             }
00899         }
00900         xmlXPathFreeObject(xpathObj);
00901 
00902         /* PORT */
00903         xpathObj = xmlXPathEvalExpression(mysql_port, xpathCtx);
00904         if(xpathObj == NULL) {
00905             log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_port);
00906             xmlXPathFreeContext(xpathCtx);
00907             xmlFreeDoc(doc);
00908             return(-1);
00909         }
00910         if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00911             if (config->port != NULL) {
00912                 StrFree(config->port);
00913             }
00914             config->port = xmlXPathCastToString(xpathObj);
00915             if (verbose) {
00916                 log_msg(config, LOG_INFO, "MySQL database port set to: %s", config->port);
00917             }
00918         }
00919         xmlXPathFreeObject(xpathObj);
00920 
00921         /* SCHEMA */
00922         xpathObj = xmlXPathEvalExpression(mysql_db, xpathCtx);
00923         if(xpathObj == NULL) {
00924             log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_db);
00925             xmlXPathFreeContext(xpathCtx);
00926             xmlFreeDoc(doc);
00927             return(-1);
00928         }
00929         if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00930             if (config->schema != NULL) {
00931                 StrFree(config->schema);
00932             }
00933             config->schema = xmlXPathCastToString(xpathObj);
00934             if (verbose) {
00935                 log_msg(config, LOG_INFO, "MySQL database schema set to: %s", config->schema);
00936             }
00937         } else {
00938             db_found = 0;
00939         }
00940         xmlXPathFreeObject(xpathObj);
00941 
00942         /* DB USER */
00943         xpathObj = xmlXPathEvalExpression(mysql_user, xpathCtx);
00944         if(xpathObj == NULL) {
00945             log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_user);
00946             xmlXPathFreeContext(xpathCtx);
00947             xmlFreeDoc(doc);
00948             return(-1);
00949         }
00950         if(xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
00951             if (config->user != NULL) {
00952                 StrFree(config->user);
00953             }
00954             config->user = xmlXPathCastToString(xpathObj);
00955             if (verbose) {
00956                 log_msg(config, LOG_INFO, "MySQL database user set to: %s", config->user);
00957             }
00958         } else {
00959             db_found = 0;
00960         }
00961         xmlXPathFreeObject(xpathObj);
00962 
00963         /* DB PASSWORD */
00964         xpathObj = xmlXPathEvalExpression(mysql_pass, xpathCtx);
00965         if(xpathObj == NULL) {
00966                         log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", mysql_pass);
00967                         xmlXPathFreeContext(xpathCtx);
00968                         xmlFreeDoc(doc);
00969                         return(-1);
00970                     }
00971                     /* password may be blank */
00972         
00973         if (config->password != NULL) {
00974             StrFree(config->password);
00975         }
00976         config->password = xmlXPathCastToString(xpathObj);
00977         if (verbose) {
00978             log_msg(config, LOG_INFO, "MySQL database password set");
00979         }
00980         xmlXPathFreeObject(xpathObj);
00981 
00982     }
00983 
00984     /* Check that we found one or the other database */
00985     if(db_found == 0) {
00986         log_msg(config, LOG_ERR, "Error: unable to find complete database connection expression in %s", filename);
00987         xmlXPathFreeContext(xpathCtx);
00988         xmlFreeDoc(doc);
00989         return(-1);
00990     }
00991 
00992     /* Check that we found the right database type */
00993     if (db_found != DbFlavour()) {
00994         log_msg(config, LOG_ERR, "Error: database in config file %s does not match libksm", filename);
00995         xmlXPathFreeContext(xpathCtx);
00996         xmlFreeDoc(doc);
00997         return(-1);
00998     }
00999 
01000     /* Evaluate xpath expression for log facility (user) */
01001     xpathObj = xmlXPathEvalExpression(log_user_expr, xpathCtx);
01002     if(xpathObj == NULL) {
01003         log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s", log_user_expr);
01004         xmlXPathFreeContext(xpathCtx);
01005         xmlFreeDoc(doc);
01006         return(-1);
01007     }
01008 
01009     if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr > 0) {
01010         /* tag present */
01011         logFacilityName = (char *)xmlXPathCastToString(xpathObj);
01012 
01013         status = get_log_user(logFacilityName, &my_log_user);
01014         if (status > 0) {
01015             log_msg(config, LOG_ERR, "Error: unable to set log user: %s, error: %i", logFacilityName, status);
01016             StrFree(logFacilityName);
01017             return status;
01018         }
01019         config->log_user = my_log_user;
01020         if (verbose) {
01021             log_msg(config, LOG_INFO, "Log User set to: %s", logFacilityName);
01022         }
01023 
01024     } else {
01025         /* tag _not_ present, use default */
01026         logFacilityName = StrStrdup( (char *)DEFAULT_LOG_FACILITY_STRING );
01027         config->log_user = DEFAULT_LOG_FACILITY;
01028         if (verbose) {
01029             log_msg(config, LOG_INFO, "Using default log user: %s", logFacilityName);
01030         }
01031     }
01032 
01033     xmlXPathFreeObject(xpathObj);
01034 
01035     log_switch(my_log_user, logFacilityName, config->program, verbose);
01036 
01037     /* Cleanup */
01038     /* TODO: some other frees are needed */
01039     xmlXPathFreeContext(xpathCtx);
01040     xmlFreeDoc(doc);
01041     StrFree(logFacilityName);
01042     StrFree(filename);
01043 
01044     return(0);
01045 
01046 }
01047 
01048 /* To overcome the potential differences in sqlite compile flags assume that it is not
01049    happy with multiple connections.
01050 
01051    The following 2 functions take out a lock and release it
01052 */
01053 
01054 int get_lite_lock(char *lock_filename, FILE* lock_fd)
01055 {
01056     struct flock fl;
01057     struct timeval tv;
01058 
01059     if (lock_fd == NULL) {
01060         log_msg(NULL, LOG_ERR, "%s could not be opened", lock_filename);
01061         return 1;
01062     }
01063 
01064     memset(&fl, 0, sizeof(struct flock));
01065     fl.l_type = F_WRLCK;
01066     fl.l_whence = SEEK_SET;
01067     fl.l_pid = getpid();
01068     
01069     while (fcntl(fileno(lock_fd), F_SETLK, &fl) == -1) {
01070         if (errno == EACCES || errno == EAGAIN) {
01071             log_msg(NULL, LOG_INFO, "%s already locked, sleep", lock_filename);
01072 
01073             /* sleep for 10 seconds TODO make this configurable? */
01074             tv.tv_sec = 10;
01075             tv.tv_usec = 0;
01076             select(0, NULL, NULL, NULL, &tv);
01077 
01078         } else {
01079             log_msg(NULL, LOG_INFO, "couldn't get lock on %s, %s", lock_filename, strerror(errno));
01080             return 1;
01081         }
01082     }
01083 
01084     return 0;
01085 
01086 }
01087 
01088 int release_lite_lock(FILE* lock_fd)
01089 {
01090     struct flock fl;
01091 
01092     if (lock_fd == NULL) {
01093         return 1;
01094     }
01095     
01096     memset(&fl, 0, sizeof(struct flock));
01097     fl.l_type = F_UNLCK;
01098     fl.l_whence = SEEK_SET;
01099 
01100     if (fcntl(fileno(lock_fd), F_SETLK, &fl) == -1) {
01101         return 1;
01102     }
01103 
01104     return 0;
01105 }
01106 
01107 /* convert the name of a log facility (user) into a number */
01108 int get_log_user(const char* username, int* usernumber)
01109 {
01110     char* case_username = NULL;
01111 
01112     if (username == NULL) {
01113         return 1;
01114     }
01115     /* Start with our default */
01116     *usernumber = DEFAULT_LOG_FACILITY;
01117 
01118     case_username = StrStrdup(username);
01119     (void) StrToUpper(case_username);
01120 
01121     /* POSIX only specifies LOG_USER and LOG_LOCAL[0 .. 7] */
01122     if (strncmp(case_username, "USER", 4) == 0) {
01123         *usernumber = LOG_USER;
01124     }
01125 #ifdef LOG_KERN
01126     else if (strncmp(case_username, "KERN", 4) == 0) {
01127         *usernumber = LOG_KERN;
01128     }
01129 #endif  /* LOG_KERN */
01130 #ifdef LOG_MAIL
01131     else if (strncmp(case_username, "MAIL", 4) == 0) {
01132         *usernumber = LOG_MAIL;
01133     }
01134 #endif  /* LOG_MAIL */
01135 #ifdef LOG_DAEMON
01136     else if (strncmp(case_username, "DAEMON", 6) == 0) {
01137         *usernumber = LOG_DAEMON;
01138     }
01139 #endif  /* LOG_DAEMON */
01140 #ifdef LOG_AUTH
01141     else if (strncmp(case_username, "AUTH", 4) == 0) {
01142         *usernumber = LOG_AUTH;
01143     }
01144 #endif  /* LOG_AUTH */
01145 #ifdef LOG_SYSLOG
01146     else if (strncmp(case_username, "SYSLOG", 6) == 0) {
01147         *usernumber = LOG_SYSLOG;
01148     }
01149 #endif  /* LOG_SYSLOG */
01150 #ifdef LOG_LPR
01151     else if (strncmp(case_username, "LPR", 3) == 0) {
01152         *usernumber = LOG_LPR;
01153     }
01154 #endif  /* LOG_LPR */
01155 #ifdef LOG_NEWS
01156     else if (strncmp(case_username, "NEWS", 4) == 0) {
01157         *usernumber = LOG_NEWS;
01158     }
01159 #endif  /* LOG_NEWS */
01160 #ifdef LOG_UUCP
01161     else if (strncmp(case_username, "UUCP", 4) == 0) {
01162         *usernumber = LOG_UUCP;
01163     }
01164 #endif  /* LOG_UUCP */
01165 #ifdef LOG_AUDIT    /* Ubuntu at least doesn't want us to use LOG_AUDIT */
01166     else if (strncmp(case_username, "AUDIT", 5) == 0) {
01167         *usernumber = LOG_AUDIT;
01168     }
01169 #endif  /* LOG_AUDIT */
01170 #ifdef LOG_CRON
01171     else if (strncmp(case_username, "CRON", 4) == 0) {
01172         *usernumber = LOG_CRON;
01173     }
01174 #endif  /* LOG_CRON */
01175     else if (strncmp(case_username, "LOCAL0", 6) == 0) {
01176         *usernumber = LOG_LOCAL0;
01177     }
01178     else if (strncmp(case_username, "LOCAL1", 6) == 0) {
01179         *usernumber = LOG_LOCAL1;
01180     }
01181     else if (strncmp(case_username, "LOCAL2", 6) == 0) {
01182         *usernumber = LOG_LOCAL2;
01183     }
01184     else if (strncmp(case_username, "LOCAL3", 6) == 0) {
01185         *usernumber = LOG_LOCAL3;
01186     }
01187     else if (strncmp(case_username, "LOCAL4", 6) == 0) {
01188         *usernumber = LOG_LOCAL4;
01189     }
01190     else if (strncmp(case_username, "LOCAL5", 6) == 0) {
01191         *usernumber = LOG_LOCAL5;
01192     }
01193     else if (strncmp(case_username, "LOCAL6", 6) == 0) {
01194         *usernumber = LOG_LOCAL6;
01195     }
01196     else if (strncmp(case_username, "LOCAL7", 6) == 0) {
01197         *usernumber = LOG_LOCAL7;
01198     }
01199 
01200     StrFree(case_username);
01201 
01202     return 0;
01203 
01204 }
01205