• Main Page
  • Data Structures
  • Files
  • File List
  • Globals

/srv/bpo/opendnssec/opendnssec-1.3.2/enforcer/enforcerd/enforcer.c

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

Generated on Mon Oct 31 2011 14:38:30 for OpenDNSSEC-enforcer by  doxygen 1.7.1