2 * Portions Copyright (C) 2004-2007, 2012, 2013 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.
32 /* $Id: cc.c,v 1.18 2007/08/28 07:20:43 tbox Exp $ */
42 #include <isc/assertions.h>
43 #include <isc/hmacmd5.h>
44 #include <isc/print.h>
46 #include <isc/stdlib.h>
48 #include <isccc/alist.h>
49 #include <isccc/base64.h>
51 #include <isccc/result.h>
52 #include <isccc/sexpr.h>
53 #include <isccc/symtab.h>
54 #include <isccc/symtype.h>
55 #include <isccc/util.h>
58 #define DUP_LIFETIME 900
60 typedef isccc_sexpr_t *sexpr_ptr;
62 static unsigned char auth_hmd5[] = {
63 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
64 ISCCC_CCMSGTYPE_TABLE, /*%< message type */
65 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */
66 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */
67 ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */
68 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */
70 * The base64 encoding of one of our HMAC-MD5 signatures is
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
78 #define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
79 #define HMD5_LENGTH 22
82 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
85 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
88 value_towire(isccc_sexpr_t *elt, isccc_region_t *target)
95 if (isccc_sexpr_binaryp(elt)) {
96 vr = isccc_sexpr_tobinary(elt);
97 len = REGION_SIZE(*vr);
98 if (REGION_SIZE(*target) < 1 + 4 + len)
99 return (ISC_R_NOSPACE);
100 PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart);
101 PUT32(len, target->rstart);
102 if (REGION_SIZE(*target) < len)
103 return (ISC_R_NOSPACE);
104 PUT_MEM(vr->rstart, len, target->rstart);
105 } else if (isccc_alist_alistp(elt)) {
106 if (REGION_SIZE(*target) < 1 + 4)
107 return (ISC_R_NOSPACE);
108 PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart);
110 * Emit a placeholder length.
112 lenp = target->rstart;
113 PUT32(0, target->rstart);
117 result = table_towire(elt, target);
118 if (result != ISC_R_SUCCESS)
120 len = (unsigned int)(target->rstart - lenp);
122 * 'len' is 4 bytes too big, since it counts
123 * the placeholder length too. Adjust and
129 } else if (isccc_sexpr_listp(elt)) {
130 if (REGION_SIZE(*target) < 1 + 4)
131 return (ISC_R_NOSPACE);
132 PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart);
134 * Emit a placeholder length and count.
136 lenp = target->rstart;
137 PUT32(0, target->rstart);
141 result = list_towire(elt, target);
142 if (result != ISC_R_SUCCESS)
144 len = (unsigned int)(target->rstart - lenp);
146 * 'len' is 4 bytes too big, since it counts
147 * the placeholder length. Adjust and emit.
154 return (ISC_R_SUCCESS);
158 table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
160 isccc_sexpr_t *kv, *elt, *k, *v;
165 for (elt = isccc_alist_first(alist);
167 elt = ISCCC_SEXPR_CDR(elt)) {
168 kv = ISCCC_SEXPR_CAR(elt);
169 k = ISCCC_SEXPR_CAR(kv);
170 ks = isccc_sexpr_tostring(k);
171 v = ISCCC_SEXPR_CDR(kv);
177 if (REGION_SIZE(*target) < 1 + len)
178 return (ISC_R_NOSPACE);
179 PUT8(len, target->rstart);
180 PUT_MEM(ks, len, target->rstart);
184 result = value_towire(v, target);
185 if (result != ISC_R_SUCCESS)
189 return (ISC_R_SUCCESS);
193 list_towire(isccc_sexpr_t *list, isccc_region_t *target)
197 while (list != NULL) {
198 result = value_towire(ISCCC_SEXPR_CAR(list), target);
199 if (result != ISC_R_SUCCESS)
201 list = ISCCC_SEXPR_CDR(list);
204 return (ISC_R_SUCCESS);
208 sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
209 isccc_region_t *secret)
213 isccc_region_t source, target;
214 unsigned char digest[ISC_MD5_DIGESTLENGTH];
215 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
217 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
218 isc_hmacmd5_update(&ctx, data, length);
219 isc_hmacmd5_sign(&ctx, digest);
220 source.rstart = digest;
221 source.rend = digest + ISC_MD5_DIGESTLENGTH;
222 target.rstart = digestb64;
223 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
224 result = isccc_base64_encode(&source, 64, "", &target);
225 if (result != ISC_R_SUCCESS)
227 PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
229 return (ISC_R_SUCCESS);
233 isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
234 isccc_region_t *secret)
236 unsigned char *hmd5_rstart, *signed_rstart;
239 if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5))
240 return (ISC_R_NOSPACE);
242 * Emit protocol version.
244 PUT32(1, target->rstart);
245 if (secret != NULL) {
247 * Emit _auth section with zeroed HMAC-MD5 signature.
248 * We'll replace the zeros with the real signature once
249 * we know what it is.
251 hmd5_rstart = target->rstart + HMD5_OFFSET;
252 PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart);
255 signed_rstart = target->rstart;
257 * Delete any existing _auth section so that we don't try
260 isccc_alist_delete(alist, "_auth");
264 result = table_towire(alist, target);
265 if (result != ISC_R_SUCCESS)
268 return (sign(signed_rstart,
269 (unsigned int)(target->rstart - signed_rstart),
270 hmd5_rstart, secret));
271 return (ISC_R_SUCCESS);
275 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
276 isccc_region_t *secret)
279 isccc_region_t source;
280 isccc_region_t target;
282 isccc_sexpr_t *_auth, *hmd5;
283 unsigned char digest[ISC_MD5_DIGESTLENGTH];
284 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
289 _auth = isccc_alist_lookup(alist, "_auth");
290 if (!isccc_alist_alistp(_auth))
291 return (ISC_R_FAILURE);
292 hmd5 = isccc_alist_lookup(_auth, "hmd5");
293 if (!isccc_sexpr_binaryp(hmd5))
294 return (ISC_R_FAILURE);
298 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
299 isc_hmacmd5_update(&ctx, data, length);
300 isc_hmacmd5_sign(&ctx, digest);
301 source.rstart = digest;
302 source.rend = digest + ISC_MD5_DIGESTLENGTH;
303 target.rstart = digestb64;
304 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
305 result = isccc_base64_encode(&source, 64, "", &target);
306 if (result != ISC_R_SUCCESS)
309 * Strip trailing == and NUL terminate target.
312 *target.rstart++ = '\0';
316 if (!isc_safe_memcmp((unsigned char *) isccc_sexpr_tostring(hmd5),
317 digestb64, HMD5_LENGTH))
318 return (ISCCC_R_BADAUTH);
320 return (ISC_R_SUCCESS);
324 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
325 isccc_sexpr_t **alistp);
328 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
331 value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep)
333 unsigned int msgtype;
335 isccc_sexpr_t *value;
336 isccc_region_t active;
339 if (REGION_SIZE(*source) < 1 + 4)
340 return (ISC_R_UNEXPECTEDEND);
341 GET8(msgtype, source->rstart);
342 GET32(len, source->rstart);
343 if (REGION_SIZE(*source) < len)
344 return (ISC_R_UNEXPECTEDEND);
345 active.rstart = source->rstart;
346 active.rend = active.rstart + len;
347 source->rstart = active.rend;
348 if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
349 value = isccc_sexpr_frombinary(&active);
352 result = ISC_R_SUCCESS;
354 result = ISC_R_NOMEMORY;
355 } else if (msgtype == ISCCC_CCMSGTYPE_TABLE)
356 result = table_fromwire(&active, NULL, valuep);
357 else if (msgtype == ISCCC_CCMSGTYPE_LIST)
358 result = list_fromwire(&active, valuep);
360 result = ISCCC_R_SYNTAX;
366 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
367 isccc_sexpr_t **alistp)
372 isccc_sexpr_t *alist, *value;
373 isc_boolean_t first_tag;
374 unsigned char *checksum_rstart;
376 REQUIRE(alistp != NULL && *alistp == NULL);
378 checksum_rstart = NULL;
379 first_tag = ISC_TRUE;
380 alist = isccc_alist_create();
382 return (ISC_R_NOMEMORY);
384 while (!REGION_EMPTY(*source)) {
385 GET8(len, source->rstart);
386 if (REGION_SIZE(*source) < len) {
387 result = ISC_R_UNEXPECTEDEND;
390 GET_MEM(key, len, source->rstart);
391 key[len] = '\0'; /* Ensure NUL termination. */
393 result = value_fromwire(source, &value);
394 if (result != ISC_R_SUCCESS)
396 if (isccc_alist_define(alist, key, value) == NULL) {
397 result = ISC_R_NOMEMORY;
400 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
401 checksum_rstart = source->rstart;
402 first_tag = ISC_FALSE;
405 if (secret != NULL) {
406 if (checksum_rstart != NULL)
407 result = verify(alist, checksum_rstart,
409 (source->rend - checksum_rstart),
412 result = ISCCC_R_BADAUTH;
414 result = ISC_R_SUCCESS;
417 if (result == ISC_R_SUCCESS)
420 isccc_sexpr_free(&alist);
426 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
428 isccc_sexpr_t *list, *value;
432 while (!REGION_EMPTY(*source)) {
434 result = value_fromwire(source, &value);
435 if (result != ISC_R_SUCCESS) {
436 isccc_sexpr_free(&list);
439 if (isccc_sexpr_addtolist(&list, value) == NULL) {
440 isccc_sexpr_free(&value);
441 isccc_sexpr_free(&list);
448 return (ISC_R_SUCCESS);
452 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
453 isccc_region_t *secret)
456 isc_uint32_t version;
458 size = REGION_SIZE(*source);
460 return (ISC_R_UNEXPECTEDEND);
461 GET32(version, source->rstart);
463 return (ISCCC_R_UNKNOWNVERSION);
465 return (table_fromwire(source, secret, alistp));
469 createmessage(isc_uint32_t version, const char *from, const char *to,
470 isc_uint32_t serial, isccc_time_t now,
471 isccc_time_t expires, isccc_sexpr_t **alistp,
472 isc_boolean_t want_expires)
474 isccc_sexpr_t *alist, *_ctrl, *_data;
477 REQUIRE(alistp != NULL && *alistp == NULL);
480 return (ISCCC_R_UNKNOWNVERSION);
482 alist = isccc_alist_create();
484 return (ISC_R_NOMEMORY);
486 result = ISC_R_NOMEMORY;
488 _ctrl = isccc_alist_create();
491 if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
492 isccc_sexpr_free(&_ctrl);
496 _data = isccc_alist_create();
499 if (isccc_alist_define(alist, "_data", _data) == NULL) {
500 isccc_sexpr_free(&_data);
504 if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
505 isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
507 isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
510 isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
513 isccc_cc_definestring(_ctrl, "_to", to) == NULL)
518 return (ISC_R_SUCCESS);
521 isccc_sexpr_free(&alist);
527 isccc_cc_createmessage(isc_uint32_t version, const char *from, const char *to,
528 isc_uint32_t serial, isccc_time_t now,
529 isccc_time_t expires, isccc_sexpr_t **alistp)
531 return (createmessage(version, from, to, serial, now, expires,
536 isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
537 isccc_sexpr_t **ackp)
541 isccc_sexpr_t *ack, *_ctrl;
545 REQUIRE(ackp != NULL && *ackp == NULL);
547 _ctrl = isccc_alist_lookup(message, "_ctrl");
548 if (!isccc_alist_alistp(_ctrl) ||
549 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
550 isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
551 return (ISC_R_FAILURE);
553 * _frm and _to are optional.
556 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
558 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
563 result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
564 if (result != ISC_R_SUCCESS)
567 _ctrl = isccc_alist_lookup(ack, "_ctrl");
569 result = ISC_R_FAILURE;
572 if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
573 result = ISC_R_NOMEMORY;
579 return (ISC_R_SUCCESS);
582 isccc_sexpr_free(&ack);
588 isccc_cc_isack(isccc_sexpr_t *message)
590 isccc_sexpr_t *_ctrl;
592 _ctrl = isccc_alist_lookup(message, "_ctrl");
593 if (!isccc_alist_alistp(_ctrl))
595 if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
601 isccc_cc_isreply(isccc_sexpr_t *message)
603 isccc_sexpr_t *_ctrl;
605 _ctrl = isccc_alist_lookup(message, "_ctrl");
606 if (!isccc_alist_alistp(_ctrl))
608 if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
614 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
615 isccc_time_t expires, isccc_sexpr_t **alistp)
617 char *_frm, *_to, *type = NULL;
619 isccc_sexpr_t *alist, *_ctrl, *_data;
622 REQUIRE(alistp != NULL && *alistp == NULL);
624 _ctrl = isccc_alist_lookup(message, "_ctrl");
625 _data = isccc_alist_lookup(message, "_data");
626 if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
627 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
628 isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
629 return (ISC_R_FAILURE);
631 * _frm and _to are optional.
634 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
636 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
638 * Create the response.
641 result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
643 if (result != ISC_R_SUCCESS)
646 _ctrl = isccc_alist_lookup(alist, "_ctrl");
648 result = ISC_R_FAILURE;
652 _data = isccc_alist_lookup(alist, "_data");
654 result = ISC_R_FAILURE;
658 if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
659 isccc_cc_definestring(_data, "type", type) == NULL)
661 result = ISC_R_NOMEMORY;
667 return (ISC_R_SUCCESS);
670 isccc_sexpr_free(&alist);
675 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
681 DE_CONST(str, r.rstart);
682 r.rend = r.rstart + len;
684 return (isccc_alist_definebinary(alist, key, &r));
688 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i)
694 snprintf(b, sizeof(b), "%u", i);
696 r.rstart = (unsigned char *)b;
697 r.rend = (unsigned char *)b + len;
699 return (isccc_alist_definebinary(alist, key, &r));
703 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp)
705 isccc_sexpr_t *kv, *v;
707 REQUIRE(strp == NULL || *strp == NULL);
709 kv = isccc_alist_assq(alist, key);
711 v = ISCCC_SEXPR_CDR(kv);
712 if (isccc_sexpr_binaryp(v)) {
714 *strp = isccc_sexpr_tostring(v);
715 return (ISC_R_SUCCESS);
717 return (ISC_R_EXISTS);
720 return (ISC_R_NOTFOUND);
724 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
727 isccc_sexpr_t *kv, *v;
729 kv = isccc_alist_assq(alist, key);
731 v = ISCCC_SEXPR_CDR(kv);
732 if (isccc_sexpr_binaryp(v)) {
734 *uintp = (isc_uint32_t)
735 strtoul(isccc_sexpr_tostring(v),
737 return (ISC_R_SUCCESS);
739 return (ISC_R_EXISTS);
742 return (ISC_R_NOTFOUND);
746 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
757 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value,
767 if (*now < value.as_uinteger)
769 if ((*now - value.as_uinteger) < DUP_LIFETIME)
775 isccc_cc_createsymtab(isccc_symtab_t **symtabp)
777 return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
782 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
784 isccc_symtab_foreach(symtab, symtab_clean, &now);
788 has_whitespace(const char *str)
794 while ((c = *str++) != '\0') {
795 if (c == ' ' || c == '\t' || c == '\n')
802 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
807 char *_ser = NULL, *_tim = NULL, *tmp;
811 isccc_symvalue_t value;
812 isccc_sexpr_t *_ctrl;
814 _ctrl = isccc_alist_lookup(message, "_ctrl");
815 if (!isccc_alist_alistp(_ctrl) ||
816 isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
817 isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
818 return (ISC_R_FAILURE);
820 INSIST(_ser != NULL);
821 INSIST(_tim != NULL);
824 * _frm and _to are optional.
827 if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
832 if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
837 * Ensure there is no newline in any of the strings. This is so
838 * we can write them to a file later.
840 if (has_whitespace(_frm) || has_whitespace(_to) ||
841 has_whitespace(_ser) || has_whitespace(_tim))
842 return (ISC_R_FAILURE);
843 len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
846 return (ISC_R_NOMEMORY);
847 snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
848 value.as_uinteger = now;
849 result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
850 isccc_symexists_reject);
851 if (result != ISC_R_SUCCESS) {
856 return (ISC_R_SUCCESS);