1 #include "slurm/slurm_loader.h"
2 
3 #include <stdlib.h>
4 #include <stdbool.h>
5 #include <sys/types.h> /* AF_INET, AF_INET6 (needed in OpenBSD) */
6 #include <sys/socket.h> /* AF_INET, AF_INET6 (needed in OpenBSD) */
7 
8 #include "log.h"
9 #include "config.h"
10 #include "common.h"
11 #include "crypto/hash.h"
12 #include "slurm/slurm_parser.h"
13 
14 #define SLURM_FILE_EXTENSION	".slurm"
15 
16 struct slurm_parser_params {
17 	struct db_table *db_table;
18 	struct db_slurm *db_slurm;
19 };
20 
21 /*
22  * Load the SLURM file(s) from the configured path.
23  *
24  * If this returns zero but @result is NULL, it's fine. There are no SLURM
25  * rules.
26  */
27 static int
load_slurm_files(struct slurm_csum_list * csums,struct db_slurm ** result)28 load_slurm_files(struct slurm_csum_list *csums, struct db_slurm **result)
29 {
30 	struct db_slurm *db;
31 	int error;
32 
33 	error = db_slurm_create(csums, &db);
34 	if (error)
35 		return error;
36 
37 	error = process_file_or_dir(config_get_slurm(), SLURM_FILE_EXTENSION,
38 	    false, slurm_parse, db);
39 	if (error)
40 		goto cancel;
41 
42 	/* Empty SLURM dir, or empty SLURM file(s) */
43 	if (!db_slurm_has_data(db)) {
44 		*result = NULL;
45 		goto cancel; /* Success. */
46 	}
47 
48 	*result = db;
49 	return 0;
50 
51 cancel:
52 	db_slurm_destroy(db);
53 	return error;
54 }
55 
56 static int
slurm_pfx_filters_apply(struct vrp const * vrp,void * arg)57 slurm_pfx_filters_apply(struct vrp const *vrp, void *arg)
58 {
59 	struct slurm_parser_params *params = arg;
60 
61 	if (db_slurm_vrp_is_filtered(params->db_slurm, vrp))
62 		db_table_remove_roa(params->db_table, vrp);
63 
64 	return 0;
65 }
66 
67 static int
slurm_pfx_assertions_add(struct slurm_prefix * prefix,void * arg)68 slurm_pfx_assertions_add(struct slurm_prefix *prefix, void *arg)
69 {
70 	struct db_table *table = arg;
71 	struct ipv4_prefix prefix4;
72 	struct ipv6_prefix prefix6;
73 	struct vrp vrp;
74 
75 	vrp = prefix->vrp;
76 	if ((prefix->data_flag & SLURM_PFX_FLAG_MAX_LENGTH) == 0)
77 		vrp.max_prefix_length = vrp.prefix_length;
78 
79 	if (vrp.addr_fam == AF_INET) {
80 		prefix4.addr = vrp.prefix.v4;
81 		prefix4.len = vrp.prefix_length;
82 		return rtrhandler_handle_roa_v4(table, vrp.asn, &prefix4,
83 		    vrp.max_prefix_length);
84 	}
85 	if (vrp.addr_fam == AF_INET6) {
86 		prefix6.addr = vrp.prefix.v6;
87 		prefix6.len = vrp.prefix_length;
88 		return rtrhandler_handle_roa_v6(table, vrp.asn, &prefix6,
89 		    vrp.max_prefix_length);
90 	}
91 
92 	pr_crit("Unknown addr family type: %u", vrp.addr_fam);
93 }
94 
95 static int
slurm_pfx_assertions_apply(struct slurm_parser_params * params)96 slurm_pfx_assertions_apply(struct slurm_parser_params *params)
97 {
98 	return db_slurm_foreach_assertion_prefix(params->db_slurm,
99 	    slurm_pfx_assertions_add, params->db_table);
100 }
101 
102 static int
slurm_bgpsec_filters_apply(struct router_key const * key,void * arg)103 slurm_bgpsec_filters_apply(struct router_key const *key, void *arg)
104 {
105 	struct slurm_parser_params *params = arg;
106 
107 	if (db_slurm_bgpsec_is_filtered(params->db_slurm, key))
108 		db_table_remove_router_key(params->db_table, key);
109 
110 	return 0;
111 }
112 
113 static int
slurm_bgpsec_assertions_add(struct slurm_bgpsec * bgpsec,void * arg)114 slurm_bgpsec_assertions_add(struct slurm_bgpsec *bgpsec, void *arg)
115 {
116 	struct db_table *table = arg;
117 
118 	return rtrhandler_handle_router_key(table, bgpsec->ski, bgpsec->asn,
119 	    bgpsec->router_public_key);
120 }
121 
122 static int
slurm_bgpsec_assertions_apply(struct slurm_parser_params * params)123 slurm_bgpsec_assertions_apply(struct slurm_parser_params *params)
124 {
125 	return db_slurm_foreach_assertion_bgpsec(params->db_slurm,
126 	    slurm_bgpsec_assertions_add, params->db_table);
127 }
128 
129 static int
__slurm_load_checksums(char const * location,void * arg)130 __slurm_load_checksums(char const *location, void *arg)
131 {
132 	struct slurm_csum_list *list;
133 	struct slurm_file_csum *csum;
134 	int error;
135 
136 	csum = malloc(sizeof(struct slurm_file_csum));
137 	if (csum == NULL)
138 		return pr_enomem();
139 
140 	error = hash_local_file("sha256", location, csum->csum,
141 	    &csum->csum_len);
142 	if (error) {
143 		free(csum);
144 		return pr_op_err("Calculating slurm hash");
145 	}
146 
147 	list = arg;
148 	SLIST_INSERT_HEAD(list, csum, next);
149 	list->list_size++;
150 
151 	return 0;
152 }
153 
154 static void
destroy_local_csum_list(struct slurm_csum_list * list)155 destroy_local_csum_list(struct slurm_csum_list *list)
156 {
157 	struct slurm_file_csum *tmp;
158 
159 	while (!SLIST_EMPTY(list)) {
160 		tmp = SLIST_FIRST(list);
161 		SLIST_REMOVE_HEAD(list, next);
162 		free(tmp);
163 	}
164 }
165 
166 static int
slurm_load_checksums(struct slurm_csum_list * csums)167 slurm_load_checksums(struct slurm_csum_list *csums)
168 {
169 	int error;
170 
171 	SLIST_INIT(csums);
172 	csums->list_size = 0;
173 
174 	error = process_file_or_dir(config_get_slurm(), SLURM_FILE_EXTENSION,
175 	    false, __slurm_load_checksums, csums);
176 	if (error)
177 		destroy_local_csum_list(csums);
178 
179 	return error;
180 }
181 
182 static bool
are_csum_lists_equals(struct slurm_csum_list * new_list,struct slurm_csum_list * old_list)183 are_csum_lists_equals(struct slurm_csum_list *new_list,
184     struct slurm_csum_list *old_list)
185 {
186 	struct slurm_file_csum *newcsum, *old;
187 	bool found = false;
188 
189 	if (new_list->list_size != old_list->list_size) {
190 		return false;
191 	}
192 
193 	SLIST_FOREACH(newcsum, new_list, next) {
194 		SLIST_FOREACH(old, old_list, next) {
195 
196 			if (newcsum->csum_len != old->csum_len)
197 				continue;
198 
199 			if (memcmp(newcsum->csum, old->csum,
200 			    newcsum->csum_len) == 0) {
201 				found = true;
202 				break;
203 			}
204 		}
205 
206 		if (!found)
207 			return false;
208 
209 		found = false;
210 	}
211 
212 	return true;
213 }
214 
215 /* Load SLURM file(s) that have updates */
216 static int
update_slurm(struct db_slurm ** slurm)217 update_slurm(struct db_slurm **slurm)
218 {
219 	struct slurm_csum_list new_csums;
220 	struct slurm_csum_list old_csums;
221 	struct db_slurm *new_slurm = NULL;
222 	int error;
223 
224 	pr_op_info("Checking if there are new or modified SLURM files");
225 
226 	error = slurm_load_checksums(&new_csums);
227 	if (error)
228 		return error;
229 
230 	/* Empty DIR or FILE SLURM not found */
231 	if (new_csums.list_size == 0)
232 		goto success;
233 
234 	if (*slurm != NULL) {
235 		db_slurm_get_csum_list(*slurm, &old_csums);
236 		if (are_csum_lists_equals(&new_csums, &old_csums)) {
237 			pr_op_info("Applying same old SLURM, no changes found.");
238 			destroy_local_csum_list(&new_csums);
239 			return 0;
240 		}
241 	}
242 
243 	pr_op_info("Applying configured SLURM");
244 
245 	error = load_slurm_files(&new_csums, &new_slurm);
246 
247 	/*
248 	 * Checksums were transferred to new_slurm on success, but they're
249 	 * still here on failure.
250 	 * Either way, new_csums is ready for cleanup.
251 	 */
252 	destroy_local_csum_list(&new_csums);
253 
254 	if (error) {
255 		/* Fall back to previous iteration's SLURM */
256 		pr_op_info("Error %d loading SLURM. The validation will continue regardless.",
257 		    error);
258 		if (*slurm != NULL) {
259 			pr_op_info("A previous valid version of the SLURM exists and will be applied.");
260 			db_slurm_log(*slurm);
261 		}
262 
263 		return 0;
264 	}
265 
266 success:
267 	/* Use new SLURM as last valid slurm */
268 	if (*slurm != NULL)
269 		db_slurm_destroy(*slurm);
270 
271 	*slurm = new_slurm;
272 	return 0;
273 }
274 
275 int
slurm_apply(struct db_table * base,struct db_slurm ** slurm)276 slurm_apply(struct db_table *base, struct db_slurm **slurm)
277 {
278 	struct slurm_parser_params params;
279 	int error;
280 
281 	if (config_get_slurm() == NULL)
282 		return 0;
283 
284 	error = update_slurm(slurm);
285 	if (error)
286 		return error;
287 
288 	if (*slurm == NULL)
289 		return 0;
290 
291 	/* Ok, apply SLURM */
292 
293 	params.db_table = base;
294 	params.db_slurm = *slurm;
295 
296 	/* TODO invert this. SLURM rules are few, and base is massive. */
297 	error = db_table_foreach_roa(base, slurm_pfx_filters_apply, &params);
298 	if (error)
299 		return error;
300 
301 	error = db_table_foreach_router_key(base, slurm_bgpsec_filters_apply,
302 	    &params);
303 	if (error)
304 		return error;
305 
306 	error = slurm_pfx_assertions_apply(&params);
307 	if (error)
308 		return error;
309 
310 	return slurm_bgpsec_assertions_apply(&params);
311 }
312