2 * Portions Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 2001-2003 Internet Software Consortium.
4 * Portions Copyright (C) 2001 Nominum, Inc.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
12 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 /* $Id: cc.c,v 1.10.18.5 2006/12/07 23:57:58 marka Exp $ */
29 #include <isc/assertions.h>
30 #include <isc/hmacmd5.h>
31 #include <isc/print.h>
32 #include <isc/stdlib.h>
34 #include <isccc/alist.h>
35 #include <isccc/base64.h>
37 #include <isccc/result.h>
38 #include <isccc/sexpr.h>
39 #include <isccc/symtab.h>
40 #include <isccc/symtype.h>
41 #include <isccc/util.h>
44 #define DUP_LIFETIME 900
46 typedef isccc_sexpr_t *sexpr_ptr;
48 static unsigned char auth_hmd5[] = {
49 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
50 ISCCC_CCMSGTYPE_TABLE, /*%< message type */
51 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */
52 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */
53 ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */
54 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */
56 * The base64 encoding of one of our HMAC-MD5 signatures is
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
64 #define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
65 #define HMD5_LENGTH 22
68 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
71 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
74 value_towire(isccc_sexpr_t *elt, isccc_region_t *target)
81 if (isccc_sexpr_binaryp(elt)) {
82 vr = isccc_sexpr_tobinary(elt);
83 len = REGION_SIZE(*vr);
84 if (REGION_SIZE(*target) < 1 + 4 + len)
85 return (ISC_R_NOSPACE);
86 PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart);
87 PUT32(len, target->rstart);
88 if (REGION_SIZE(*target) < len)
89 return (ISC_R_NOSPACE);
90 PUT_MEM(vr->rstart, len, target->rstart);
91 } else if (isccc_alist_alistp(elt)) {
92 if (REGION_SIZE(*target) < 1 + 4)
93 return (ISC_R_NOSPACE);
94 PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart);
96 * Emit a placeholder length.
98 lenp = target->rstart;
99 PUT32(0, target->rstart);
103 result = table_towire(elt, target);
104 if (result != ISC_R_SUCCESS)
106 len = (size_t)(target->rstart - lenp);
108 * 'len' is 4 bytes too big, since it counts
109 * the placeholder length too. Adjust and
115 } else if (isccc_sexpr_listp(elt)) {
116 if (REGION_SIZE(*target) < 1 + 4)
117 return (ISC_R_NOSPACE);
118 PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart);
120 * Emit a placeholder length and count.
122 lenp = target->rstart;
123 PUT32(0, target->rstart);
127 result = list_towire(elt, target);
128 if (result != ISC_R_SUCCESS)
130 len = (size_t)(target->rstart - lenp);
132 * 'len' is 4 bytes too big, since it counts
133 * the placeholder length. Adjust and emit.
140 return (ISC_R_SUCCESS);
144 table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
146 isccc_sexpr_t *kv, *elt, *k, *v;
151 for (elt = isccc_alist_first(alist);
153 elt = ISCCC_SEXPR_CDR(elt)) {
154 kv = ISCCC_SEXPR_CAR(elt);
155 k = ISCCC_SEXPR_CAR(kv);
156 ks = isccc_sexpr_tostring(k);
157 v = ISCCC_SEXPR_CDR(kv);
163 if (REGION_SIZE(*target) < 1 + len)
164 return (ISC_R_NOSPACE);
165 PUT8(len, target->rstart);
166 PUT_MEM(ks, len, target->rstart);
170 result = value_towire(v, target);
171 if (result != ISC_R_SUCCESS)
175 return (ISC_R_SUCCESS);
179 list_towire(isccc_sexpr_t *list, isccc_region_t *target)
183 while (list != NULL) {
184 result = value_towire(ISCCC_SEXPR_CAR(list), target);
185 if (result != ISC_R_SUCCESS)
187 list = ISCCC_SEXPR_CDR(list);
190 return (ISC_R_SUCCESS);
194 sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
195 isccc_region_t *secret)
199 isccc_region_t source, target;
200 unsigned char digest[ISC_MD5_DIGESTLENGTH];
201 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
203 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
204 isc_hmacmd5_update(&ctx, data, length);
205 isc_hmacmd5_sign(&ctx, digest);
206 source.rstart = digest;
207 source.rend = digest + ISC_MD5_DIGESTLENGTH;
208 target.rstart = digestb64;
209 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
210 result = isccc_base64_encode(&source, 64, "", &target);
211 if (result != ISC_R_SUCCESS)
213 PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
215 return (ISC_R_SUCCESS);
219 isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
220 isccc_region_t *secret)
222 unsigned char *hmd5_rstart, *signed_rstart;
225 if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5))
226 return (ISC_R_NOSPACE);
228 * Emit protocol version.
230 PUT32(1, target->rstart);
231 if (secret != NULL) {
233 * Emit _auth section with zeroed HMAC-MD5 signature.
234 * We'll replace the zeros with the real signature once
235 * we know what it is.
237 hmd5_rstart = target->rstart + HMD5_OFFSET;
238 PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart);
241 signed_rstart = target->rstart;
243 * Delete any existing _auth section so that we don't try
246 isccc_alist_delete(alist, "_auth");
250 result = table_towire(alist, target);
251 if (result != ISC_R_SUCCESS)
254 return (sign(signed_rstart, (target->rstart - signed_rstart),
255 hmd5_rstart, secret));
256 return (ISC_R_SUCCESS);
260 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
261 isccc_region_t *secret)
264 isccc_region_t source;
265 isccc_region_t target;
267 isccc_sexpr_t *_auth, *hmd5;
268 unsigned char digest[ISC_MD5_DIGESTLENGTH];
269 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
274 _auth = isccc_alist_lookup(alist, "_auth");
276 return (ISC_R_FAILURE);
277 hmd5 = isccc_alist_lookup(_auth, "hmd5");
279 return (ISC_R_FAILURE);
283 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
284 isc_hmacmd5_update(&ctx, data, length);
285 isc_hmacmd5_sign(&ctx, digest);
286 source.rstart = digest;
287 source.rend = digest + ISC_MD5_DIGESTLENGTH;
288 target.rstart = digestb64;
289 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
290 result = isccc_base64_encode(&source, 64, "", &target);
291 if (result != ISC_R_SUCCESS)
294 * Strip trailing == and NUL terminate target.
297 *target.rstart++ = '\0';
301 if (strcmp((char *)digestb64, isccc_sexpr_tostring(hmd5)) != 0)
302 return (ISCCC_R_BADAUTH);
304 return (ISC_R_SUCCESS);
308 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
309 isccc_sexpr_t **alistp);
312 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
315 value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep)
317 unsigned int msgtype;
319 isccc_sexpr_t *value;
320 isccc_region_t active;
323 if (REGION_SIZE(*source) < 1 + 4)
324 return (ISC_R_UNEXPECTEDEND);
325 GET8(msgtype, source->rstart);
326 GET32(len, source->rstart);
327 if (REGION_SIZE(*source) < len)
328 return (ISC_R_UNEXPECTEDEND);
329 active.rstart = source->rstart;
330 active.rend = active.rstart + len;
331 source->rstart = active.rend;
332 if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
333 value = isccc_sexpr_frombinary(&active);
336 result = ISC_R_SUCCESS;
338 result = ISC_R_NOMEMORY;
339 } else if (msgtype == ISCCC_CCMSGTYPE_TABLE)
340 result = table_fromwire(&active, NULL, valuep);
341 else if (msgtype == ISCCC_CCMSGTYPE_LIST)
342 result = list_fromwire(&active, valuep);
344 result = ISCCC_R_SYNTAX;
350 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
351 isccc_sexpr_t **alistp)
356 isccc_sexpr_t *alist, *value;
357 isc_boolean_t first_tag;
358 unsigned char *checksum_rstart;
360 REQUIRE(alistp != NULL && *alistp == NULL);
362 checksum_rstart = NULL;
363 first_tag = ISC_TRUE;
364 alist = isccc_alist_create();
366 return (ISC_R_NOMEMORY);
368 while (!REGION_EMPTY(*source)) {
369 GET8(len, source->rstart);
370 if (REGION_SIZE(*source) < len) {
371 result = ISC_R_UNEXPECTEDEND;
374 GET_MEM(key, len, source->rstart);
375 key[len] = '\0'; /* Ensure NUL termination. */
377 result = value_fromwire(source, &value);
378 if (result != ISC_R_SUCCESS)
380 if (isccc_alist_define(alist, key, value) == NULL) {
381 result = ISC_R_NOMEMORY;
384 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
385 checksum_rstart = source->rstart;
386 first_tag = ISC_FALSE;
391 if (secret != NULL) {
392 if (checksum_rstart != NULL)
393 return (verify(alist, checksum_rstart,
394 (source->rend - checksum_rstart),
396 return (ISCCC_R_BADAUTH);
399 return (ISC_R_SUCCESS);
402 isccc_sexpr_free(&alist);
408 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
410 isccc_sexpr_t *list, *value;
414 while (!REGION_EMPTY(*source)) {
416 result = value_fromwire(source, &value);
417 if (result != ISC_R_SUCCESS) {
418 isccc_sexpr_free(&list);
421 if (isccc_sexpr_addtolist(&list, value) == NULL) {
422 isccc_sexpr_free(&value);
423 isccc_sexpr_free(&list);
430 return (ISC_R_SUCCESS);
434 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
435 isccc_region_t *secret)
438 isc_uint32_t version;
440 size = REGION_SIZE(*source);
442 return (ISC_R_UNEXPECTEDEND);
443 GET32(version, source->rstart);
445 return (ISCCC_R_UNKNOWNVERSION);
447 return (table_fromwire(source, secret, alistp));
451 createmessage(isc_uint32_t version, const char *from, const char *to,
452 isc_uint32_t serial, isccc_time_t now,
453 isccc_time_t expires, isccc_sexpr_t **alistp,
454 isc_boolean_t want_expires)
456 isccc_sexpr_t *alist, *_ctrl, *_data;
459 REQUIRE(alistp != NULL && *alistp == NULL);
462 return (ISCCC_R_UNKNOWNVERSION);
464 alist = isccc_alist_create();
466 return (ISC_R_NOMEMORY);
468 result = ISC_R_NOMEMORY;
470 _ctrl = isccc_alist_create();
473 if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
474 isccc_sexpr_free(&_ctrl);
478 _data = isccc_alist_create();
481 if (isccc_alist_define(alist, "_data", _data) == NULL) {
482 isccc_sexpr_free(&_data);
486 if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
487 isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
489 isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
492 isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
495 isccc_cc_definestring(_ctrl, "_to", to) == NULL)
500 return (ISC_R_SUCCESS);
503 isccc_sexpr_free(&alist);
509 isccc_cc_createmessage(isc_uint32_t version, const char *from, const char *to,
510 isc_uint32_t serial, isccc_time_t now,
511 isccc_time_t expires, isccc_sexpr_t **alistp)
513 return (createmessage(version, from, to, serial, now, expires,
518 isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
519 isccc_sexpr_t **ackp)
523 isccc_sexpr_t *ack, *_ctrl;
527 REQUIRE(ackp != NULL && *ackp == NULL);
529 _ctrl = isccc_alist_lookup(message, "_ctrl");
531 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
532 isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
533 return (ISC_R_FAILURE);
535 * _frm and _to are optional.
538 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
540 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
545 result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
546 if (result != ISC_R_SUCCESS)
549 _ctrl = isccc_alist_lookup(ack, "_ctrl");
551 return (ISC_R_FAILURE);
552 if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
553 result = ISC_R_NOMEMORY;
559 return (ISC_R_SUCCESS);
562 isccc_sexpr_free(&ack);
568 isccc_cc_isack(isccc_sexpr_t *message)
570 isccc_sexpr_t *_ctrl;
572 _ctrl = isccc_alist_lookup(message, "_ctrl");
575 if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
581 isccc_cc_isreply(isccc_sexpr_t *message)
583 isccc_sexpr_t *_ctrl;
585 _ctrl = isccc_alist_lookup(message, "_ctrl");
588 if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
594 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
595 isccc_time_t expires, isccc_sexpr_t **alistp)
597 char *_frm, *_to, *type;
599 isccc_sexpr_t *alist, *_ctrl, *_data;
602 REQUIRE(alistp != NULL && *alistp == NULL);
604 _ctrl = isccc_alist_lookup(message, "_ctrl");
605 _data = isccc_alist_lookup(message, "_data");
608 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
609 isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
610 return (ISC_R_FAILURE);
612 * _frm and _to are optional.
615 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
617 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
619 * Create the response.
622 result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
624 if (result != ISC_R_SUCCESS)
626 _ctrl = isccc_alist_lookup(alist, "_ctrl");
628 return (ISC_R_FAILURE);
629 _data = isccc_alist_lookup(alist, "_data");
631 return (ISC_R_FAILURE);
632 if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
633 isccc_cc_definestring(_data, "type", type) == NULL) {
634 isccc_sexpr_free(&alist);
635 return (ISC_R_NOMEMORY);
640 return (ISC_R_SUCCESS);
644 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
650 DE_CONST(str, r.rstart);
651 r.rend = r.rstart + len;
653 return (isccc_alist_definebinary(alist, key, &r));
657 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i)
663 snprintf(b, sizeof(b), "%u", i);
665 r.rstart = (unsigned char *)b;
666 r.rend = (unsigned char *)b + len;
668 return (isccc_alist_definebinary(alist, key, &r));
672 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp)
674 isccc_sexpr_t *kv, *v;
676 kv = isccc_alist_assq(alist, key);
678 v = ISCCC_SEXPR_CDR(kv);
679 if (isccc_sexpr_binaryp(v)) {
681 *strp = isccc_sexpr_tostring(v);
682 return (ISC_R_SUCCESS);
684 return (ISC_R_EXISTS);
687 return (ISC_R_NOTFOUND);
691 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
694 isccc_sexpr_t *kv, *v;
696 kv = isccc_alist_assq(alist, key);
698 v = ISCCC_SEXPR_CDR(kv);
699 if (isccc_sexpr_binaryp(v)) {
701 *uintp = (isc_uint32_t)
702 strtoul(isccc_sexpr_tostring(v),
704 return (ISC_R_SUCCESS);
706 return (ISC_R_EXISTS);
709 return (ISC_R_NOTFOUND);
713 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
724 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value,
734 if (*now < value.as_uinteger)
736 if ((*now - value.as_uinteger) < DUP_LIFETIME)
742 isccc_cc_createsymtab(isccc_symtab_t **symtabp)
744 return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
749 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
751 isccc_symtab_foreach(symtab, symtab_clean, &now);
755 has_whitespace(const char *str)
761 while ((c = *str++) != '\0') {
762 if (c == ' ' || c == '\t' || c == '\n')
769 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
774 char *_ser, *_tim, *tmp;
778 isccc_symvalue_t value;
779 isccc_sexpr_t *_ctrl;
781 _ctrl = isccc_alist_lookup(message, "_ctrl");
783 isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
784 isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
785 return (ISC_R_FAILURE);
787 * _frm and _to are optional.
789 if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
793 if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
798 * Ensure there is no newline in any of the strings. This is so
799 * we can write them to a file later.
801 if (has_whitespace(_frm) || has_whitespace(_to) ||
802 has_whitespace(_ser) || has_whitespace(_tim))
803 return (ISC_R_FAILURE);
804 len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
807 return (ISC_R_NOMEMORY);
808 snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
809 value.as_uinteger = now;
810 result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
811 isccc_symexists_reject);
812 if (result != ISC_R_SUCCESS) {
817 return (ISC_R_SUCCESS);