OpenDNSSEC-enforcer  1.3.4
/build/buildd/opendnssec-1.3.4/enforcer/enforcerd/enforcer.c
Go to the documentation of this file.
00001 /*
00002  * $Id: enforcer.c 5838 2011-11-08 14:28:05Z sion $
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  * enforcer.c code implements the server_main
00031  * function needed by daemon.c
00032  *
00033  * The bit that makes the daemon do something useful
00034  */
00035 
00036 #include <stdlib.h>
00037 #include <errno.h>
00038 #include <string.h>
00039 #include <stdio.h>
00040 #include <syslog.h>
00041 
00042 #include <libxml/xmlreader.h>
00043 #include <libxml/xpath.h>
00044 
00045 #include "config.h"
00046 
00047 #include "daemon.h"
00048 #include "daemon_util.h"
00049 #include "enforcer.h"
00050 #include "kaspaccess.h"
00051 
00052 #include "ksm/ksm.h"
00053 #include "ksm/memory.h"
00054 #include "ksm/string_util.h"
00055 #include "ksm/string_util2.h"
00056 #include "ksm/datetime.h"
00057 #include "ksm/db_fields.h"
00058 
00059 #include "libhsm.h"
00060 #include "libhsmdns.h"
00061 
00062     int
00063 server_init(DAEMONCONFIG *config)
00064 {
00065     if (config == NULL) {
00066         log_msg(NULL, LOG_ERR, "Error in server_init, no config provided");
00067         exit(1);
00068     }
00069 
00070     /* set the default pidfile if nothing was provided on the command line*/
00071     if (config->pidfile == NULL) {
00072         config->pidfile = OPENDNSSEC_ENFORCER_PIDFILE;
00073     }
00074 
00075     return 0;
00076 }
00077 
00078 /*
00079  * Main loop of enforcerd server
00080  */
00081     void
00082 server_main(DAEMONCONFIG *config)
00083 {
00084     DB_RESULT handle;
00085     DB_HANDLE dbhandle;
00086     int status = 0;
00087     struct timeval tv;
00088     KSM_POLICY *policy;
00089     int result;
00090     hsm_ctx_t *ctx = NULL;
00091     char *hsm_error_message = NULL;
00092 
00093     FILE *lock_fd = NULL;  /* for sqlite file locking */
00094     char *lock_filename = NULL;
00095 
00096     if (config == NULL) {
00097         log_msg(NULL, LOG_ERR, "Error in server_main, no config provided");
00098         exit(1);
00099     }
00100 
00101     policy = KsmPolicyAlloc();
00102     if (policy == NULL) {
00103         log_msg(config, LOG_ERR, "Malloc for policy struct failed");
00104         unlink(config->pidfile);
00105         exit(1);
00106     }
00107     kaspSetPolicyDefaults(policy, NULL);
00108 
00109     /* Read the config file */
00110     status = ReadConfig(config , 0);
00111     if (status != 0) {
00112         log_msg(config, LOG_ERR, "Error reading config");
00113         unlink(config->pidfile);
00114         exit(1);
00115     }
00116 
00117     /* If we are doing key generation then connect to the hsm */
00118 /*    if (config->manualKeyGeneration == 0) {*/
00119         /* We keep the HSM connection open for the lifetime of the daemon */
00120         if (config->configfile != NULL) {
00121             result = hsm_open(config->configfile, hsm_prompt_pin, NULL);
00122         } else {
00123             result = hsm_open(OPENDNSSEC_CONFIG_FILE, hsm_prompt_pin, NULL);
00124         }
00125         if (result) {
00126             hsm_error_message = hsm_get_error(ctx);
00127             if (hsm_error_message) {
00128                 log_msg(config, LOG_ERR, "%s", hsm_error_message);
00129                 free(hsm_error_message);
00130             } else {
00131                 /* decode the error code ourselves 
00132                    TODO find if there is a better way to do this (and can all of these be returned? are there others?) */
00133                 switch (result) {
00134                     case HSM_ERROR:
00135                         log_msg(config, LOG_ERR, "hsm_open() result: HSM error");
00136                         break;
00137                     case HSM_PIN_INCORRECT:
00138                         log_msg(config, LOG_ERR, "hsm_open() result: incorrect PIN");
00139                         break;
00140                     case HSM_CONFIG_FILE_ERROR:
00141                         log_msg(config, LOG_ERR, "hsm_open() result: config file error");
00142                         break;
00143                     case HSM_REPOSITORY_NOT_FOUND:
00144                         log_msg(config, LOG_ERR, "hsm_open() result: repository not found");
00145                         break;
00146                     case HSM_NO_REPOSITORIES:
00147                         log_msg(config, LOG_ERR, "hsm_open() result: no repositories");
00148                         break;
00149                     default:
00150                         log_msg(config, LOG_ERR, "hsm_open() result: %d", result);
00151                 }
00152             }
00153             unlink(config->pidfile);
00154             exit(1);
00155         }
00156         log_msg(config, LOG_INFO, "HSM opened successfully.");
00157         ctx = hsm_create_context();
00158     /*}*/
00159 
00160     while (1) {
00161 
00162         /* Read the config file */
00163         status = ReadConfig(config, 1);
00164         if (status != 0) {
00165             log_msg(config, LOG_ERR, "Error reading config");
00166             unlink(config->pidfile);
00167             exit(1);
00168         }
00169         /* If we are in sqlite mode then take a lock out on a file to
00170            prevent multiple access (not sure that we can be sure that sqlite is
00171            safe for multiple processes to access). */
00172         if (DbFlavour() == SQLITE_DB) {
00173 
00174             /* set up lock filename (it may have changed?) */
00175             lock_filename = NULL;
00176             StrAppend(&lock_filename, (char *)config->schema);
00177             StrAppend(&lock_filename, ".our_lock");
00178 
00179             lock_fd = fopen(lock_filename, "w");
00180             status = get_lite_lock(lock_filename, lock_fd);
00181             StrFree(lock_filename);
00182             if (status != 0) {
00183                 log_msg(config, LOG_ERR, "Error getting db lock");
00184                 unlink(config->pidfile);
00185                 exit(1);
00186             }
00187         }
00188 
00189         log_msg(config, LOG_INFO, "Connecting to Database...");
00190         kaspConnect(config, &dbhandle);
00191 
00192         /* Read all policies */
00193         status = KsmPolicyInit(&handle, NULL);
00194         if (status == 0) {
00195             /* get the first policy */
00196             status = KsmPolicy(handle, policy);
00197             while (status == 0) {
00198                 log_msg(config, LOG_INFO, "Policy %s found.", policy->name);
00199                 /* Clear the policy struct */
00200                 kaspSetPolicyDefaults(policy, NULL);
00201 
00202                 /* Read the parameters for that policy */
00203                 status = kaspReadPolicy(policy);
00204 
00205                 /* Update the salt if it is not up to date */
00206                 if (policy->denial->version == 3)
00207                 {
00208                     status = KsmPolicyUpdateSalt(policy);
00209                     if (status != 0) {
00210                         /* Don't return? */
00211                         log_msg(config, LOG_ERR, "Error (%d) updating salt for %s", status, policy->name);
00212                     }
00213                 }
00214 
00215                 /* Do keygen stuff if required */
00216                 if (config->manualKeyGeneration == 0) {
00217                     status = do_keygen(config, policy, ctx);
00218                 }
00219 
00220                 /* TODO move communicated stuff here eventually */
00221                 /* Find all zones and do communication stuff */
00222 
00223                 /* Purge dead keys if we are asked to in this policy */
00224                 if (policy->keys->purge != -1) {
00225                     status = do_purge(policy->keys->purge, policy->id);
00226                 }
00227 
00228                 /* get next policy */
00229                 status = KsmPolicy(handle, policy);
00230             }
00231         } else {
00232             log_msg(config, LOG_ERR, "Error querying KASP DB for policies.");
00233             unlink(config->pidfile);
00234             exit(1);
00235         }
00236 
00237         /* Communicate zones to the signer */
00238         do_communication(config, policy);
00239         
00240         DbFreeResult(handle);
00241 
00242         /* Disconnect from DB in case we are asleep for a long time */
00243         log_msg(config, LOG_INFO, "Disconnecting from Database...");
00244         kaspDisconnect(&dbhandle);
00245 
00246         /* Release sqlite lock file (if we have it) */
00247         if (DbFlavour() == SQLITE_DB) {
00248             status = release_lite_lock(lock_fd);
00249             if (status != 0) {
00250                 log_msg(config, LOG_ERR, "Error releasing db lock");
00251                 unlink(config->pidfile);
00252                 exit(1);
00253             }
00254             fclose(lock_fd);
00255         }
00256 
00257         if (config->once == true ){
00258             log_msg(config, LOG_INFO, "Running once only, exiting...");
00259             break;
00260         }
00261 
00262         /* If we have been sent a SIGTERM then it is time to exit */
00263         if (config->term == 1 ){
00264             log_msg(config, LOG_INFO, "Received SIGTERM, exiting...");
00265             break;
00266         }
00267         /* Or SIGINT */
00268         if (config->term == 2 ){
00269             log_msg(config, LOG_INFO, "Received SIGINT, exiting...");
00270             break;
00271         }
00272 
00273         /* sleep for the interval */
00274         tv.tv_sec = config->interval;
00275         tv.tv_usec = 0;
00276         log_msg(config, LOG_INFO, "Sleeping for %i seconds.",config->interval);
00277         select(0, NULL, NULL, NULL, &tv);
00278 
00279         /* If we have been sent a SIGTERM then it is time to exit */
00280         if (config->term == 1 ){
00281             log_msg(config, LOG_INFO, "Received SIGTERM, exiting...");
00282             break;
00283         }
00284         /* Or SIGINT */
00285         if (config->term == 2 ){
00286             log_msg(config, LOG_INFO, "Received SIGINT, exiting...");
00287             break;
00288         }
00289 
00290                 /* Make sure that we can still talk to the HSM; this call exits if
00291                    we can not (after trying to reconnect) */
00292                 check_hsm_connection(&ctx, config);
00293 
00294     }
00295 
00296     /*
00297      * Destroy HSM context
00298      */
00299     if (ctx) {
00300         hsm_destroy_context(ctx);
00301     }
00302 
00303     result = hsm_close();
00304     log_msg(config, LOG_INFO, "all done! hsm_close result: %d", result);
00305 
00306     KsmPolicyFree(policy);
00307 
00308     if (unlink(config->pidfile) == -1) {
00309         log_msg(config, LOG_ERR, "unlink pidfile %s failed: %s",
00310                 config->pidfile?config->pidfile:"(null)",
00311                 strerror(errno));
00312     }
00313 
00314     xmlCleanupParser();
00315 
00316 }
00317 
00318 int do_keygen(DAEMONCONFIG *config, KSM_POLICY* policy, hsm_ctx_t *ctx)
00319 {
00320     int status = 0;
00321 
00322     char *rightnow;
00323     int i = 0;
00324     char *id;
00325     hsm_key_t *key = NULL;
00326     char *hsm_error_message = NULL;
00327     DB_ID ignore = 0;
00328     int ksks_needed = 0;    /* Total No of ksks needed before next generation run */
00329     int zsks_needed = 0;    /* Total No of zsks needed before next generation run */
00330     int keys_in_queue = 0;  /* number of unused keys */
00331     int new_keys = 0;       /* number of keys required */
00332     unsigned int current_count = 0;  /* number of keys already in HSM */
00333 
00334     int same_keys = 0;      /* Do ksks and zsks look the same ? */
00335     int ksks_created = 0;   /* Were any KSKs created? */
00336     
00337     DB_RESULT result; 
00338     int zone_count = 0;     /* Number of zones on policy */
00339 
00340     if  (policy->shared_keys == 1 ) {
00341         log_msg(config, LOG_INFO, "Key sharing is On");
00342     } else {
00343         log_msg(config, LOG_INFO, "Key sharing is Off.");
00344     }
00345 
00346     rightnow = DtParseDateTimeString("now");
00347 
00348     /* Check datetime in case it came back NULL */
00349     if (rightnow == NULL) {
00350         log_msg(config, LOG_DEBUG, "Couldn't turn \"now\" into a date, quitting...");
00351         exit(1);
00352     }
00353 
00354     /* See if our ZSKs and KSKs look the same */
00355     if (policy->ksk->sm == policy->zsk->sm && policy->ksk->bits == policy->zsk->bits && policy->ksk->algorithm == policy->zsk->algorithm) {
00356         same_keys = 1;
00357     } else {
00358         same_keys = 0;
00359     }
00360 
00361     /* How many zones on this policy */ 
00362     status = KsmZoneCountInit(&result, policy->id); 
00363     if (status == 0) { 
00364         status = KsmZoneCount(result, &zone_count); 
00365     } 
00366     DbFreeResult(result); 
00367 
00368     if (status == 0) { 
00369         /* make sure that we have at least one zone */ 
00370         if (zone_count == 0) { 
00371             log_msg(config, LOG_INFO, "No zones on policy %s, skipping...", policy->name);
00372             StrFree(rightnow);
00373             return status; 
00374         } 
00375     } else {
00376         log_msg(NULL, LOG_ERR, "Could not count zones on policy %s", policy->name);
00377         StrFree(rightnow);
00378         return status; 
00379     }
00380 
00381     /* Find out how many ksk keys are needed for the POLICY */
00382     status = KsmKeyPredict(policy->id, KSM_TYPE_KSK, policy->shared_keys, config->interval, &ksks_needed, policy->ksk->rollover_scheme, zone_count);
00383     if (status != 0) {
00384         log_msg(NULL, LOG_ERR, "Could not predict ksk requirement for next interval for %s", policy->name);
00385         /* TODO exit? continue with next policy? */
00386     }
00387     /* Find out how many suitable keys we have */
00388     status = KsmKeyCountStillGood(policy->id, policy->ksk->sm, policy->ksk->bits, policy->ksk->algorithm, config->interval, rightnow, &keys_in_queue, KSM_TYPE_KSK);
00389     if (status != 0) {
00390         log_msg(NULL, LOG_ERR, "Could not count current ksk numbers for policy %s", policy->name);
00391         /* TODO exit? continue with next policy? */
00392     }
00393     /* Correct for shared keys */
00394     if (policy->shared_keys == KSM_KEYS_SHARED) {
00395         keys_in_queue /= zone_count;
00396     }
00397 
00398     new_keys = ksks_needed - keys_in_queue;
00399     /* fprintf(stderr, "keygen(ksk): new_keys(%d) = keys_needed(%d) - keys_in_queue(%d)\n", new_keys, ksks_needed, keys_in_queue); */
00400 
00401     /* Check capacity of HSM will not be exceeded */
00402     if (policy->ksk->sm_capacity != 0 && new_keys >= 0) {
00403         current_count = hsm_count_keys_repository(ctx, policy->ksk->sm_name);
00404         if (current_count >= policy->ksk->sm_capacity) {
00405             log_msg(config, LOG_ERR, "Repository %s is full, cannot create more KSKs for policy %s\n", policy->ksk->sm_name, policy->name);
00406             new_keys = 0;
00407         }
00408         else if (current_count + new_keys >  policy->ksk->sm_capacity) {
00409             log_msg(config, LOG_WARNING, "Repository %s is nearly full, will create %lu KSKs for policy %s (reduced from %d)\n", policy->ksk->sm_name, policy->ksk->sm_capacity - current_count, policy->name, new_keys);
00410             new_keys = policy->ksk->sm_capacity - current_count;
00411         }
00412     }
00413 
00414     /* Create the required keys */
00415     for (i=new_keys ; i > 0 ; i--){
00416         if (hsm_supported_algorithm(policy->ksk->algorithm) == 0) {
00417             /* NOTE: for now we know that libhsm only supports RSA keys */
00418             key = hsm_generate_rsa_key(ctx, policy->ksk->sm_name, policy->ksk->bits);
00419             if (key) {
00420                 log_msg(config, LOG_DEBUG, "Created key in repository %s", policy->ksk->sm_name);
00421             } else {
00422                 log_msg(config, LOG_ERR, "Error creating key in repository %s", policy->ksk->sm_name);
00423                 hsm_error_message = hsm_get_error(ctx);
00424                 if (hsm_error_message) {
00425                     log_msg(config, LOG_ERR, "%s", hsm_error_message);
00426                     free(hsm_error_message);
00427                 }
00428                 unlink(config->pidfile);
00429                 exit(1);
00430             }
00431             id = hsm_get_key_id(ctx, key);
00432             hsm_key_free(key);
00433             status = KsmKeyPairCreate(policy->id, id, policy->ksk->sm, policy->ksk->bits, policy->ksk->algorithm, rightnow, &ignore);
00434             if (status != 0) {
00435                 log_msg(config, LOG_ERR,"Error creating key in Database");
00436                 hsm_error_message = hsm_get_error(ctx);
00437                 if (hsm_error_message) {
00438                     log_msg(config, LOG_ERR, "%s", hsm_error_message);
00439                     free(hsm_error_message);
00440                 }
00441                 unlink(config->pidfile);
00442                 exit(1);
00443             }
00444             log_msg(config, LOG_INFO, "Created KSK size: %i, alg: %i with id: %s in repository: %s and database.", policy->ksk->bits,
00445                     policy->ksk->algorithm, id, policy->ksk->sm_name);
00446             free(id);
00447         } else {
00448             log_msg(config, LOG_ERR, "Key algorithm %d unsupported by libhsm, exiting...", policy->ksk->algorithm);
00449             unlink(config->pidfile);
00450             exit(1);
00451         }
00452     }
00453     ksks_created = new_keys;
00454 
00455     /* Find out how many zsk keys are needed */
00456     keys_in_queue = 0;
00457     new_keys = 0;
00458     current_count = 0;
00459 
00460     /* Find out how many zsk keys are needed for the POLICY */
00461     status = KsmKeyPredict(policy->id, KSM_TYPE_ZSK, policy->shared_keys, config->interval, &zsks_needed, 0, zone_count);
00462     if (status != 0) {
00463         log_msg(NULL, LOG_ERR, "Could not predict zsk requirement for next intervalfor %s", policy->name);
00464         /* TODO exit? continue with next policy? */
00465     }
00466     /* Find out how many suitable keys we have */
00467     status = KsmKeyCountStillGood(policy->id, policy->zsk->sm, policy->zsk->bits, policy->zsk->algorithm, config->interval, rightnow, &keys_in_queue, KSM_TYPE_ZSK);
00468     if (status != 0) {
00469         log_msg(NULL, LOG_ERR, "Could not count current zsk numbers for policy %s", policy->name);
00470         /* TODO exit? continue with next policy? */
00471     }
00472     /* Correct for shared keys */
00473     if (policy->shared_keys == KSM_KEYS_SHARED) {
00474         keys_in_queue /= zone_count;
00475     }
00476     /* Might have to account for ksks */
00477     if (same_keys) {
00478         keys_in_queue -= ksks_needed;
00479     }
00480 
00481     new_keys = zsks_needed - keys_in_queue;
00482     /* fprintf(stderr, "keygen(zsk): new_keys(%d) = keys_needed(%d) - keys_in_queue(%d)\n", new_keys, zsks_needed, keys_in_queue); */
00483 
00484     /* Check capacity of HSM will not be exceeded */
00485     if (policy->zsk->sm_capacity != 0 && new_keys >= 0) {
00486         current_count = hsm_count_keys_repository(ctx, policy->zsk->sm_name);
00487         if (current_count >= policy->zsk->sm_capacity) {
00488             log_msg(config, LOG_ERR, "Repository %s is full, cannot create more ZSKs for policy %s\n", policy->zsk->sm_name, policy->name);
00489             new_keys = 0;
00490         }
00491         else if (current_count + new_keys >  policy->zsk->sm_capacity) {
00492             log_msg(config, LOG_WARNING, "Repository %s is nearly full, will create %lu ZSKs for policy %s (reduced from %d)\n", policy->zsk->sm_name, policy->zsk->sm_capacity - current_count, policy->name, new_keys);
00493             new_keys = policy->zsk->sm_capacity - current_count;
00494         }
00495     }
00496 
00497     /* Create the required keys */
00498     for (i = new_keys ; i > 0 ; i--) {
00499         if (hsm_supported_algorithm(policy->zsk->algorithm) == 0) {
00500             /* NOTE: for now we know that libhsm only supports RSA keys */
00501             key = hsm_generate_rsa_key(ctx, policy->zsk->sm_name, policy->zsk->bits);
00502             if (key) {
00503                 log_msg(config, LOG_DEBUG, "Created key in repository %s", policy->zsk->sm_name);
00504             } else {
00505                 log_msg(config, LOG_ERR, "Error creating key in repository %s", policy->zsk->sm_name);
00506                 hsm_error_message = hsm_get_error(ctx);
00507                 if (hsm_error_message) {
00508                     log_msg(config, LOG_ERR, "%s", hsm_error_message);
00509                     free(hsm_error_message);
00510                 }
00511                 unlink(config->pidfile);
00512                 hsm_key_free(key);
00513                 exit(1);
00514             }
00515             id = hsm_get_key_id(ctx, key);
00516             hsm_key_free(key);
00517             status = KsmKeyPairCreate(policy->id, id, policy->zsk->sm, policy->zsk->bits, policy->zsk->algorithm, rightnow, &ignore);
00518             if (status != 0) {
00519                 log_msg(config, LOG_ERR,"Error creating key in Database");
00520                 hsm_error_message = hsm_get_error(ctx);
00521                 if (hsm_error_message) {
00522                     log_msg(config, LOG_ERR, "%s", hsm_error_message);
00523                     free(hsm_error_message);
00524                 }
00525                 unlink(config->pidfile);
00526                 exit(1);
00527             }
00528             log_msg(config, LOG_INFO, "Created ZSK size: %i, alg: %i with id: %s in repository: %s and database.", policy->zsk->bits,
00529                     policy->zsk->algorithm, id, policy->zsk->sm_name);
00530             free(id);
00531         } else {
00532             log_msg(config, LOG_ERR, "Key algorithm %d unsupported by libhsm, exiting...", policy->zsk->algorithm);
00533             unlink(config->pidfile);
00534             exit(1);
00535         }
00536     }
00537     StrFree(rightnow);
00538 
00539     /* Log if a backup needs to be run for these keys */
00540     if (ksks_created && policy->ksk->require_backup) {
00541         log_msg(config, LOG_INFO, "NOTE: keys generated in repository %s will not become active until they have been backed up", policy->ksk->sm_name);
00542     }
00543     if (new_keys && policy->zsk->require_backup && (policy->zsk->sm != policy->ksk->sm)) {
00544         log_msg(config, LOG_INFO, "NOTE: keys generated in repository %s will not become active until they have been backed up", policy->zsk->sm_name);
00545     }
00546 
00547     return status;
00548 }
00549 
00550 int do_communication(DAEMONCONFIG *config, KSM_POLICY* policy)
00551 {
00552     int status = 0;
00553     int status2 = 0;
00554 
00555     xmlTextReaderPtr reader = NULL;
00556     xmlDocPtr doc = NULL;
00557     xmlXPathContextPtr xpathCtx = NULL;
00558     xmlXPathObjectPtr xpathObj = NULL;
00559 
00560     int ret = 0; /* status of the XML parsing */
00561     char* zonelist_filename = NULL;
00562     char* zone_name;
00563     char* current_policy;
00564     char* current_filename;
00565     char *tag_name;
00566     int zone_id = -1;
00567     int signer_flag = 1; /* Is the signer responding? (1 == yes) */
00568     char* ksk_expected = NULL;  /* When is the next ksk rollover expected? */
00569 
00570     xmlChar *name_expr = (unsigned char*) "name";
00571     xmlChar *policy_expr = (unsigned char*) "//Zone/Policy";
00572     xmlChar *filename_expr = (unsigned char*) "//Zone/SignerConfiguration";
00573 
00574     char* temp_char = NULL;
00575 
00576     /* Stuff to see if we need to log an "impending rollover" warning */
00577     char* datetime = NULL;
00578     int roll_time = 0;
00579 
00580     /* Let's find our zonelist from the conf.xml */
00581     if (config->configfile != NULL) {
00582         status = read_zonelist_filename(config->configfile, &zonelist_filename);
00583     } else {
00584         status = read_zonelist_filename(OPENDNSSEC_CONFIG_FILE, &zonelist_filename);
00585     }
00586     
00587     if (status != 0) {
00588         log_msg(NULL, LOG_ERR, "couldn't read zonelist filename");
00589         unlink(config->pidfile);
00590         exit(1);
00591     }
00592 
00593     /* In case zonelist is huge use the XmlTextReader API so that we don't hold the whole file in memory */
00594     reader = xmlNewTextReaderFilename(zonelist_filename);
00595     if (reader != NULL) {
00596         ret = xmlTextReaderRead(reader);
00597         while (ret == 1) {
00598             tag_name = (char*) xmlTextReaderLocalName(reader);
00599             /* Found <Zone> */
00600             if (strncmp(tag_name, "Zone", 4) == 0 
00601                     && strncmp(tag_name, "ZoneList", 8) != 0
00602                     && xmlTextReaderNodeType(reader) == 1) {
00603                 /* Get the zone name (TODO what if this is null?) */
00604                 zone_name = NULL;
00605                 temp_char = (char*) xmlTextReaderGetAttribute(reader, name_expr);
00606                 StrAppend(&zone_name, temp_char);
00607                 StrFree(temp_char);
00608                 /* Make sure that we got something */
00609                 if (zone_name == NULL) {
00610                     /* error */
00611                     log_msg(NULL, LOG_ERR, "Error extracting zone name from %s", zonelist_filename);
00612                     /* Don't return? try to parse the rest of the zones? */
00613                     ret = xmlTextReaderRead(reader);
00614                     StrFree(tag_name);
00615                     continue;
00616                 }
00617 
00618 
00619                 log_msg(config, LOG_INFO, "Zone %s found.", zone_name);
00620 
00621                 /* Get zone ID from name (or skip if it doesn't exist) */
00622                 status = KsmZoneIdFromName(zone_name, &zone_id);
00623                 if (status != 0 || zone_id == -1)
00624                 {
00625                     /* error */
00626                     log_msg(NULL, LOG_ERR, "Error looking up zone \"%s\" in database (please make sure that the zonelist file is up to date)", zone_name);
00627                     /* Don't return? try to parse the rest of the zones? */
00628                     ret = xmlTextReaderRead(reader);
00629                     StrFree(tag_name);
00630                     StrFree(zone_name);
00631                     continue;
00632                 }
00633 
00634                 /* Expand this node and get the rest of the info with XPath */
00635                 xmlTextReaderExpand(reader);
00636                 doc = xmlTextReaderCurrentDoc(reader);
00637                 if (doc == NULL) {
00638                     log_msg(config, LOG_ERR, "Error: can not read zone \"%s\"; skipping", zone_name);
00639                     /* Don't return? try to parse the rest of the zones? */
00640                     ret = xmlTextReaderRead(reader);
00641                     StrFree(tag_name);
00642                     StrFree(zone_name);
00643                     continue;
00644                 }
00645 
00646                 /* TODO should we validate here? Or should we validate the whole document? */
00647 
00648                 xpathCtx = xmlXPathNewContext(doc);
00649                 if(xpathCtx == NULL) {
00650                     log_msg(config, LOG_ERR,"Error: can not create XPath context for \"%s\"; skipping zone", zone_name);
00651                     /* Don't return? try to parse the rest of the zones? */
00652                     ret = xmlTextReaderRead(reader);
00653                     StrFree(tag_name);
00654                     StrFree(zone_name);
00655                     continue;
00656                 }
00657 
00658                 /* Extract the Policy name and signer configuration filename for this zone */
00659                 /* Evaluate xpath expression for policy */
00660                 xpathObj = xmlXPathEvalExpression(policy_expr, xpathCtx);
00661                 if(xpathObj == NULL) {
00662                     log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s; skipping zone", policy_expr);
00663                     /* Don't return? try to parse the rest of the zones? */
00664                     ret = xmlTextReaderRead(reader);
00665                     StrFree(tag_name);
00666                     StrFree(zone_name);
00667                     continue;
00668                 }
00669                 current_policy = NULL;
00670                 temp_char = (char*) xmlXPathCastToString(xpathObj);
00671                 StrAppend(&current_policy, temp_char);
00672                 StrFree(temp_char);
00673                 log_msg(config, LOG_INFO, "Policy for %s set to %s.", zone_name, current_policy);
00674                 xmlXPathFreeObject(xpathObj);
00675 
00676                 if (strcmp(current_policy, policy->name) != 0) {
00677 
00678                     /* Read new Policy */ 
00679                     kaspSetPolicyDefaults(policy, current_policy);
00680 
00681                     status2 = KsmPolicyRead(policy);
00682                     if (status2 != 0) {
00683                         /* Don't return? try to parse the rest of the zones? */
00684                         log_msg(config, LOG_ERR, "Error reading policy");
00685                         ret = xmlTextReaderRead(reader);
00686                         StrFree(tag_name);
00687                         StrFree(zone_name);
00688                         continue;
00689                     }
00690                     log_msg(config, LOG_INFO, "Policy %s found in DB.", policy->name);
00691 
00692                 } /* else */ 
00693                   /* Policy is same as previous zone, do not re-read */
00694 
00695                 StrFree(current_policy);
00696 
00697                 /* Evaluate xpath expression for signer configuration filename */
00698                 xpathObj = xmlXPathEvalExpression(filename_expr, xpathCtx);
00699                 xmlXPathFreeContext(xpathCtx);
00700 
00701                 if(xpathObj == NULL) {
00702                     log_msg(config, LOG_ERR, "Error: unable to evaluate xpath expression: %s; skipping zone", filename_expr);
00703                     /* Don't return? try to parse the rest of the zones? */
00704                     ret = xmlTextReaderRead(reader);
00705                     StrFree(tag_name);
00706                     StrFree(zone_name);
00707                     continue;
00708                 }
00709                 current_filename = NULL;
00710                 temp_char = (char*)xmlXPathCastToString(xpathObj);
00711                 StrAppend(&current_filename, temp_char);
00712                 StrFree(temp_char);
00713                 log_msg(config, LOG_INFO, "Config will be output to %s.", current_filename);
00714                 xmlXPathFreeObject(xpathObj);
00715                 /* TODO should we check that we have not written to this file in this run?*/
00716                 /* Make sure that enough keys are allocated to this zone */
00717                 status2 = allocateKeysToZone(policy, KSM_TYPE_ZSK, zone_id, config->interval, zone_name, config->manualKeyGeneration, 0);
00718                 if (status2 != 0) {
00719                     log_msg(config, LOG_ERR, "Error allocating zsks to zone %s", zone_name);
00720                     /* Don't return? try to parse the rest of the zones? */
00721                     ret = xmlTextReaderRead(reader);
00722                     StrFree(tag_name);
00723                     StrFree(zone_name);
00724                     StrFree(current_filename);
00725                     continue;
00726                 }
00727                 status2 = allocateKeysToZone(policy, KSM_TYPE_KSK, zone_id, config->interval, zone_name, config->manualKeyGeneration, policy->ksk->rollover_scheme);
00728                 if (status2 != 0) {
00729                     log_msg(config, LOG_ERR, "Error allocating ksks to zone %s", zone_name);
00730                     /* Don't return? try to parse the rest of the zones? */
00731                     ret = xmlTextReaderRead(reader);
00732                     StrFree(tag_name);
00733                     StrFree(zone_name);
00734                     StrFree(current_filename);
00735                     continue;
00736                 }
00737 
00738                 /* turn this zone and policy into a file */
00739                 status2 = commGenSignConf(zone_name, zone_id, current_filename, policy, &signer_flag, config->interval, config->manualKeyGeneration, config->DSSubmitCmd);
00740                 if (status2 == -2) {
00741                     log_msg(config, LOG_ERR, "Signconf not written for %s", zone_name);
00742                     /* Don't return? try to parse the rest of the zones? */
00743                     ret = xmlTextReaderRead(reader);
00744                     StrFree(tag_name);
00745                     StrFree(zone_name);
00746                     StrFree(current_filename);
00747                     continue;
00748                 }
00749                 else if (status2 != 0) {
00750                     log_msg(config, LOG_ERR, "Error writing signconf for %s", zone_name);
00751                     /* Don't return? try to parse the rest of the zones? */
00752                     ret = xmlTextReaderRead(reader);
00753                     StrFree(tag_name);
00754                     StrFree(zone_name);
00755                     StrFree(current_filename);
00756                     continue;
00757                 }
00758 
00759                 /* See if we need to send a warning about an impending rollover */
00760                 if (config->rolloverNotify != -1) {
00761                     datetime = DtParseDateTimeString("now");
00762 
00763                     /* Check datetime in case it came back NULL */
00764                     if (datetime == NULL) {
00765                         log_msg(config, LOG_DEBUG, "Couldn't turn \"now\" into a date, quitting...");
00766                         unlink(config->pidfile);
00767                         exit(1);
00768                     }
00769 
00770                     /* First the KSK */
00771                     status2 = KsmCheckNextRollover(KSM_TYPE_KSK, zone_id, &ksk_expected);
00772                     if (status2 == -1) {
00773                         log_msg(config, LOG_INFO, "No active KSKs yet for zone %s, can't check for impending rollover", zone_name);
00774                     }
00775                     else if (status2 != 0) {
00776                         log_msg(config, LOG_ERR, "Error checking for impending rollover for %s", zone_name);
00777                         /* TODO should we quit or continue? */
00778                     } else {
00779                         status2 = DtDateDiff(ksk_expected, datetime, &roll_time);
00780                         if (status2 != 0) {
00781                             log_msg(config, LOG_ERR, "Error checking for impending rollover for %s", zone_name);
00782                         } else {
00783 
00784                             if (roll_time <= config->rolloverNotify) {
00785                                 log_msg(config, LOG_INFO, "Rollover of KSK expected at %s for %s", ksk_expected, zone_name);
00786                             }
00787                             StrFree(ksk_expected);
00788                         }
00789                     }
00790                     StrFree(datetime);
00791                 }
00792 
00793                 StrFree(current_filename);
00794                 StrFree(zone_name);
00795             }
00796             /* Read the next line */
00797             ret = xmlTextReaderRead(reader);
00798             StrFree(tag_name);
00799         }
00800         xmlFreeTextReader(reader);
00801         if (ret != 0) {
00802             log_msg(config, LOG_ERR, "%s : failed to parse", zonelist_filename);
00803         }
00804     } else {
00805         log_msg(config, LOG_ERR, "Unable to open %s", zonelist_filename);
00806     }
00807 
00808     xmlFreeDoc(doc);
00809     StrFree(zonelist_filename);
00810 
00811     return status;
00812 }
00813 
00814 /*
00815  *  generate the configuration file for the signer
00816 
00817  *  returns 0 on success and -1 if something went wrong
00818  *                           -2 if the RequestKeys call failed
00819  */
00820 int commGenSignConf(char* zone_name, int zone_id, char* current_filename, KSM_POLICY *policy, int* signer_flag, int run_interval, int man_key_gen, const char* DSSubmitCmd)
00821 {
00822     int status = 0;
00823     int status2 = 0;
00824     FILE *file, *file2;
00825     int char1, char2;      /* for the comparison between 2 files */
00826     int same = 0;
00827     char *temp_filename;    /* In case this fails we write to a temp file and only overwrite
00828                                the current file when we are finished */
00829     char *old_filename;     /* Keep a copy of the previous version, just in case! (Also gets
00830                                round potentially different behaviour of rename over existing
00831                                file.) */
00832     char *signer_command;   /* how we will call the signer */
00833     int     gencnt;         /* Number of keys in generate state */
00834     int     NewDS = 0;      /* Did we change the DS Set in any way? */
00835     char*   datetime = DtParseDateTimeString("now");
00836 
00837     /* Check datetime in case it came back NULL */
00838     if (datetime == NULL) {
00839         log_msg(NULL, LOG_DEBUG, "Couldn't turn \"now\" into a date, quitting...");
00840         exit(1);
00841     }
00842 
00843     if (zone_name == NULL || current_filename == NULL || policy == NULL)
00844     {
00845         /* error */
00846         log_msg(NULL, LOG_ERR, "commGenSignConf, NULL policy or zone provided");
00847         MemFree(datetime);
00848         return -1;
00849     }
00850 
00851     old_filename = NULL;
00852     StrAppend(&old_filename, current_filename);
00853     StrAppend(&old_filename, ".OLD");
00854 
00855     temp_filename = NULL;
00856     StrAppend(&temp_filename, current_filename);
00857     StrAppend(&temp_filename, ".tmp");
00858 
00859     file = fopen(temp_filename, "w");
00860 
00861     if (file == NULL)
00862     {
00863         /* error */
00864         log_msg(NULL, LOG_ERR, "Could not open: %s", temp_filename);
00865         MemFree(datetime);
00866         StrFree(temp_filename);
00867         StrFree(old_filename);
00868         return -1;
00869     }
00870 
00871     fprintf(file, "<SignerConfiguration>\n");
00872     fprintf(file, "\t<Zone name=\"%s\">\n", zone_name);
00873 
00874     fprintf(file, "\t\t<Signatures>\n");
00875     fprintf(file, "\t\t\t<Resign>PT%dS</Resign>\n", policy->signature->resign);
00876     fprintf(file, "\t\t\t<Refresh>PT%dS</Refresh>\n", policy->signer->refresh);
00877     fprintf(file, "\t\t\t<Validity>\n");
00878     fprintf(file, "\t\t\t\t<Default>PT%dS</Default>\n", policy->signature->valdefault);
00879     fprintf(file, "\t\t\t\t<Denial>PT%dS</Denial>\n", policy->signature->valdenial);
00880     fprintf(file, "\t\t\t</Validity>\n");
00881     fprintf(file, "\t\t\t<Jitter>PT%dS</Jitter>\n", policy->signer->jitter);
00882     fprintf(file, "\t\t\t<InceptionOffset>PT%dS</InceptionOffset>\n", policy->signature->clockskew);
00883     fprintf(file, "\t\t</Signatures>\n");
00884 
00885     fprintf(file, "\n");
00886 
00887     fprintf(file, "\t\t<Denial>\n");
00888     if (policy->denial->version == 3)
00889     {
00890         fprintf(file, "\t\t\t<NSEC3>\n");
00891         if (policy->denial->optout == 1)
00892         {
00893             fprintf(file, "\t\t\t\t<OptOut />\n");
00894         }
00895         fprintf(file, "\t\t\t\t<Hash>\n");
00896         fprintf(file, "\t\t\t\t\t<Algorithm>%d</Algorithm>\n", policy->denial->algorithm);
00897         fprintf(file, "\t\t\t\t\t<Iterations>%d</Iterations>\n", policy->denial->iteration);
00898         if (policy->denial->salt[0] == '\0') {
00899             fprintf(file, "\t\t\t\t\t<Salt>-</Salt>\n");
00900         } else {
00901             fprintf(file, "\t\t\t\t\t<Salt>%s</Salt>\n", policy->denial->salt);
00902         }
00903         fprintf(file, "\t\t\t\t</Hash>\n");
00904         fprintf(file, "\t\t\t</NSEC3>\n");
00905     } else {
00906         fprintf(file, "\t\t\t<NSEC />\n");
00907     }
00908 
00909     fprintf(file, "\t\t</Denial>\n");
00910 
00911     fprintf(file, "\n");
00912 
00913     /* start of keys section */ 
00914     fprintf(file, "\t\t<Keys>\n");
00915     fprintf(file, "\t\t\t<TTL>PT%dS</TTL>\n", policy->ksk->ttl);
00916 
00917     /* get new keys _only_ if we don't have them from before */
00918     status = KsmRequestKeys(0, 0, datetime, commKeyConfig, file, policy->id, zone_id, run_interval, &NewDS);
00919     if (status != 0) {
00920         /* 
00921          * Something went wrong (it should have been logged) stop this zone.
00922          * Clean up the files, don't call the signer and move on to the next zone.
00923          */
00924         log_msg(NULL, LOG_ERR, "KsmRequestKeys returned: %d", status);
00925 
00926         /* check for the specific case of not having any keys 
00927            TODO check that this code can ever be executed after the restructure */
00928         if (status == -1) {
00929             status2 = KsmRequestGenerateCount(KSM_TYPE_KSK, &gencnt, zone_id);
00930             if (status2 == 0 && gencnt == 0) {
00931                 if(man_key_gen == 1) {
00932                     log_msg(NULL, LOG_ERR, "There are no KSKs in the generate state; please use \"ods-ksmutil key generate\" to create some.");
00933                 } else {
00934                     log_msg(NULL, LOG_WARNING, "There are no KSKs in the generate state; ods-enforcerd will create some on its next run.");
00935                 }
00936             }
00937             else if (status2 == 0) {
00938                 status2 = KsmRequestGenerateCount(KSM_TYPE_ZSK, &gencnt, zone_id);
00939                 if (status2 == 0 && gencnt == 0) {
00940                     if(man_key_gen == 1) {
00941                         log_msg(NULL, LOG_ERR, "There are no ZSKs in the generate state; please use \"ods-ksmutil key generate\" to create some.");
00942                     } else {
00943                         log_msg(NULL, LOG_WARNING, "There are no ZSKs in the generate state; ods-enforcerd will create some on its next run.");
00944                     }
00945                 }
00946             }
00947             else {
00948                 log_msg(NULL, LOG_ERR, "KsmRequestGenerateCount returned: %d", status2);
00949             }
00950         }
00951 
00952         status = fclose(file);
00953         unlink(temp_filename);
00954         MemFree(datetime);
00955         StrFree(temp_filename);
00956         StrFree(old_filename);
00957 
00958         return -2;
00959     }
00960 
00961     fprintf(file, "\t\t</Keys>\n");
00962 
00963     fprintf(file, "\n");
00964 
00965     fprintf(file, "\t\t<SOA>\n");
00966     fprintf(file, "\t\t\t<TTL>PT%dS</TTL>\n", policy->signer->soattl);
00967     fprintf(file, "\t\t\t<Minimum>PT%dS</Minimum>\n", policy->signer->soamin);
00968     fprintf(file, "\t\t\t<Serial>%s</Serial>\n", KsmKeywordSerialValueToName( policy->signer->serial) );
00969     fprintf(file, "\t\t</SOA>\n");
00970 
00971     if (strncmp(policy->audit, "NULL", 4) != 0) {
00972         fprintf(file, "\n");
00973         fprintf(file, "\t\t<Audit />\n");
00974         fprintf(file, "\n");
00975     }
00976 
00977     fprintf(file, "\t</Zone>\n");
00978     fprintf(file, "</SignerConfiguration>\n");
00979 
00980     /* Force flush of stream to disc cache and then onto disc proper
00981      * Do we need to do this? It might be significant on ext4
00982      * NOTE though that there may be a significant overhead associated with it
00983      * ALSO, if we do lose power maybe we should disregard any files when we come
00984      *       back as we won't know if they are now too old? */
00985     /* 
00986        if (fflush(file) != 0) {
00987        MemFree(datetime);
00988        return -1;
00989        }
00990 
00991        if (fsync(fileno(file)) != 0) {
00992        MemFree(datetime);
00993        return -1;
00994        }
00995      */
00996 
00997     status = fclose(file);
00998     MemFree(datetime);
00999 
01000     if (status == EOF) /* close failed... do something? */
01001     {
01002         log_msg(NULL, LOG_ERR, "Could not close: %s", temp_filename);
01003         StrFree(temp_filename);
01004         StrFree(old_filename);
01005         return -1;
01006     }
01007 
01008     /* compare our temp file with the current one (if it exists) */
01009     file = fopen(temp_filename, "rb");
01010     if (file == NULL)
01011     {
01012         /* error */
01013         log_msg(NULL, LOG_ERR, "Could not reopen: %s", temp_filename);
01014         StrFree(temp_filename);
01015         StrFree(old_filename);
01016         return -1;
01017     }
01018 
01019     file2 = fopen(current_filename, "rb"); /* Might not exist */
01020 
01021     /* If current_filename exists then compare its contents to temp_filename */
01022     if (file2 != NULL) {
01023         same = 1;
01024         while(!feof(file)) {
01025             char1 = fgetc(file);
01026             if(ferror(file)) {
01027                 log_msg(NULL, LOG_ERR, "Could not read: %s", temp_filename);
01028                 fclose(file);
01029                 fclose(file2);
01030                 StrFree(temp_filename);
01031                 StrFree(old_filename);
01032                 return -1;
01033             }
01034             char2 = fgetc(file2);
01035             if(ferror(file2)) {
01036                 log_msg(NULL, LOG_ERR, "Could not read: %s", current_filename);
01037                 fclose(file);
01038                 fclose(file2);
01039                 StrFree(temp_filename);
01040                 StrFree(old_filename);
01041                 return -1;
01042             }
01043             if(char1 != char2) {
01044                 same = 0;
01045                 break;
01046             }
01047         }
01048 
01049         status = fclose(file2);
01050         if (status == EOF) /* close failed... do something? */
01051         {
01052             log_msg(NULL, LOG_ERR, "Could not close: %s", current_filename);
01053             fclose(file);
01054             StrFree(temp_filename);
01055             StrFree(old_filename);
01056             return -1;
01057         }
01058     }
01059 
01060     status = fclose(file);
01061     if (status == EOF) /* close failed... do something? */
01062     {
01063         log_msg(NULL, LOG_ERR, "Could not close: %s", temp_filename);
01064         StrFree(temp_filename);
01065         StrFree(old_filename);
01066         return -1;
01067     }
01068 
01069     /* If either current_filename does not exist, or if it is different to temp then same will == 0 */
01070 
01071     if (same == 0) {
01072 
01073         /* we now have a complete xml file. First move the old one out of the way */
01074         status = rename(current_filename, old_filename);
01075         if (status != 0 && status != -1)
01076         {
01077             /* cope with initial condition of files not existing */
01078             log_msg(NULL, LOG_ERR, "Could not rename: %s -> %s", current_filename, old_filename);
01079             StrFree(old_filename);
01080             StrFree(temp_filename);
01081             return -1;
01082         }
01083 
01084         /* Then copy our temp into place */
01085         if (rename(temp_filename, current_filename) != 0)
01086         {
01087             log_msg(NULL, LOG_ERR, "Could not rename: %s -> %s", temp_filename, current_filename);
01088             StrFree(old_filename);
01089             StrFree(temp_filename);
01090             return -1;
01091         }
01092 
01093         if (*signer_flag == 1) {
01094             /* call the signer engine to tell it that something changed */
01095             /* TODO for beta version connect straight to the socket
01096                should we make a blocking call on this?
01097                should we call it here or after we have written all of the files?
01098                have timeout if call is blocking */
01099             signer_command = NULL;
01100             StrAppend(&signer_command, SIGNER_CLI_UPDATE);
01101             StrAppend(&signer_command, " ");
01102             StrAppend(&signer_command, zone_name);
01103 
01104             status = system(signer_command);
01105             if (status != 0)
01106             {
01107                 log_msg(NULL, LOG_ERR, "Could not call signer engine");
01108                 log_msg(NULL, LOG_INFO, "Will continue: call 'ods-signer update' to manually update zones");
01109                 *signer_flag = 0;
01110             }
01111 
01112             StrFree(signer_command);
01113         }
01114     }
01115     else {
01116         log_msg(NULL, LOG_INFO, "No change to: %s", current_filename);
01117         if (remove(temp_filename) != 0)
01118         {
01119             log_msg(NULL, LOG_ERR, "Could not remove: %s", temp_filename);
01120             StrFree(old_filename);
01121             StrFree(temp_filename);
01122             return -1;
01123         }
01124     }
01125 
01126     /* If the DS set changed then log/do something about it */
01127     if (NewDS == 1) {
01128         log_msg(NULL, LOG_INFO, "DSChanged");
01129         status = NewDSSet(zone_id, zone_name, DSSubmitCmd);
01130     }
01131 
01132     StrFree(old_filename);
01133     StrFree(temp_filename);
01134 
01135     return 0;
01136 }
01137 
01138 /*
01139  * CallBack to print key info in signerConfiguration
01140  */
01141 
01142 int commKeyConfig(void* context, KSM_KEYDATA* key_data)
01143 {
01144     FILE *file = (FILE *)context;
01145 
01146     fprintf(file, "\t\t\t<Key>\n");
01147     fprintf(file, "\t\t\t\t<Flags>%d</Flags>\n", key_data->keytype); 
01148     fprintf(file, "\t\t\t\t<Algorithm>%d</Algorithm>\n", key_data->algorithm); 
01149     fprintf(file, "\t\t\t\t<Locator>%s</Locator>\n", key_data->location);
01150 
01151     if (key_data->keytype == KSM_TYPE_KSK)
01152     {
01153         fprintf(file, "\t\t\t\t<KSK />\n");
01154     }
01155     if (key_data->keytype == KSM_TYPE_ZSK && key_data->state == KSM_STATE_ACTIVE)
01156     {
01157         fprintf(file, "\t\t\t\t<ZSK />\n");
01158     }
01159     if ((key_data->state > KSM_STATE_GENERATE && key_data->state < KSM_STATE_DEAD) || key_data->state == KSM_STATE_KEYPUBLISH)
01160     {
01161         fprintf(file, "\t\t\t\t<Publish />\n");
01162     }
01163     fprintf(file, "\t\t\t</Key>\n");
01164     fprintf(file, "\n");
01165 
01166     return 0;
01167 }
01168 
01169 /* allocateKeysToZone
01170  *
01171  * Description:
01172  *      Allocates existing keys to zones
01173  *
01174  * Arguments:
01175  *      policy
01176  *          policy that the keys were created for
01177  *      key_type
01178  *          KSK or ZSK
01179  *      zone_id
01180  *          ID of zone in question
01181  *      interval
01182  *          time before next run
01183  *      zone_name
01184  *          just in case we need to log something
01185  *      man_key_gen
01186  *          lack of keys may be an issue for the user to fix
01187  *      int rollover_scheme
01188  *          KSK rollover scheme in use
01189  *
01190  * Returns:
01191  *      int
01192  *          Status return.  0=> Success, non-zero => error.
01193  *          1 == error with input
01194  *          2 == not enough keys to satisfy policy
01195  *          3 == database error
01196  -*/
01197 
01198 
01199 int allocateKeysToZone(KSM_POLICY *policy, int key_type, int zone_id, uint16_t interval, const char* zone_name, int man_key_gen, int rollover_scheme)
01200 {
01201     int status = 0;
01202     int keys_needed = 0;
01203     int keys_in_queue = 0;
01204     int keys_pending_retirement = 0;
01205     int new_keys = 0;
01206     int key_pair_id = 0;
01207     int i = 0;
01208     DB_ID ignore = 0;
01209     KSM_PARCOLL collection; /* Parameters collection */
01210     char*   datetime = DtParseDateTimeString("now");
01211 
01212     /* Check datetime in case it came back NULL */
01213     if (datetime == NULL) {
01214         log_msg(NULL, LOG_DEBUG, "Couldn't turn \"now\" into a date, quitting...");
01215         exit(1);
01216     }
01217 
01218     if (policy == NULL) {
01219         log_msg(NULL, LOG_ERR, "NULL policy sent to allocateKeysToZone");
01220         StrFree(datetime);
01221         return 1;
01222     }
01223 
01224     if (key_type != KSM_TYPE_KSK && key_type != KSM_TYPE_ZSK) {
01225         log_msg(NULL, LOG_ERR, "Unknown keytype: %i in allocateKeysToZone", key_type);
01226         StrFree(datetime);
01227         return 1;
01228     }
01229 
01230     /* Get list of parameters */
01231     status = KsmParameterCollection(&collection, policy->id);
01232     if (status != 0) {
01233         StrFree(datetime);
01234         return status;
01235     }
01236 
01237     /* Make sure that enough keys are allocated to this zone */
01238     /* How many do we need ? (set sharing to 1 so that we get the number needed for a single zone on this policy */
01239     status = KsmKeyPredict(policy->id, key_type, 1, interval, &keys_needed, rollover_scheme, 1);
01240     if (status != 0) {
01241         log_msg(NULL, LOG_ERR, "Could not predict key requirement for next interval for %s", zone_name);
01242         StrFree(datetime);
01243         return 3;
01244     }
01245 
01246     /* How many do we have ? TODO should this include the currently active key?*/
01247     status = KsmKeyCountQueue(key_type, &keys_in_queue, zone_id);
01248     if (status != 0) {
01249         log_msg(NULL, LOG_ERR, "Could not count current key numbers for zone %s", zone_name);
01250         StrFree(datetime);
01251         return 3;
01252     }
01253 
01254     /* or about to retire */
01255     status = KsmRequestPendingRetireCount(key_type, datetime, &collection, &keys_pending_retirement, zone_id, interval);
01256     if (status != 0) {
01257         log_msg(NULL, LOG_ERR, "Could not count keys which may retire before the next run (for zone %s)", zone_name);
01258         StrFree(datetime);
01259         return 3;
01260     }
01261 
01262     StrFree(datetime);
01263     new_keys = keys_needed - (keys_in_queue - keys_pending_retirement);
01264 
01265     /* fprintf(stderr, "comm(%d) %s: new_keys(%d) = keys_needed(%d) - (keys_in_queue(%d) - keys_pending_retirement(%d))\n", key_type, zone_name, new_keys, keys_needed, keys_in_queue, keys_pending_retirement); */
01266 
01267     /* Allocate keys */
01268     for (i=0 ; i < new_keys ; i++){
01269         key_pair_id = 0;
01270         if (key_type == KSM_TYPE_KSK) {
01271             status = KsmKeyGetUnallocated(policy->id, policy->ksk->sm, policy->ksk->bits, policy->ksk->algorithm, zone_id, policy->keys->share_keys, &key_pair_id);
01272             if (status == -1 || key_pair_id == 0) {
01273                 if (man_key_gen == 0) {
01274                     log_msg(NULL, LOG_WARNING, "Not enough keys to satisfy ksk policy for zone: %s", zone_name);
01275                     log_msg(NULL, LOG_WARNING, "ods-enforcerd will create some more keys on its next run");
01276                 }
01277                 else {
01278                     log_msg(NULL, LOG_ERR, "Not enough keys to satisfy ksk policy for zone: %s", zone_name);
01279                     log_msg(NULL, LOG_ERR, "please use \"ods-ksmutil key generate\" to create some more keys.");
01280                 }
01281                 return 2;
01282             }
01283             else if (status != 0) {
01284                 log_msg(NULL, LOG_ERR, "Could not get an unallocated ksk for zone: %s", zone_name);
01285                 return 3;
01286             }
01287         } else {
01288             status = KsmKeyGetUnallocated(policy->id, policy->zsk->sm, policy->zsk->bits, policy->zsk->algorithm, zone_id, policy->keys->share_keys, &key_pair_id);
01289             if (status == -1 || key_pair_id == 0) {
01290                 if (man_key_gen == 0) {
01291                     log_msg(NULL, LOG_WARNING, "Not enough keys to satisfy zsk policy for zone: %s", zone_name);
01292                     log_msg(NULL, LOG_WARNING, "ods-enforcerd will create some more keys on its next run");
01293                 }
01294                 else {
01295                     log_msg(NULL, LOG_ERR, "Not enough keys to satisfy zsk policy for zone: %s", zone_name);
01296                     log_msg(NULL, LOG_ERR, "please use \"ods-ksmutil key generate\" to create some more keys.");
01297                 }
01298                 return 2;
01299             }
01300             else if (status != 0) {
01301                 log_msg(NULL, LOG_ERR, "Could not get an unallocated zsk for zone: %s", zone_name);
01302                 return 3;
01303             }
01304         }
01305         if(key_pair_id > 0) {
01306             status = KsmDnssecKeyCreate(zone_id, key_pair_id, key_type, KSM_STATE_GENERATE, datetime, NULL, &ignore);
01307             /* fprintf(stderr, "comm(%d) %s: allocated keypair id %d\n", key_type, zone_name, key_pair_id); */
01308         } else {
01309             /* This shouldn't happen */
01310             log_msg(NULL, LOG_ERR, "KsmKeyGetUnallocated returned bad key_id %d for zone: %s; exiting...", key_pair_id, zone_name);
01311             exit(1);
01312         }
01313 
01314     }
01315 
01316     return status;
01317 }
01318 
01319 /* 
01320  *  Read the conf.xml file, extract the location of the zonelist.
01321  */
01322 int read_zonelist_filename(const char* filename, char** zone_list_filename)
01323 {
01324     xmlTextReaderPtr reader = NULL;
01325     xmlDocPtr doc = NULL;
01326     xmlXPathContextPtr xpathCtx = NULL;
01327     xmlXPathObjectPtr xpathObj = NULL;
01328     int ret = 0; /* status of the XML parsing */
01329     char* temp_char = NULL;
01330     char* tag_name = NULL;
01331 
01332     xmlChar *zonelist_expr = (unsigned char*) "//Common/ZoneListFile";
01333 
01334     /* Start reading the file; we will be looking for "Common" tags */ 
01335     reader = xmlNewTextReaderFilename(filename);
01336     if (reader != NULL) {
01337         ret = xmlTextReaderRead(reader);
01338         while (ret == 1) {
01339             tag_name = (char*) xmlTextReaderLocalName(reader);
01340             /* Found <Common> */
01341             if (strncmp(tag_name, "Common", 6) == 0 
01342                     && xmlTextReaderNodeType(reader) == 1) {
01343 
01344                 /* Expand this node and get the rest of the info with XPath */
01345                 xmlTextReaderExpand(reader);
01346                 doc = xmlTextReaderCurrentDoc(reader);
01347                 if (doc == NULL) {
01348                     log_msg(NULL, LOG_ERR, "Error: can not read Common section of %s", filename);
01349                     /* Don't return? try to parse the rest of the file? */
01350                     ret = xmlTextReaderRead(reader);
01351                     continue;
01352                 }
01353 
01354                 xpathCtx = xmlXPathNewContext(doc);
01355                 if(xpathCtx == NULL) {
01356                     log_msg(NULL, LOG_ERR, "Error: can not create XPath context for Common section");
01357                     /* Don't return? try to parse the rest of the file? */
01358                     ret = xmlTextReaderRead(reader);
01359                     continue;
01360                 }
01361 
01362                 /* Evaluate xpath expression for ZoneListFile */
01363                 xpathObj = xmlXPathEvalExpression(zonelist_expr, xpathCtx);
01364                 if(xpathObj == NULL) {
01365                     log_msg(NULL, LOG_ERR, "Error: unable to evaluate xpath expression: %s", zonelist_expr);
01366                     /* Don't return? try to parse the rest of the file? */
01367                     ret = xmlTextReaderRead(reader);
01368                     continue;
01369                 }
01370                 *zone_list_filename = NULL;
01371                 temp_char = (char *)xmlXPathCastToString(xpathObj);
01372                 StrAppend(zone_list_filename, temp_char);
01373                 StrFree(temp_char);
01374                 xmlXPathFreeObject(xpathObj);
01375                 log_msg(NULL, LOG_INFO, "zonelist filename set to %s.", *zone_list_filename);
01376             }
01377             /* Read the next line */
01378             ret = xmlTextReaderRead(reader);
01379             StrFree(tag_name);
01380         }
01381         xmlFreeTextReader(reader);
01382         if (ret != 0) {
01383             log_msg(NULL, LOG_ERR, "%s : failed to parse", filename);
01384             return(1);
01385         }
01386     } else {
01387         log_msg(NULL, LOG_ERR, "Unable to open %s", filename);
01388         return(1);
01389     }
01390     if (xpathCtx) {
01391         xmlXPathFreeContext(xpathCtx);
01392     }
01393     if (doc) {
01394         xmlFreeDoc(doc);
01395     }
01396 
01397     return 0;
01398 }
01399 
01400 /*+
01401  * do_purge - Purge dead Keys
01402  *
01403  *
01404  * Arguments:
01405  *
01406  *      int interval
01407  *          how long a key needs to have been dead for before we purge it
01408  *
01409  *      int policy_id
01410  *          ID of the policy
01411  *
01412  * Returns:
01413  *      int
01414  *          Status return.  0 on success.
01415  *                          other on fail
01416  */
01417 
01418 int do_purge(int interval, int policy_id)
01419 {
01420     char*       sql = NULL;     /* SQL query */
01421     char*       sql1 = NULL;     /* SQL query */
01422     char*       sql2 = NULL;    /* SQL query */
01423     char*       sql3 = NULL;    /* SQL query */
01424     int         status = 0;     /* Status return */
01425     char        stringval[KSM_INT_STR_SIZE];  /* For Integer to String conversion */
01426     DB_RESULT   result;         /* Result of the query */
01427     DB_ROW      row = NULL;     /* Row data */
01428 
01429     char            buffer[KSM_SQL_SIZE];    /* Long enough for any statement */
01430     unsigned int    nchar;          /* Number of characters converted */
01431 
01432     int         temp_id = -1;       /* place to store the key id returned */
01433     char*       temp_loc = NULL;    /* place to store location returned */
01434     int         count = 0;          /* How many keys don't match the purge */
01435 
01436     char *rightnow;
01437 
01438     /* Key information */
01439     hsm_key_t *key = NULL;
01440 
01441     log_msg(NULL, LOG_DEBUG, "Purging keys...");
01442 
01443     rightnow = DtParseDateTimeString("now");
01444 
01445     /* Check datetime in case it came back NULL */
01446     if (rightnow == NULL) {
01447         log_msg(NULL, LOG_DEBUG, "Couldn't turn \"now\" into a date, quitting...");
01448         exit(1);
01449     }
01450 
01451     /* Select rows */
01452     StrAppend(&sql, "select distinct id, location from KEYDATA_VIEW where state = 6 ");
01453 
01454     if (policy_id != -1) {
01455         StrAppend(&sql, "and policy_id = ");
01456         snprintf(stringval, KSM_INT_STR_SIZE, "%d", policy_id);
01457         StrAppend(&sql, stringval);
01458     }
01459 
01460     DusEnd(&sql);
01461 
01462     status = DbExecuteSql(DbHandle(), sql, &result);
01463 
01464     if (status == 0) {
01465         status = DbFetchRow(result, &row);
01466         while (status == 0) {
01467             /* Got a row, check it */
01468             DbInt(row, 0, &temp_id);
01469             DbString(row, 1, &temp_loc);
01470 
01471             sql1 = DqsCountInit("dnsseckeys");
01472             DdsConditionInt(&sql1, "keypair_id", DQS_COMPARE_EQ, temp_id, 0);
01473             DdsConditionInt(&sql1, "(state", DQS_COMPARE_NE, KSM_STATE_DEAD, 1);
01474 
01475 #ifdef USE_MYSQL
01476             nchar = snprintf(buffer, sizeof(buffer),
01477                     " or state = %d and DEAD > DATE_ADD('%s', INTERVAL -%d SECOND)) ", KSM_STATE_DEAD, rightnow, interval);
01478 #else
01479             nchar = snprintf(buffer, sizeof(buffer),
01480                     " or state = %d and DEAD > DATETIME('%s', '-%d SECONDS')) ", KSM_STATE_DEAD, rightnow, interval);
01481 #endif /* USE_MYSQL */
01482 
01483             StrAppend(&sql1, buffer);
01484             DqsEnd(&sql1);
01485 
01486             status = DbIntQuery(DbHandle(), &count, sql1);
01487             DqsFree(sql1);
01488 
01489             if (status != 0) {
01490                 log_msg(NULL, LOG_ERR, "SQL failed: %s\n", DbErrmsg(DbHandle()));
01491                 DbStringFree(temp_loc);
01492                 DbFreeRow(row);
01493                 StrFree(rightnow);
01494                 return status;
01495             }
01496 
01497             /* If the count is zero then there is no reason not to purge this key */
01498             if (count == 0) {
01499 
01500                 /* Delete from dnsseckeys */
01501                 sql2 = DdsInit("dnsseckeys");
01502                 DdsConditionInt(&sql2, "keypair_id", DQS_COMPARE_EQ, temp_id, 0);
01503                 DdsEnd(&sql);
01504 
01505                 status = DbExecuteSqlNoResult(DbHandle(), sql2);
01506                 DdsFree(sql2);
01507                 if (status != 0)
01508                 {
01509                     log_msg(NULL, LOG_ERR, "SQL failed: %s\n", DbErrmsg(DbHandle()));
01510                     DbStringFree(temp_loc);
01511                     DbFreeRow(row);
01512                     StrFree(rightnow);
01513                     return status;
01514                 }
01515 
01516                 /* Delete from keypairs */
01517                 sql3 = DdsInit("keypairs");
01518                 DdsConditionInt(&sql3, "id", DQS_COMPARE_EQ, temp_id, 0);
01519                 DdsEnd(&sql);
01520 
01521                 status = DbExecuteSqlNoResult(DbHandle(), sql3);
01522                 DdsFree(sql3);
01523                 if (status != 0)
01524                 {
01525                     log_msg(NULL, LOG_ERR, "SQL failed: %s\n", DbErrmsg(DbHandle()));
01526                     DbStringFree(temp_loc);
01527                     DbFreeRow(row);
01528                     StrFree(rightnow);
01529                     return status;
01530                 }
01531 
01532                 /* Delete from the HSM */
01533                 key = hsm_find_key_by_id(NULL, temp_loc);
01534 
01535                 if (!key) {
01536                     log_msg(NULL, LOG_ERR, "Key not found: %s\n", temp_loc);
01537                     DbStringFree(temp_loc);
01538                     DbFreeRow(row);
01539                     StrFree(rightnow);
01540                     return -1;
01541                 }
01542 
01543                 status = hsm_remove_key(NULL, key);
01544 
01545                 hsm_key_free(key);
01546 
01547                 if (!status) {
01548                     log_msg(NULL, LOG_INFO, "Key remove successful.\n");
01549                 } else {
01550                     log_msg(NULL, LOG_ERR, "Key remove failed.\n");
01551                     DbStringFree(temp_loc);
01552                     DbFreeRow(row);
01553                     StrFree(rightnow);
01554                     return -1;
01555                 }
01556             }
01557 
01558             /* NEXT! */ 
01559             status = DbFetchRow(result, &row);
01560         }
01561 
01562         /* Convert EOF status to success */
01563 
01564         if (status == -1) {
01565             status = 0;
01566         }
01567 
01568         DbFreeResult(result);
01569     }
01570 
01571     DusFree(sql);
01572     DbFreeRow(row);
01573 
01574     DbStringFree(temp_loc);
01575     StrFree(rightnow);
01576 
01577     return status;
01578 }
01579 
01580 int NewDSSet(int zone_id, const char* zone_name, const char* DSSubmitCmd) {
01581     int     where = 0;          /* for the SELECT statement */
01582     char*   sql = NULL;     /* SQL statement (when verifying) */
01583     char*   sql2 = NULL;    /* SQL statement (if getting DS) */
01584     int     status = 0;     /* Status return */
01585     int     count = 0;      /* How many keys fit our select? */
01586     int     i = 0;          /* A counter */
01587     int     j = 0;          /* Another counter */
01588     char*   insql = NULL;   /* SQL "IN" clause */
01589     int*    keyids;         /* List of IDs of keys to promote */
01590     DB_RESULT    result;    /* List result set */
01591     KSM_KEYDATA  data;      /* Data for this key */
01592     size_t  nchar;          /* Number of characters written */
01593     char    buffer[256];    /* For constructing part of the command */
01594     char*       count_clause = NULL;
01595     char*       where_clause = NULL;
01596     int         id = -1;        /* ID of key which will retire */
01597     int         active_count = -1;        /* Number of currently active keys */
01598 
01599     char        stringval[KSM_INT_STR_SIZE];  /* For Integer to String conversion */
01600     DB_RESULT   result3;        /* Result of DS query */
01601     KSM_KEYDATA data3;        /* DS information */
01602     char*   ds_buffer = NULL;   /* Contents of DS records */
01603     char*   ds_seen_buffer = NULL;   /* Which keys have we promoted */
01604     char*   temp_char = NULL;   /* Contents of DS records */
01605 
01606     /* Key information */
01607     hsm_key_t *key = NULL;
01608     ldns_rr *dnskey_rr = NULL;
01609     hsm_sign_params_t *sign_params = NULL;
01610 
01611     FILE *fp;
01612     int bytes_written = -1;
01613 
01614     nchar = snprintf(buffer, sizeof(buffer), "(%d, %d, %d, %d, %d, %d, %d, %d)",
01615             KSM_STATE_PUBLISH, KSM_STATE_READY, KSM_STATE_ACTIVE,
01616             KSM_STATE_DSSUB, KSM_STATE_DSPUBLISH, KSM_STATE_DSREADY, 
01617             KSM_STATE_KEYPUBLISH, KSM_STATE_RETIRE);
01618     if (nchar >= sizeof(buffer)) {
01619         status = -1;
01620         return status;
01621     }
01622 
01623     /* Find the oldest active key, this is the one which will be retired
01624        NOTE; this may not match any keys */
01625 
01626     count_clause = DqsCountInit("KEYDATA_VIEW");
01627     DqsConditionInt(&count_clause, "KEYTYPE", DQS_COMPARE_EQ, KSM_TYPE_KSK, where++);
01628     DqsConditionInt(&count_clause, "STATE", DQS_COMPARE_EQ, KSM_STATE_ACTIVE, where++);
01629     if (zone_id != -1) {
01630         DqsConditionInt(&count_clause, "ZONE_ID", DQS_COMPARE_EQ, zone_id, where++);
01631     }
01632 
01633     status = DbIntQuery(DbHandle(), &active_count, count_clause);
01634     StrFree(count_clause);
01635     if (status != 0)
01636     {
01637         log_msg(NULL, LOG_ERR, "Error: failed to find ID of key to retire\n");
01638         return status;
01639     }
01640 
01641     if (active_count > 0) {
01642 
01643         snprintf(stringval, KSM_INT_STR_SIZE, "%d", zone_id);
01644         StrAppend(&where_clause, "select id from KEYDATA_VIEW where state = 4 and keytype = 257 and zone_id = ");
01645         StrAppend(&where_clause, stringval);
01646         StrAppend(&where_clause, " and retire = (select min(retire) from KEYDATA_VIEW where state = 4 and keytype = 257 and zone_id = ");
01647         StrAppend(&where_clause, stringval);
01648         StrAppend(&where_clause, ")");
01649 
01650         /* Execute query and free up the query string */
01651         status = DbIntQuery(DbHandle(), &id, where_clause);
01652         StrFree(where_clause);
01653         if (status != 0)
01654         {
01655             log_msg(NULL, LOG_ERR, "Error: failed to find ID of key to retire\n");
01656             return status;
01657         }
01658     }
01659 
01660     /* First up we need to count how many DSs we will have */
01661     where = 0;
01662     sql = DqsCountInit("KEYDATA_VIEW");
01663     DqsConditionInt(&sql, "KEYTYPE", DQS_COMPARE_EQ, KSM_TYPE_KSK, where++);
01664     DqsConditionKeyword(&sql, "STATE", DQS_COMPARE_IN, buffer, where++);
01665     if (zone_id != -1) {
01666         DqsConditionInt(&sql, "ZONE_ID", DQS_COMPARE_EQ, zone_id, where++);
01667     }
01668     if (id != -1) {
01669         DqsConditionInt(&sql, "ID", DQS_COMPARE_NE, id, where++);
01670     }
01671     DqsEnd(&sql);
01672 
01673     status = DbIntQuery(DbHandle(), &count, sql);
01674     DqsFree(sql);
01675 
01676     if (status != 0) {
01677         /*status = MsgLog(KME_SQLFAIL, DbErrmsg(DbHandle()));*/
01678         return status;
01679     }
01680 
01681     if (count == 0) {
01682         /* No KSKs in zone?  */
01683         return status;
01684     }
01685 
01686     /* Allocate space for the list of key IDs */
01687     keyids = MemMalloc(count * sizeof(int));
01688 
01689     /* Get the list of IDs */
01690 
01691     where = 0;
01692     sql = DqsSpecifyInit("KEYDATA_VIEW", DB_KEYDATA_FIELDS);
01693     DqsConditionInt(&sql, "KEYTYPE", DQS_COMPARE_EQ, KSM_TYPE_KSK, where++);
01694     DqsConditionKeyword(&sql, "STATE", DQS_COMPARE_IN, buffer, where++);
01695     if (zone_id != -1) {
01696         DqsConditionInt(&sql, "ZONE_ID", DQS_COMPARE_EQ, zone_id, where++);
01697     }
01698     if (id != -1) {
01699         DqsConditionInt(&sql, "ID", DQS_COMPARE_NE, id, where++);
01700     }
01701     DqsEnd(&sql);
01702 
01703     status = KsmKeyInitSql(&result, sql);
01704     DqsFree(sql);
01705 
01706     if (status == 0) {
01707         while (status == 0) {
01708             status = KsmKey(result, &data);
01709             if (status == 0) {
01710                 keyids[i] = data.keypair_id;
01711                 i++;
01712             }
01713         }
01714 
01715         /* Convert EOF status to success */
01716 
01717         if (status == -1) {
01718             status = 0;
01719         } else {
01720             /*status = MsgLog(KME_SQLFAIL, DbErrmsg(DbHandle()));*/
01721             StrFree(keyids);
01722             return status;
01723         }
01724 
01725         KsmKeyEnd(result);
01726 
01727     } else {
01728         /*status = MsgLog(KME_SQLFAIL, DbErrmsg(DbHandle()));*/
01729         StrFree(keyids);
01730         return status;
01731     }
01732 
01733     /*
01734      * Now construct the "IN" statement listing the IDs of the keys we
01735      * are planning to change the state of.
01736      */
01737 
01738     StrAppend(&insql, "(");
01739     for (j = 0; j < i; ++j) {
01740         if (j != 0) {
01741             StrAppend(&insql, ",");
01742         }
01743         snprintf(buffer, sizeof(buffer), "%d", keyids[j]);
01744         StrAppend(&insql, buffer);
01745     }
01746     StrAppend(&insql, ")");
01747 
01748     StrFree(keyids);
01749 
01750     /* Indicate that the DS record should now be submitted */
01751     sql2 = DqsSpecifyInit("KEYDATA_VIEW", DB_KEYDATA_FIELDS);
01752     DqsConditionKeyword(&sql2, "ID", DQS_COMPARE_IN, insql, 0);
01753     DqsConditionInt(&sql2, "ZONE_ID", DQS_COMPARE_EQ, zone_id, 1);
01754     DqsEnd(&sql2);
01755 
01756     log_msg(NULL, LOG_INFO, "DS Record set has changed, the current set looks like:");
01757 
01758     status = KsmKeyInitSql(&result3, sql2);
01759     DqsFree(sql2);
01760     if (status == 0) {
01761         status = KsmKey(result3, &data3);
01762         while (status == 0) {
01763 
01764             /* Code to output the DNSKEY record  (stolen from hsmutil) */
01765             key = hsm_find_key_by_id(NULL, data3.location);
01766 
01767             if (!key) {
01768                 log_msg(NULL, LOG_ERR, "Key %s in DB but not repository.", data3.location);
01769                 StrFree(insql);
01770                 return status;
01771             }
01772 
01773             StrAppend(&ds_seen_buffer, ", ");
01774             StrAppend(&ds_seen_buffer, data3.location);
01775 
01776             sign_params = hsm_sign_params_new();
01777             sign_params->owner = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, zone_name);
01778             sign_params->algorithm = data3.algorithm;
01779             sign_params->flags = LDNS_KEY_ZONE_KEY;
01780             sign_params->flags += LDNS_KEY_SEP_KEY;
01781             dnskey_rr = hsm_get_dnskey(NULL, key, sign_params);
01782 
01783             temp_char = ldns_rr2str(dnskey_rr);
01784             ldns_rr_free(dnskey_rr);
01785 
01786             /* Replace tab with white-space */
01787             for (i = 0; temp_char[i]; ++i) {
01788                 if (temp_char[i] == '\t') {
01789                     temp_char[i] = ' ';
01790                 }
01791             }
01792             log_msg(NULL, LOG_INFO, "%s", temp_char);
01793 
01794             /* We need to strip off trailing comments before we send
01795                to any clients that might be listening */
01796             for (i = 0; temp_char[i]; ++i) {
01797                 if (temp_char[i] == ';') {
01798                     temp_char[i] = '\n';
01799                     temp_char[i+1] = '\0';
01800                     break;
01801                 }
01802             }
01803             StrAppend(&ds_buffer, temp_char);
01804             StrFree(temp_char);
01805 
01806 /*            StrAppend(&ds_buffer, "\n;KSK DS record (SHA1):\n");
01807             ds_sha1_rr = ldns_key_rr2ds(dnskey_rr, LDNS_SHA1);
01808             temp_char = ldns_rr2str(ds_sha1_rr);
01809             StrAppend(&ds_buffer, temp_char);
01810             StrFree(temp_char);
01811 
01812             StrAppend(&ds_buffer, "\n;KSK DS record (SHA256):\n");
01813             ds_sha256_rr = ldns_key_rr2ds(dnskey_rr, LDNS_SHA256);
01814             temp_char = ldns_rr2str(ds_sha256_rr);
01815             StrAppend(&ds_buffer, temp_char);
01816             StrFree(temp_char);
01817 */
01818 
01819             hsm_sign_params_free(sign_params);
01820             hsm_key_free(key);
01821             status = KsmKey(result3, &data3);
01822         }
01823         /* Convert EOF status to success */
01824         if (status == -1) {
01825             status = 0;
01826         }
01827 
01828         KsmKeyEnd(result3);
01829     }
01830 
01831     if (DSSubmitCmd[0] != '\0') {
01832         /* send records to the configured command */
01833         fp = popen(DSSubmitCmd, "w");
01834         if (fp == NULL) {
01835             log_msg(NULL, LOG_ERR, "Failed to run command: %s: %s", DSSubmitCmd, strerror(errno));
01836             return -1;
01837         }
01838         bytes_written = fprintf(fp, "%s", ds_buffer);
01839         if (bytes_written < 0) {
01840             log_msg(NULL, LOG_ERR, "Failed to write to %s: %s", DSSubmitCmd, strerror(errno));
01841             return -1;
01842         }
01843 
01844         if (pclose(fp) == -1) {
01845             log_msg(NULL, LOG_ERR, "Failed to close %s: %s", DSSubmitCmd, strerror(errno));
01846             return -1;
01847         }
01848     }
01849 
01850     StrFree(ds_buffer);
01851 
01852     log_msg(NULL, LOG_INFO, "Once the new DS records are seen in DNS please issue the ds-seen command for zone %s with the following cka_ids%s", zone_name, ds_seen_buffer);
01853 
01854     StrFree(ds_seen_buffer);
01855 
01856     StrFree(insql);
01857 
01858     return status;
01859 }
01860 
01861 void check_hsm_connection(hsm_ctx_t **ctx, DAEMONCONFIG *config)
01862 {
01863         int result = 0;
01864         char *hsm_error_message = NULL;
01865 
01866         result = hsm_check_context(*ctx);
01867 
01868         /* If we didn't get HSM_OK then close and reopen HSM */
01869         if (result != HSM_OK) {
01870 
01871                 if (*ctx) {
01872                         hsm_destroy_context(*ctx);
01873                 }
01874 
01875                 result = hsm_close();
01876 
01877                 if (config->configfile != NULL) {
01878                         result = hsm_open(config->configfile, hsm_prompt_pin, NULL);
01879                 } else {
01880                         result = hsm_open(OPENDNSSEC_CONFIG_FILE, hsm_prompt_pin, NULL);
01881                 }
01882                 if (result) {
01883                         hsm_error_message = hsm_get_error(*ctx);
01884                         if (hsm_error_message) {
01885                                 log_msg(config, LOG_ERR, hsm_error_message);
01886                                 free(hsm_error_message);
01887                         } else {
01888                                 /* decode the error code ourselves
01889                                    TODO find if there is a better way to do this (and can all
01890                                    of these be returned? are there others?) */
01891                                 switch (result) {
01892                                         case HSM_ERROR:
01893                                                 log_msg(config, LOG_ERR, "hsm_open() result: HSM error");
01894                                                 break;
01895                                         case HSM_PIN_INCORRECT:
01896                                                 log_msg(config, LOG_ERR, "hsm_open() result: incorrect PIN");
01897                                                 break;
01898                                         case HSM_CONFIG_FILE_ERROR:
01899                                                 log_msg(config, LOG_ERR, "hsm_open() result: config file error");
01900                                                 break;
01901                                         case HSM_REPOSITORY_NOT_FOUND:
01902                                                 log_msg(config, LOG_ERR, "hsm_open() result: repository not found");
01903                                                 break;
01904                                         case HSM_NO_REPOSITORIES:
01905                                                 log_msg(config, LOG_ERR, "hsm_open() result: no repositories");
01906                                                 break;
01907                                         default:
01908                                                 log_msg(config, LOG_ERR, "hsm_open() result: %d", result);
01909                                 }
01910                         }
01911                         unlink(config->pidfile);
01912                         exit(1);
01913                 }
01914                 log_msg(config, LOG_INFO, "HSM reopened successfully.");
01915                 *ctx = hsm_create_context();
01916         } else {
01917                 log_msg(config, LOG_INFO, "HSM connection open.");
01918         }
01919 
01920 }
01921