OpenDNSSEC-enforcer
1.3.8
|
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