1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * SPDX-License-Identifier: MPL-2.0 AND ISC
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14 /*
15 * Copyright (C) 2001 Nominum, Inc.
16 *
17 * Permission to use, copy, modify, and/or distribute this software for any
18 * purpose with or without fee is hereby granted, provided that the above
19 * copyright notice and this permission notice appear in all copies.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
22 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
24 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
25 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
26 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
27 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28 */
29
30 /*! \file */
31
32 #include <errno.h>
33 #include <inttypes.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <isc/assertions.h>
40 #include <isc/hmac.h>
41 #include <isc/print.h>
42 #include <isc/safe.h>
43
44 #include <pk11/site.h>
45
46 #include <isccc/alist.h>
47 #include <isccc/base64.h>
48 #include <isccc/cc.h>
49 #include <isccc/result.h>
50 #include <isccc/sexpr.h>
51 #include <isccc/symtab.h>
52 #include <isccc/symtype.h>
53 #include <isccc/util.h>
54
55 #define MAX_TAGS 256
56 #define DUP_LIFETIME 900
57
58 typedef isccc_sexpr_t *sexpr_ptr;
59
60 static unsigned char auth_hmd5[] = {
61 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
62 ISCCC_CCMSGTYPE_TABLE, /*%< message type */
63 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */
64 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */
65 ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */
66 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */
67 /*
68 * The base64 encoding of one of our HMAC-MD5 signatures is
69 * 22 bytes.
70 */
71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
73 };
74
75 #define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
76 #define HMD5_LENGTH 22
77
78 static unsigned char auth_hsha[] = {
79 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
80 ISCCC_CCMSGTYPE_TABLE, /*%< message type */
81 0x00, 0x00, 0x00, 0x63, /*%< length == 99 */
82 0x04, 0x68, 0x73, 0x68, 0x61, /*%< len + hsha */
83 ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */
84 0x00, 0x00, 0x00, 0x59, /*%< length == 89 */
85 0x00, /*%< algorithm */
86 /*
87 * The base64 encoding of one of our HMAC-SHA* signatures is
88 * 88 bytes.
89 */
90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
93 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 0x00, 0x00, 0x00, 0x00
98 };
99
100 #define HSHA_OFFSET 22 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 + 1 */
101 #define HSHA_LENGTH 88
102
103 static isc_result_t
104 table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
105
106 static isc_result_t
107 list_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
108
109 static isc_result_t
value_towire(isccc_sexpr_t * elt,isc_buffer_t ** buffer)110 value_towire(isccc_sexpr_t *elt, isc_buffer_t **buffer) {
111 unsigned int len;
112 isccc_region_t *vr;
113 isc_result_t result;
114
115 if (isccc_sexpr_binaryp(elt)) {
116 vr = isccc_sexpr_tobinary(elt);
117 len = REGION_SIZE(*vr);
118 result = isc_buffer_reserve(buffer, 1 + 4);
119 if (result != ISC_R_SUCCESS) {
120 return (ISC_R_NOSPACE);
121 }
122 isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_BINARYDATA);
123 isc_buffer_putuint32(*buffer, len);
124
125 result = isc_buffer_reserve(buffer, len);
126 if (result != ISC_R_SUCCESS) {
127 return (ISC_R_NOSPACE);
128 }
129 isc_buffer_putmem(*buffer, vr->rstart, len);
130 } else if (isccc_alist_alistp(elt)) {
131 unsigned int used;
132 isc_buffer_t b;
133
134 result = isc_buffer_reserve(buffer, 1 + 4);
135 if (result != ISC_R_SUCCESS) {
136 return (ISC_R_NOSPACE);
137 }
138 isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_TABLE);
139 /*
140 * Emit a placeholder length.
141 */
142 used = (*buffer)->used;
143 isc_buffer_putuint32(*buffer, 0);
144
145 /*
146 * Emit the table.
147 */
148 result = table_towire(elt, buffer);
149 if (result != ISC_R_SUCCESS) {
150 return (result);
151 }
152
153 len = (*buffer)->used - used;
154 /*
155 * 'len' is 4 bytes too big, since it counts
156 * the placeholder length too. Adjust and
157 * emit.
158 */
159 INSIST(len >= 4U);
160 len -= 4;
161
162 isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
163 isc_buffer_putuint32(&b, len);
164 } else if (isccc_sexpr_listp(elt)) {
165 unsigned int used;
166 isc_buffer_t b;
167
168 result = isc_buffer_reserve(buffer, 1 + 4);
169 if (result != ISC_R_SUCCESS) {
170 return (ISC_R_NOSPACE);
171 }
172 isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_LIST);
173 /*
174 * Emit a placeholder length.
175 */
176 used = (*buffer)->used;
177 isc_buffer_putuint32(*buffer, 0);
178
179 /*
180 * Emit the list.
181 */
182 result = list_towire(elt, buffer);
183 if (result != ISC_R_SUCCESS) {
184 return (result);
185 }
186
187 len = (*buffer)->used - used;
188 /*
189 * 'len' is 4 bytes too big, since it counts
190 * the placeholder length too. Adjust and
191 * emit.
192 */
193 INSIST(len >= 4U);
194 len -= 4;
195
196 isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
197 isc_buffer_putuint32(&b, len);
198 }
199
200 return (ISC_R_SUCCESS);
201 }
202
203 static isc_result_t
table_towire(isccc_sexpr_t * alist,isc_buffer_t ** buffer)204 table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer) {
205 isccc_sexpr_t *kv, *elt, *k, *v;
206 char *ks;
207 isc_result_t result;
208 unsigned int len;
209
210 for (elt = isccc_alist_first(alist); elt != NULL;
211 elt = ISCCC_SEXPR_CDR(elt)) {
212 kv = ISCCC_SEXPR_CAR(elt);
213 k = ISCCC_SEXPR_CAR(kv);
214 ks = isccc_sexpr_tostring(k);
215 v = ISCCC_SEXPR_CDR(kv);
216 len = (unsigned int)strlen(ks);
217 INSIST(len <= 255U);
218 /*
219 * Emit the key name.
220 */
221 result = isc_buffer_reserve(buffer, 1 + len);
222 if (result != ISC_R_SUCCESS) {
223 return (ISC_R_NOSPACE);
224 }
225 isc_buffer_putuint8(*buffer, (uint8_t)len);
226 isc_buffer_putmem(*buffer, (const unsigned char *)ks, len);
227 /*
228 * Emit the value.
229 */
230 result = value_towire(v, buffer);
231 if (result != ISC_R_SUCCESS) {
232 return (result);
233 }
234 }
235
236 return (ISC_R_SUCCESS);
237 }
238
239 static isc_result_t
list_towire(isccc_sexpr_t * list,isc_buffer_t ** buffer)240 list_towire(isccc_sexpr_t *list, isc_buffer_t **buffer) {
241 isc_result_t result;
242
243 while (list != NULL) {
244 result = value_towire(ISCCC_SEXPR_CAR(list), buffer);
245 if (result != ISC_R_SUCCESS) {
246 return (result);
247 }
248 list = ISCCC_SEXPR_CDR(list);
249 }
250
251 return (ISC_R_SUCCESS);
252 }
253
254 static isc_result_t
sign(unsigned char * data,unsigned int length,unsigned char * hmac,uint32_t algorithm,isccc_region_t * secret)255 sign(unsigned char *data, unsigned int length, unsigned char *hmac,
256 uint32_t algorithm, isccc_region_t *secret) {
257 const isc_md_type_t *md_type;
258 isc_result_t result;
259 isccc_region_t source, target;
260 unsigned char digest[ISC_MAX_MD_SIZE];
261 unsigned int digestlen;
262 unsigned char digestb64[HSHA_LENGTH + 4];
263
264 source.rstart = digest;
265
266 switch (algorithm) {
267 case ISCCC_ALG_HMACMD5:
268 md_type = ISC_MD_MD5;
269 break;
270 case ISCCC_ALG_HMACSHA1:
271 md_type = ISC_MD_SHA1;
272 break;
273 case ISCCC_ALG_HMACSHA224:
274 md_type = ISC_MD_SHA224;
275 break;
276 case ISCCC_ALG_HMACSHA256:
277 md_type = ISC_MD_SHA256;
278 break;
279 case ISCCC_ALG_HMACSHA384:
280 md_type = ISC_MD_SHA384;
281 break;
282 case ISCCC_ALG_HMACSHA512:
283 md_type = ISC_MD_SHA512;
284 break;
285 default:
286 return (ISC_R_NOTIMPLEMENTED);
287 }
288
289 result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
290 length, digest, &digestlen);
291 if (result != ISC_R_SUCCESS) {
292 return (result);
293 }
294 source.rend = digest + digestlen;
295
296 memset(digestb64, 0, sizeof(digestb64));
297 target.rstart = digestb64;
298 target.rend = digestb64 + sizeof(digestb64);
299 result = isccc_base64_encode(&source, 64, "", &target);
300 if (result != ISC_R_SUCCESS) {
301 return (result);
302 }
303 if (algorithm == ISCCC_ALG_HMACMD5) {
304 PUT_MEM(digestb64, HMD5_LENGTH, hmac);
305 } else {
306 PUT_MEM(digestb64, HSHA_LENGTH, hmac);
307 }
308 return (ISC_R_SUCCESS);
309 }
310
311 isc_result_t
isccc_cc_towire(isccc_sexpr_t * alist,isc_buffer_t ** buffer,uint32_t algorithm,isccc_region_t * secret)312 isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, uint32_t algorithm,
313 isccc_region_t *secret) {
314 unsigned int hmac_base, signed_base;
315 isc_result_t result;
316
317 result = isc_buffer_reserve(buffer,
318 4 + ((algorithm == ISCCC_ALG_HMACMD5)
319 ? sizeof(auth_hmd5)
320 : sizeof(auth_hsha)));
321 if (result != ISC_R_SUCCESS) {
322 return (ISC_R_NOSPACE);
323 }
324
325 /*
326 * Emit protocol version.
327 */
328 isc_buffer_putuint32(*buffer, 1);
329
330 if (secret != NULL) {
331 /*
332 * Emit _auth section with zeroed HMAC signature.
333 * We'll replace the zeros with the real signature once
334 * we know what it is.
335 */
336 if (algorithm == ISCCC_ALG_HMACMD5) {
337 hmac_base = (*buffer)->used + HMD5_OFFSET;
338 isc_buffer_putmem(*buffer, auth_hmd5,
339 sizeof(auth_hmd5));
340 } else {
341 unsigned char *hmac_alg;
342
343 hmac_base = (*buffer)->used + HSHA_OFFSET;
344 hmac_alg = (unsigned char *)isc_buffer_used(*buffer) +
345 HSHA_OFFSET - 1;
346 isc_buffer_putmem(*buffer, auth_hsha,
347 sizeof(auth_hsha));
348 *hmac_alg = algorithm;
349 }
350 } else {
351 hmac_base = 0;
352 }
353 signed_base = (*buffer)->used;
354 /*
355 * Delete any existing _auth section so that we don't try
356 * to encode it.
357 */
358 isccc_alist_delete(alist, "_auth");
359 /*
360 * Emit the message.
361 */
362 result = table_towire(alist, buffer);
363 if (result != ISC_R_SUCCESS) {
364 return (result);
365 }
366 if (secret != NULL) {
367 return (sign((unsigned char *)(*buffer)->base + signed_base,
368 (*buffer)->used - signed_base,
369 (unsigned char *)(*buffer)->base + hmac_base,
370 algorithm, secret));
371 }
372 return (ISC_R_SUCCESS);
373 }
374
375 static isc_result_t
verify(isccc_sexpr_t * alist,unsigned char * data,unsigned int length,uint32_t algorithm,isccc_region_t * secret)376 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
377 uint32_t algorithm, isccc_region_t *secret) {
378 const isc_md_type_t *md_type;
379 isccc_region_t source;
380 isccc_region_t target;
381 isc_result_t result;
382 isccc_sexpr_t *_auth, *hmac;
383 unsigned char digest[ISC_MAX_MD_SIZE];
384 unsigned int digestlen;
385 unsigned char digestb64[HSHA_LENGTH * 4];
386
387 /*
388 * Extract digest.
389 */
390 _auth = isccc_alist_lookup(alist, "_auth");
391 if (!isccc_alist_alistp(_auth)) {
392 return (ISC_R_FAILURE);
393 }
394 if (algorithm == ISCCC_ALG_HMACMD5) {
395 hmac = isccc_alist_lookup(_auth, "hmd5");
396 } else {
397 hmac = isccc_alist_lookup(_auth, "hsha");
398 }
399 if (!isccc_sexpr_binaryp(hmac)) {
400 return (ISC_R_FAILURE);
401 }
402 /*
403 * Compute digest.
404 */
405 source.rstart = digest;
406
407 switch (algorithm) {
408 case ISCCC_ALG_HMACMD5:
409 md_type = ISC_MD_MD5;
410 break;
411 case ISCCC_ALG_HMACSHA1:
412 md_type = ISC_MD_SHA1;
413 break;
414 case ISCCC_ALG_HMACSHA224:
415 md_type = ISC_MD_SHA224;
416 break;
417 case ISCCC_ALG_HMACSHA256:
418 md_type = ISC_MD_SHA256;
419 break;
420 case ISCCC_ALG_HMACSHA384:
421 md_type = ISC_MD_SHA384;
422 break;
423 case ISCCC_ALG_HMACSHA512:
424 md_type = ISC_MD_SHA512;
425 break;
426 default:
427 return (ISC_R_NOTIMPLEMENTED);
428 }
429
430 result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
431 length, digest, &digestlen);
432 if (result != ISC_R_SUCCESS) {
433 return (result);
434 }
435 source.rend = digest + digestlen;
436
437 target.rstart = digestb64;
438 target.rend = digestb64 + sizeof(digestb64);
439 memset(digestb64, 0, sizeof(digestb64));
440 result = isccc_base64_encode(&source, 64, "", &target);
441 if (result != ISC_R_SUCCESS) {
442 return (result);
443 }
444
445 /*
446 * Verify.
447 */
448 if (algorithm == ISCCC_ALG_HMACMD5) {
449 isccc_region_t *region;
450 unsigned char *value;
451
452 region = isccc_sexpr_tobinary(hmac);
453 if ((region->rend - region->rstart) != HMD5_LENGTH) {
454 return (ISCCC_R_BADAUTH);
455 }
456 value = region->rstart;
457 if (!isc_safe_memequal(value, digestb64, HMD5_LENGTH)) {
458 return (ISCCC_R_BADAUTH);
459 }
460 } else {
461 isccc_region_t *region;
462 unsigned char *value;
463 uint32_t valalg;
464
465 region = isccc_sexpr_tobinary(hmac);
466
467 /*
468 * Note: with non-MD5 algorithms, there's an extra octet
469 * to identify which algorithm is in use.
470 */
471 if ((region->rend - region->rstart) != HSHA_LENGTH + 1) {
472 return (ISCCC_R_BADAUTH);
473 }
474 value = region->rstart;
475 GET8(valalg, value);
476 if ((valalg != algorithm) ||
477 !isc_safe_memequal(value, digestb64, HSHA_LENGTH)) {
478 return (ISCCC_R_BADAUTH);
479 }
480 }
481
482 return (ISC_R_SUCCESS);
483 }
484
485 static isc_result_t
486 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
487 uint32_t algorithm, isccc_sexpr_t **alistp);
488
489 static isc_result_t
490 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
491
492 static isc_result_t
value_fromwire(isccc_region_t * source,isccc_sexpr_t ** valuep)493 value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep) {
494 unsigned int msgtype;
495 uint32_t len;
496 isccc_sexpr_t *value;
497 isccc_region_t active;
498 isc_result_t result;
499
500 if (REGION_SIZE(*source) < 1 + 4) {
501 return (ISC_R_UNEXPECTEDEND);
502 }
503 GET8(msgtype, source->rstart);
504 GET32(len, source->rstart);
505 if (REGION_SIZE(*source) < len) {
506 return (ISC_R_UNEXPECTEDEND);
507 }
508 active.rstart = source->rstart;
509 active.rend = active.rstart + len;
510 source->rstart = active.rend;
511 if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
512 value = isccc_sexpr_frombinary(&active);
513 if (value != NULL) {
514 *valuep = value;
515 result = ISC_R_SUCCESS;
516 } else {
517 result = ISC_R_NOMEMORY;
518 }
519 } else if (msgtype == ISCCC_CCMSGTYPE_TABLE) {
520 result = table_fromwire(&active, NULL, 0, valuep);
521 } else if (msgtype == ISCCC_CCMSGTYPE_LIST) {
522 result = list_fromwire(&active, valuep);
523 } else {
524 result = ISCCC_R_SYNTAX;
525 }
526
527 return (result);
528 }
529
530 static isc_result_t
table_fromwire(isccc_region_t * source,isccc_region_t * secret,uint32_t algorithm,isccc_sexpr_t ** alistp)531 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
532 uint32_t algorithm, isccc_sexpr_t **alistp) {
533 char key[256];
534 uint32_t len;
535 isc_result_t result;
536 isccc_sexpr_t *alist, *value;
537 bool first_tag;
538 unsigned char *checksum_rstart;
539
540 REQUIRE(alistp != NULL && *alistp == NULL);
541
542 checksum_rstart = NULL;
543 first_tag = true;
544 alist = isccc_alist_create();
545 if (alist == NULL) {
546 return (ISC_R_NOMEMORY);
547 }
548
549 while (!REGION_EMPTY(*source)) {
550 GET8(len, source->rstart);
551 if (REGION_SIZE(*source) < len) {
552 result = ISC_R_UNEXPECTEDEND;
553 goto bad;
554 }
555 GET_MEM(key, len, source->rstart);
556 key[len] = '\0'; /* Ensure NUL termination. */
557 value = NULL;
558 result = value_fromwire(source, &value);
559 if (result != ISC_R_SUCCESS) {
560 goto bad;
561 }
562 if (isccc_alist_define(alist, key, value) == NULL) {
563 result = ISC_R_NOMEMORY;
564 goto bad;
565 }
566 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0) {
567 checksum_rstart = source->rstart;
568 }
569 first_tag = false;
570 }
571
572 if (secret != NULL) {
573 if (checksum_rstart != NULL) {
574 result = verify(
575 alist, checksum_rstart,
576 (unsigned int)(source->rend - checksum_rstart),
577 algorithm, secret);
578 } else {
579 result = ISCCC_R_BADAUTH;
580 }
581 } else {
582 result = ISC_R_SUCCESS;
583 }
584
585 bad:
586 if (result == ISC_R_SUCCESS) {
587 *alistp = alist;
588 } else {
589 isccc_sexpr_free(&alist);
590 }
591
592 return (result);
593 }
594
595 static isc_result_t
list_fromwire(isccc_region_t * source,isccc_sexpr_t ** listp)596 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp) {
597 isccc_sexpr_t *list, *value;
598 isc_result_t result;
599
600 list = NULL;
601 while (!REGION_EMPTY(*source)) {
602 value = NULL;
603 result = value_fromwire(source, &value);
604 if (result != ISC_R_SUCCESS) {
605 isccc_sexpr_free(&list);
606 return (result);
607 }
608 if (isccc_sexpr_addtolist(&list, value) == NULL) {
609 isccc_sexpr_free(&value);
610 isccc_sexpr_free(&list);
611 return (ISC_R_NOMEMORY);
612 }
613 }
614
615 *listp = list;
616
617 return (ISC_R_SUCCESS);
618 }
619
620 isc_result_t
isccc_cc_fromwire(isccc_region_t * source,isccc_sexpr_t ** alistp,uint32_t algorithm,isccc_region_t * secret)621 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
622 uint32_t algorithm, isccc_region_t *secret) {
623 unsigned int size;
624 uint32_t version;
625
626 size = REGION_SIZE(*source);
627 if (size < 4) {
628 return (ISC_R_UNEXPECTEDEND);
629 }
630 GET32(version, source->rstart);
631 if (version != 1) {
632 return (ISCCC_R_UNKNOWNVERSION);
633 }
634
635 return (table_fromwire(source, secret, algorithm, alistp));
636 }
637
638 static isc_result_t
createmessage(uint32_t version,const char * from,const char * to,uint32_t serial,isccc_time_t now,isccc_time_t expires,isccc_sexpr_t ** alistp,bool want_expires)639 createmessage(uint32_t version, const char *from, const char *to,
640 uint32_t serial, isccc_time_t now, isccc_time_t expires,
641 isccc_sexpr_t **alistp, bool want_expires) {
642 isccc_sexpr_t *alist, *_ctrl, *_data;
643 isc_result_t result;
644
645 REQUIRE(alistp != NULL && *alistp == NULL);
646
647 if (version != 1) {
648 return (ISCCC_R_UNKNOWNVERSION);
649 }
650
651 alist = isccc_alist_create();
652 if (alist == NULL) {
653 return (ISC_R_NOMEMORY);
654 }
655
656 result = ISC_R_NOMEMORY;
657
658 _ctrl = isccc_alist_create();
659 if (_ctrl == NULL) {
660 goto bad;
661 }
662 if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
663 isccc_sexpr_free(&_ctrl);
664 goto bad;
665 }
666
667 _data = isccc_alist_create();
668 if (_data == NULL) {
669 goto bad;
670 }
671 if (isccc_alist_define(alist, "_data", _data) == NULL) {
672 isccc_sexpr_free(&_data);
673 goto bad;
674 }
675
676 if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
677 isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
678 (want_expires &&
679 isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
680 {
681 goto bad;
682 }
683 if (from != NULL && isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
684 {
685 goto bad;
686 }
687 if (to != NULL && isccc_cc_definestring(_ctrl, "_to", to) == NULL) {
688 goto bad;
689 }
690
691 *alistp = alist;
692
693 return (ISC_R_SUCCESS);
694
695 bad:
696 isccc_sexpr_free(&alist);
697
698 return (result);
699 }
700
701 isc_result_t
isccc_cc_createmessage(uint32_t version,const char * from,const char * to,uint32_t serial,isccc_time_t now,isccc_time_t expires,isccc_sexpr_t ** alistp)702 isccc_cc_createmessage(uint32_t version, const char *from, const char *to,
703 uint32_t serial, isccc_time_t now, isccc_time_t expires,
704 isccc_sexpr_t **alistp) {
705 return (createmessage(version, from, to, serial, now, expires, alistp,
706 true));
707 }
708
709 isc_result_t
isccc_cc_createack(isccc_sexpr_t * message,bool ok,isccc_sexpr_t ** ackp)710 isccc_cc_createack(isccc_sexpr_t *message, bool ok, isccc_sexpr_t **ackp) {
711 char *_frm, *_to;
712 uint32_t serial;
713 isccc_sexpr_t *ack, *_ctrl;
714 isc_result_t result;
715 isccc_time_t t;
716
717 REQUIRE(ackp != NULL && *ackp == NULL);
718
719 _ctrl = isccc_alist_lookup(message, "_ctrl");
720 if (!isccc_alist_alistp(_ctrl) ||
721 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
722 isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
723 {
724 return (ISC_R_FAILURE);
725 }
726 /*
727 * _frm and _to are optional.
728 */
729 _frm = NULL;
730 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
731 _to = NULL;
732 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
733 /*
734 * Create the ack.
735 */
736 ack = NULL;
737 result = createmessage(1, _to, _frm, serial, t, 0, &ack, false);
738 if (result != ISC_R_SUCCESS) {
739 return (result);
740 }
741
742 _ctrl = isccc_alist_lookup(ack, "_ctrl");
743 if (_ctrl == NULL) {
744 result = ISC_R_FAILURE;
745 goto bad;
746 }
747 if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
748 result = ISC_R_NOMEMORY;
749 goto bad;
750 }
751
752 *ackp = ack;
753
754 return (ISC_R_SUCCESS);
755
756 bad:
757 isccc_sexpr_free(&ack);
758
759 return (result);
760 }
761
762 bool
isccc_cc_isack(isccc_sexpr_t * message)763 isccc_cc_isack(isccc_sexpr_t *message) {
764 isccc_sexpr_t *_ctrl;
765
766 _ctrl = isccc_alist_lookup(message, "_ctrl");
767 if (!isccc_alist_alistp(_ctrl)) {
768 return (false);
769 }
770 if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS) {
771 return (true);
772 }
773 return (false);
774 }
775
776 bool
isccc_cc_isreply(isccc_sexpr_t * message)777 isccc_cc_isreply(isccc_sexpr_t *message) {
778 isccc_sexpr_t *_ctrl;
779
780 _ctrl = isccc_alist_lookup(message, "_ctrl");
781 if (!isccc_alist_alistp(_ctrl)) {
782 return (false);
783 }
784 if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS) {
785 return (true);
786 }
787 return (false);
788 }
789
790 isc_result_t
isccc_cc_createresponse(isccc_sexpr_t * message,isccc_time_t now,isccc_time_t expires,isccc_sexpr_t ** alistp)791 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
792 isccc_time_t expires, isccc_sexpr_t **alistp) {
793 char *_frm, *_to, *type = NULL;
794 uint32_t serial;
795 isccc_sexpr_t *alist, *_ctrl, *_data;
796 isc_result_t result;
797
798 REQUIRE(alistp != NULL && *alistp == NULL);
799
800 _ctrl = isccc_alist_lookup(message, "_ctrl");
801 _data = isccc_alist_lookup(message, "_data");
802 if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
803 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
804 isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
805 {
806 return (ISC_R_FAILURE);
807 }
808 /*
809 * _frm and _to are optional.
810 */
811 _frm = NULL;
812 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
813 _to = NULL;
814 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
815 /*
816 * Create the response.
817 */
818 alist = NULL;
819 result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
820 &alist);
821 if (result != ISC_R_SUCCESS) {
822 return (result);
823 }
824
825 _ctrl = isccc_alist_lookup(alist, "_ctrl");
826 if (_ctrl == NULL) {
827 result = ISC_R_FAILURE;
828 goto bad;
829 }
830
831 _data = isccc_alist_lookup(alist, "_data");
832 if (_data == NULL) {
833 result = ISC_R_FAILURE;
834 goto bad;
835 }
836
837 if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
838 isccc_cc_definestring(_data, "type", type) == NULL)
839 {
840 result = ISC_R_NOMEMORY;
841 goto bad;
842 }
843
844 *alistp = alist;
845
846 return (ISC_R_SUCCESS);
847
848 bad:
849 isccc_sexpr_free(&alist);
850 return (result);
851 }
852
853 isccc_sexpr_t *
isccc_cc_definestring(isccc_sexpr_t * alist,const char * key,const char * str)854 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) {
855 size_t len;
856 isccc_region_t r;
857
858 len = strlen(str);
859 DE_CONST(str, r.rstart);
860 r.rend = r.rstart + len;
861
862 return (isccc_alist_definebinary(alist, key, &r));
863 }
864
865 isccc_sexpr_t *
isccc_cc_defineuint32(isccc_sexpr_t * alist,const char * key,uint32_t i)866 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, uint32_t i) {
867 char b[100];
868 size_t len;
869 isccc_region_t r;
870
871 snprintf(b, sizeof(b), "%u", i);
872 len = strlen(b);
873 r.rstart = (unsigned char *)b;
874 r.rend = (unsigned char *)b + len;
875
876 return (isccc_alist_definebinary(alist, key, &r));
877 }
878
879 isc_result_t
isccc_cc_lookupstring(isccc_sexpr_t * alist,const char * key,char ** strp)880 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) {
881 isccc_sexpr_t *kv, *v;
882
883 REQUIRE(strp == NULL || *strp == NULL);
884
885 kv = isccc_alist_assq(alist, key);
886 if (kv != NULL) {
887 v = ISCCC_SEXPR_CDR(kv);
888 if (isccc_sexpr_binaryp(v)) {
889 if (strp != NULL) {
890 *strp = isccc_sexpr_tostring(v);
891 }
892 return (ISC_R_SUCCESS);
893 } else {
894 return (ISC_R_EXISTS);
895 }
896 }
897
898 return (ISC_R_NOTFOUND);
899 }
900
901 isc_result_t
isccc_cc_lookupuint32(isccc_sexpr_t * alist,const char * key,uint32_t * uintp)902 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key, uint32_t *uintp) {
903 isccc_sexpr_t *kv, *v;
904
905 kv = isccc_alist_assq(alist, key);
906 if (kv != NULL) {
907 v = ISCCC_SEXPR_CDR(kv);
908 if (isccc_sexpr_binaryp(v)) {
909 if (uintp != NULL) {
910 *uintp = (uint32_t)strtoul(
911 isccc_sexpr_tostring(v), NULL, 10);
912 }
913 return (ISC_R_SUCCESS);
914 } else {
915 return (ISC_R_EXISTS);
916 }
917 }
918
919 return (ISC_R_NOTFOUND);
920 }
921
922 static void
symtab_undefine(char * key,unsigned int type,isccc_symvalue_t value,void * arg)923 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
924 void *arg) {
925 UNUSED(type);
926 UNUSED(value);
927 UNUSED(arg);
928
929 free(key);
930 }
931
932 static bool
symtab_clean(char * key,unsigned int type,isccc_symvalue_t value,void * arg)933 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, void *arg) {
934 isccc_time_t *now;
935
936 UNUSED(key);
937 UNUSED(type);
938
939 now = arg;
940
941 if (*now < value.as_uinteger) {
942 return (false);
943 }
944 if ((*now - value.as_uinteger) < DUP_LIFETIME) {
945 return (false);
946 }
947 return (true);
948 }
949
950 isc_result_t
isccc_cc_createsymtab(isccc_symtab_t ** symtabp)951 isccc_cc_createsymtab(isccc_symtab_t **symtabp) {
952 return (isccc_symtab_create(11897, symtab_undefine, NULL, false,
953 symtabp));
954 }
955
956 void
isccc_cc_cleansymtab(isccc_symtab_t * symtab,isccc_time_t now)957 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) {
958 isccc_symtab_foreach(symtab, symtab_clean, &now);
959 }
960
961 static bool
has_whitespace(const char * str)962 has_whitespace(const char *str) {
963 char c;
964
965 if (str == NULL) {
966 return (false);
967 }
968 while ((c = *str++) != '\0') {
969 if (c == ' ' || c == '\t' || c == '\n') {
970 return (true);
971 }
972 }
973 return (false);
974 }
975
976 isc_result_t
isccc_cc_checkdup(isccc_symtab_t * symtab,isccc_sexpr_t * message,isccc_time_t now)977 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
978 isccc_time_t now) {
979 const char *_frm;
980 const char *_to;
981 char *_ser = NULL, *_tim = NULL, *tmp;
982 isc_result_t result;
983 char *key;
984 size_t len;
985 isccc_symvalue_t value;
986 isccc_sexpr_t *_ctrl;
987
988 _ctrl = isccc_alist_lookup(message, "_ctrl");
989 if (!isccc_alist_alistp(_ctrl) ||
990 isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
991 isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
992 {
993 return (ISC_R_FAILURE);
994 }
995
996 INSIST(_ser != NULL);
997 INSIST(_tim != NULL);
998
999 /*
1000 * _frm and _to are optional.
1001 */
1002 tmp = NULL;
1003 if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS) {
1004 _frm = "";
1005 } else {
1006 _frm = tmp;
1007 }
1008 tmp = NULL;
1009 if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS) {
1010 _to = "";
1011 } else {
1012 _to = tmp;
1013 }
1014 /*
1015 * Ensure there is no newline in any of the strings. This is so
1016 * we can write them to a file later.
1017 */
1018 if (has_whitespace(_frm) || has_whitespace(_to) ||
1019 has_whitespace(_ser) || has_whitespace(_tim))
1020 {
1021 return (ISC_R_FAILURE);
1022 }
1023 len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
1024 key = malloc(len);
1025 if (key == NULL) {
1026 return (ISC_R_NOMEMORY);
1027 }
1028 snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
1029 value.as_uinteger = now;
1030 result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
1031 isccc_symexists_reject);
1032 if (result != ISC_R_SUCCESS) {
1033 free(key);
1034 return (result);
1035 }
1036
1037 return (ISC_R_SUCCESS);
1038 }
1039