2 * Portions Copyright (C) 2004-2007, 2012, 2013, 2015, 2016 Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 2001-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
11 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
12 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * Portions Copyright (C) 2001 Nominum, Inc.
19 * Permission to use, copy, modify, and/or distribute this software for any
20 * purpose with or without fee is hereby granted, provided that the above
21 * copyright notice and this permission notice appear in all copies.
23 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
24 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
26 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
29 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
40 #include <isc/assertions.h>
41 #include <isc/hmacmd5.h>
42 #include <isc/print.h>
44 #include <isc/stdlib.h>
46 #include <isccc/alist.h>
47 #include <isccc/base64.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>
56 #define DUP_LIFETIME 900
58 typedef isccc_sexpr_t *sexpr_ptr;
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 */
68 * The base64 encoding of one of our HMAC-MD5 signatures is
71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
76 #define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
77 #define HMD5_LENGTH 22
80 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
83 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
86 value_towire(isccc_sexpr_t *elt, isccc_region_t *target) {
92 if (isccc_sexpr_binaryp(elt)) {
93 vr = isccc_sexpr_tobinary(elt);
94 len = REGION_SIZE(*vr);
95 if (REGION_SIZE(*target) < 1 + 4 + len)
96 return (ISC_R_NOSPACE);
97 PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart);
98 PUT32(len, target->rstart);
99 if (REGION_SIZE(*target) < len)
100 return (ISC_R_NOSPACE);
101 PUT_MEM(vr->rstart, len, target->rstart);
102 } else if (isccc_alist_alistp(elt)) {
103 if (REGION_SIZE(*target) < 1 + 4)
104 return (ISC_R_NOSPACE);
105 PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart);
107 * Emit a placeholder length.
109 lenp = target->rstart;
110 PUT32(0, target->rstart);
114 result = table_towire(elt, target);
115 if (result != ISC_R_SUCCESS)
117 len = (unsigned int)(target->rstart - lenp);
119 * 'len' is 4 bytes too big, since it counts
120 * the placeholder length too. Adjust and
126 } else if (isccc_sexpr_listp(elt)) {
127 if (REGION_SIZE(*target) < 1 + 4)
128 return (ISC_R_NOSPACE);
129 PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart);
131 * Emit a placeholder length and count.
133 lenp = target->rstart;
134 PUT32(0, target->rstart);
138 result = list_towire(elt, target);
139 if (result != ISC_R_SUCCESS)
141 len = (unsigned int)(target->rstart - lenp);
143 * 'len' is 4 bytes too big, since it counts
144 * the placeholder length. Adjust and emit.
151 return (ISC_R_SUCCESS);
155 table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
157 isccc_sexpr_t *kv, *elt, *k, *v;
162 for (elt = isccc_alist_first(alist);
164 elt = ISCCC_SEXPR_CDR(elt)) {
165 kv = ISCCC_SEXPR_CAR(elt);
166 k = ISCCC_SEXPR_CAR(kv);
167 ks = isccc_sexpr_tostring(k);
168 v = ISCCC_SEXPR_CDR(kv);
169 len = (unsigned int)strlen(ks);
174 if (REGION_SIZE(*target) < 1 + len)
175 return (ISC_R_NOSPACE);
176 PUT8(len, target->rstart);
177 PUT_MEM(ks, len, target->rstart);
181 result = value_towire(v, target);
182 if (result != ISC_R_SUCCESS)
186 return (ISC_R_SUCCESS);
190 list_towire(isccc_sexpr_t *list, isccc_region_t *target)
194 while (list != NULL) {
195 result = value_towire(ISCCC_SEXPR_CAR(list), target);
196 if (result != ISC_R_SUCCESS)
198 list = ISCCC_SEXPR_CDR(list);
201 return (ISC_R_SUCCESS);
205 sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
206 isccc_region_t *secret)
210 isccc_region_t source, target;
211 unsigned char digest[ISC_MD5_DIGESTLENGTH];
212 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
214 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
215 isc_hmacmd5_update(&ctx, data, length);
216 isc_hmacmd5_sign(&ctx, digest);
217 source.rstart = digest;
218 source.rend = digest + ISC_MD5_DIGESTLENGTH;
219 target.rstart = digestb64;
220 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
221 result = isccc_base64_encode(&source, 64, "", &target);
222 if (result != ISC_R_SUCCESS)
224 PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
226 return (ISC_R_SUCCESS);
230 isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
231 isccc_region_t *secret)
233 unsigned char *hmd5_rstart, *signed_rstart;
236 if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5))
237 return (ISC_R_NOSPACE);
239 * Emit protocol version.
241 PUT32(1, target->rstart);
242 if (secret != NULL) {
244 * Emit _auth section with zeroed HMAC-MD5 signature.
245 * We'll replace the zeros with the real signature once
246 * we know what it is.
248 hmd5_rstart = target->rstart + HMD5_OFFSET;
249 PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart);
252 signed_rstart = target->rstart;
254 * Delete any existing _auth section so that we don't try
257 isccc_alist_delete(alist, "_auth");
261 result = table_towire(alist, target);
262 if (result != ISC_R_SUCCESS)
265 return (sign(signed_rstart,
266 (unsigned int)(target->rstart - signed_rstart),
267 hmd5_rstart, secret));
268 return (ISC_R_SUCCESS);
272 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
273 isccc_region_t *secret)
276 isccc_region_t source;
277 isccc_region_t target;
279 isccc_sexpr_t *_auth, *hmd5;
280 unsigned char digest[ISC_MD5_DIGESTLENGTH];
281 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
286 _auth = isccc_alist_lookup(alist, "_auth");
287 if (!isccc_alist_alistp(_auth))
288 return (ISC_R_FAILURE);
289 hmd5 = isccc_alist_lookup(_auth, "hmd5");
290 if (!isccc_sexpr_binaryp(hmd5))
291 return (ISC_R_FAILURE);
295 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
296 isc_hmacmd5_update(&ctx, data, length);
297 isc_hmacmd5_sign(&ctx, digest);
298 source.rstart = digest;
299 source.rend = digest + ISC_MD5_DIGESTLENGTH;
300 target.rstart = digestb64;
301 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
302 result = isccc_base64_encode(&source, 64, "", &target);
303 if (result != ISC_R_SUCCESS)
306 * Strip trailing == and NUL terminate target.
309 *target.rstart++ = '\0';
313 if (!isc_safe_memequal((unsigned char *) isccc_sexpr_tostring(hmd5),
314 digestb64, HMD5_LENGTH))
315 return (ISCCC_R_BADAUTH);
317 return (ISC_R_SUCCESS);
321 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
322 isccc_sexpr_t **alistp);
325 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
328 value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep) {
329 unsigned int msgtype;
331 isccc_sexpr_t *value;
332 isccc_region_t active;
335 if (REGION_SIZE(*source) < 1 + 4)
336 return (ISC_R_UNEXPECTEDEND);
337 GET8(msgtype, source->rstart);
338 GET32(len, source->rstart);
339 if (REGION_SIZE(*source) < len)
340 return (ISC_R_UNEXPECTEDEND);
341 active.rstart = source->rstart;
342 active.rend = active.rstart + len;
343 source->rstart = active.rend;
344 if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
345 value = isccc_sexpr_frombinary(&active);
348 result = ISC_R_SUCCESS;
350 result = ISC_R_NOMEMORY;
351 } else if (msgtype == ISCCC_CCMSGTYPE_TABLE)
352 result = table_fromwire(&active, NULL, valuep);
353 else if (msgtype == ISCCC_CCMSGTYPE_LIST)
354 result = list_fromwire(&active, valuep);
356 result = ISCCC_R_SYNTAX;
362 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
363 isccc_sexpr_t **alistp)
368 isccc_sexpr_t *alist, *value;
369 isc_boolean_t first_tag;
370 unsigned char *checksum_rstart;
372 REQUIRE(alistp != NULL && *alistp == NULL);
374 checksum_rstart = NULL;
375 first_tag = ISC_TRUE;
376 alist = isccc_alist_create();
378 return (ISC_R_NOMEMORY);
380 while (!REGION_EMPTY(*source)) {
381 GET8(len, source->rstart);
382 if (REGION_SIZE(*source) < len) {
383 result = ISC_R_UNEXPECTEDEND;
386 GET_MEM(key, len, source->rstart);
387 key[len] = '\0'; /* Ensure NUL termination. */
389 result = value_fromwire(source, &value);
390 if (result != ISC_R_SUCCESS)
392 if (isccc_alist_define(alist, key, value) == NULL) {
393 result = ISC_R_NOMEMORY;
396 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
397 checksum_rstart = source->rstart;
398 first_tag = ISC_FALSE;
401 if (secret != NULL) {
402 if (checksum_rstart != NULL)
403 result = verify(alist, checksum_rstart,
405 (source->rend - checksum_rstart),
408 result = ISCCC_R_BADAUTH;
410 result = ISC_R_SUCCESS;
413 if (result == ISC_R_SUCCESS)
416 isccc_sexpr_free(&alist);
422 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp) {
423 isccc_sexpr_t *list, *value;
427 while (!REGION_EMPTY(*source)) {
429 result = value_fromwire(source, &value);
430 if (result != ISC_R_SUCCESS) {
431 isccc_sexpr_free(&list);
434 if (isccc_sexpr_addtolist(&list, value) == NULL) {
435 isccc_sexpr_free(&value);
436 isccc_sexpr_free(&list);
437 return (ISC_R_NOMEMORY);
443 return (ISC_R_SUCCESS);
447 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
448 isccc_region_t *secret)
451 isc_uint32_t version;
453 size = REGION_SIZE(*source);
455 return (ISC_R_UNEXPECTEDEND);
456 GET32(version, source->rstart);
458 return (ISCCC_R_UNKNOWNVERSION);
460 return (table_fromwire(source, secret, alistp));
464 createmessage(isc_uint32_t version, const char *from, const char *to,
465 isc_uint32_t serial, isccc_time_t now,
466 isccc_time_t expires, isccc_sexpr_t **alistp,
467 isc_boolean_t want_expires)
469 isccc_sexpr_t *alist, *_ctrl, *_data;
472 REQUIRE(alistp != NULL && *alistp == NULL);
475 return (ISCCC_R_UNKNOWNVERSION);
477 alist = isccc_alist_create();
479 return (ISC_R_NOMEMORY);
481 result = ISC_R_NOMEMORY;
483 _ctrl = isccc_alist_create();
486 if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
487 isccc_sexpr_free(&_ctrl);
491 _data = isccc_alist_create();
494 if (isccc_alist_define(alist, "_data", _data) == NULL) {
495 isccc_sexpr_free(&_data);
499 if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
500 isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
502 isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
505 isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
508 isccc_cc_definestring(_ctrl, "_to", to) == NULL)
513 return (ISC_R_SUCCESS);
516 isccc_sexpr_free(&alist);
522 isccc_cc_createmessage(isc_uint32_t version, const char *from, const char *to,
523 isc_uint32_t serial, isccc_time_t now,
524 isccc_time_t expires, isccc_sexpr_t **alistp)
526 return (createmessage(version, from, to, serial, now, expires,
531 isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
532 isccc_sexpr_t **ackp)
536 isccc_sexpr_t *ack, *_ctrl;
540 REQUIRE(ackp != NULL && *ackp == NULL);
542 _ctrl = isccc_alist_lookup(message, "_ctrl");
543 if (!isccc_alist_alistp(_ctrl) ||
544 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
545 isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
546 return (ISC_R_FAILURE);
548 * _frm and _to are optional.
551 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
553 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
558 result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
559 if (result != ISC_R_SUCCESS)
562 _ctrl = isccc_alist_lookup(ack, "_ctrl");
564 result = ISC_R_FAILURE;
567 if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
568 result = ISC_R_NOMEMORY;
574 return (ISC_R_SUCCESS);
577 isccc_sexpr_free(&ack);
583 isccc_cc_isack(isccc_sexpr_t *message) {
584 isccc_sexpr_t *_ctrl;
586 _ctrl = isccc_alist_lookup(message, "_ctrl");
587 if (!isccc_alist_alistp(_ctrl))
589 if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
595 isccc_cc_isreply(isccc_sexpr_t *message) {
596 isccc_sexpr_t *_ctrl;
598 _ctrl = isccc_alist_lookup(message, "_ctrl");
599 if (!isccc_alist_alistp(_ctrl))
601 if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
607 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
608 isccc_time_t expires, isccc_sexpr_t **alistp)
610 char *_frm, *_to, *type = NULL;
612 isccc_sexpr_t *alist, *_ctrl, *_data;
615 REQUIRE(alistp != NULL && *alistp == NULL);
617 _ctrl = isccc_alist_lookup(message, "_ctrl");
618 _data = isccc_alist_lookup(message, "_data");
619 if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
620 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
621 isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
622 return (ISC_R_FAILURE);
624 * _frm and _to are optional.
627 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
629 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
631 * Create the response.
634 result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
636 if (result != ISC_R_SUCCESS)
639 _ctrl = isccc_alist_lookup(alist, "_ctrl");
641 result = ISC_R_FAILURE;
645 _data = isccc_alist_lookup(alist, "_data");
647 result = ISC_R_FAILURE;
651 if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
652 isccc_cc_definestring(_data, "type", type) == NULL)
654 result = ISC_R_NOMEMORY;
660 return (ISC_R_SUCCESS);
663 isccc_sexpr_free(&alist);
668 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) {
673 DE_CONST(str, r.rstart);
674 r.rend = r.rstart + len;
676 return (isccc_alist_definebinary(alist, key, &r));
680 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i) {
685 snprintf(b, sizeof(b), "%u", i);
687 r.rstart = (unsigned char *)b;
688 r.rend = (unsigned char *)b + len;
690 return (isccc_alist_definebinary(alist, key, &r));
694 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) {
695 isccc_sexpr_t *kv, *v;
697 REQUIRE(strp == NULL || *strp == NULL);
699 kv = isccc_alist_assq(alist, key);
701 v = ISCCC_SEXPR_CDR(kv);
702 if (isccc_sexpr_binaryp(v)) {
704 *strp = isccc_sexpr_tostring(v);
705 return (ISC_R_SUCCESS);
707 return (ISC_R_EXISTS);
710 return (ISC_R_NOTFOUND);
714 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
717 isccc_sexpr_t *kv, *v;
719 kv = isccc_alist_assq(alist, key);
721 v = ISCCC_SEXPR_CDR(kv);
722 if (isccc_sexpr_binaryp(v)) {
724 *uintp = (isc_uint32_t)
725 strtoul(isccc_sexpr_tostring(v),
727 return (ISC_R_SUCCESS);
729 return (ISC_R_EXISTS);
732 return (ISC_R_NOTFOUND);
736 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
747 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, void *arg) {
755 if (*now < value.as_uinteger)
757 if ((*now - value.as_uinteger) < DUP_LIFETIME)
763 isccc_cc_createsymtab(isccc_symtab_t **symtabp) {
764 return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
769 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) {
770 isccc_symtab_foreach(symtab, symtab_clean, &now);
774 has_whitespace(const char *str) {
779 while ((c = *str++) != '\0') {
780 if (c == ' ' || c == '\t' || c == '\n')
787 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
792 char *_ser = NULL, *_tim = NULL, *tmp;
796 isccc_symvalue_t value;
797 isccc_sexpr_t *_ctrl;
799 _ctrl = isccc_alist_lookup(message, "_ctrl");
800 if (!isccc_alist_alistp(_ctrl) ||
801 isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
802 isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
803 return (ISC_R_FAILURE);
805 INSIST(_ser != NULL);
806 INSIST(_tim != NULL);
809 * _frm and _to are optional.
812 if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
817 if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
822 * Ensure there is no newline in any of the strings. This is so
823 * we can write them to a file later.
825 if (has_whitespace(_frm) || has_whitespace(_to) ||
826 has_whitespace(_ser) || has_whitespace(_tim))
827 return (ISC_R_FAILURE);
828 len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
831 return (ISC_R_NOMEMORY);
832 snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
833 value.as_uinteger = now;
834 result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
835 isccc_symexists_reject);
836 if (result != ISC_R_SUCCESS) {
841 return (ISC_R_SUCCESS);