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.3.2.5 2004/08/28 06:25:23 marka Exp $ */
27 #include <isc/assertions.h>
28 #include <isc/hmacmd5.h>
29 #include <isc/print.h>
30 #include <isc/stdlib.h>
32 #include <isccc/alist.h>
33 #include <isccc/base64.h>
35 #include <isccc/result.h>
36 #include <isccc/sexpr.h>
37 #include <isccc/symtab.h>
38 #include <isccc/symtype.h>
39 #include <isccc/util.h>
42 #define DUP_LIFETIME 900
44 typedef isccc_sexpr_t *sexpr_ptr;
46 static unsigned char auth_hmd5[] = {
47 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /* len + _auth */
48 ISCCC_CCMSGTYPE_TABLE, /* message type */
49 0x00, 0x00, 0x00, 0x20, /* length == 32 */
50 0x04, 0x68, 0x6d, 0x64, 0x35, /* len + hmd5 */
51 ISCCC_CCMSGTYPE_BINARYDATA, /* message type */
52 0x00, 0x00, 0x00, 0x16, /* length == 22 */
54 * The base64 encoding of one of our HMAC-MD5 signatures is
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
62 #define HMD5_OFFSET 21 /* 6 + 1 + 4 + 5 + 1 + 4 */
63 #define HMD5_LENGTH 22
66 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
69 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
72 value_towire(isccc_sexpr_t *elt, isccc_region_t *target)
79 if (isccc_sexpr_binaryp(elt)) {
80 vr = isccc_sexpr_tobinary(elt);
81 len = REGION_SIZE(*vr);
82 if (REGION_SIZE(*target) < 1 + 4 + len)
83 return (ISC_R_NOSPACE);
84 PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart);
85 PUT32(len, target->rstart);
86 if (REGION_SIZE(*target) < len)
87 return (ISC_R_NOSPACE);
88 PUT_MEM(vr->rstart, len, target->rstart);
89 } else if (isccc_alist_alistp(elt)) {
90 if (REGION_SIZE(*target) < 1 + 4)
91 return (ISC_R_NOSPACE);
92 PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart);
94 * Emit a placeholder length.
96 lenp = target->rstart;
97 PUT32(0, target->rstart);
101 result = table_towire(elt, target);
102 if (result != ISC_R_SUCCESS)
104 len = (size_t)(target->rstart - lenp);
106 * 'len' is 4 bytes too big, since it counts
107 * the placeholder length too. Adjust and
113 } else if (isccc_sexpr_listp(elt)) {
114 if (REGION_SIZE(*target) < 1 + 4)
115 return (ISC_R_NOSPACE);
116 PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart);
118 * Emit a placeholder length and count.
120 lenp = target->rstart;
121 PUT32(0, target->rstart);
125 result = list_towire(elt, target);
126 if (result != ISC_R_SUCCESS)
128 len = (size_t)(target->rstart - lenp);
130 * 'len' is 4 bytes too big, since it counts
131 * the placeholder length. Adjust and emit.
138 return (ISC_R_SUCCESS);
142 table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
144 isccc_sexpr_t *kv, *elt, *k, *v;
149 for (elt = isccc_alist_first(alist);
151 elt = ISCCC_SEXPR_CDR(elt)) {
152 kv = ISCCC_SEXPR_CAR(elt);
153 k = ISCCC_SEXPR_CAR(kv);
154 ks = isccc_sexpr_tostring(k);
155 v = ISCCC_SEXPR_CDR(kv);
161 if (REGION_SIZE(*target) < 1 + len)
162 return (ISC_R_NOSPACE);
163 PUT8(len, target->rstart);
164 PUT_MEM(ks, len, target->rstart);
168 result = value_towire(v, target);
169 if (result != ISC_R_SUCCESS)
173 return (ISC_R_SUCCESS);
177 list_towire(isccc_sexpr_t *list, isccc_region_t *target)
181 while (list != NULL) {
182 result = value_towire(ISCCC_SEXPR_CAR(list), target);
183 if (result != ISC_R_SUCCESS)
185 list = ISCCC_SEXPR_CDR(list);
188 return (ISC_R_SUCCESS);
192 sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
193 isccc_region_t *secret)
197 isccc_region_t source, target;
198 unsigned char digest[ISC_MD5_DIGESTLENGTH];
199 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
201 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
202 isc_hmacmd5_update(&ctx, data, length);
203 isc_hmacmd5_sign(&ctx, digest);
204 source.rstart = digest;
205 source.rend = digest + ISC_MD5_DIGESTLENGTH;
206 target.rstart = digestb64;
207 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
208 result = isccc_base64_encode(&source, 64, "", &target);
209 if (result != ISC_R_SUCCESS)
211 PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
213 return (ISC_R_SUCCESS);
217 isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
218 isccc_region_t *secret)
220 unsigned char *hmd5_rstart, *signed_rstart;
223 if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5))
224 return (ISC_R_NOSPACE);
226 * Emit protocol version.
228 PUT32(1, target->rstart);
229 if (secret != NULL) {
231 * Emit _auth section with zeroed HMAC-MD5 signature.
232 * We'll replace the zeros with the real signature once
233 * we know what it is.
235 hmd5_rstart = target->rstart + HMD5_OFFSET;
236 PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart);
239 signed_rstart = target->rstart;
241 * Delete any existing _auth section so that we don't try
244 isccc_alist_delete(alist, "_auth");
248 result = table_towire(alist, target);
249 if (result != ISC_R_SUCCESS)
252 return (sign(signed_rstart, (target->rstart - signed_rstart),
253 hmd5_rstart, secret));
254 return (ISC_R_SUCCESS);
258 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
259 isccc_region_t *secret)
262 isccc_region_t source;
263 isccc_region_t target;
265 isccc_sexpr_t *_auth, *hmd5;
266 unsigned char digest[ISC_MD5_DIGESTLENGTH];
267 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
272 _auth = isccc_alist_lookup(alist, "_auth");
274 return (ISC_R_FAILURE);
275 hmd5 = isccc_alist_lookup(_auth, "hmd5");
277 return (ISC_R_FAILURE);
281 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
282 isc_hmacmd5_update(&ctx, data, length);
283 isc_hmacmd5_sign(&ctx, digest);
284 source.rstart = digest;
285 source.rend = digest + ISC_MD5_DIGESTLENGTH;
286 target.rstart = digestb64;
287 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
288 result = isccc_base64_encode(&source, 64, "", &target);
289 if (result != ISC_R_SUCCESS)
292 * Strip trailing == and NUL terminate target.
295 *target.rstart++ = '\0';
299 if (strcmp((char *)digestb64, isccc_sexpr_tostring(hmd5)) != 0)
300 return (ISCCC_R_BADAUTH);
302 return (ISC_R_SUCCESS);
306 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
307 isccc_sexpr_t **alistp);
310 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
313 value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep)
315 unsigned int msgtype;
317 isccc_sexpr_t *value;
318 isccc_region_t active;
321 if (REGION_SIZE(*source) < 1 + 4)
322 return (ISC_R_UNEXPECTEDEND);
323 GET8(msgtype, source->rstart);
324 GET32(len, source->rstart);
325 if (REGION_SIZE(*source) < len)
326 return (ISC_R_UNEXPECTEDEND);
327 active.rstart = source->rstart;
328 active.rend = active.rstart + len;
329 source->rstart = active.rend;
330 if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
331 value = isccc_sexpr_frombinary(&active);
334 result = ISC_R_SUCCESS;
336 result = ISC_R_NOMEMORY;
337 } else if (msgtype == ISCCC_CCMSGTYPE_TABLE)
338 result = table_fromwire(&active, NULL, valuep);
339 else if (msgtype == ISCCC_CCMSGTYPE_LIST)
340 result = list_fromwire(&active, valuep);
342 result = ISCCC_R_SYNTAX;
348 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
349 isccc_sexpr_t **alistp)
354 isccc_sexpr_t *alist, *value;
355 isc_boolean_t first_tag;
356 unsigned char *checksum_rstart;
358 REQUIRE(alistp != NULL && *alistp == NULL);
360 checksum_rstart = NULL;
361 first_tag = ISC_TRUE;
362 alist = isccc_alist_create();
364 return (ISC_R_NOMEMORY);
366 while (!REGION_EMPTY(*source)) {
367 GET8(len, source->rstart);
368 if (REGION_SIZE(*source) < len) {
369 result = ISC_R_UNEXPECTEDEND;
372 GET_MEM(key, len, source->rstart);
373 key[len] = '\0'; /* Ensure NUL termination. */
375 result = value_fromwire(source, &value);
376 if (result != ISC_R_SUCCESS)
378 if (isccc_alist_define(alist, key, value) == NULL) {
379 result = ISC_R_NOMEMORY;
382 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
383 checksum_rstart = source->rstart;
384 first_tag = ISC_FALSE;
389 if (secret != NULL) {
390 if (checksum_rstart != NULL)
391 return (verify(alist, checksum_rstart,
392 (source->rend - checksum_rstart),
394 return (ISCCC_R_BADAUTH);
397 return (ISC_R_SUCCESS);
400 isccc_sexpr_free(&alist);
406 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
408 isccc_sexpr_t *list, *value;
412 while (!REGION_EMPTY(*source)) {
414 result = value_fromwire(source, &value);
415 if (result != ISC_R_SUCCESS) {
416 isccc_sexpr_free(&list);
419 if (isccc_sexpr_addtolist(&list, value) == NULL) {
420 isccc_sexpr_free(&value);
421 isccc_sexpr_free(&list);
428 return (ISC_R_SUCCESS);
432 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
433 isccc_region_t *secret)
436 isc_uint32_t version;
438 size = REGION_SIZE(*source);
440 return (ISC_R_UNEXPECTEDEND);
441 GET32(version, source->rstart);
443 return (ISCCC_R_UNKNOWNVERSION);
445 return (table_fromwire(source, secret, alistp));
449 createmessage(isc_uint32_t version, const char *from, const char *to,
450 isc_uint32_t serial, isccc_time_t now,
451 isccc_time_t expires, isccc_sexpr_t **alistp,
452 isc_boolean_t want_expires)
454 isccc_sexpr_t *alist, *_ctrl, *_data;
457 REQUIRE(alistp != NULL && *alistp == NULL);
460 return (ISCCC_R_UNKNOWNVERSION);
462 alist = isccc_alist_create();
464 return (ISC_R_NOMEMORY);
466 result = ISC_R_NOMEMORY;
468 _ctrl = isccc_alist_create();
469 _data = isccc_alist_create();
470 if (_ctrl == NULL || _data == NULL)
472 if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL ||
473 isccc_alist_define(alist, "_data", _data) == NULL)
475 if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
476 isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
478 isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
481 isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
484 isccc_cc_definestring(_ctrl, "_to", to) == NULL)
489 return (ISC_R_SUCCESS);
492 isccc_sexpr_free(&alist);
498 isccc_cc_createmessage(isc_uint32_t version, const char *from, const char *to,
499 isc_uint32_t serial, isccc_time_t now,
500 isccc_time_t expires, isccc_sexpr_t **alistp)
502 return (createmessage(version, from, to, serial, now, expires,
507 isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
508 isccc_sexpr_t **ackp)
512 isccc_sexpr_t *ack, *_ctrl;
516 REQUIRE(ackp != NULL && *ackp == NULL);
518 _ctrl = isccc_alist_lookup(message, "_ctrl");
520 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
521 isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
522 return (ISC_R_FAILURE);
524 * _frm and _to are optional.
527 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
529 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
534 result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
535 if (result != ISC_R_SUCCESS)
538 _ctrl = isccc_alist_lookup(ack, "_ctrl");
540 return (ISC_R_FAILURE);
541 if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
542 result = ISC_R_NOMEMORY;
548 return (ISC_R_SUCCESS);
551 isccc_sexpr_free(&ack);
557 isccc_cc_isack(isccc_sexpr_t *message)
559 isccc_sexpr_t *_ctrl;
561 _ctrl = isccc_alist_lookup(message, "_ctrl");
564 if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
570 isccc_cc_isreply(isccc_sexpr_t *message)
572 isccc_sexpr_t *_ctrl;
574 _ctrl = isccc_alist_lookup(message, "_ctrl");
577 if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
583 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
584 isccc_time_t expires, isccc_sexpr_t **alistp)
586 char *_frm, *_to, *type;
588 isccc_sexpr_t *alist, *_ctrl, *_data;
591 REQUIRE(alistp != NULL && *alistp == NULL);
593 _ctrl = isccc_alist_lookup(message, "_ctrl");
594 _data = isccc_alist_lookup(message, "_data");
597 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
598 isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
599 return (ISC_R_FAILURE);
601 * _frm and _to are optional.
604 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
606 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
608 * Create the response.
611 result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
613 if (result != ISC_R_SUCCESS)
615 _ctrl = isccc_alist_lookup(alist, "_ctrl");
617 return (ISC_R_FAILURE);
618 _data = isccc_alist_lookup(alist, "_data");
620 return (ISC_R_FAILURE);
621 if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
622 isccc_cc_definestring(_data, "type", type) == NULL) {
623 isccc_sexpr_free(&alist);
624 return (ISC_R_NOMEMORY);
629 return (ISC_R_SUCCESS);
633 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
639 DE_CONST(str, r.rstart);
640 r.rend = r.rstart + len;
642 return (isccc_alist_definebinary(alist, key, &r));
646 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i)
652 snprintf(b, sizeof(b), "%u", i);
654 r.rstart = (unsigned char *)b;
655 r.rend = (unsigned char *)b + len;
657 return (isccc_alist_definebinary(alist, key, &r));
661 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp)
663 isccc_sexpr_t *kv, *v;
665 kv = isccc_alist_assq(alist, key);
667 v = ISCCC_SEXPR_CDR(kv);
668 if (isccc_sexpr_binaryp(v)) {
670 *strp = isccc_sexpr_tostring(v);
671 return (ISC_R_SUCCESS);
673 return (ISC_R_EXISTS);
676 return (ISC_R_NOTFOUND);
680 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
683 isccc_sexpr_t *kv, *v;
685 kv = isccc_alist_assq(alist, key);
687 v = ISCCC_SEXPR_CDR(kv);
688 if (isccc_sexpr_binaryp(v)) {
690 *uintp = (isc_uint32_t)
691 strtoul(isccc_sexpr_tostring(v),
693 return (ISC_R_SUCCESS);
695 return (ISC_R_EXISTS);
698 return (ISC_R_NOTFOUND);
702 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
713 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value,
723 if (*now < value.as_uinteger)
725 if ((*now - value.as_uinteger) < DUP_LIFETIME)
731 isccc_cc_createsymtab(isccc_symtab_t **symtabp)
733 return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
738 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
740 isccc_symtab_foreach(symtab, symtab_clean, &now);
744 has_whitespace(const char *str)
750 while ((c = *str++) != '\0') {
751 if (c == ' ' || c == '\t' || c == '\n')
758 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
763 char *_ser, *_tim, *tmp;
767 isccc_symvalue_t value;
768 isccc_sexpr_t *_ctrl;
770 _ctrl = isccc_alist_lookup(message, "_ctrl");
772 isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
773 isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
774 return (ISC_R_FAILURE);
776 * _frm and _to are optional.
778 if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
782 if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
787 * Ensure there is no newline in any of the strings. This is so
788 * we can write them to a file later.
790 if (has_whitespace(_frm) || has_whitespace(_to) ||
791 has_whitespace(_ser) || has_whitespace(_tim))
792 return (ISC_R_FAILURE);
793 len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
796 return (ISC_R_NOMEMORY);
797 snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
798 value.as_uinteger = now;
799 result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
800 isccc_symexists_reject);
801 if (result != ISC_R_SUCCESS) {
806 return (ISC_R_SUCCESS);