File: | lib/libswan/proposals.c |
Warning: | line 427, column 24 Value stored to 'ps' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * Algorithm info parsing and creation functions |
3 | * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> |
4 | * |
5 | * Copyright (C) 2012 Paul Wouters <paul@libreswan.org> |
6 | * Copyright (C) 2015-2020 Andrew Cagney <cagney@gnu.org> |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the |
10 | * Free Software Foundation; either version 2 of the License, or (at your |
11 | * option) any later version. See <https://www.gnu.org/licenses/gpl2.txt>. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, but |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
16 | * for more details. |
17 | */ |
18 | |
19 | #include <stdio.h> |
20 | #include <string.h> |
21 | #include <limits.h> |
22 | |
23 | #include "lswlog.h" |
24 | #include "lswalloc.h" |
25 | #include "constants.h" |
26 | #include "proposals.h" |
27 | #include "ike_alg.h" |
28 | #include "ike_alg_integ.h" |
29 | #include "ike_alg_dh.h" |
30 | #include "alg_byname.h" |
31 | |
32 | struct proposal { |
33 | /* |
34 | * The algorithm entries. |
35 | */ |
36 | struct algorithm *algorithms[PROPOSAL_ALGORITHM_ROOF]; |
37 | /* |
38 | * Which protocol is this proposal intended for? |
39 | */ |
40 | const struct proposal_protocol *protocol; |
41 | struct proposal *next; |
42 | }; |
43 | |
44 | struct proposals { |
45 | bool_Bool defaulted; |
46 | int ref_cnt; |
47 | struct proposal *proposals; |
48 | }; |
49 | |
50 | struct proposal_parser *alloc_proposal_parser(const struct proposal_policy *policy, |
51 | const struct proposal_protocol *protocol) |
52 | { |
53 | struct proposal_parser *parser = alloc_thing(struct proposal_parser, "parser")((struct proposal_parser*) alloc_bytes(sizeof(struct proposal_parser ), ("parser"))); |
54 | parser->policy = policy; |
55 | parser->protocol = protocol; |
56 | parser->error[0] = '\0'; |
57 | return parser; |
58 | } |
59 | |
60 | void free_proposal_parser(struct proposal_parser **parser) |
61 | { |
62 | pfree(*parser); |
63 | *parser = NULL((void*)0); |
64 | } |
65 | |
66 | bool_Bool proposal_encrypt_aead(const struct proposal *proposal) |
67 | { |
68 | if (proposal->algorithms[PROPOSAL_encrypt] == NULL((void*)0)) { |
69 | return false0; |
70 | } |
71 | FOR_EACH_ALGORITHM(proposal, encrypt, alg)for (struct algorithm *alg = next_algorithm(proposal, PROPOSAL_encrypt , ((void*)0)); alg != ((void*)0); alg = next_algorithm(proposal , PROPOSAL_encrypt, alg)) { |
72 | const struct encrypt_desc *encrypt = encrypt_desc(alg->desc); |
73 | if (!encrypt_desc_is_aead(encrypt)) { |
74 | return false0; |
75 | } |
76 | } |
77 | return true1; |
78 | } |
79 | |
80 | bool_Bool proposal_encrypt_norm(const struct proposal *proposal) |
81 | { |
82 | if (proposal->algorithms[PROPOSAL_encrypt] == NULL((void*)0)) { |
83 | return false0; |
84 | } |
85 | FOR_EACH_ALGORITHM(proposal, encrypt, alg)for (struct algorithm *alg = next_algorithm(proposal, PROPOSAL_encrypt , ((void*)0)); alg != ((void*)0); alg = next_algorithm(proposal , PROPOSAL_encrypt, alg)) { |
86 | const struct encrypt_desc *encrypt = encrypt_desc(alg->desc); |
87 | if (encrypt_desc_is_aead(encrypt)) { |
88 | return false0; |
89 | } |
90 | } |
91 | return true1; |
92 | } |
93 | |
94 | bool_Bool proposal_integ_none(const struct proposal *proposal) |
95 | { |
96 | /* interpret NULL as NONE */ |
97 | FOR_EACH_ALGORITHM(proposal, integ, alg)for (struct algorithm *alg = next_algorithm(proposal, PROPOSAL_integ , ((void*)0)); alg != ((void*)0); alg = next_algorithm(proposal , PROPOSAL_integ, alg)) { |
98 | const struct integ_desc *integ = integ_desc(alg->desc); |
99 | if (integ != &ike_alg_integ_none) { |
100 | return false0; |
101 | } |
102 | } |
103 | return true1; |
104 | } |
105 | |
106 | bool_Bool proposal_aead_none_ok(struct proposal_parser *parser, |
107 | const struct proposal *proposal) |
108 | { |
109 | if (impair.allow_null_none) { |
110 | return true1; |
111 | } |
112 | |
113 | if (proposal->algorithms[PROPOSAL_encrypt] == NULL((void*)0)) { |
114 | return true1; |
115 | } |
116 | |
117 | /* are any and all encrypt algorithms AEAD? */ |
118 | bool_Bool aead = proposal_encrypt_aead(proposal); |
119 | bool_Bool norm = proposal_encrypt_norm(proposal); |
120 | |
121 | if (!aead && !norm) { |
122 | proposal_error(parser, "AEAD and non-AEAD %s encryption algorithm cannot be combined", |
123 | proposal->protocol->name); |
124 | return false0; |
125 | } |
126 | |
127 | /* are any and all integ algorithms NONE? */ |
128 | bool_Bool none = proposal_integ_none(proposal); |
129 | |
130 | if (aead && !none) { |
131 | const struct ike_alg *encrypt = proposal->algorithms[PROPOSAL_encrypt]->desc; |
132 | /* |
133 | * At least one of the integrity algorithms wasn't |
134 | * NONE. For instance, esp=aes_gcm-sha1" is invalid. |
135 | */ |
136 | proposal_error(parser, "AEAD %s encryption algorithm %s must have 'NONE' as the integrity algorithm", |
137 | proposal->protocol->name, |
138 | encrypt->fqn); |
139 | return false0; |
140 | } |
141 | |
142 | if (norm && none) { |
143 | const struct ike_alg *encrypt = proposal->algorithms[PROPOSAL_encrypt]->desc; |
144 | /* |
145 | * Not AEAD and either there was no integrity |
146 | * algorithm (implying NONE) or at least one integrity |
147 | * algorithm was NONE. For instance, |
148 | * esp=aes_cbc-none" is invalid. |
149 | */ |
150 | proposal_error(parser, "non-AEAD %s encryption algorithm %s cannot have 'NONE' as the integrity algorithm", |
151 | proposal->protocol->name, |
152 | encrypt->fqn); |
153 | return false0; |
154 | } |
155 | |
156 | return true1; |
157 | } |
158 | |
159 | |
160 | /* |
161 | * proposals struct can be shared by several connections instances, |
162 | * handle free() with ref_cnts. |
163 | */ |
164 | |
165 | void proposals_addref(struct proposals **proposals) |
166 | { |
167 | if ((*proposals) != NULL((void*)0)) { |
168 | (*proposals)->ref_cnt++; |
169 | } |
170 | } |
171 | |
172 | void proposals_delref(struct proposals **proposals) |
173 | { |
174 | if ((*proposals) != NULL((void*)0)) { |
175 | if ((*proposals)->ref_cnt == 0) { |
176 | free_proposal(&(*proposals)->proposals); |
177 | pfree((*proposals)); |
178 | } else { |
179 | (*proposals)->ref_cnt--; |
180 | } |
181 | *proposals = NULL((void*)0); |
182 | } |
183 | } |
184 | struct proposal *next_proposal(const struct proposals *proposals, |
185 | struct proposal *last) |
186 | { |
187 | if (last == NULL((void*)0)) { |
188 | return proposals->proposals; |
189 | } else { |
190 | return last->next; |
191 | } |
192 | } |
193 | |
194 | unsigned nr_proposals(struct proposals *proposals) |
195 | { |
196 | unsigned nr = 0; |
197 | FOR_EACH_PROPOSAL(proposals, proposal)for (struct proposal *proposal = next_proposal(proposals, ((void *)0)); proposal != ((void*)0); proposal = next_proposal(proposals , proposal)) { |
198 | nr++; |
199 | } |
200 | return nr; |
201 | } |
202 | |
203 | void append_proposal(struct proposals *proposals, struct proposal **proposal) |
204 | { |
205 | struct proposal **end = &proposals->proposals; |
206 | /* check for duplicates */ |
207 | while ((*end) != NULL((void*)0)) { |
208 | bool_Bool same = true1; |
209 | for (enum proposal_algorithm pa = 0; |
210 | same && pa < PROPOSAL_ALGORITHM_ROOF; pa++) { |
211 | struct algorithm *old = (*end)->algorithms[pa]; |
212 | struct algorithm *new = (*proposal)->algorithms[pa]; |
213 | while (same) { |
214 | if (new == NULL((void*)0) && old == NULL((void*)0)) { |
215 | break; |
216 | } |
217 | if (new == NULL((void*)0) || old == NULL((void*)0)) { |
218 | same = false0; |
219 | break; |
220 | } |
221 | if (new->desc != old->desc) { |
222 | same = false0; |
223 | break; |
224 | } |
225 | /* |
226 | * If list already contains encryption |
227 | * with ENCKEYLEN=0 then new is a |
228 | * duplicate as 0 generates all keys. |
229 | * Ignore reverse vis aes128,aes. |
230 | */ |
231 | if (old->desc->algo_type == IKE_ALG_ENCRYPT&ike_alg_encrypt && |
232 | (old->enckeylen != 0 && |
233 | new->enckeylen != old->enckeylen)) { |
234 | same = false0; |
235 | break; |
236 | } |
237 | new = new->next; |
238 | old = old->next; |
239 | } |
240 | } |
241 | if (same) { |
242 | dbg("discarding duplicate proposal"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("discarding duplicate proposal"); } }; |
243 | free_proposal(proposal); |
244 | return; |
245 | } |
246 | end = &(*end)->next; |
247 | } |
248 | *end = *proposal; |
249 | *proposal = NULL((void*)0); |
250 | } |
251 | |
252 | struct v1_proposal v1_proposal(const struct proposal *proposal) |
253 | { |
254 | struct v1_proposal v1 = { |
255 | .protocol = proposal->protocol, |
256 | #define D(ALG) .ALG = proposal->algorithms[PROPOSAL_##ALG] != NULL((void*)0) ? ALG##_desc(proposal->algorithms[PROPOSAL_##ALG]->desc) : NULL((void*)0) |
257 | D(encrypt), |
258 | D(prf), |
259 | D(integ), |
260 | D(dh), |
261 | #undef D |
262 | }; |
263 | v1.enckeylen = proposal->algorithms[PROPOSAL_encrypt] != NULL((void*)0) ? proposal->algorithms[PROPOSAL_encrypt]->enckeylen : 0; |
264 | |
265 | return v1; |
266 | } |
267 | |
268 | struct algorithm *next_algorithm(const struct proposal *proposal, |
269 | enum proposal_algorithm algorithm, |
270 | struct algorithm *last) |
271 | { |
272 | if (last == NULL((void*)0)) { |
273 | /* |
274 | * Hack, there should there a way to index algorithm |
275 | * types; however the old enum proved very dangerous. |
276 | */ |
277 | passert(algorithm < elemsof(proposal->algorithms)){ _Bool assertion__ = algorithm < (sizeof(proposal->algorithms ) / sizeof(*(proposal->algorithms))); if (!assertion__) { lsw_passert_fail ((where_t) { .func = __func__, .basename = "proposals.c" , .line = 277}, "%s", "algorithm < elemsof(proposal->algorithms)" ); } }; |
278 | return proposal->algorithms[algorithm]; |
279 | } else { |
280 | return last->next; |
281 | } |
282 | } |
283 | |
284 | void free_algorithms(struct proposal *proposal, |
285 | enum proposal_algorithm algorithm) |
286 | { |
287 | passert(algorithm < elemsof(proposal->algorithms)){ _Bool assertion__ = algorithm < (sizeof(proposal->algorithms ) / sizeof(*(proposal->algorithms))); if (!assertion__) { lsw_passert_fail ((where_t) { .func = __func__, .basename = "proposals.c" , .line = 287}, "%s", "algorithm < elemsof(proposal->algorithms)" ); } }; |
288 | struct algorithm *alg = proposal->algorithms[algorithm]; |
289 | while (alg != NULL((void*)0)) { |
290 | struct algorithm *del = alg; |
291 | alg = alg->next; |
292 | pfree(del); |
293 | } |
294 | proposal->algorithms[algorithm] = NULL((void*)0); |
295 | } |
296 | |
297 | struct proposal *alloc_proposal(struct proposal_parser *parser) |
298 | { |
299 | struct proposal *proposal = alloc_thing(struct proposal, "proposal")((struct proposal*) alloc_bytes(sizeof(struct proposal), ("proposal" ))); |
300 | proposal->protocol = parser->protocol; |
301 | return proposal; |
302 | } |
303 | |
304 | void free_proposal(struct proposal **proposals) |
305 | { |
306 | struct proposal *proposal = *proposals; |
307 | while (proposal != NULL((void*)0)) { |
308 | struct proposal *del = proposal; |
309 | proposal = proposal->next; |
310 | for (enum proposal_algorithm algorithm = 0; |
311 | algorithm < PROPOSAL_ALGORITHM_ROOF; |
312 | algorithm++) { |
313 | free_algorithms(del, algorithm); |
314 | } |
315 | pfree(del); |
316 | } |
317 | *proposals = NULL((void*)0); |
318 | } |
319 | |
320 | |
321 | /* |
322 | * XXX: hack, need to come up with a type safe way of mapping an |
323 | * ike_alg onto an index. |
324 | */ |
325 | static enum proposal_algorithm ike_to_proposal_algorithm(const struct ike_alg *alg) |
326 | { |
327 | if (alg->algo_type == IKE_ALG_ENCRYPT&ike_alg_encrypt) { |
328 | return PROPOSAL_encrypt; |
329 | } else if (alg->algo_type == IKE_ALG_PRF&ike_alg_prf) { |
330 | return PROPOSAL_prf; |
331 | } else if (alg->algo_type == IKE_ALG_INTEG&ike_alg_integ) { |
332 | return PROPOSAL_integ; |
333 | } else if (alg->algo_type == IKE_ALG_DH&ike_alg_dh) { |
334 | return PROPOSAL_dh; |
335 | } else { |
336 | PASSERT_FAIL("unexpected algorithm type %s",lsw_passert_fail((where_t) { .func = __func__, .basename = "proposals.c" , .line = 337}, "unexpected algorithm type %s", ike_alg_type_name (alg->algo_type)) |
337 | ike_alg_type_name(alg->algo_type))lsw_passert_fail((where_t) { .func = __func__, .basename = "proposals.c" , .line = 337}, "unexpected algorithm type %s", ike_alg_type_name (alg->algo_type)); |
338 | } |
339 | } |
340 | |
341 | void append_algorithm(struct proposal_parser *parser, |
342 | struct proposal *proposal, |
343 | const struct ike_alg *alg, |
344 | int enckeylen) |
345 | { |
346 | enum proposal_algorithm algorithm = ike_to_proposal_algorithm(alg); |
347 | passert(algorithm < elemsof(proposal->algorithms)){ _Bool assertion__ = algorithm < (sizeof(proposal->algorithms ) / sizeof(*(proposal->algorithms))); if (!assertion__) { lsw_passert_fail ((where_t) { .func = __func__, .basename = "proposals.c" , .line = 347}, "%s", "algorithm < elemsof(proposal->algorithms)" ); } }; |
348 | struct algorithm **end = &proposal->algorithms[algorithm]; |
349 | /* find end, and check for duplicates */ |
350 | while ((*end) != NULL((void*)0)) { |
351 | /* |
352 | * enckeylen=0 acts as a wildcard |
353 | */ |
354 | if (alg == (*end)->desc && |
355 | (alg->algo_type != IKE_ALG_ENCRYPT&ike_alg_encrypt || |
356 | ((*end)->enckeylen == 0 || |
357 | enckeylen == (*end)->enckeylen))) { |
358 | log_message(parser->policy->logger_rc_flags, parser->policy->logger, |
359 | "discarding duplicate %s %s algorithm %s", |
360 | parser->protocol->name, ike_alg_type_name(alg->algo_type), alg->fqn); |
361 | return; |
362 | } |
363 | end = &(*end)->next; |
364 | } |
365 | struct algorithm new_algorithm = { |
366 | .desc = alg, |
367 | .enckeylen = enckeylen, |
368 | }; |
369 | DBGF(DBG_PROPOSAL_PARSER, "appending %s %s algorithm %s[_%d]",{ if ((cur_debugging & (((lset_t)1 << (DBG_TMI_IX)) ))) { DBG_log("appending %s %s algorithm %s[_%d]", parser-> protocol->name, ike_alg_type_name(alg->algo_type), alg-> fqn, enckeylen); } } |
370 | parser->protocol->name, ike_alg_type_name(alg->algo_type), alg->fqn,{ if ((cur_debugging & (((lset_t)1 << (DBG_TMI_IX)) ))) { DBG_log("appending %s %s algorithm %s[_%d]", parser-> protocol->name, ike_alg_type_name(alg->algo_type), alg-> fqn, enckeylen); } } |
371 | enckeylen){ if ((cur_debugging & (((lset_t)1 << (DBG_TMI_IX)) ))) { DBG_log("appending %s %s algorithm %s[_%d]", parser-> protocol->name, ike_alg_type_name(alg->algo_type), alg-> fqn, enckeylen); } }; |
372 | *end = clone_thing(new_algorithm, "alg")((__typeof__(&(new_algorithm))) clone_bytes((const void * )&(new_algorithm), sizeof(new_algorithm), ("alg"))); |
373 | } |
374 | |
375 | void jam_proposal(struct jambuf *log, |
376 | const struct proposal *proposal) |
377 | { |
378 | const char *ps = ""; |
379 | |
380 | const char *as = ""; |
381 | |
382 | as = ps; |
383 | FOR_EACH_ALGORITHM(proposal, encrypt, alg)for (struct algorithm *alg = next_algorithm(proposal, PROPOSAL_encrypt , ((void*)0)); alg != ((void*)0); alg = next_algorithm(proposal , PROPOSAL_encrypt, alg)) { |
384 | const struct encrypt_desc *encrypt = encrypt_desc(alg->desc); |
385 | jam_string(log, as); ps = "-"; as = "+"; |
386 | jam_string(log, encrypt->common.fqn); |
387 | if (alg->enckeylen != 0) { |
388 | jam(log, "_%d", alg->enckeylen); |
389 | } |
390 | } |
391 | |
392 | as = ps; |
393 | FOR_EACH_ALGORITHM(proposal, prf, alg)for (struct algorithm *alg = next_algorithm(proposal, PROPOSAL_prf , ((void*)0)); alg != ((void*)0); alg = next_algorithm(proposal , PROPOSAL_prf, alg)) { |
394 | const struct prf_desc *prf = prf_desc(alg->desc); |
395 | jam_string(log, as); ps = "-"; as = "+"; |
396 | jam_string(log, prf->common.fqn); |
397 | } |
398 | |
399 | bool_Bool print_integ = (impair.proposal_parser || |
400 | /* no PRF */ |
401 | next_algorithm(proposal, PROPOSAL_prf, NULL((void*)0)) == NULL((void*)0) || |
402 | /* AEAD should have NONE */ |
403 | (proposal_encrypt_aead(proposal) && !proposal_integ_none(proposal))); |
404 | if (!print_integ && proposal_encrypt_norm(proposal)) { |
405 | /* non-AEAD should have matching PRF and INTEG */ |
406 | for (struct algorithm *integ = next_algorithm(proposal, PROPOSAL_integ, NULL((void*)0)), |
407 | *prf = next_algorithm(proposal, PROPOSAL_prf, NULL((void*)0)); |
408 | !print_integ && (integ != NULL((void*)0) || prf != NULL((void*)0)); |
409 | integ = next_algorithm(proposal, PROPOSAL_integ, integ), |
410 | prf = next_algorithm(proposal, PROPOSAL_prf, prf)) { |
411 | print_integ = (integ == NULL((void*)0) || prf == NULL((void*)0) || |
412 | &integ_desc(integ->desc)->prf->common != prf->desc); |
413 | } |
414 | } |
415 | as = ps; |
416 | if (print_integ) { |
417 | FOR_EACH_ALGORITHM(proposal, integ, alg)for (struct algorithm *alg = next_algorithm(proposal, PROPOSAL_integ , ((void*)0)); alg != ((void*)0); alg = next_algorithm(proposal , PROPOSAL_integ, alg)) { |
418 | const struct integ_desc *integ = integ_desc(alg->desc); |
419 | jam_string(log, as); ps = "-"; as = "+"; |
420 | jam_string(log, integ->common.fqn); |
421 | } |
422 | } |
423 | |
424 | as = ps; |
425 | FOR_EACH_ALGORITHM(proposal, dh, alg)for (struct algorithm *alg = next_algorithm(proposal, PROPOSAL_dh , ((void*)0)); alg != ((void*)0); alg = next_algorithm(proposal , PROPOSAL_dh, alg)) { |
426 | const struct dh_desc *dh = dh_desc(alg->desc); |
427 | jam_string(log, as); ps = "-"; as = "+"; |
Value stored to 'ps' is never read | |
428 | jam_string(log, dh->common.fqn); |
429 | } |
430 | } |
431 | |
432 | void jam_proposals(struct jambuf *log, const struct proposals *proposals) |
433 | { |
434 | const char *sep = ""; |
435 | FOR_EACH_PROPOSAL(proposals, proposal)for (struct proposal *proposal = next_proposal(proposals, ((void *)0)); proposal != ((void*)0); proposal = next_proposal(proposals , proposal)) { |
436 | jam_string(log, sep); |
437 | jam_proposal(log, proposal); |
438 | sep = ", "; |
439 | } |
440 | } |
441 | |
442 | /* |
443 | * When PFS=no ignore any DH algorithms, and when PFS=yes reject |
444 | * mixing implicit and explicit DH. |
445 | */ |
446 | static bool_Bool proposals_pfs_vs_dh_check(struct proposal_parser *parser, |
447 | struct proposals *proposals) |
448 | { |
449 | /* scrape the proposals for dh algorithms */ |
450 | const struct proposal *first_null = NULL((void*)0); |
451 | const struct proposal *first_none = NULL((void*)0); |
452 | const struct ike_alg *first_dh = NULL((void*)0); |
453 | const struct ike_alg *second_dh = NULL((void*)0); |
454 | FOR_EACH_PROPOSAL(proposals, proposal)for (struct proposal *proposal = next_proposal(proposals, ((void *)0)); proposal != ((void*)0); proposal = next_proposal(proposals , proposal)) { |
455 | if (proposal->algorithms[PROPOSAL_dh] == NULL((void*)0)) { |
456 | if (first_null == NULL((void*)0)) { |
457 | first_null = proposal; |
458 | } |
459 | } else if (proposal->algorithms[PROPOSAL_dh]->desc == &ike_alg_dh_none.common) { |
460 | if (first_none == NULL((void*)0)) { |
461 | first_none = proposal; |
462 | } |
463 | } else if (first_dh == NULL((void*)0)) { |
464 | first_dh = proposal->algorithms[PROPOSAL_dh]->desc; |
465 | } else if (second_dh == NULL((void*)0) && |
466 | first_dh != proposal->algorithms[PROPOSAL_dh]->desc) { |
467 | second_dh = proposal->algorithms[PROPOSAL_dh]->desc; |
468 | } |
469 | } |
470 | |
471 | if (first_dh == NULL((void*)0) && first_none == NULL((void*)0)) { |
472 | /* no DH is always ok */ |
473 | return true1; |
474 | } |
475 | |
476 | /* |
477 | * Try to generate very specific errors first. For instance, |
478 | * given PFS=no esp=aes,aes;dh21, an error stating that dh21 |
479 | * is not valid because of PFS is more helpful than an error |
480 | * saying that all or no proposals need PFS. |
481 | */ |
482 | |
483 | /* |
484 | * Since PFS=NO overrides any DH, don't silently ignore it. |
485 | * Check this early so that a conflict with PFS=no code gets |
486 | * reported before anything else. |
487 | */ |
488 | if (!parser->policy->pfs && (first_dh != NULL((void*)0) || first_none != NULL((void*)0))) { |
489 | FOR_EACH_PROPOSAL(proposals, proposal)for (struct proposal *proposal = next_proposal(proposals, ((void *)0)); proposal != ((void*)0); proposal = next_proposal(proposals , proposal)) { |
490 | const struct ike_alg *dh = NULL((void*)0); |
491 | if (proposal->algorithms[PROPOSAL_dh] != NULL((void*)0)) { |
492 | dh = proposal->algorithms[PROPOSAL_dh]->desc; |
493 | } |
494 | if (dh == &ike_alg_dh_none.common) { |
495 | log_message(parser->policy->logger_rc_flags, parser->policy->logger, |
496 | "ignoring redundant %s DH algorithm NONE as PFS policy is disabled", |
497 | parser->protocol->name); |
498 | } else if (dh != NULL((void*)0)) { |
499 | log_message(parser->policy->logger_rc_flags, parser->policy->logger, |
500 | "ignoring %s DH algorithm %s as PFS policy is disabled", |
501 | parser->protocol->name, dh->fqn); |
502 | } |
503 | free_algorithms(proposal, PROPOSAL_dh); |
504 | } |
505 | return true1; |
506 | } |
507 | |
508 | /* |
509 | * Since at least one proposal included DH, all proposals |
510 | * should. A proposal without DH is an error. |
511 | * |
512 | * (The converse, no proposals including DH was handled right |
513 | * at the start). |
514 | */ |
515 | if (first_null != NULL((void*)0)) { |
516 | /* DH was specified */ |
517 | proposal_error(parser, "either all or no %s proposals should specify DH", |
518 | parser->protocol->name); |
519 | if (!impair_proposal_errors(parser)) { |
520 | return false0; |
521 | } |
522 | } |
523 | |
524 | switch (parser->policy->version) { |
525 | |
526 | case IKEv1: |
527 | /* |
528 | * IKEv1 only allows one DH algorithm. |
529 | */ |
530 | if (first_dh != NULL((void*)0) && second_dh != NULL((void*)0)) { |
531 | proposal_error(parser, "more than one IKEv1 %s DH algorithm (%s, %s) is not allowed in quick mode", |
532 | parser->protocol->name, |
533 | first_dh->fqn, |
534 | second_dh->fqn); |
535 | if (!impair_proposal_errors(parser)) { |
536 | return false0; |
537 | } |
538 | } |
539 | break; |
540 | |
541 | case IKEv2: |
542 | /* |
543 | * IKEv2, only implements one DH algorithm. |
544 | */ |
545 | if (first_dh != NULL((void*)0) && second_dh != NULL((void*)0)) { |
546 | proposal_error(parser, "more than one IKEv2 %s DH algorithm (%s, %s) requires unimplemented CHILD_SA INVALID_KE", |
547 | parser->protocol->name, |
548 | first_dh->fqn, |
549 | second_dh->fqn); |
550 | if (!impair_proposal_errors(parser)) { |
551 | return false0; |
552 | } |
553 | } |
554 | break; |
555 | |
556 | default: |
557 | /* ignore */ |
558 | break; |
559 | } |
560 | |
561 | return true1; |
562 | } |
563 | |
564 | void proposal_error(struct proposal_parser *parser, const char *fmt, ...) |
565 | { |
566 | va_list ap; |
567 | va_start(ap, fmt)__builtin_va_start(ap, fmt); |
568 | vsnprintf(parser->error, sizeof(parser->error), fmt, ap); |
569 | va_end(ap)__builtin_va_end(ap); |
570 | } |
571 | |
572 | bool_Bool impair_proposal_errors(struct proposal_parser *parser) |
573 | { |
574 | pexpect(parser->error[0] != '\0')({ _Bool assertion__ = parser->error[0] != '\0'; if (!assertion__ ) { log_pexpect((where_t) { .func = __func__, .basename = "proposals.c" , .line = 574}, "%s", "parser->error[0] != '\\0'"); } assertion__ ; }); |
575 | if (impair.proposal_parser) { |
576 | log_message(parser->policy->logger_rc_flags, |
577 | parser->policy->logger, |
578 | "IMPAIR: ignoring proposal error: %s", |
579 | parser->error); |
580 | parser->error[0] = '\0'; |
581 | return true1; |
582 | } else { |
583 | return false0; |
584 | } |
585 | } |
586 | |
587 | struct proposals *proposals_from_str(struct proposal_parser *parser, |
588 | const char *str) |
589 | { |
590 | struct proposals *proposals = alloc_thing(struct proposals, "proposals")((struct proposals*) alloc_bytes(sizeof(struct proposals), ("proposals" ))); |
591 | unsigned parser_version = parser->policy->parser_version; |
592 | if (parser_version == 0) { |
593 | parser_version = parser->policy->version; |
594 | } |
595 | if (str == NULL((void*)0)) { |
596 | proposals->defaulted = true1; |
597 | /* may still be null */ |
598 | str = parser->protocol->defaults[parser->policy->version]->proposals; |
599 | } |
600 | bool_Bool ok; |
601 | switch (parser_version) { |
602 | case 2: ok = v2_proposals_parse_str(parser, proposals, shunk1(str)); break; |
603 | default: ok = v1_proposals_parse_str(parser, proposals, shunk1(str)); break; |
604 | } |
605 | if (!ok) { |
606 | proposals_delref(&proposals); |
607 | return NULL((void*)0); |
608 | } |
609 | if (proposals->proposals == NULL((void*)0)) { |
610 | proposals_delref(&proposals); |
611 | return NULL((void*)0); |
612 | } |
613 | if (parser->policy->check_pfs_vs_dh && |
614 | !proposals_pfs_vs_dh_check(parser, proposals)) { |
615 | pexpect(parser->error[0] != '\0')({ _Bool assertion__ = parser->error[0] != '\0'; if (!assertion__ ) { log_pexpect((where_t) { .func = __func__, .basename = "proposals.c" , .line = 615}, "%s", "parser->error[0] != '\\0'"); } assertion__ ; }); |
616 | proposals_delref(&proposals); |
617 | return NULL((void*)0); |
618 | } |
619 | return proposals; |
620 | } |
621 | |
622 | bool_Bool default_proposals(struct proposals *proposals) |
623 | { |
624 | return proposals == NULL((void*)0) || proposals->defaulted; |
625 | } |
626 | |
627 | /* |
628 | * Try to parse any of <ealg>-<ekeylen>, <ealg>_<ekeylen>, |
629 | * <ealg><ekeylen>, or <ealg> using some look-ahead. |
630 | */ |
631 | |
632 | static int parse_proposal_eklen(struct proposal_parser *parser, shunk_t print, shunk_t buf) |
633 | { |
634 | /* convert -<eklen> if present */ |
635 | char *end = NULL((void*)0); |
636 | long eklen = strtol(buf.ptr, &end, 10); |
637 | if (buf.ptr + buf.len != end) { |
638 | proposal_error(parser, "%s encryption algorithm '"PRI_SHUNK"%.*s""' key length contains a non-numeric character", |
639 | parser->protocol->name, |
640 | pri_shunk(print)((int) (print).len), (const char *) ((print).ptr)); |
641 | return 0; |
642 | } |
643 | if (eklen >= INT_MAX2147483647) { |
644 | proposal_error(parser, "%s encryption algorithm '"PRI_SHUNK"%.*s""' key length WAY too big", |
645 | parser->protocol->name, |
646 | pri_shunk(print)((int) (print).len), (const char *) ((print).ptr)); |
647 | return 0; |
648 | } |
649 | if (eklen == 0) { |
650 | proposal_error(parser, "%s encryption key length is zero", |
651 | parser->protocol->name); |
652 | return 0; |
653 | } |
654 | return eklen; |
655 | } |
656 | |
657 | bool_Bool proposal_parse_encrypt(struct proposal_parser *parser, |
658 | struct proposal_tokenizer *tokens, |
659 | const struct ike_alg **encrypt, |
660 | int *encrypt_keylen) |
661 | { |
662 | if (tokens->this.len == 0) { |
663 | return false0; |
664 | } |
665 | |
666 | /* |
667 | * Does it match <ealg>-<eklen>? |
668 | * |
669 | * Use the tokens NEXT lookahead to check <eklen> first. If |
670 | * it starts with a digit then just assume <ealg>-<ealg> and |
671 | * error out if it is not so. |
672 | */ |
673 | if (tokens->this_term == '-' && |
674 | tokens->next.len > 0 && |
675 | hunk_char_isdigit(tokens->next, 0)({ unsigned char c_ = ({ const typeof(tokens->next) hunk_ = tokens->next; size_t index_ = 0; const char *string_ = hunk_ .ptr; index_ < hunk_.len ? string_[0] : '\0'; }); ((*__ctype_b_loc ())[(int) ((c_))] & (unsigned short int) _ISdigit); })) { |
676 | /* assume <ealg>-<eklen> */ |
677 | shunk_t ealg = tokens->this; |
678 | shunk_t eklen = tokens->next; |
679 | /* print "<ealg>-<eklen>" in errors */ |
680 | shunk_t print = shunk2(ealg.ptr, eklen.ptr + eklen.len - ealg.ptr); |
681 | int enckeylen = parse_proposal_eklen(parser, print, eklen); |
682 | if (enckeylen <= 0) { |
683 | pexpect(parser->error[0] != '\0')({ _Bool assertion__ = parser->error[0] != '\0'; if (!assertion__ ) { log_pexpect((where_t) { .func = __func__, .basename = "proposals.c" , .line = 683}, "%s", "parser->error[0] != '\\0'"); } assertion__ ; }); |
684 | return false0; |
685 | } |
686 | const struct ike_alg *alg = encrypt_alg_byname(parser, ealg, |
687 | enckeylen, print); |
688 | if (alg == NULL((void*)0)) { |
689 | DBGF(DBG_PROPOSAL_PARSER, "<ealg>byname('"PRI_SHUNK"') with <eklen>='"PRI_SHUNK"' failed: %s",{ if ((cur_debugging & (((lset_t)1 << (DBG_TMI_IX)) ))) { DBG_log("<ealg>byname('""%.*s""') with <eklen>='" "%.*s""' failed: %s", ((int) (ealg).len), (const char *) ((ealg ).ptr), ((int) (eklen).len), (const char *) ((eklen).ptr), parser ->error); } } |
690 | pri_shunk(ealg), pri_shunk(eklen), parser->error){ if ((cur_debugging & (((lset_t)1 << (DBG_TMI_IX)) ))) { DBG_log("<ealg>byname('""%.*s""') with <eklen>='" "%.*s""' failed: %s", ((int) (ealg).len), (const char *) ((ealg ).ptr), ((int) (eklen).len), (const char *) ((eklen).ptr), parser ->error); } }; |
691 | return false0; |
692 | } |
693 | /* consume <ealg>-<eklen> */ |
694 | proposal_next_token(tokens); |
695 | proposal_next_token(tokens); |
696 | // append_algorithm(parser, proposal, alg, enckeylen); |
697 | *encrypt = alg; *encrypt_keylen = enckeylen; |
698 | return true1; |
699 | } |
700 | |
701 | /* |
702 | * Does it match <ealg> (without any _<eklen> suffix?) |
703 | */ |
704 | const shunk_t print = tokens->this; |
705 | shunk_t ealg = tokens->this; |
706 | const struct ike_alg *alg = encrypt_alg_byname(parser, ealg, |
707 | 0/*enckeylen*/, print); |
708 | if (alg != NULL((void*)0)) { |
709 | /* consume <ealg> */ |
710 | proposal_next_token(tokens); |
711 | // append_algorithm(parser, proposal, alg, 0/*enckeylen*/); |
712 | *encrypt = alg; *encrypt_keylen = 0; |
713 | return true1; |
714 | } |
715 | /* buffer still contains error from <ealg> lookup */ |
716 | pexpect(parser->error[0] != '\0')({ _Bool assertion__ = parser->error[0] != '\0'; if (!assertion__ ) { log_pexpect((where_t) { .func = __func__, .basename = "proposals.c" , .line = 716}, "%s", "parser->error[0] != '\\0'"); } assertion__ ; }); |
717 | |
718 | |
719 | /* |
720 | * See if there's a trailing <eklen> in <ealg>. If there |
721 | * isn't then the lookup error above can be returned. |
722 | */ |
723 | size_t end = ealg.len; |
724 | while (end > 0 && hunk_char_isdigit(ealg, end-1)({ unsigned char c_ = ({ const typeof(ealg) hunk_ = ealg; size_t index_ = end-1; const char *string_ = hunk_.ptr; index_ < hunk_.len ? string_[end-1] : '\0'; }); ((*__ctype_b_loc ())[ (int) ((c_))] & (unsigned short int) _ISdigit); })) { |
725 | end--; |
726 | } |
727 | if (end == ealg.len) { |
728 | /* |
729 | * no trailing <eklen> digits and <ealg> was rejected |
730 | * by above); error still contains message from not |
731 | * finding just <ealg>. |
732 | */ |
733 | passert(parser->error[0] != '\0'){ _Bool assertion__ = parser->error[0] != '\0'; if (!assertion__ ) { lsw_passert_fail((where_t) { .func = __func__, .basename = "proposals.c" , .line = 733}, "%s", "parser->error[0] != '\\0'" ); } }; |
734 | return false0; // warning_or_false(parser, "encryption", print); |
735 | } |
736 | |
737 | /* |
738 | * Try parsing the <eklen> found in <ealg>. For something |
739 | * like aes_gcm_16, above lookup should have found the |
740 | * algorithm so isn't a problem here. |
741 | */ |
742 | shunk_t eklen = shunk_slice(ealg, end, ealg.len); |
743 | int enckeylen = parse_proposal_eklen(parser, print, eklen); |
744 | if (enckeylen <= 0) { |
745 | pexpect(parser->error[0] != '\0')({ _Bool assertion__ = parser->error[0] != '\0'; if (!assertion__ ) { log_pexpect((where_t) { .func = __func__, .basename = "proposals.c" , .line = 745}, "%s", "parser->error[0] != '\\0'"); } assertion__ ; }); |
746 | return false0; |
747 | } |
748 | |
749 | /* |
750 | * The <eklen> in <ealg><eklen> or <ealg>_<eklen> parsed; trim |
751 | * <eklen> from <ealg> and then try the lookup. |
752 | */ |
753 | ealg = shunk_slice(ealg, 0, end); |
754 | if (hunk_char_ischar(ealg, ealg.len-1, "_")({ unsigned char c_ = ({ const typeof(ealg) hunk_ = ealg; size_t index_ = ealg.len-1; const char *string_ = hunk_.ptr; index_ < hunk_.len ? string_[ealg.len-1] : '\0'; }); strchr("_", c_); })) { |
755 | ealg = shunk_slice(ealg, 0, end-1); |
756 | } |
757 | parser->error[0] = '\0'; /* zap old error */ |
758 | alg = encrypt_alg_byname(parser, ealg, enckeylen, print); |
759 | if (alg == NULL((void*)0)) { |
760 | pexpect(parser->error[0] != '\0')({ _Bool assertion__ = parser->error[0] != '\0'; if (!assertion__ ) { log_pexpect((where_t) { .func = __func__, .basename = "proposals.c" , .line = 760}, "%s", "parser->error[0] != '\\0'"); } assertion__ ; }); |
761 | return false0; // warning_or_false(parser, "encryption", print); |
762 | } |
763 | |
764 | /* consume <ealg> */ |
765 | proposal_next_token(tokens); |
766 | // append_algorithm(parser, proposal, alg, enckeylen); |
767 | *encrypt = alg; *encrypt_keylen = enckeylen; |
768 | return true1; |
769 | } |
770 | |
771 | struct proposal_tokenizer proposal_first_token(shunk_t input, const char *delims) |
772 | { |
773 | struct proposal_tokenizer token = { |
774 | .input = input, |
775 | .delims = delims, |
776 | }; |
777 | /* parse next */ |
778 | proposal_next_token(&token); |
779 | /* next<-this; parse next */ |
780 | proposal_next_token(&token); |
781 | return token; |
782 | } |
783 | |
784 | void proposal_next_token(struct proposal_tokenizer *tokens) |
785 | { |
786 | /* shuffle terminators */ |
787 | tokens->prev_term = tokens->this_term; |
788 | tokens->this_term = tokens->next_term; |
789 | /* shuffle tokens */ |
790 | tokens->this = tokens->next; |
791 | tokens->next = shunk_token(&tokens->input, &tokens->next_term, tokens->delims); |
792 | if (DBGP(DBG_PROPOSAL_PARSER)(cur_debugging & (((lset_t)1 << (DBG_TMI_IX))))) { |
793 | JAMBUF(buf)for (char lswbuf[((size_t)1024)], *lswbuf_ = lswbuf; lswbuf_ != ((void*)0); lswbuf_ = ((void*)0)) for (struct jambuf jambuf = array_as_jambuf((lswbuf), sizeof(lswbuf)), *buf = &jambuf ; buf != ((void*)0); buf = ((void*)0)) { |
794 | jam(buf, "token: "); |
795 | if (tokens->prev_term != '\0') { |
796 | jam(buf, "'%c'", tokens->prev_term); |
797 | } else { |
798 | jam(buf, "''"); |
799 | } |
800 | jam(buf, " "); |
801 | if (tokens->this.ptr == NULL((void*)0)) { |
802 | jam(buf, "<null>"); |
803 | } else { |
804 | jam(buf, "\""PRI_SHUNK"%.*s""\"", pri_shunk(tokens->this)((int) (tokens->this).len), (const char *) ((tokens->this ).ptr)); |
805 | } |
806 | jam(buf, " "); |
807 | if (tokens->this_term != '\0') { |
808 | jam(buf, "'%c'", tokens->this_term); |
809 | } else { |
810 | jam(buf, "''"); |
811 | } |
812 | jam(buf, " "); |
813 | if (tokens->this.ptr == NULL((void*)0)) { |
814 | jam(buf, "<null>"); |
815 | } else { |
816 | jam(buf, "\""PRI_SHUNK"%.*s""\"", pri_shunk(tokens->this)((int) (tokens->this).len), (const char *) ((tokens->this ).ptr)); |
817 | } |
818 | jam(buf, " "); |
819 | if (tokens->next_term != '\0') { |
820 | jam(buf, "'%c'", tokens->next_term); |
821 | } else { |
822 | jam(buf, "''"); |
823 | } |
824 | jambuf_to_debug_stream(buf); /* XXX: grrr */ |
825 | } |
826 | } |
827 | } |