2 * Portions Copyright (C) 2004 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.4.2.5 2004/03/09 06:12:25 marka Exp $ */
28 #include <isccc/alist.h>
29 #include <isc/assertions.h>
30 #include <isccc/base64.h>
32 #include <isc/hmacmd5.h>
33 #include <isccc/result.h>
34 #include <isccc/sexpr.h>
35 #include <isccc/symtab.h>
36 #include <isccc/symtype.h>
37 #include <isccc/util.h>
40 #define DUP_LIFETIME 900
42 typedef isccc_sexpr_t *sexpr_ptr;
44 static unsigned char auth_hmd5[] = {
45 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /* len + _auth */
46 ISCCC_CCMSGTYPE_TABLE, /* message type */
47 0x00, 0x00, 0x00, 0x20, /* length == 32 */
48 0x04, 0x68, 0x6d, 0x64, 0x35, /* len + hmd5 */
49 ISCCC_CCMSGTYPE_BINARYDATA, /* message type */
50 0x00, 0x00, 0x00, 0x16, /* length == 22 */
52 * The base64 encoding of one of our HMAC-MD5 signatures is
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
60 #define HMD5_OFFSET 21 /* 6 + 1 + 4 + 5 + 1 + 4 */
61 #define HMD5_LENGTH 22
64 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
67 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
70 value_towire(isccc_sexpr_t *elt, isccc_region_t *target)
77 if (isccc_sexpr_binaryp(elt)) {
78 vr = isccc_sexpr_tobinary(elt);
79 len = REGION_SIZE(*vr);
80 if (REGION_SIZE(*target) < 1 + 4 + len)
81 return (ISC_R_NOSPACE);
82 PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart);
83 PUT32(len, target->rstart);
84 if (REGION_SIZE(*target) < len)
85 return (ISC_R_NOSPACE);
86 PUT_MEM(vr->rstart, len, target->rstart);
87 } else if (isccc_alist_alistp(elt)) {
88 if (REGION_SIZE(*target) < 1 + 4)
89 return (ISC_R_NOSPACE);
90 PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart);
92 * Emit a placeholder length.
94 lenp = target->rstart;
95 PUT32(0, target->rstart);
99 result = table_towire(elt, target);
100 if (result != ISC_R_SUCCESS)
102 len = (size_t)(target->rstart - lenp);
104 * 'len' is 4 bytes too big, since it counts
105 * the placeholder length too. Adjust and
111 } else if (isccc_sexpr_listp(elt)) {
112 if (REGION_SIZE(*target) < 1 + 4)
113 return (ISC_R_NOSPACE);
114 PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart);
116 * Emit a placeholder length and count.
118 lenp = target->rstart;
119 PUT32(0, target->rstart);
123 result = list_towire(elt, target);
124 if (result != ISC_R_SUCCESS)
126 len = (size_t)(target->rstart - lenp);
128 * 'len' is 4 bytes too big, since it counts
129 * the placeholder length. Adjust and emit.
136 return (ISC_R_SUCCESS);
140 table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
142 isccc_sexpr_t *kv, *elt, *k, *v;
147 for (elt = isccc_alist_first(alist);
149 elt = ISCCC_SEXPR_CDR(elt)) {
150 kv = ISCCC_SEXPR_CAR(elt);
151 k = ISCCC_SEXPR_CAR(kv);
152 ks = isccc_sexpr_tostring(k);
153 v = ISCCC_SEXPR_CDR(kv);
159 if (REGION_SIZE(*target) < 1 + len)
160 return (ISC_R_NOSPACE);
161 PUT8(len, target->rstart);
162 PUT_MEM(ks, len, target->rstart);
166 result = value_towire(v, target);
167 if (result != ISC_R_SUCCESS)
171 return (ISC_R_SUCCESS);
175 list_towire(isccc_sexpr_t *list, isccc_region_t *target)
179 while (list != NULL) {
180 result = value_towire(ISCCC_SEXPR_CAR(list), target);
181 if (result != ISC_R_SUCCESS)
183 list = ISCCC_SEXPR_CDR(list);
186 return (ISC_R_SUCCESS);
190 sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
191 isccc_region_t *secret)
195 isccc_region_t source, target;
196 unsigned char digest[ISC_MD5_DIGESTLENGTH];
197 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
199 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
200 isc_hmacmd5_update(&ctx, data, length);
201 isc_hmacmd5_sign(&ctx, digest);
202 source.rstart = digest;
203 source.rend = digest + ISC_MD5_DIGESTLENGTH;
204 target.rstart = digestb64;
205 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
206 result = isccc_base64_encode(&source, 64, "", &target);
207 if (result != ISC_R_SUCCESS)
209 PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
211 return (ISC_R_SUCCESS);
215 isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
216 isccc_region_t *secret)
218 unsigned char *hmd5_rstart, *signed_rstart;
221 if (REGION_SIZE(*target) < 4 + sizeof auth_hmd5)
222 return (ISC_R_NOSPACE);
224 * Emit protocol version.
226 PUT32(1, target->rstart);
227 if (secret != NULL) {
229 * Emit _auth section with zeroed HMAC-MD5 signature.
230 * We'll replace the zeros with the real signature once
231 * we know what it is.
233 hmd5_rstart = target->rstart + HMD5_OFFSET;
234 PUT_MEM(auth_hmd5, sizeof auth_hmd5, target->rstart);
237 signed_rstart = target->rstart;
239 * Delete any existing _auth section so that we don't try
242 isccc_alist_delete(alist, "_auth");
246 result = table_towire(alist, target);
247 if (result != ISC_R_SUCCESS)
250 return (sign(signed_rstart, (target->rstart - signed_rstart),
251 hmd5_rstart, secret));
252 return (ISC_R_SUCCESS);
256 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
257 isccc_region_t *secret)
260 isccc_region_t source;
261 isccc_region_t target;
263 isccc_sexpr_t *_auth, *hmd5;
264 unsigned char digest[ISC_MD5_DIGESTLENGTH];
265 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
270 _auth = isccc_alist_lookup(alist, "_auth");
272 return (ISC_R_FAILURE);
273 hmd5 = isccc_alist_lookup(_auth, "hmd5");
275 return (ISC_R_FAILURE);
279 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
280 isc_hmacmd5_update(&ctx, data, length);
281 isc_hmacmd5_sign(&ctx, digest);
282 source.rstart = digest;
283 source.rend = digest + ISC_MD5_DIGESTLENGTH;
284 target.rstart = digestb64;
285 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
286 result = isccc_base64_encode(&source, 64, "", &target);
287 if (result != ISC_R_SUCCESS)
290 * Strip trailing == and NUL terminate target.
293 *target.rstart++ = '\0';
297 if (strcmp((char *)digestb64, isccc_sexpr_tostring(hmd5)) != 0)
298 return (ISCCC_R_BADAUTH);
300 return (ISC_R_SUCCESS);
304 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
305 isccc_sexpr_t **alistp);
308 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
311 value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep)
313 unsigned int msgtype;
315 isccc_sexpr_t *value;
316 isccc_region_t active;
319 if (REGION_SIZE(*source) < 1 + 4)
320 return (ISC_R_UNEXPECTEDEND);
321 GET8(msgtype, source->rstart);
322 GET32(len, source->rstart);
323 if (REGION_SIZE(*source) < len)
324 return (ISC_R_UNEXPECTEDEND);
325 active.rstart = source->rstart;
326 active.rend = active.rstart + len;
327 source->rstart = active.rend;
328 if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
329 value = isccc_sexpr_frombinary(&active);
332 result = ISC_R_SUCCESS;
334 result = ISC_R_NOMEMORY;
335 } else if (msgtype == ISCCC_CCMSGTYPE_TABLE)
336 result = table_fromwire(&active, NULL, valuep);
337 else if (msgtype == ISCCC_CCMSGTYPE_LIST)
338 result = list_fromwire(&active, valuep);
340 result = ISCCC_R_SYNTAX;
346 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
347 isccc_sexpr_t **alistp)
352 isccc_sexpr_t *alist, *value;
353 isc_boolean_t first_tag;
354 unsigned char *checksum_rstart;
356 REQUIRE(alistp != NULL && *alistp == NULL);
358 checksum_rstart = NULL;
359 first_tag = ISC_TRUE;
360 alist = isccc_alist_create();
362 return (ISC_R_NOMEMORY);
364 while (!REGION_EMPTY(*source)) {
365 GET8(len, source->rstart);
366 if (REGION_SIZE(*source) < len) {
367 result = ISC_R_UNEXPECTEDEND;
370 GET_MEM(key, len, source->rstart);
371 key[len] = '\0'; /* Ensure NUL termination. */
373 result = value_fromwire(source, &value);
374 if (result != ISC_R_SUCCESS)
376 if (isccc_alist_define(alist, key, value) == NULL) {
377 result = ISC_R_NOMEMORY;
380 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
381 checksum_rstart = source->rstart;
382 first_tag = ISC_FALSE;
387 if (secret != NULL) {
388 if (checksum_rstart != NULL)
389 return (verify(alist, checksum_rstart,
390 (source->rend - checksum_rstart),
392 return (ISCCC_R_BADAUTH);
395 return (ISC_R_SUCCESS);
398 isccc_sexpr_free(&alist);
404 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
406 isccc_sexpr_t *list, *value;
410 while (!REGION_EMPTY(*source)) {
412 result = value_fromwire(source, &value);
413 if (result != ISC_R_SUCCESS) {
414 isccc_sexpr_free(&list);
417 if (isccc_sexpr_addtolist(&list, value) == NULL) {
418 isccc_sexpr_free(&value);
419 isccc_sexpr_free(&list);
426 return (ISC_R_SUCCESS);
430 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
431 isccc_region_t *secret)
434 isc_uint32_t version;
436 size = REGION_SIZE(*source);
438 return (ISC_R_UNEXPECTEDEND);
439 GET32(version, source->rstart);
441 return (ISCCC_R_UNKNOWNVERSION);
443 return (table_fromwire(source, secret, alistp));
447 createmessage(isc_uint32_t version, const char *from, const char *to,
448 isc_uint32_t serial, isccc_time_t now,
449 isccc_time_t expires, isccc_sexpr_t **alistp,
450 isc_boolean_t want_expires)
452 isccc_sexpr_t *alist, *_ctrl, *_data;
455 REQUIRE(alistp != NULL && *alistp == NULL);
458 return (ISCCC_R_UNKNOWNVERSION);
460 alist = isccc_alist_create();
462 return (ISC_R_NOMEMORY);
464 result = ISC_R_NOMEMORY;
466 _ctrl = isccc_alist_create();
467 _data = isccc_alist_create();
468 if (_ctrl == NULL || _data == NULL)
470 if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL ||
471 isccc_alist_define(alist, "_data", _data) == NULL)
473 if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
474 isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
476 isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
479 isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
482 isccc_cc_definestring(_ctrl, "_to", to) == NULL)
487 return (ISC_R_SUCCESS);
490 isccc_sexpr_free(&alist);
496 isccc_cc_createmessage(isc_uint32_t version, const char *from, const char *to,
497 isc_uint32_t serial, isccc_time_t now,
498 isccc_time_t expires, isccc_sexpr_t **alistp)
500 return (createmessage(version, from, to, serial, now, expires,
505 isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
506 isccc_sexpr_t **ackp)
510 isccc_sexpr_t *ack, *_ctrl;
514 REQUIRE(ackp != NULL && *ackp == NULL);
516 _ctrl = isccc_alist_lookup(message, "_ctrl");
518 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
519 isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
520 return (ISC_R_FAILURE);
522 * _frm and _to are optional.
525 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
527 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
532 result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
533 if (result != ISC_R_SUCCESS)
536 _ctrl = isccc_alist_lookup(ack, "_ctrl");
538 return (ISC_R_FAILURE);
539 if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
540 result = ISC_R_NOMEMORY;
546 return (ISC_R_SUCCESS);
549 isccc_sexpr_free(&ack);
555 isccc_cc_isack(isccc_sexpr_t *message)
557 isccc_sexpr_t *_ctrl;
559 _ctrl = isccc_alist_lookup(message, "_ctrl");
562 if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
568 isccc_cc_isreply(isccc_sexpr_t *message)
570 isccc_sexpr_t *_ctrl;
572 _ctrl = isccc_alist_lookup(message, "_ctrl");
575 if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
581 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
582 isccc_time_t expires, isccc_sexpr_t **alistp)
584 char *_frm, *_to, *type;
586 isccc_sexpr_t *alist, *_ctrl, *_data;
589 REQUIRE(alistp != NULL && *alistp == NULL);
591 _ctrl = isccc_alist_lookup(message, "_ctrl");
592 _data = isccc_alist_lookup(message, "_data");
595 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
596 isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
597 return (ISC_R_FAILURE);
599 * _frm and _to are optional.
602 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
604 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
606 * Create the response.
609 result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
611 if (result != ISC_R_SUCCESS)
613 _ctrl = isccc_alist_lookup(alist, "_ctrl");
615 return (ISC_R_FAILURE);
616 _data = isccc_alist_lookup(alist, "_data");
618 return (ISC_R_FAILURE);
619 if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
620 isccc_cc_definestring(_data, "type", type) == NULL) {
621 isccc_sexpr_free(&alist);
622 return (ISC_R_NOMEMORY);
627 return (ISC_R_SUCCESS);
631 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
637 DE_CONST(str, r.rstart);
638 r.rend = r.rstart + len;
640 return (isccc_alist_definebinary(alist, key, &r));
644 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i)
652 r.rstart = (unsigned char *)b;
653 r.rend = (unsigned char *)b + len;
655 return (isccc_alist_definebinary(alist, key, &r));
659 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp)
661 isccc_sexpr_t *kv, *v;
663 kv = isccc_alist_assq(alist, key);
665 v = ISCCC_SEXPR_CDR(kv);
666 if (isccc_sexpr_binaryp(v)) {
668 *strp = isccc_sexpr_tostring(v);
669 return (ISC_R_SUCCESS);
671 return (ISC_R_EXISTS);
674 return (ISC_R_NOTFOUND);
678 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
681 isccc_sexpr_t *kv, *v;
683 kv = isccc_alist_assq(alist, key);
685 v = ISCCC_SEXPR_CDR(kv);
686 if (isccc_sexpr_binaryp(v)) {
688 *uintp = (isc_uint32_t)
689 strtoul(isccc_sexpr_tostring(v),
691 return (ISC_R_SUCCESS);
693 return (ISC_R_EXISTS);
696 return (ISC_R_NOTFOUND);
700 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
711 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value,
721 if (*now < value.as_uinteger)
723 if ((*now - value.as_uinteger) < DUP_LIFETIME)
729 isccc_cc_createsymtab(isccc_symtab_t **symtabp)
731 return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
736 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
738 isccc_symtab_foreach(symtab, symtab_clean, &now);
742 has_whitespace(const char *str)
748 while ((c = *str++) != '\0') {
749 if (c == ' ' || c == '\t' || c == '\n')
756 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
761 char *_ser, *_tim, *tmp;
765 isccc_symvalue_t value;
766 isccc_sexpr_t *_ctrl;
768 _ctrl = isccc_alist_lookup(message, "_ctrl");
770 isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
771 isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
772 return (ISC_R_FAILURE);
774 * _frm and _to are optional.
776 if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
780 if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
785 * Ensure there is no newline in any of the strings. This is so
786 * we can write them to a file later.
788 if (has_whitespace(_frm) || has_whitespace(_to) ||
789 has_whitespace(_ser) || has_whitespace(_tim))
790 return (ISC_R_FAILURE);
791 len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
794 return (ISC_R_NOMEMORY);
795 sprintf(key, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
796 value.as_uinteger = now;
797 result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
798 isccc_symexists_reject);
799 if (result != ISC_R_SUCCESS) {
804 return (ISC_R_SUCCESS);