00001
00002
00003
00004
00005
00006
00007
00008 #include "config.h"
00009 #include <stdlib.h>
00010 #include <unistd.h>
00011
00012 #include <errno.h>
00013
00014 #include <time.h>
00015
00016 #include <ldns/ldns.h>
00017
00018 #define MAX_FILENAME_LEN 250
00019
00020 void
00021 usage(FILE *fp, const char *prog) {
00022 fprintf(fp, "%s [OPTIONS] zonefile key [key [key]]\n", prog);
00023 fprintf(fp, " signs the zone with the given key(s)\n");
00024 fprintf(fp, " -e <date>\texpiration date\n");
00025 fprintf(fp, " -f <file>\toutput zone to file (default <name>.signed)\n");
00026 fprintf(fp, " -i <date>\tinception date\n");
00027 fprintf(fp, " -o <domain>\torigin for the zone\n");
00028 fprintf(fp, " -v\t\tprint version and exit\n");
00029 fprintf(fp, " keys must be specified by their base name: K<name>+<alg>+<id>\n");
00030 fprintf(fp, " both a .key and .private file must present\n");
00031 fprintf(fp, " A date can be a timestamp (seconds since the epoch), or of\n the form <YYYYMMdd[hhmmss]>\n");
00032 }
00033
00034 void check_tm(struct tm tm)
00035 {
00036 if (tm.tm_year < 70) {
00037 fprintf(stderr, "You cannot specify dates before 1970\n");
00038 exit(EXIT_FAILURE);
00039 }
00040 if (tm.tm_mon < 0 || tm.tm_mon > 11) {
00041 fprintf(stderr, "The month must be in the range 1 to 12\n");
00042 exit(EXIT_FAILURE);
00043 }
00044 if (tm.tm_mday < 1 || tm.tm_mday > 31) {
00045 fprintf(stderr, "The day must be in the range 1 to 31\n");
00046 exit(EXIT_FAILURE);
00047 }
00048
00049 if (tm.tm_hour < 0 || tm.tm_hour > 23) {
00050 fprintf(stderr, "The hour must be in the range 0-23\n");
00051 exit(EXIT_FAILURE);
00052 }
00053
00054 if (tm.tm_min < 0 || tm.tm_min > 59) {
00055 fprintf(stderr, "The minute must be in the range 0-59\n");
00056 exit(EXIT_FAILURE);
00057 }
00058
00059 if (tm.tm_sec < 0 || tm.tm_sec > 59) {
00060 fprintf(stderr, "The second must be in the range 0-59\n");
00061 exit(EXIT_FAILURE);
00062 }
00063
00064 }
00065
00066 int
00067 main(int argc, char *argv[])
00068 {
00069 const char *zonefile_name;
00070 FILE *zonefile = NULL;
00071 uint16_t default_ttl = LDNS_DEFAULT_TTL;
00072 int line_nr = 0;
00073 int c;
00074 int argi;
00075
00076 ldns_zone *orig_zone;
00077 ldns_rr_list *orig_rrs = NULL;
00078 ldns_rr *orig_soa = NULL;
00079 ldns_zone *signed_zone;
00080
00081 const char *keyfile_name_base;
00082 char *keyfile_name;
00083 FILE *keyfile = NULL;
00084 ldns_key *key = NULL;
00085 ldns_rr *pubkey;
00086 ldns_key_list *keys;
00087 ldns_status s;
00088
00089
00090 char *outputfile_name = NULL;
00091 FILE *outputfile;
00092
00093
00094
00095
00096 struct tm tm;
00097 uint32_t inception;
00098 uint32_t expiration;
00099 ldns_rdf *origin = NULL;
00100 uint16_t ttl = 0;
00101 ldns_rr_class class = LDNS_RR_CLASS_IN;
00102
00103 char *prog = strdup(argv[0]);
00104
00105 inception = 0;
00106 expiration = 0;
00107
00108 while ((c = getopt(argc, argv, "e:f:i:o:v")) != -1) {
00109 switch (c) {
00110 case 'e':
00111
00112
00113
00114
00115 memset(&tm, 0, sizeof(tm));
00116
00117 if (strlen(optarg) == 8 &&
00118 sscanf(optarg, "%4d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday)
00119 ) {
00120 tm.tm_year -= 1900;
00121 tm.tm_mon--;
00122 check_tm(tm);
00123 expiration = (uint32_t) mktime_from_utc(&tm);
00124 } else if (strlen(optarg) == 14 &&
00125 sscanf(optarg, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec)
00126 ) {
00127 tm.tm_year -= 1900;
00128 tm.tm_mon--;
00129 check_tm(tm);
00130 expiration = (uint32_t) mktime_from_utc(&tm);
00131 } else {
00132 expiration = (uint32_t) atol(optarg);
00133 }
00134 break;
00135 case 'f':
00136 outputfile_name = LDNS_XMALLOC(char, MAX_FILENAME_LEN);
00137 strncpy(outputfile_name, optarg, MAX_FILENAME_LEN);
00138 break;
00139 case 'i':
00140 memset(&tm, 0, sizeof(tm));
00141
00142 if (strlen(optarg) == 8 &&
00143 sscanf(optarg, "%4d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday)
00144 ) {
00145 tm.tm_year -= 1900;
00146 tm.tm_mon--;
00147 check_tm(tm);
00148 inception = (uint32_t) mktime_from_utc(&tm);
00149 } else if (strlen(optarg) == 14 &&
00150 sscanf(optarg, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec)
00151 ) {
00152 tm.tm_year -= 1900;
00153 tm.tm_mon--;
00154 check_tm(tm);
00155 inception = (uint32_t) mktime_from_utc(&tm);
00156 } else {
00157 inception = (uint32_t) atol(optarg);
00158 }
00159 break;
00160 case 'o':
00161 if (ldns_str2rdf_dname(&origin, optarg) != LDNS_STATUS_OK) {
00162 fprintf(stderr, "Bad origin, not a correct domain name\n");
00163 usage(stderr, prog);
00164 exit(EXIT_FAILURE);
00165 }
00166
00167 break;
00168 case 'v':
00169 printf("zone signer version %s (ldns version %s)\n", LDNS_VERSION, ldns_version());
00170 exit(EXIT_SUCCESS);
00171 break;
00172 default:
00173 usage(stderr, prog);
00174 exit(EXIT_SUCCESS);
00175 }
00176 }
00177
00178 argc -= optind;
00179 argv += optind;
00180
00181 if (argc < 2) {
00182 usage(stdout, prog);
00183 exit(EXIT_FAILURE);
00184 } else {
00185 zonefile_name = argv[0];
00186 }
00187
00188
00189
00190 zonefile = fopen(zonefile_name, "r");
00191
00192 if (!zonefile) {
00193 fprintf(stderr, "Error: unable to read %s (%s)\n", zonefile_name, strerror(errno));
00194 exit(EXIT_FAILURE);
00195 } else {
00196 s = ldns_zone_new_frm_fp_l(&orig_zone, zonefile, origin, ttl, class, &line_nr);
00197 if (s != LDNS_STATUS_OK) {
00198 fprintf(stderr, "Zone not read, error: %s at %s line %d\n",
00199 ldns_get_errorstr_by_id(s),
00200 zonefile_name, line_nr);
00201 exit(EXIT_FAILURE);
00202 } else {
00203 orig_soa = ldns_zone_soa(orig_zone);
00204 if (!orig_soa) {
00205 fprintf(stderr, "Error reading zonefile: missing SOA record\n");
00206 exit(EXIT_FAILURE);
00207 }
00208 orig_rrs = ldns_zone_rrs(orig_zone);
00209 if (!orig_rrs) {
00210 fprintf(stderr, "Error reading zonefile: no resource records\n");
00211 exit(EXIT_FAILURE);
00212 }
00213 }
00214 fclose(zonefile);
00215 }
00216
00217 if (!origin) {
00218 origin = ldns_rr_owner(orig_soa);
00219 }
00220
00221 keys = ldns_key_list_new();
00222
00223
00224 argi = 1;
00225 while (argi < argc) {
00226 keyfile_name_base = argv[argi];
00227 keyfile_name = LDNS_XMALLOC(char, strlen(keyfile_name_base) + 9);
00228 snprintf(keyfile_name, strlen(keyfile_name_base) + 9, "%s.private", keyfile_name_base);
00229 keyfile = fopen(keyfile_name, "r");
00230 line_nr = 0;
00231 if (!keyfile) {
00232 fprintf(stderr, "Error: unable to read %s: %s\n", keyfile_name, strerror(errno));
00233 } else {
00234 s = ldns_key_new_frm_fp_l(&key, keyfile, &line_nr);
00235 fclose(keyfile);
00236 if (s == LDNS_STATUS_OK) {
00237
00238
00239
00240 if (expiration != 0) {
00241 ldns_key_set_expiration(key, expiration);
00242 }
00243 if (inception != 0) {
00244 ldns_key_set_inception(key, inception);
00245 }
00246
00247 LDNS_FREE(keyfile_name);
00248 keyfile_name = LDNS_XMALLOC(char, strlen(keyfile_name_base) + 5);
00249 snprintf(keyfile_name, strlen(keyfile_name_base) + 5, "%s.key", keyfile_name_base);
00250 keyfile = fopen(keyfile_name, "r");
00251 line_nr = 0;
00252 if (!keyfile) {
00253 fprintf(stderr, "Error: unable to read %s: %s\n", keyfile_name, strerror(errno));
00254 } else {
00255 if (ldns_rr_new_frm_fp_l(&pubkey, keyfile, &default_ttl, NULL, NULL, &line_nr) ==
00256 LDNS_STATUS_OK) {
00257 ldns_key_set_pubkey_owner(key, ldns_rdf_clone(ldns_rr_owner(pubkey)));
00258 ldns_key_set_flags(key, ldns_rdf2native_int16(ldns_rr_rdf(pubkey, 0)));
00259 ldns_key_set_keytag(key, ldns_calc_keytag(pubkey));
00260 }
00261 ldns_key_list_push_key(keys, key);
00262 ldns_zone_push_rr(orig_zone, ldns_rr_clone(pubkey));
00263 ldns_rr_free(pubkey);
00264 }
00265 LDNS_FREE(keyfile_name);
00266
00267 } else {
00268 fprintf(stderr, "Error reading key from %s at line %d\n", argv[argi], line_nr);
00269 }
00270 }
00271
00272 argi++;
00273 }
00274
00275 if (ldns_key_list_key_count(keys) < 1) {
00276 fprintf(stderr, "Error: no keys to sign with. Aborting.\n\n");
00277 usage(stderr, prog);
00278 exit(EXIT_FAILURE);
00279 }
00280
00281 signed_zone = ldns_zone_sign(orig_zone, keys);
00282
00283 if (!outputfile_name) {
00284 outputfile_name = LDNS_XMALLOC(char, MAX_FILENAME_LEN);
00285 snprintf(outputfile_name, MAX_FILENAME_LEN, "%s.signed", zonefile_name);
00286 }
00287
00288 if (signed_zone) {
00289 outputfile = fopen(outputfile_name, "w");
00290 if (!outputfile) {
00291 fprintf(stderr, "Unable to open %s for writing: %s\n", outputfile_name, strerror(errno));
00292 } else {
00293 ldns_zone_print(outputfile, signed_zone);
00294 fclose(outputfile);
00295 }
00296 ldns_zone_deep_free(signed_zone);
00297 } else {
00298 fprintf(stderr, "Error signing zone.");
00299 exit(EXIT_FAILURE);
00300 }
00301
00302 ldns_key_list_free(keys);
00303 ldns_zone_deep_free(orig_zone);
00304
00305 LDNS_FREE(outputfile_name);
00306
00307 free(prog);
00308 exit(EXIT_SUCCESS);
00309 }