1 /*
2  * Copyright (c) 2014 Stichting NLnet Labs
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #include "config.h"
29 
30 #include <sys/stat.h>
31 #include <getopt.h>
32 
33 #include "cmdhandler.h"
34 #include "daemon/engine.h"
35 #include "enforcer/enforce_task.h"
36 #include "file.h"
37 #include "log.h"
38 #include "str.h"
39 #include "clientpipe.h"
40 #include "duration.h"
41 #include "db/key_data.h"
42 #include "db/zone_db.h"
43 #include "db/db_error.h"
44 #include "db/hsm_key.h"
45 #include "libhsm.h"
46 #include "libhsmdns.h"
47 
48 #include "keystate/keystate_ds.h"
49 
50 static const char *module_str = "keystate_ds_x_cmd";
51 
52 /** Retrieve KEY from HSM, should only be called for DNSKEYs
53  * @param id, locator of DNSKEY on HSM
54  * @param zone, name of zone key belongs to
55  * @param algorithm, alg of DNSKEY
56  * @param ttl, ttl DS should get. if 0 DNSKEY_TTL is used.
57  * @return RR on succes, NULL on error */
58 static ldns_rr *
get_dnskey(const char * id,const char * zone,int alg,uint32_t ttl)59 get_dnskey(const char *id, const char *zone, int alg, uint32_t ttl)
60 {
61 	libhsm_key_t *key;
62 	hsm_sign_params_t *sign_params;
63 	ldns_rr *dnskey_rr;
64 	/* Code to output the DNSKEY record  (stolen from hsmutil) */
65 	hsm_ctx_t *hsm_ctx = hsm_create_context();
66 	if (!hsm_ctx) {
67 		ods_log_error("[%s] Could not connect to HSM", module_str);
68 		return NULL;
69 	}
70 	if (!(key = hsm_find_key_by_id(hsm_ctx, id))) {
71 		hsm_destroy_context(hsm_ctx);
72 		return NULL;
73 	}
74 
75 	/* Sign params only need to be kept around
76 	 * for the hsm_get_dnskey() call. */
77 	sign_params = hsm_sign_params_new();
78 	sign_params->owner = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, zone);
79 	sign_params->algorithm = (ldns_algorithm) alg;
80 	sign_params->flags = LDNS_KEY_ZONE_KEY | LDNS_KEY_SEP_KEY;
81 
82 	/* Get the DNSKEY record */
83 	dnskey_rr = hsm_get_dnskey(hsm_ctx, key, sign_params);
84 
85 	libhsm_key_free(key);
86 	hsm_sign_params_free(sign_params);
87 	hsm_destroy_context(hsm_ctx);
88 
89 	/* Override the TTL in the dnskey rr */
90 	if (ttl) ldns_rr_set_ttl(dnskey_rr, ttl);
91 
92 	return dnskey_rr;
93 }
94 
95 /** returns non 0 on error */
96 static int
exec_dnskey_by_id(int sockfd,key_data_t * key,const char * ds_command,const char * action)97 exec_dnskey_by_id(int sockfd, key_data_t *key, const char* ds_command,
98 	const char* action)
99 {
100 	ldns_rr *dnskey_rr;
101 	int ttl = 0, status, i;
102 	const char *locator;
103 	char *rrstr, *chrptr;
104 	zone_db_t* zone;
105 	struct stat stat_ret;
106         int cka = 0;
107 	char *pos = NULL;
108 
109 	assert(key);
110 
111 	zone = key_data_get_zone(key);
112 	if(key_data_cache_hsm_key(key) != DB_OK) {
113 		ods_log_error_and_printf(sockfd, module_str,
114 			"Error fetching from database");
115 		zone_db_free(zone);
116 		return 1;
117 	}
118 	locator = hsm_key_locator(key_data_hsm_key(key));
119 	if (!locator) {
120 		zone_db_free(zone);
121 		return 1;
122 	}
123 	/* This fetches the states from the DB, I'm only assuming they get
124 	 * cleaned up when 'key' is cleaned(?) */
125 	if (key_data_cache_key_states(key) != DB_OK) {
126 		zone_db_free(zone);
127 		return 1;
128 	}
129 
130 	ttl = key_state_ttl(key_data_cached_dnskey(key));
131 
132 	dnskey_rr = get_dnskey(locator, zone_db_name(zone), key_data_algorithm(key), ttl);
133 	zone_db_free(zone);
134 	if (!dnskey_rr) return 2;
135 
136 	rrstr = ldns_rr2str(dnskey_rr);
137 
138 	/* Replace tab with white-space */
139 	for (i = 0; rrstr[i]; ++i) {
140 		if (rrstr[i] == '\t') rrstr[i] = ' ';
141 	}
142 
143 	/* We need to strip off trailing comments before we send
144 	 to any clients that might be listening */
145 	if ((chrptr = strchr(rrstr, ';'))) {
146 		chrptr[0] = '\n';
147 		chrptr[1] = '\0';
148 	}
149 
150 	if (!ds_command || ds_command[0] == '\0') {
151 		ods_log_error_and_printf(sockfd, module_str,
152 			"No \"DelegationSigner%sCommand\" "
153 			"configured.", action);
154 		status = 1;
155 	} else {
156 		pos = strstr(ds_command, " --cka_id");
157                 if (pos){
158                         cka = 1;
159                         *pos = '\0';
160                         rrstr[strlen(rrstr)-1] = '\0';
161                         pos = NULL;
162                 }
163 
164 		if (stat(ds_command, &stat_ret) != 0) {
165 			ods_log_error_and_printf(sockfd, module_str,
166 				"Cannot stat file %s: %s", ds_command,
167 				strerror(errno));
168 			status = 2;
169 		} else if (S_ISREG(stat_ret.st_mode) &&
170 				!(stat_ret.st_mode & S_IXUSR ||
171 				  stat_ret.st_mode & S_IXGRP ||
172 				  stat_ret.st_mode & S_IXOTH)) {
173 			/* Then see if it is a regular file, then if usr, grp or
174 			 * all have execute set */
175 			status = 3;
176 			ods_log_error_and_printf(sockfd, module_str,
177 				"File %s is not executable", ds_command);
178 		} else {
179 			/* send records to the configured command */
180 			FILE *fp = popen(ds_command, "w");
181 			if (fp == NULL) {
182 				status = 4;
183 				ods_log_error_and_printf(sockfd, module_str,
184 					"failed to run command: %s: %s",ds_command,
185 					strerror(errno));
186 			} else {
187 				int bytes_written;
188 				if (cka)
189 					bytes_written = fprintf(fp, "%s; {cka_id = %s}\n", rrstr, locator);
190 				else
191 					bytes_written = fprintf(fp, "%s", rrstr);
192 				if (bytes_written < 0) {
193 					status = 5;
194 					ods_log_error_and_printf(sockfd,  module_str,
195 						 "Failed to write to %s: %s", ds_command,
196 						 strerror(errno));
197 				} else if (pclose(fp) == -1) {
198 					status = 6;
199 					ods_log_error_and_printf(sockfd, module_str,
200 						"failed to close %s: %s", ds_command,
201 						strerror(errno));
202 				} else {
203 					client_printf(sockfd, "key %sed to %s\n",
204 						action, ds_command);
205 					status = 0;
206 				}
207 			}
208 		}
209 	}
210 	LDNS_FREE(rrstr);
211 	ldns_rr_free(dnskey_rr);
212 	return status;
213 }
214 
215 static int
submit_dnskey_by_id(int sockfd,key_data_t * key,engine_type * engine)216 submit_dnskey_by_id(int sockfd, key_data_t *key, engine_type* engine)
217 {
218 	const char* ds_submit_command;
219 	ds_submit_command = engine->config->delegation_signer_submit_command;
220 	return exec_dnskey_by_id(sockfd, key, ds_submit_command, "submit");
221 }
222 
223 static int
retract_dnskey_by_id(int sockfd,key_data_t * key,engine_type * engine)224 retract_dnskey_by_id(int sockfd, key_data_t *key, engine_type* engine)
225 {
226 	const char* ds_retract_command;
227 	ds_retract_command = engine->config->delegation_signer_retract_command;
228 	return exec_dnskey_by_id(sockfd, key, ds_retract_command, "retract");
229 }
230 
231 static int
ds_list_keys(db_connection_t * dbconn,int sockfd,key_data_ds_at_parent_t state)232 ds_list_keys(db_connection_t *dbconn, int sockfd,
233 	key_data_ds_at_parent_t state)
234 {
235 	const char *fmth = "%-31s %-13s %-13s %-40s\n";
236 	const char *fmtl = "%-31s %-13s %-13u %-40s\n";
237 
238 	key_data_list_t *key_list;
239 	const key_data_t *key;
240 	zone_db_t *zone = NULL;
241 	hsm_key_t* hsmkey = NULL;
242 	db_clause_list_t* clause_list;
243 	db_clause_t* clause;
244 
245 	if (!(key_list = key_data_list_new(dbconn))
246 		|| !(clause_list = db_clause_list_new()))
247 	{
248 		key_data_list_free(key_list);
249 		return 10;
250 	}
251 	if (!(clause = key_data_role_clause(clause_list, KEY_DATA_ROLE_ZSK))
252 		|| db_clause_set_type(clause, DB_CLAUSE_NOT_EQUAL)
253 		|| !(clause = key_data_ds_at_parent_clause(clause_list, state))
254 		|| db_clause_set_type(clause, DB_CLAUSE_EQUAL))
255 	{
256 		key_data_list_free(key_list);
257 		db_clause_list_free(clause_list);
258 		return 11;
259 	}
260 	if (key_data_list_get_by_clauses(key_list, clause_list)) {
261 		key_data_list_free(key_list);
262 		db_clause_list_free(clause_list);
263 		return 12;
264 	}
265 	db_clause_list_free(clause_list);
266 
267 	client_printf(sockfd, fmth, "Zone:", "Key role:", "Keytag:", "Id:");
268 
269 	for (key = key_data_list_next(key_list); key;
270 		key = key_data_list_next(key_list))
271 	{
272 		zone = key_data_get_zone(key);
273 		hsmkey = key_data_get_hsm_key(key);
274 		client_printf(sockfd, fmtl,
275 			(zone ? zone_db_name(zone) : "NOT_FOUND"),
276 			key_data_role_text(key), key_data_keytag(key),
277 			(hsmkey ? hsm_key_locator(hsmkey) : "NOT_FOUND")
278 		);
279 		zone_db_free(zone);
280 		hsm_key_free(hsmkey);
281 	}
282 	key_data_list_free(key_list);
283 	return 0;
284 }
285 
286 static int
push_clauses(db_clause_list_t * clause_list,zone_db_t * zone,key_data_ds_at_parent_t state_from,const hsm_key_t * hsmkey,int keytag)287 push_clauses(db_clause_list_t *clause_list, zone_db_t *zone,
288 	key_data_ds_at_parent_t state_from, const hsm_key_t* hsmkey, int keytag)
289 {
290 	db_clause_t* clause;
291 
292 	if (!key_data_zone_id_clause(clause_list, zone_db_id(zone)))
293 		return 1;
294 	if (!(clause = key_data_role_clause(clause_list, KEY_DATA_ROLE_ZSK)) ||
295 			db_clause_set_type(clause, DB_CLAUSE_NOT_EQUAL))
296 		return 1;
297 	if (!key_data_ds_at_parent_clause(clause_list, state_from))
298 		return 1;
299 
300 	/* filter in id and or keytag conditionally. */
301 	if (hsmkey) {
302 		if (hsmkey && !key_data_hsm_key_id_clause(clause_list, hsm_key_id(hsmkey)))
303 			return 1;
304 	}
305 	if (keytag > 0) {
306 		if (!key_data_keytag_clause(clause_list, keytag))
307 			return 1;
308 	}
309 	return 0;
310 }
311 
312 /** Update timestamp on DS of key to now */
313 static int
ds_changed(key_data_t * key)314 ds_changed(key_data_t *key)
315 {
316 	key_state_list_t* keystatelist;
317 	key_state_t* keystate;
318 
319 	if(key_data_retrieve_key_state_list(key)) return 1;
320 	keystatelist = key_data_key_state_list(key);
321 	keystate = key_state_list_get_begin(keystatelist);
322 	if (!keystate) return 1;
323 
324 	while (keystate) {
325 		key_state_t* keystate_next;
326 		if (keystate->type == KEY_STATE_TYPE_DS) {
327 			keystate->last_change = time_now();
328 			if(key_state_update(keystate)) {
329 				key_state_free(keystate);
330 				return 1;
331 			}
332 			key_state_free(keystate);
333 			return 0;
334 		}
335 		keystate_next = key_state_list_get_next(keystatelist);
336 		key_state_free(keystate);
337 		keystate = keystate_next;
338 	}
339 	return 1;
340 }
341 
342 /* Change DS state, when zonename not given do it for all zones!
343  */
344 int
change_keys_from_to(db_connection_t * dbconn,int sockfd,const char * zonename,const hsm_key_t * hsmkey,int keytag,key_data_ds_at_parent_t state_from,key_data_ds_at_parent_t state_to,engine_type * engine)345 change_keys_from_to(db_connection_t *dbconn, int sockfd,
346 	const char *zonename, const hsm_key_t* hsmkey, int keytag,
347 	key_data_ds_at_parent_t state_from, key_data_ds_at_parent_t state_to,
348 	engine_type *engine)
349 {
350 	key_data_list_t *key_list = NULL;
351 	key_data_t *key;
352 	zone_db_t *zone = NULL;
353 	int status = 0, key_match = 0, key_mod = 0;
354 	db_clause_list_t* clause_list = NULL;
355 	db_clause_t* clause = NULL;
356 	char *tmp_zone_name;
357 
358 	if (zonename) {
359 		if (!(key_list = key_data_list_new(dbconn)) ||
360 			!(clause_list = db_clause_list_new()) ||
361 			!(zone = zone_db_new_get_by_name(dbconn, zonename)) ||
362 			push_clauses(clause_list, zone, state_from, hsmkey, keytag) ||
363 			key_data_list_get_by_clauses(key_list, clause_list))
364 		{
365 			key_data_list_free(key_list);
366 			db_clause_list_free(clause_list);
367 			zone_db_free(zone);
368 			client_printf_err(sockfd, "Could not find ksk for zone %s, "
369 				"does zone exist?\n", zonename);
370 			ods_log_error("[%s] Error fetching from database", module_str);
371 			return 10;
372 		}
373 		db_clause_list_free(clause_list);
374 	} else {
375 		/* Select all KSKs */
376 		if (!(clause_list = db_clause_list_new()) ||
377 			!key_data_ds_at_parent_clause(clause_list, state_from) ||
378 			!(clause = key_data_role_clause(clause_list, KEY_DATA_ROLE_ZSK)) ||
379 			db_clause_set_type(clause, DB_CLAUSE_NOT_EQUAL) != DB_OK ||
380 			!(key_list = key_data_list_new_get_by_clauses(dbconn, clause_list)))
381 		{
382 			key_data_list_free(key_list);
383 			db_clause_list_free(clause_list);
384 			ods_log_error("[%s] Error fetching from database", module_str);
385 			return 14;
386 		}
387 		db_clause_list_free(clause_list);
388 	}
389 	while ((key = key_data_list_get_next(key_list))) {
390 		key_match++;
391 		/* if from is submit also exec dsSubmit command? */
392 		if (state_from == KEY_DATA_DS_AT_PARENT_SUBMIT &&
393 			state_to == KEY_DATA_DS_AT_PARENT_SUBMITTED)
394 		{
395 			(void)submit_dnskey_by_id(sockfd, key, engine);
396 		} else if (state_from == KEY_DATA_DS_AT_PARENT_RETRACT &&
397 			state_to == KEY_DATA_DS_AT_PARENT_RETRACTED)
398 		{
399 			(void)retract_dnskey_by_id(sockfd, key, engine);
400 		}
401 
402 		if (key_data_set_ds_at_parent(key, state_to) ||
403 			key_data_update(key) || ds_changed(key) )
404 		{
405 			key_data_free(key);
406 			ods_log_error("[%s] Error writing to database", module_str);
407 			client_printf(sockfd, "[%s] Error writing to database", module_str);
408 			status = 12;
409 			break;
410 		}
411 		key_mod++;
412 		/* We need to schedule enforce for owner of key. */
413 		tmp_zone_name = zone_db_ext_zonename_from_id(dbconn, &key->zone_id);
414 		if (tmp_zone_name)
415 			enforce_task_flush_zone(engine, tmp_zone_name);
416 		free(tmp_zone_name);
417 		key_data_free(key);
418 	}
419 	key_data_list_free(key_list);
420 
421 	client_printf(sockfd, "%d KSK matches found.\n", key_match);
422 	if (!key_match) status = 11;
423 	client_printf(sockfd, "%d KSKs changed.\n", key_mod);
424 	if (zone && key_mod > 0) {
425 		zone->next_change = 0; /* asap */
426 		(void)zone_db_update(zone);
427 	}
428 	zone_db_free(zone);
429 	return status;
430 }
431 
432 int
run_ds_cmd(int sockfd,const char * cmd,db_connection_t * dbconn,key_data_ds_at_parent_t state_from,key_data_ds_at_parent_t state_to,engine_type * engine)433 run_ds_cmd(int sockfd, const char *cmd,
434 	db_connection_t *dbconn, key_data_ds_at_parent_t state_from,
435 	key_data_ds_at_parent_t state_to, engine_type *engine)
436 {
437 	#define NARGV 6
438 	const char *zonename = NULL, *cka_id = NULL, *keytag_s = NULL;
439 	int keytag = -1;
440 	hsm_key_t* hsmkey = NULL;
441 	int ret;
442 	char buf[ODS_SE_MAXLINE];
443 	zone_db_t* zone = NULL;
444 	int all = 0;
445 	int argc = 0, long_index = 0, opt = 0;
446 	const char* argv[NARGV];
447 
448 	static struct option long_options[] = {
449 		{"zone", required_argument, 0, 'z'},
450 		{"cka_id", required_argument, 0, 'k'},
451 		{"keytag", required_argument, 0, 'x'},
452 		{"all", no_argument, 0, 'a'},
453 		{0, 0, 0, 0}
454 	};
455 
456 	strncpy(buf, cmd, ODS_SE_MAXLINE);
457 	buf[sizeof(buf)-1] = '\0';
458 	argc = ods_str_explode(buf, NARGV, argv);
459 	if (argc == -1) {
460 		client_printf_err(sockfd, "too many arguments\n");
461 		ods_log_error("[%s] too many arguments for %s command",
462 				module_str, cmd);
463 		return -1;
464 	}
465 
466 	optind = 0;
467 	while ((opt = getopt_long(argc, (char* const*)argv, "z:k:x:a", long_options, &long_index)) != -1) {
468 		switch (opt) {
469 			case 'z':
470 				zonename = optarg;
471 				break;
472 			case 'k':
473 				cka_id = optarg;
474 				break;
475 			case 'x':
476 				keytag_s = optarg;
477 				break;
478 			case 'a':
479 				all = 1;
480 				break;
481 			default:
482 				client_printf_err(sockfd, "unknown arguments\n");
483 				ods_log_error("[%s] unknown arguments for %s command",
484 						module_str, cmd);
485 				return -1;
486 		}
487 	}
488 
489 	if (!all && !zonename && !cka_id && !keytag_s) {
490 		return ds_list_keys(dbconn, sockfd, state_from);
491 	}
492 
493 	if (keytag_s) {
494 		keytag = atoi(keytag_s);
495 		if (keytag < 0 || keytag >= 65536) {
496 			ods_log_warning("[%s] value \"%d\" for --keytag is invalid",
497 				module_str, keytag);
498                         client_printf_err(sockfd, "value \"%d\" for --keytag is invalid\n",
499                                 keytag);
500 
501 			return 1;
502 		}
503 	}
504 
505 	if (all && zonename) {
506 		ods_log_warning ("[%s] Error: Unable to use --zone and --all together", module_str);
507 		client_printf_err(sockfd, "Error: Unable to use --zone and --all together\n");
508 		return -1;
509 	}
510 
511 	if (zonename && (!(zone = zone_db_new(dbconn)) || zone_db_get_by_name(zone, zonename))) {
512 		ods_log_warning ("[%s] Error: Unable to find a zone named \"%s\" in database\n", module_str, zonename);
513 	        client_printf_err(sockfd, "Error: Unable to find a zone named \"%s\" in database\n", zonename);
514 		zone_db_free(zone);
515 		zone = NULL;
516         	return -1;
517 	}
518 	zone_db_free(zone);
519 	zone = NULL;
520         if (!zonename && (keytag != -1 || cka_id)) {
521                 ods_log_warning ("[%s] Error: expected --zone <zone>", module_str);
522                 client_printf_err(sockfd, "Error: expected --zone <zone>\n");
523                 return -1;
524         }
525 
526 	if (!(zonename && ((cka_id && keytag == -1) || (!cka_id && keytag != -1))) && !all)
527 	{
528 		ods_log_warning("[%s] expected --zone and either --cka_id or "
529 			"--keytag option or expected --all", module_str);
530 		client_printf_err(sockfd, "expected --zone and either --cka_id or "
531 			"--keytag option or expected --all.\n");
532 		return -1;
533 	}
534 
535 	if (cka_id && !(hsmkey = hsm_key_new_get_by_locator(dbconn, cka_id))) {
536 			client_printf_err(sockfd, "CKA_ID %s can not be found!\n", cka_id);
537 			return -1;
538 	}
539 	ret = change_keys_from_to(dbconn, sockfd, zonename, hsmkey, keytag,
540 		state_from, state_to, engine);
541 	hsm_key_free(hsmkey);
542 	return ret;
543 }
544