]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/bind9/lib/isccc/cc.c
Fix multiple vulnerabilities of BIND. [SA-16:13]
[FreeBSD/releng/9.3.git] / contrib / bind9 / lib / isccc / cc.c
1 /*
2  * Portions Copyright (C) 2004-2007, 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
3  * Portions Copyright (C) 2001-2003  Internet Software Consortium.
4  *
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.
8  *
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.
16  *
17  * Portions Copyright (C) 2001  Nominum, Inc.
18  *
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.
22  *
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.
30  */
31
32 /* $Id: cc.c,v 1.18 2007/08/28 07:20:43 tbox Exp $ */
33
34 /*! \file */
35
36 #include <config.h>
37
38 #include <stdio.h>
39 #include <string.h>
40 #include <errno.h>
41
42 #include <isc/assertions.h>
43 #include <isc/hmacmd5.h>
44 #include <isc/print.h>
45 #include <isc/safe.h>
46 #include <isc/stdlib.h>
47
48 #include <isccc/alist.h>
49 #include <isccc/base64.h>
50 #include <isccc/cc.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>
56
57 #define MAX_TAGS                256
58 #define DUP_LIFETIME            900
59
60 typedef isccc_sexpr_t *sexpr_ptr;
61
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 */
69         /*
70          * The base64 encoding of one of our HMAC-MD5 signatures is
71          * 22 bytes.
72          */
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
76 };
77
78 #define HMD5_OFFSET     21              /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
79 #define HMD5_LENGTH     22
80
81 static isc_result_t
82 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
83
84 static isc_result_t
85 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
86
87 static isc_result_t
88 value_towire(isccc_sexpr_t *elt, isccc_region_t *target)
89 {
90         unsigned int len;
91         unsigned char *lenp;
92         isccc_region_t *vr;
93         isc_result_t result;
94
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);
109                 /*
110                  * Emit a placeholder length.
111                  */
112                 lenp = target->rstart;
113                 PUT32(0, target->rstart);
114                 /*
115                  * Emit the table.
116                  */
117                 result = table_towire(elt, target);
118                 if (result != ISC_R_SUCCESS)
119                         return (result);
120                 len = (unsigned int)(target->rstart - lenp);
121                 /*
122                  * 'len' is 4 bytes too big, since it counts
123                  * the placeholder length too.  Adjust and
124                  * emit.
125                  */
126                 INSIST(len >= 4U);
127                 len -= 4;
128                 PUT32(len, lenp);
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);
133                 /*
134                  * Emit a placeholder length and count.
135                  */
136                 lenp = target->rstart;
137                 PUT32(0, target->rstart);
138                 /*
139                  * Emit the list.
140                  */
141                 result = list_towire(elt, target);
142                 if (result != ISC_R_SUCCESS)
143                         return (result);
144                 len = (unsigned int)(target->rstart - lenp);
145                 /*
146                  * 'len' is 4 bytes too big, since it counts
147                  * the placeholder length.  Adjust and emit.
148                  */
149                 INSIST(len >= 4U);
150                 len -= 4;
151                 PUT32(len, lenp);
152         }
153
154         return (ISC_R_SUCCESS);
155 }
156
157 static isc_result_t
158 table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
159 {
160         isccc_sexpr_t *kv, *elt, *k, *v;
161         char *ks;
162         isc_result_t result;
163         size_t len;
164
165         for (elt = isccc_alist_first(alist);
166              elt != NULL;
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);
172                 len = strlen(ks);
173                 INSIST(len <= 255U);
174                 /*
175                  * Emit the key name.
176                  */
177                 if (REGION_SIZE(*target) < 1 + len)
178                         return (ISC_R_NOSPACE);
179                 PUT8(len, target->rstart);
180                 PUT_MEM(ks, len, target->rstart);
181                 /*
182                  * Emit the value.
183                  */
184                 result = value_towire(v, target);
185                 if (result != ISC_R_SUCCESS)
186                         return (result);
187         }
188
189         return (ISC_R_SUCCESS);
190 }
191
192 static isc_result_t
193 list_towire(isccc_sexpr_t *list, isccc_region_t *target)
194 {
195         isc_result_t result;
196
197         while (list != NULL) {
198                 result = value_towire(ISCCC_SEXPR_CAR(list), target);
199                 if (result != ISC_R_SUCCESS)
200                         return (result);
201                 list = ISCCC_SEXPR_CDR(list);
202         }
203
204         return (ISC_R_SUCCESS);
205 }
206
207 static isc_result_t
208 sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
209      isccc_region_t *secret)
210 {
211         isc_hmacmd5_t ctx;
212         isc_result_t result;
213         isccc_region_t source, target;
214         unsigned char digest[ISC_MD5_DIGESTLENGTH];
215         unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
216
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)
226                 return (result);
227         PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
228
229         return (ISC_R_SUCCESS);
230 }
231
232 isc_result_t
233 isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
234               isccc_region_t *secret)
235 {
236         unsigned char *hmd5_rstart, *signed_rstart;
237         isc_result_t result;
238
239         if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5))
240                 return (ISC_R_NOSPACE);
241         /*
242          * Emit protocol version.
243          */
244         PUT32(1, target->rstart);
245         if (secret != NULL) {
246                 /*
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.
250                  */
251                 hmd5_rstart = target->rstart + HMD5_OFFSET;
252                 PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart);
253         } else
254                 hmd5_rstart = NULL;
255         signed_rstart = target->rstart;
256         /*
257          * Delete any existing _auth section so that we don't try
258          * to encode it.
259          */
260         isccc_alist_delete(alist, "_auth");
261         /*
262          * Emit the message.
263          */
264         result = table_towire(alist, target);
265         if (result != ISC_R_SUCCESS)
266                 return (result);
267         if (secret != NULL)
268                 return (sign(signed_rstart,
269                              (unsigned int)(target->rstart - signed_rstart),
270                              hmd5_rstart, secret));
271         return (ISC_R_SUCCESS);
272 }
273
274 static isc_result_t
275 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
276        isccc_region_t *secret)
277 {
278         isc_hmacmd5_t ctx;
279         isccc_region_t source;
280         isccc_region_t target;
281         isc_result_t result;
282         isccc_sexpr_t *_auth, *hmd5;
283         unsigned char digest[ISC_MD5_DIGESTLENGTH];
284         unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
285
286         /*
287          * Extract digest.
288          */
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);
295         /*
296          * Compute digest.
297          */
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)
307                 return (result);
308         /*
309          * Strip trailing == and NUL terminate target.
310          */
311         target.rstart -= 2;
312         *target.rstart++ = '\0';
313         /*
314          * Verify.
315          */
316         if (!isc_safe_memcmp((unsigned char *) isccc_sexpr_tostring(hmd5),
317                              digestb64, HMD5_LENGTH))
318                 return (ISCCC_R_BADAUTH);
319
320         return (ISC_R_SUCCESS);
321 }
322
323 static isc_result_t
324 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
325                isccc_sexpr_t **alistp);
326
327 static isc_result_t
328 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
329
330 static isc_result_t
331 value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep)
332 {
333         unsigned int msgtype;
334         isc_uint32_t len;
335         isccc_sexpr_t *value;
336         isccc_region_t active;
337         isc_result_t result;
338
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);
350                 if (value != NULL) {
351                         *valuep = value;
352                         result = ISC_R_SUCCESS;
353                 } else
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);
359         else
360                 result = ISCCC_R_SYNTAX;
361
362         return (result);
363 }
364
365 static isc_result_t
366 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
367                isccc_sexpr_t **alistp)
368 {
369         char key[256];
370         isc_uint32_t len;
371         isc_result_t result;
372         isccc_sexpr_t *alist, *value;
373         isc_boolean_t first_tag;
374         unsigned char *checksum_rstart;
375
376         REQUIRE(alistp != NULL && *alistp == NULL);
377
378         checksum_rstart = NULL;
379         first_tag = ISC_TRUE;
380         alist = isccc_alist_create();
381         if (alist == NULL)
382                 return (ISC_R_NOMEMORY);
383
384         while (!REGION_EMPTY(*source)) {
385                 GET8(len, source->rstart);
386                 if (REGION_SIZE(*source) < len) {
387                         result = ISC_R_UNEXPECTEDEND;
388                         goto bad;
389                 }
390                 GET_MEM(key, len, source->rstart);
391                 key[len] = '\0';        /* Ensure NUL termination. */
392                 value = NULL;
393                 result = value_fromwire(source, &value);
394                 if (result != ISC_R_SUCCESS)
395                         goto bad;
396                 if (isccc_alist_define(alist, key, value) == NULL) {
397                         result = ISC_R_NOMEMORY;
398                         goto bad;
399                 }
400                 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
401                         checksum_rstart = source->rstart;
402                 first_tag = ISC_FALSE;
403         }
404
405         if (secret != NULL) {
406                 if (checksum_rstart != NULL)
407                         result = verify(alist, checksum_rstart,
408                                         (unsigned int)
409                                         (source->rend - checksum_rstart),
410                                         secret);
411                 else
412                         result = ISCCC_R_BADAUTH;
413         } else
414                 result = ISC_R_SUCCESS;
415
416  bad:
417         if (result == ISC_R_SUCCESS)
418                 *alistp = alist;
419         else
420                 isccc_sexpr_free(&alist);
421
422         return (result);
423 }
424
425 static isc_result_t
426 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
427 {
428         isccc_sexpr_t *list, *value;
429         isc_result_t result;
430
431         list = NULL;
432         while (!REGION_EMPTY(*source)) {
433                 value = NULL;
434                 result = value_fromwire(source, &value);
435                 if (result != ISC_R_SUCCESS) {
436                         isccc_sexpr_free(&list);
437                         return (result);
438                 }
439                 if (isccc_sexpr_addtolist(&list, value) == NULL) {
440                         isccc_sexpr_free(&value);
441                         isccc_sexpr_free(&list);
442                         return (result);
443                 }
444         }
445
446         *listp = list;
447
448         return (ISC_R_SUCCESS);
449 }
450
451 isc_result_t
452 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
453                 isccc_region_t *secret)
454 {
455         unsigned int size;
456         isc_uint32_t version;
457
458         size = REGION_SIZE(*source);
459         if (size < 4)
460                 return (ISC_R_UNEXPECTEDEND);
461         GET32(version, source->rstart);
462         if (version != 1)
463                 return (ISCCC_R_UNKNOWNVERSION);
464
465         return (table_fromwire(source, secret, alistp));
466 }
467
468 static isc_result_t
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)
473 {
474         isccc_sexpr_t *alist, *_ctrl, *_data;
475         isc_result_t result;
476
477         REQUIRE(alistp != NULL && *alistp == NULL);
478
479         if (version != 1)
480                 return (ISCCC_R_UNKNOWNVERSION);
481
482         alist = isccc_alist_create();
483         if (alist == NULL)
484                 return (ISC_R_NOMEMORY);
485
486         result = ISC_R_NOMEMORY;
487
488         _ctrl = isccc_alist_create();
489         if (_ctrl == NULL)
490                 goto bad;
491         if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
492                 isccc_sexpr_free(&_ctrl);
493                 goto bad;
494         }
495
496         _data = isccc_alist_create();
497         if (_data == NULL)
498                 goto bad;
499         if (isccc_alist_define(alist, "_data", _data) == NULL) {
500                 isccc_sexpr_free(&_data);
501                 goto bad;
502         }
503
504         if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
505             isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
506             (want_expires &&
507              isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
508                 goto bad;
509         if (from != NULL &&
510             isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
511                 goto bad;
512         if (to != NULL &&
513             isccc_cc_definestring(_ctrl, "_to", to) == NULL)
514                 goto bad;
515
516         *alistp = alist;
517
518         return (ISC_R_SUCCESS);
519
520  bad:
521         isccc_sexpr_free(&alist);
522
523         return (result);
524 }
525
526 isc_result_t
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)
530 {
531         return (createmessage(version, from, to, serial, now, expires,
532                               alistp, ISC_TRUE));
533 }
534
535 isc_result_t
536 isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
537                  isccc_sexpr_t **ackp)
538 {
539         char *_frm, *_to;
540         isc_uint32_t serial;
541         isccc_sexpr_t *ack, *_ctrl;
542         isc_result_t result;
543         isccc_time_t t;
544
545         REQUIRE(ackp != NULL && *ackp == NULL);
546
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);
552         /*
553          * _frm and _to are optional.
554          */
555         _frm = NULL;
556         (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
557         _to = NULL;
558         (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
559         /*
560          * Create the ack.
561          */
562         ack = NULL;
563         result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
564         if (result != ISC_R_SUCCESS)
565                 return (result);
566
567         _ctrl = isccc_alist_lookup(ack, "_ctrl");
568         if (_ctrl == NULL) {
569                 result = ISC_R_FAILURE;
570                 goto bad;
571         }
572         if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
573                 result = ISC_R_NOMEMORY;
574                 goto bad;
575         }
576
577         *ackp = ack;
578
579         return (ISC_R_SUCCESS);
580
581  bad:
582         isccc_sexpr_free(&ack);
583
584         return (result);
585 }
586
587 isc_boolean_t
588 isccc_cc_isack(isccc_sexpr_t *message)
589 {
590         isccc_sexpr_t *_ctrl;
591
592         _ctrl = isccc_alist_lookup(message, "_ctrl");
593         if (!isccc_alist_alistp(_ctrl))
594                 return (ISC_FALSE);
595         if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
596                 return (ISC_TRUE);
597         return (ISC_FALSE);
598 }
599
600 isc_boolean_t
601 isccc_cc_isreply(isccc_sexpr_t *message)
602 {
603         isccc_sexpr_t *_ctrl;
604
605         _ctrl = isccc_alist_lookup(message, "_ctrl");
606         if (!isccc_alist_alistp(_ctrl))
607                 return (ISC_FALSE);
608         if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
609                 return (ISC_TRUE);
610         return (ISC_FALSE);
611 }
612
613 isc_result_t
614 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
615                       isccc_time_t expires, isccc_sexpr_t **alistp)
616 {
617         char *_frm, *_to, *type = NULL;
618         isc_uint32_t serial;
619         isccc_sexpr_t *alist, *_ctrl, *_data;
620         isc_result_t result;
621
622         REQUIRE(alistp != NULL && *alistp == NULL);
623
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);
630         /*
631          * _frm and _to are optional.
632          */
633         _frm = NULL;
634         (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
635         _to = NULL;
636         (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
637         /*
638          * Create the response.
639          */
640         alist = NULL;
641         result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
642                                          &alist);
643         if (result != ISC_R_SUCCESS)
644                 return (result);
645
646         _ctrl = isccc_alist_lookup(alist, "_ctrl");
647         if (_ctrl == NULL) {
648                 result = ISC_R_FAILURE;
649                 goto bad;
650         }
651
652         _data = isccc_alist_lookup(alist, "_data");
653         if (_data == NULL) {
654                 result = ISC_R_FAILURE;
655                 goto bad;
656         }
657
658         if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
659             isccc_cc_definestring(_data, "type", type) == NULL)
660         {
661                 result = ISC_R_NOMEMORY;
662                 goto bad;
663         }
664
665         *alistp = alist;
666
667         return (ISC_R_SUCCESS);
668
669  bad:
670         isccc_sexpr_free(&alist);
671         return (result);
672 }
673
674 isccc_sexpr_t *
675 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
676 {
677         size_t len;
678         isccc_region_t r;
679
680         len = strlen(str);
681         DE_CONST(str, r.rstart);
682         r.rend = r.rstart + len;
683
684         return (isccc_alist_definebinary(alist, key, &r));
685 }
686
687 isccc_sexpr_t *
688 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i)
689 {
690         char b[100];
691         size_t len;
692         isccc_region_t r;
693
694         snprintf(b, sizeof(b), "%u", i);
695         len = strlen(b);
696         r.rstart = (unsigned char *)b;
697         r.rend = (unsigned char *)b + len;
698
699         return (isccc_alist_definebinary(alist, key, &r));
700 }
701
702 isc_result_t
703 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp)
704 {
705         isccc_sexpr_t *kv, *v;
706
707         REQUIRE(strp == NULL || *strp == NULL);
708
709         kv = isccc_alist_assq(alist, key);
710         if (kv != NULL) {
711                 v = ISCCC_SEXPR_CDR(kv);
712                 if (isccc_sexpr_binaryp(v)) {
713                         if (strp != NULL)
714                                 *strp = isccc_sexpr_tostring(v);
715                         return (ISC_R_SUCCESS);
716                 } else
717                         return (ISC_R_EXISTS);
718         }
719
720         return (ISC_R_NOTFOUND);
721 }
722
723 isc_result_t
724 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
725                        isc_uint32_t *uintp)
726 {
727         isccc_sexpr_t *kv, *v;
728
729         kv = isccc_alist_assq(alist, key);
730         if (kv != NULL) {
731                 v = ISCCC_SEXPR_CDR(kv);
732                 if (isccc_sexpr_binaryp(v)) {
733                         if (uintp != NULL)
734                                 *uintp = (isc_uint32_t)
735                                         strtoul(isccc_sexpr_tostring(v),
736                                                 NULL, 10);
737                         return (ISC_R_SUCCESS);
738                 } else
739                         return (ISC_R_EXISTS);
740         }
741
742         return (ISC_R_NOTFOUND);
743 }
744
745 static void
746 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
747                 void *arg)
748 {
749         UNUSED(type);
750         UNUSED(value);
751         UNUSED(arg);
752
753         free(key);
754 }
755
756 static isc_boolean_t
757 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value,
758              void *arg)
759 {
760         isccc_time_t *now;
761
762         UNUSED(key);
763         UNUSED(type);
764
765         now = arg;
766
767         if (*now < value.as_uinteger)
768                 return (ISC_FALSE);
769         if ((*now - value.as_uinteger) < DUP_LIFETIME)
770                 return (ISC_FALSE);
771         return (ISC_TRUE);
772 }
773
774 isc_result_t
775 isccc_cc_createsymtab(isccc_symtab_t **symtabp)
776 {
777         return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
778                                   symtabp));
779 }
780
781 void
782 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
783 {
784         isccc_symtab_foreach(symtab, symtab_clean, &now);
785 }
786
787 static isc_boolean_t
788 has_whitespace(const char *str)
789 {
790         char c;
791
792         if (str == NULL)
793                 return (ISC_FALSE);
794         while ((c = *str++) != '\0') {
795                 if (c == ' ' || c == '\t' || c == '\n')
796                         return (ISC_TRUE);
797         }
798         return (ISC_FALSE);
799 }
800
801 isc_result_t
802 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
803                 isccc_time_t now)
804 {
805         const char *_frm;
806         const char *_to;
807         char *_ser = NULL, *_tim = NULL, *tmp;
808         isc_result_t result;
809         char *key;
810         size_t len;
811         isccc_symvalue_t value;
812         isccc_sexpr_t *_ctrl;
813
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);
819
820         INSIST(_ser != NULL);
821         INSIST(_tim != NULL);
822
823         /*
824          * _frm and _to are optional.
825          */
826         tmp = NULL;
827         if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
828                 _frm = "";
829         else
830                 _frm = tmp;
831         tmp = NULL;
832         if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
833                 _to = "";
834         else
835                 _to = tmp;
836         /*
837          * Ensure there is no newline in any of the strings.  This is so
838          * we can write them to a file later.
839          */
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;
844         key = malloc(len);
845         if (key == NULL)
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) {
852                 free(key);
853                 return (result);
854         }
855
856         return (ISC_R_SUCCESS);
857 }