1 /*  Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  */
16 
17 #include <assert.h>
18 
19 #include "knot/common/log.h"
20 #include "knot/conf/conf.h"
21 #include "knot/dnssec/zone-events.h"
22 #include "knot/updates/apply.h"
23 #include "knot/zone/zone.h"
24 #include "libknot/errcode.h"
25 
log_dnssec_next(const knot_dname_t * zone,knot_time_t refresh_at)26 static void log_dnssec_next(const knot_dname_t *zone, knot_time_t refresh_at)
27 {
28 	char time_str[64] = { 0 };
29 	struct tm time_gm = { 0 };
30 	time_t refresh = refresh_at;
31 	localtime_r(&refresh, &time_gm);
32 	strftime(time_str, sizeof(time_str), KNOT_LOG_TIME_FORMAT, &time_gm);
33 	if (refresh_at == 0) {
34 		log_zone_warning(zone, "DNSSEC, next signing not scheduled");
35 	} else {
36 		log_zone_info(zone, "DNSSEC, next signing at %s", time_str);
37 	}
38 }
39 
event_dnssec_reschedule(conf_t * conf,zone_t * zone,const zone_sign_reschedule_t * refresh,bool zone_changed)40 void event_dnssec_reschedule(conf_t *conf, zone_t *zone,
41 			     const zone_sign_reschedule_t *refresh, bool zone_changed)
42 {
43 	time_t now = time(NULL);
44 	time_t ignore = -1;
45 	knot_time_t refresh_at = refresh->next_sign;
46 
47 	if (knot_time_cmp(refresh->next_rollover, refresh_at) < 0) {
48 		refresh_at = refresh->next_rollover;
49 	}
50 
51 	log_dnssec_next(zone->name, (time_t)refresh_at);
52 
53 	if (refresh->plan_ds_check) {
54 		zone->timers.next_ds_check = now;
55 	}
56 
57 	if (refresh->last_nsec3resalt) {
58 		zone->timers.last_resalt = refresh->last_nsec3resalt;
59 	}
60 
61 	zone_events_schedule_at(zone,
62 		ZONE_EVENT_DNSSEC, refresh_at ? (time_t)refresh_at : ignore,
63 		ZONE_EVENT_DS_CHECK, refresh->plan_ds_check ? now : ignore,
64 		ZONE_EVENT_NSEC3RESALT, refresh->next_nsec3resalt ? refresh->next_nsec3resalt : ignore,
65 		ZONE_EVENT_NOTIFY, zone_changed ? now : ignore
66 	);
67 }
68 
event_dnssec(conf_t * conf,zone_t * zone)69 int event_dnssec(conf_t *conf, zone_t *zone)
70 {
71 	assert(zone);
72 
73 	zone_sign_reschedule_t resch = { 0 };
74 	zone_sign_roll_flags_t r_flags = KEY_ROLL_ALLOW_ALL;
75 	int sign_flags = 0;
76 	bool zone_changed = false;
77 
78 	if (zone_get_flag(zone, ZONE_FORCE_RESIGN, true)) {
79 		log_zone_info(zone->name, "DNSSEC, dropping previous "
80 		              "signatures, re-signing zone");
81 		sign_flags = ZONE_SIGN_DROP_SIGNATURES;
82 	} else {
83 		log_zone_info(zone->name, "DNSSEC, signing zone");
84 		sign_flags = 0;
85 	}
86 
87 	if (zone_get_flag(zone, ZONE_FORCE_KSK_ROLL, true)) {
88 		r_flags |= KEY_ROLL_FORCE_KSK_ROLL;
89 	}
90 	if (zone_get_flag(zone, ZONE_FORCE_ZSK_ROLL, true)) {
91 		r_flags |= KEY_ROLL_FORCE_ZSK_ROLL;
92 	}
93 
94 	zone_update_t up;
95 	int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL);
96 	if (ret != KNOT_EOK) {
97 		return ret;
98 	}
99 
100 	ret = knot_dnssec_zone_sign(&up, conf, sign_flags, r_flags, 0, &resch);
101 	if (ret != KNOT_EOK) {
102 		goto done;
103 	}
104 
105 	zone_changed = !zone_update_no_change(&up);
106 
107 	ret = zone_update_commit(conf, &up);
108 	if (ret != KNOT_EOK) {
109 		goto done;
110 	}
111 
112 done:
113 	// Schedule dependent events
114 	event_dnssec_reschedule(conf, zone, &resch, zone_changed);
115 
116 	if (ret != KNOT_EOK) {
117 		zone_update_clear(&up);
118 	}
119 	return ret;
120 }
121