cc.c revision 1.7 1 1.7 christos /* $NetBSD: cc.c,v 1.7 2023/01/25 21:43:32 christos Exp $ */
2 1.1 christos
3 1.1 christos /*
4 1.6 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 1.6 christos *
6 1.6 christos * SPDX-License-Identifier: MPL-2.0 AND ISC
7 1.1 christos *
8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public
9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this
10 1.5 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 1.1 christos *
12 1.1 christos * See the COPYRIGHT file distributed with this work for additional
13 1.1 christos * information regarding copyright ownership.
14 1.6 christos */
15 1.6 christos
16 1.6 christos /*
17 1.6 christos * Copyright (C) 2001 Nominum, Inc.
18 1.1 christos *
19 1.1 christos * Permission to use, copy, modify, and/or distribute this software for any
20 1.1 christos * purpose with or without fee is hereby granted, provided that the above
21 1.1 christos * copyright notice and this permission notice appear in all copies.
22 1.1 christos *
23 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
24 1.1 christos * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
25 1.1 christos * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
26 1.1 christos * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28 1.1 christos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
29 1.1 christos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 1.1 christos */
31 1.1 christos
32 1.1 christos /*! \file */
33 1.1 christos
34 1.4 christos #include <errno.h>
35 1.4 christos #include <inttypes.h>
36 1.3 christos #include <stdbool.h>
37 1.1 christos #include <stdio.h>
38 1.4 christos #include <stdlib.h>
39 1.1 christos #include <string.h>
40 1.1 christos
41 1.1 christos #include <isc/assertions.h>
42 1.3 christos #include <isc/hmac.h>
43 1.1 christos #include <isc/print.h>
44 1.1 christos #include <isc/safe.h>
45 1.1 christos
46 1.1 christos #include <pk11/site.h>
47 1.1 christos
48 1.1 christos #include <isccc/alist.h>
49 1.1 christos #include <isccc/base64.h>
50 1.1 christos #include <isccc/cc.h>
51 1.1 christos #include <isccc/result.h>
52 1.1 christos #include <isccc/sexpr.h>
53 1.1 christos #include <isccc/symtab.h>
54 1.1 christos #include <isccc/symtype.h>
55 1.1 christos #include <isccc/util.h>
56 1.1 christos
57 1.4 christos #define MAX_TAGS 256
58 1.4 christos #define DUP_LIFETIME 900
59 1.1 christos
60 1.1 christos typedef isccc_sexpr_t *sexpr_ptr;
61 1.1 christos
62 1.1 christos static unsigned char auth_hmd5[] = {
63 1.4 christos 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
64 1.4 christos ISCCC_CCMSGTYPE_TABLE, /*%< message type */
65 1.4 christos 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */
66 1.4 christos 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */
67 1.4 christos ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */
68 1.4 christos 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */
69 1.1 christos /*
70 1.1 christos * The base64 encoding of one of our HMAC-MD5 signatures is
71 1.1 christos * 22 bytes.
72 1.1 christos */
73 1.4 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 1.4 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
75 1.1 christos };
76 1.1 christos
77 1.4 christos #define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
78 1.4 christos #define HMD5_LENGTH 22
79 1.1 christos
80 1.1 christos static unsigned char auth_hsha[] = {
81 1.4 christos 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
82 1.4 christos ISCCC_CCMSGTYPE_TABLE, /*%< message type */
83 1.4 christos 0x00, 0x00, 0x00, 0x63, /*%< length == 99 */
84 1.4 christos 0x04, 0x68, 0x73, 0x68, 0x61, /*%< len + hsha */
85 1.4 christos ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */
86 1.4 christos 0x00, 0x00, 0x00, 0x59, /*%< length == 89 */
87 1.4 christos 0x00, /*%< algorithm */
88 1.1 christos /*
89 1.1 christos * The base64 encoding of one of our HMAC-SHA* signatures is
90 1.1 christos * 88 bytes.
91 1.1 christos */
92 1.4 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
93 1.4 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94 1.4 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95 1.4 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96 1.4 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 1.4 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 1.4 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 1.4 christos 0x00, 0x00, 0x00, 0x00
100 1.1 christos };
101 1.1 christos
102 1.4 christos #define HSHA_OFFSET 22 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 + 1 */
103 1.4 christos #define HSHA_LENGTH 88
104 1.1 christos
105 1.1 christos static isc_result_t
106 1.1 christos table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
107 1.1 christos
108 1.1 christos static isc_result_t
109 1.1 christos list_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
110 1.1 christos
111 1.1 christos static isc_result_t
112 1.1 christos value_towire(isccc_sexpr_t *elt, isc_buffer_t **buffer) {
113 1.1 christos unsigned int len;
114 1.1 christos isccc_region_t *vr;
115 1.1 christos isc_result_t result;
116 1.1 christos
117 1.1 christos if (isccc_sexpr_binaryp(elt)) {
118 1.1 christos vr = isccc_sexpr_tobinary(elt);
119 1.1 christos len = REGION_SIZE(*vr);
120 1.1 christos result = isc_buffer_reserve(buffer, 1 + 4);
121 1.4 christos if (result != ISC_R_SUCCESS) {
122 1.1 christos return (ISC_R_NOSPACE);
123 1.4 christos }
124 1.1 christos isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_BINARYDATA);
125 1.1 christos isc_buffer_putuint32(*buffer, len);
126 1.1 christos
127 1.1 christos result = isc_buffer_reserve(buffer, len);
128 1.4 christos if (result != ISC_R_SUCCESS) {
129 1.1 christos return (ISC_R_NOSPACE);
130 1.4 christos }
131 1.1 christos isc_buffer_putmem(*buffer, vr->rstart, len);
132 1.1 christos } else if (isccc_alist_alistp(elt)) {
133 1.1 christos unsigned int used;
134 1.1 christos isc_buffer_t b;
135 1.1 christos
136 1.1 christos result = isc_buffer_reserve(buffer, 1 + 4);
137 1.4 christos if (result != ISC_R_SUCCESS) {
138 1.1 christos return (ISC_R_NOSPACE);
139 1.4 christos }
140 1.1 christos isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_TABLE);
141 1.1 christos /*
142 1.1 christos * Emit a placeholder length.
143 1.1 christos */
144 1.1 christos used = (*buffer)->used;
145 1.1 christos isc_buffer_putuint32(*buffer, 0);
146 1.1 christos
147 1.1 christos /*
148 1.1 christos * Emit the table.
149 1.1 christos */
150 1.1 christos result = table_towire(elt, buffer);
151 1.4 christos if (result != ISC_R_SUCCESS) {
152 1.1 christos return (result);
153 1.4 christos }
154 1.1 christos
155 1.1 christos len = (*buffer)->used - used;
156 1.1 christos /*
157 1.1 christos * 'len' is 4 bytes too big, since it counts
158 1.1 christos * the placeholder length too. Adjust and
159 1.1 christos * emit.
160 1.1 christos */
161 1.1 christos INSIST(len >= 4U);
162 1.1 christos len -= 4;
163 1.1 christos
164 1.4 christos isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
165 1.1 christos isc_buffer_putuint32(&b, len);
166 1.1 christos } else if (isccc_sexpr_listp(elt)) {
167 1.1 christos unsigned int used;
168 1.1 christos isc_buffer_t b;
169 1.1 christos
170 1.1 christos result = isc_buffer_reserve(buffer, 1 + 4);
171 1.4 christos if (result != ISC_R_SUCCESS) {
172 1.1 christos return (ISC_R_NOSPACE);
173 1.4 christos }
174 1.1 christos isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_LIST);
175 1.1 christos /*
176 1.1 christos * Emit a placeholder length.
177 1.1 christos */
178 1.1 christos used = (*buffer)->used;
179 1.1 christos isc_buffer_putuint32(*buffer, 0);
180 1.1 christos
181 1.1 christos /*
182 1.1 christos * Emit the list.
183 1.1 christos */
184 1.1 christos result = list_towire(elt, buffer);
185 1.4 christos if (result != ISC_R_SUCCESS) {
186 1.1 christos return (result);
187 1.4 christos }
188 1.1 christos
189 1.1 christos len = (*buffer)->used - used;
190 1.1 christos /*
191 1.1 christos * 'len' is 4 bytes too big, since it counts
192 1.1 christos * the placeholder length too. Adjust and
193 1.1 christos * emit.
194 1.1 christos */
195 1.1 christos INSIST(len >= 4U);
196 1.1 christos len -= 4;
197 1.1 christos
198 1.4 christos isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
199 1.1 christos isc_buffer_putuint32(&b, len);
200 1.1 christos }
201 1.1 christos
202 1.1 christos return (ISC_R_SUCCESS);
203 1.1 christos }
204 1.1 christos
205 1.1 christos static isc_result_t
206 1.1 christos table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer) {
207 1.1 christos isccc_sexpr_t *kv, *elt, *k, *v;
208 1.1 christos char *ks;
209 1.1 christos isc_result_t result;
210 1.1 christos unsigned int len;
211 1.1 christos
212 1.4 christos for (elt = isccc_alist_first(alist); elt != NULL;
213 1.7 christos elt = ISCCC_SEXPR_CDR(elt))
214 1.7 christos {
215 1.1 christos kv = ISCCC_SEXPR_CAR(elt);
216 1.1 christos k = ISCCC_SEXPR_CAR(kv);
217 1.1 christos ks = isccc_sexpr_tostring(k);
218 1.1 christos v = ISCCC_SEXPR_CDR(kv);
219 1.1 christos len = (unsigned int)strlen(ks);
220 1.1 christos INSIST(len <= 255U);
221 1.1 christos /*
222 1.1 christos * Emit the key name.
223 1.1 christos */
224 1.1 christos result = isc_buffer_reserve(buffer, 1 + len);
225 1.4 christos if (result != ISC_R_SUCCESS) {
226 1.1 christos return (ISC_R_NOSPACE);
227 1.4 christos }
228 1.3 christos isc_buffer_putuint8(*buffer, (uint8_t)len);
229 1.4 christos isc_buffer_putmem(*buffer, (const unsigned char *)ks, len);
230 1.1 christos /*
231 1.1 christos * Emit the value.
232 1.1 christos */
233 1.1 christos result = value_towire(v, buffer);
234 1.4 christos if (result != ISC_R_SUCCESS) {
235 1.1 christos return (result);
236 1.4 christos }
237 1.1 christos }
238 1.1 christos
239 1.1 christos return (ISC_R_SUCCESS);
240 1.1 christos }
241 1.1 christos
242 1.1 christos static isc_result_t
243 1.1 christos list_towire(isccc_sexpr_t *list, isc_buffer_t **buffer) {
244 1.1 christos isc_result_t result;
245 1.1 christos
246 1.1 christos while (list != NULL) {
247 1.1 christos result = value_towire(ISCCC_SEXPR_CAR(list), buffer);
248 1.4 christos if (result != ISC_R_SUCCESS) {
249 1.1 christos return (result);
250 1.4 christos }
251 1.1 christos list = ISCCC_SEXPR_CDR(list);
252 1.1 christos }
253 1.1 christos
254 1.1 christos return (ISC_R_SUCCESS);
255 1.1 christos }
256 1.1 christos
257 1.1 christos static isc_result_t
258 1.1 christos sign(unsigned char *data, unsigned int length, unsigned char *hmac,
259 1.4 christos uint32_t algorithm, isccc_region_t *secret) {
260 1.4 christos const isc_md_type_t *md_type;
261 1.1 christos isc_result_t result;
262 1.1 christos isccc_region_t source, target;
263 1.3 christos unsigned char digest[ISC_MAX_MD_SIZE];
264 1.3 christos unsigned int digestlen;
265 1.1 christos unsigned char digestb64[HSHA_LENGTH + 4];
266 1.1 christos
267 1.1 christos source.rstart = digest;
268 1.1 christos
269 1.1 christos switch (algorithm) {
270 1.1 christos case ISCCC_ALG_HMACMD5:
271 1.3 christos md_type = ISC_MD_MD5;
272 1.1 christos break;
273 1.1 christos case ISCCC_ALG_HMACSHA1:
274 1.3 christos md_type = ISC_MD_SHA1;
275 1.1 christos break;
276 1.1 christos case ISCCC_ALG_HMACSHA224:
277 1.3 christos md_type = ISC_MD_SHA224;
278 1.1 christos break;
279 1.1 christos case ISCCC_ALG_HMACSHA256:
280 1.3 christos md_type = ISC_MD_SHA256;
281 1.1 christos break;
282 1.1 christos case ISCCC_ALG_HMACSHA384:
283 1.3 christos md_type = ISC_MD_SHA384;
284 1.1 christos break;
285 1.1 christos case ISCCC_ALG_HMACSHA512:
286 1.3 christos md_type = ISC_MD_SHA512;
287 1.1 christos break;
288 1.3 christos default:
289 1.3 christos return (ISC_R_NOTIMPLEMENTED);
290 1.3 christos }
291 1.1 christos
292 1.4 christos result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
293 1.4 christos length, digest, &digestlen);
294 1.3 christos if (result != ISC_R_SUCCESS) {
295 1.3 christos return (result);
296 1.1 christos }
297 1.3 christos source.rend = digest + digestlen;
298 1.1 christos
299 1.1 christos memset(digestb64, 0, sizeof(digestb64));
300 1.1 christos target.rstart = digestb64;
301 1.1 christos target.rend = digestb64 + sizeof(digestb64);
302 1.1 christos result = isccc_base64_encode(&source, 64, "", &target);
303 1.4 christos if (result != ISC_R_SUCCESS) {
304 1.1 christos return (result);
305 1.4 christos }
306 1.4 christos if (algorithm == ISCCC_ALG_HMACMD5) {
307 1.1 christos PUT_MEM(digestb64, HMD5_LENGTH, hmac);
308 1.4 christos } else {
309 1.1 christos PUT_MEM(digestb64, HSHA_LENGTH, hmac);
310 1.4 christos }
311 1.1 christos return (ISC_R_SUCCESS);
312 1.1 christos }
313 1.1 christos
314 1.1 christos isc_result_t
315 1.4 christos isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, uint32_t algorithm,
316 1.4 christos isccc_region_t *secret) {
317 1.1 christos unsigned int hmac_base, signed_base;
318 1.1 christos isc_result_t result;
319 1.1 christos
320 1.1 christos result = isc_buffer_reserve(buffer,
321 1.4 christos 4 + ((algorithm == ISCCC_ALG_HMACMD5)
322 1.4 christos ? sizeof(auth_hmd5)
323 1.4 christos : sizeof(auth_hsha)));
324 1.4 christos if (result != ISC_R_SUCCESS) {
325 1.1 christos return (ISC_R_NOSPACE);
326 1.4 christos }
327 1.1 christos
328 1.1 christos /*
329 1.1 christos * Emit protocol version.
330 1.1 christos */
331 1.1 christos isc_buffer_putuint32(*buffer, 1);
332 1.1 christos
333 1.1 christos if (secret != NULL) {
334 1.1 christos /*
335 1.1 christos * Emit _auth section with zeroed HMAC signature.
336 1.1 christos * We'll replace the zeros with the real signature once
337 1.1 christos * we know what it is.
338 1.1 christos */
339 1.1 christos if (algorithm == ISCCC_ALG_HMACMD5) {
340 1.1 christos hmac_base = (*buffer)->used + HMD5_OFFSET;
341 1.4 christos isc_buffer_putmem(*buffer, auth_hmd5,
342 1.4 christos sizeof(auth_hmd5));
343 1.4 christos } else {
344 1.1 christos unsigned char *hmac_alg;
345 1.1 christos
346 1.1 christos hmac_base = (*buffer)->used + HSHA_OFFSET;
347 1.4 christos hmac_alg = (unsigned char *)isc_buffer_used(*buffer) +
348 1.4 christos HSHA_OFFSET - 1;
349 1.4 christos isc_buffer_putmem(*buffer, auth_hsha,
350 1.4 christos sizeof(auth_hsha));
351 1.1 christos *hmac_alg = algorithm;
352 1.1 christos }
353 1.4 christos } else {
354 1.1 christos hmac_base = 0;
355 1.4 christos }
356 1.1 christos signed_base = (*buffer)->used;
357 1.1 christos /*
358 1.1 christos * Delete any existing _auth section so that we don't try
359 1.1 christos * to encode it.
360 1.1 christos */
361 1.1 christos isccc_alist_delete(alist, "_auth");
362 1.1 christos /*
363 1.1 christos * Emit the message.
364 1.1 christos */
365 1.1 christos result = table_towire(alist, buffer);
366 1.4 christos if (result != ISC_R_SUCCESS) {
367 1.1 christos return (result);
368 1.4 christos }
369 1.4 christos if (secret != NULL) {
370 1.4 christos return (sign((unsigned char *)(*buffer)->base + signed_base,
371 1.1 christos (*buffer)->used - signed_base,
372 1.4 christos (unsigned char *)(*buffer)->base + hmac_base,
373 1.1 christos algorithm, secret));
374 1.4 christos }
375 1.1 christos return (ISC_R_SUCCESS);
376 1.1 christos }
377 1.1 christos
378 1.1 christos static isc_result_t
379 1.1 christos verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
380 1.4 christos uint32_t algorithm, isccc_region_t *secret) {
381 1.4 christos const isc_md_type_t *md_type;
382 1.1 christos isccc_region_t source;
383 1.1 christos isccc_region_t target;
384 1.1 christos isc_result_t result;
385 1.1 christos isccc_sexpr_t *_auth, *hmac;
386 1.3 christos unsigned char digest[ISC_MAX_MD_SIZE];
387 1.3 christos unsigned int digestlen;
388 1.1 christos unsigned char digestb64[HSHA_LENGTH * 4];
389 1.1 christos
390 1.1 christos /*
391 1.1 christos * Extract digest.
392 1.1 christos */
393 1.1 christos _auth = isccc_alist_lookup(alist, "_auth");
394 1.4 christos if (!isccc_alist_alistp(_auth)) {
395 1.1 christos return (ISC_R_FAILURE);
396 1.4 christos }
397 1.4 christos if (algorithm == ISCCC_ALG_HMACMD5) {
398 1.1 christos hmac = isccc_alist_lookup(_auth, "hmd5");
399 1.4 christos } else {
400 1.1 christos hmac = isccc_alist_lookup(_auth, "hsha");
401 1.4 christos }
402 1.4 christos if (!isccc_sexpr_binaryp(hmac)) {
403 1.1 christos return (ISC_R_FAILURE);
404 1.4 christos }
405 1.1 christos /*
406 1.1 christos * Compute digest.
407 1.1 christos */
408 1.1 christos source.rstart = digest;
409 1.3 christos
410 1.1 christos switch (algorithm) {
411 1.1 christos case ISCCC_ALG_HMACMD5:
412 1.3 christos md_type = ISC_MD_MD5;
413 1.1 christos break;
414 1.1 christos case ISCCC_ALG_HMACSHA1:
415 1.3 christos md_type = ISC_MD_SHA1;
416 1.1 christos break;
417 1.1 christos case ISCCC_ALG_HMACSHA224:
418 1.3 christos md_type = ISC_MD_SHA224;
419 1.1 christos break;
420 1.1 christos case ISCCC_ALG_HMACSHA256:
421 1.3 christos md_type = ISC_MD_SHA256;
422 1.1 christos break;
423 1.1 christos case ISCCC_ALG_HMACSHA384:
424 1.3 christos md_type = ISC_MD_SHA384;
425 1.1 christos break;
426 1.1 christos case ISCCC_ALG_HMACSHA512:
427 1.3 christos md_type = ISC_MD_SHA512;
428 1.1 christos break;
429 1.3 christos default:
430 1.3 christos return (ISC_R_NOTIMPLEMENTED);
431 1.3 christos }
432 1.1 christos
433 1.4 christos result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
434 1.4 christos length, digest, &digestlen);
435 1.3 christos if (result != ISC_R_SUCCESS) {
436 1.3 christos return (result);
437 1.1 christos }
438 1.3 christos source.rend = digest + digestlen;
439 1.3 christos
440 1.1 christos target.rstart = digestb64;
441 1.1 christos target.rend = digestb64 + sizeof(digestb64);
442 1.1 christos memset(digestb64, 0, sizeof(digestb64));
443 1.1 christos result = isccc_base64_encode(&source, 64, "", &target);
444 1.4 christos if (result != ISC_R_SUCCESS) {
445 1.1 christos return (result);
446 1.4 christos }
447 1.1 christos
448 1.1 christos /*
449 1.1 christos * Verify.
450 1.1 christos */
451 1.1 christos if (algorithm == ISCCC_ALG_HMACMD5) {
452 1.1 christos isccc_region_t *region;
453 1.1 christos unsigned char *value;
454 1.1 christos
455 1.1 christos region = isccc_sexpr_tobinary(hmac);
456 1.4 christos if ((region->rend - region->rstart) != HMD5_LENGTH) {
457 1.1 christos return (ISCCC_R_BADAUTH);
458 1.4 christos }
459 1.1 christos value = region->rstart;
460 1.4 christos if (!isc_safe_memequal(value, digestb64, HMD5_LENGTH)) {
461 1.1 christos return (ISCCC_R_BADAUTH);
462 1.4 christos }
463 1.3 christos } else {
464 1.1 christos isccc_region_t *region;
465 1.1 christos unsigned char *value;
466 1.3 christos uint32_t valalg;
467 1.1 christos
468 1.1 christos region = isccc_sexpr_tobinary(hmac);
469 1.1 christos
470 1.1 christos /*
471 1.1 christos * Note: with non-MD5 algorithms, there's an extra octet
472 1.1 christos * to identify which algorithm is in use.
473 1.1 christos */
474 1.4 christos if ((region->rend - region->rstart) != HSHA_LENGTH + 1) {
475 1.1 christos return (ISCCC_R_BADAUTH);
476 1.4 christos }
477 1.1 christos value = region->rstart;
478 1.1 christos GET8(valalg, value);
479 1.1 christos if ((valalg != algorithm) ||
480 1.7 christos !isc_safe_memequal(value, digestb64, HSHA_LENGTH))
481 1.7 christos {
482 1.1 christos return (ISCCC_R_BADAUTH);
483 1.4 christos }
484 1.1 christos }
485 1.1 christos
486 1.1 christos return (ISC_R_SUCCESS);
487 1.1 christos }
488 1.1 christos
489 1.1 christos static isc_result_t
490 1.1 christos table_fromwire(isccc_region_t *source, isccc_region_t *secret,
491 1.3 christos uint32_t algorithm, isccc_sexpr_t **alistp);
492 1.1 christos
493 1.1 christos static isc_result_t
494 1.1 christos list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
495 1.1 christos
496 1.1 christos static isc_result_t
497 1.1 christos value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep) {
498 1.1 christos unsigned int msgtype;
499 1.3 christos uint32_t len;
500 1.1 christos isccc_sexpr_t *value;
501 1.1 christos isccc_region_t active;
502 1.1 christos isc_result_t result;
503 1.1 christos
504 1.4 christos if (REGION_SIZE(*source) < 1 + 4) {
505 1.1 christos return (ISC_R_UNEXPECTEDEND);
506 1.4 christos }
507 1.1 christos GET8(msgtype, source->rstart);
508 1.1 christos GET32(len, source->rstart);
509 1.4 christos if (REGION_SIZE(*source) < len) {
510 1.1 christos return (ISC_R_UNEXPECTEDEND);
511 1.4 christos }
512 1.1 christos active.rstart = source->rstart;
513 1.1 christos active.rend = active.rstart + len;
514 1.1 christos source->rstart = active.rend;
515 1.1 christos if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
516 1.1 christos value = isccc_sexpr_frombinary(&active);
517 1.1 christos if (value != NULL) {
518 1.1 christos *valuep = value;
519 1.1 christos result = ISC_R_SUCCESS;
520 1.4 christos } else {
521 1.1 christos result = ISC_R_NOMEMORY;
522 1.4 christos }
523 1.4 christos } else if (msgtype == ISCCC_CCMSGTYPE_TABLE) {
524 1.1 christos result = table_fromwire(&active, NULL, 0, valuep);
525 1.4 christos } else if (msgtype == ISCCC_CCMSGTYPE_LIST) {
526 1.1 christos result = list_fromwire(&active, valuep);
527 1.4 christos } else {
528 1.1 christos result = ISCCC_R_SYNTAX;
529 1.4 christos }
530 1.1 christos
531 1.1 christos return (result);
532 1.1 christos }
533 1.1 christos
534 1.1 christos static isc_result_t
535 1.1 christos table_fromwire(isccc_region_t *source, isccc_region_t *secret,
536 1.4 christos uint32_t algorithm, isccc_sexpr_t **alistp) {
537 1.1 christos char key[256];
538 1.3 christos uint32_t len;
539 1.1 christos isc_result_t result;
540 1.1 christos isccc_sexpr_t *alist, *value;
541 1.3 christos bool first_tag;
542 1.1 christos unsigned char *checksum_rstart;
543 1.1 christos
544 1.1 christos REQUIRE(alistp != NULL && *alistp == NULL);
545 1.1 christos
546 1.1 christos checksum_rstart = NULL;
547 1.3 christos first_tag = true;
548 1.1 christos alist = isccc_alist_create();
549 1.4 christos if (alist == NULL) {
550 1.1 christos return (ISC_R_NOMEMORY);
551 1.4 christos }
552 1.1 christos
553 1.1 christos while (!REGION_EMPTY(*source)) {
554 1.1 christos GET8(len, source->rstart);
555 1.1 christos if (REGION_SIZE(*source) < len) {
556 1.1 christos result = ISC_R_UNEXPECTEDEND;
557 1.1 christos goto bad;
558 1.1 christos }
559 1.1 christos GET_MEM(key, len, source->rstart);
560 1.4 christos key[len] = '\0'; /* Ensure NUL termination. */
561 1.1 christos value = NULL;
562 1.1 christos result = value_fromwire(source, &value);
563 1.4 christos if (result != ISC_R_SUCCESS) {
564 1.1 christos goto bad;
565 1.4 christos }
566 1.1 christos if (isccc_alist_define(alist, key, value) == NULL) {
567 1.1 christos result = ISC_R_NOMEMORY;
568 1.1 christos goto bad;
569 1.1 christos }
570 1.4 christos if (first_tag && secret != NULL && strcmp(key, "_auth") == 0) {
571 1.1 christos checksum_rstart = source->rstart;
572 1.4 christos }
573 1.3 christos first_tag = false;
574 1.1 christos }
575 1.1 christos
576 1.1 christos if (secret != NULL) {
577 1.4 christos if (checksum_rstart != NULL) {
578 1.4 christos result = verify(
579 1.4 christos alist, checksum_rstart,
580 1.4 christos (unsigned int)(source->rend - checksum_rstart),
581 1.4 christos algorithm, secret);
582 1.4 christos } else {
583 1.1 christos result = ISCCC_R_BADAUTH;
584 1.4 christos }
585 1.4 christos } else {
586 1.1 christos result = ISC_R_SUCCESS;
587 1.4 christos }
588 1.1 christos
589 1.4 christos bad:
590 1.4 christos if (result == ISC_R_SUCCESS) {
591 1.1 christos *alistp = alist;
592 1.4 christos } else {
593 1.1 christos isccc_sexpr_free(&alist);
594 1.4 christos }
595 1.1 christos
596 1.1 christos return (result);
597 1.1 christos }
598 1.1 christos
599 1.1 christos static isc_result_t
600 1.1 christos list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp) {
601 1.1 christos isccc_sexpr_t *list, *value;
602 1.1 christos isc_result_t result;
603 1.1 christos
604 1.1 christos list = NULL;
605 1.1 christos while (!REGION_EMPTY(*source)) {
606 1.1 christos value = NULL;
607 1.1 christos result = value_fromwire(source, &value);
608 1.1 christos if (result != ISC_R_SUCCESS) {
609 1.1 christos isccc_sexpr_free(&list);
610 1.1 christos return (result);
611 1.1 christos }
612 1.1 christos if (isccc_sexpr_addtolist(&list, value) == NULL) {
613 1.1 christos isccc_sexpr_free(&value);
614 1.1 christos isccc_sexpr_free(&list);
615 1.1 christos return (ISC_R_NOMEMORY);
616 1.1 christos }
617 1.1 christos }
618 1.1 christos
619 1.1 christos *listp = list;
620 1.1 christos
621 1.1 christos return (ISC_R_SUCCESS);
622 1.1 christos }
623 1.1 christos
624 1.1 christos isc_result_t
625 1.1 christos isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
626 1.4 christos uint32_t algorithm, isccc_region_t *secret) {
627 1.1 christos unsigned int size;
628 1.3 christos uint32_t version;
629 1.1 christos
630 1.1 christos size = REGION_SIZE(*source);
631 1.4 christos if (size < 4) {
632 1.1 christos return (ISC_R_UNEXPECTEDEND);
633 1.4 christos }
634 1.1 christos GET32(version, source->rstart);
635 1.4 christos if (version != 1) {
636 1.1 christos return (ISCCC_R_UNKNOWNVERSION);
637 1.4 christos }
638 1.1 christos
639 1.1 christos return (table_fromwire(source, secret, algorithm, alistp));
640 1.1 christos }
641 1.1 christos
642 1.1 christos static isc_result_t
643 1.3 christos createmessage(uint32_t version, const char *from, const char *to,
644 1.4 christos uint32_t serial, isccc_time_t now, isccc_time_t expires,
645 1.4 christos isccc_sexpr_t **alistp, bool want_expires) {
646 1.1 christos isccc_sexpr_t *alist, *_ctrl, *_data;
647 1.1 christos isc_result_t result;
648 1.1 christos
649 1.1 christos REQUIRE(alistp != NULL && *alistp == NULL);
650 1.1 christos
651 1.4 christos if (version != 1) {
652 1.1 christos return (ISCCC_R_UNKNOWNVERSION);
653 1.4 christos }
654 1.1 christos
655 1.1 christos alist = isccc_alist_create();
656 1.4 christos if (alist == NULL) {
657 1.1 christos return (ISC_R_NOMEMORY);
658 1.4 christos }
659 1.1 christos
660 1.1 christos result = ISC_R_NOMEMORY;
661 1.1 christos
662 1.1 christos _ctrl = isccc_alist_create();
663 1.4 christos if (_ctrl == NULL) {
664 1.1 christos goto bad;
665 1.4 christos }
666 1.1 christos if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
667 1.1 christos isccc_sexpr_free(&_ctrl);
668 1.1 christos goto bad;
669 1.1 christos }
670 1.1 christos
671 1.1 christos _data = isccc_alist_create();
672 1.4 christos if (_data == NULL) {
673 1.1 christos goto bad;
674 1.4 christos }
675 1.1 christos if (isccc_alist_define(alist, "_data", _data) == NULL) {
676 1.1 christos isccc_sexpr_free(&_data);
677 1.1 christos goto bad;
678 1.1 christos }
679 1.1 christos
680 1.1 christos if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
681 1.1 christos isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
682 1.1 christos (want_expires &&
683 1.1 christos isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
684 1.4 christos {
685 1.1 christos goto bad;
686 1.4 christos }
687 1.4 christos if (from != NULL && isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
688 1.4 christos {
689 1.1 christos goto bad;
690 1.4 christos }
691 1.4 christos if (to != NULL && isccc_cc_definestring(_ctrl, "_to", to) == NULL) {
692 1.1 christos goto bad;
693 1.4 christos }
694 1.1 christos
695 1.1 christos *alistp = alist;
696 1.1 christos
697 1.1 christos return (ISC_R_SUCCESS);
698 1.1 christos
699 1.4 christos bad:
700 1.1 christos isccc_sexpr_free(&alist);
701 1.1 christos
702 1.1 christos return (result);
703 1.1 christos }
704 1.1 christos
705 1.1 christos isc_result_t
706 1.3 christos isccc_cc_createmessage(uint32_t version, const char *from, const char *to,
707 1.4 christos uint32_t serial, isccc_time_t now, isccc_time_t expires,
708 1.4 christos isccc_sexpr_t **alistp) {
709 1.4 christos return (createmessage(version, from, to, serial, now, expires, alistp,
710 1.4 christos true));
711 1.1 christos }
712 1.1 christos
713 1.1 christos isc_result_t
714 1.4 christos isccc_cc_createack(isccc_sexpr_t *message, bool ok, isccc_sexpr_t **ackp) {
715 1.1 christos char *_frm, *_to;
716 1.3 christos uint32_t serial;
717 1.1 christos isccc_sexpr_t *ack, *_ctrl;
718 1.1 christos isc_result_t result;
719 1.1 christos isccc_time_t t;
720 1.1 christos
721 1.1 christos REQUIRE(ackp != NULL && *ackp == NULL);
722 1.1 christos
723 1.1 christos _ctrl = isccc_alist_lookup(message, "_ctrl");
724 1.1 christos if (!isccc_alist_alistp(_ctrl) ||
725 1.1 christos isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
726 1.1 christos isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
727 1.4 christos {
728 1.1 christos return (ISC_R_FAILURE);
729 1.4 christos }
730 1.1 christos /*
731 1.1 christos * _frm and _to are optional.
732 1.1 christos */
733 1.1 christos _frm = NULL;
734 1.1 christos (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
735 1.1 christos _to = NULL;
736 1.1 christos (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
737 1.1 christos /*
738 1.1 christos * Create the ack.
739 1.1 christos */
740 1.1 christos ack = NULL;
741 1.3 christos result = createmessage(1, _to, _frm, serial, t, 0, &ack, false);
742 1.4 christos if (result != ISC_R_SUCCESS) {
743 1.1 christos return (result);
744 1.4 christos }
745 1.1 christos
746 1.1 christos _ctrl = isccc_alist_lookup(ack, "_ctrl");
747 1.1 christos if (_ctrl == NULL) {
748 1.1 christos result = ISC_R_FAILURE;
749 1.1 christos goto bad;
750 1.1 christos }
751 1.1 christos if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
752 1.1 christos result = ISC_R_NOMEMORY;
753 1.1 christos goto bad;
754 1.1 christos }
755 1.1 christos
756 1.1 christos *ackp = ack;
757 1.1 christos
758 1.1 christos return (ISC_R_SUCCESS);
759 1.1 christos
760 1.4 christos bad:
761 1.1 christos isccc_sexpr_free(&ack);
762 1.1 christos
763 1.1 christos return (result);
764 1.1 christos }
765 1.1 christos
766 1.3 christos bool
767 1.1 christos isccc_cc_isack(isccc_sexpr_t *message) {
768 1.1 christos isccc_sexpr_t *_ctrl;
769 1.1 christos
770 1.1 christos _ctrl = isccc_alist_lookup(message, "_ctrl");
771 1.4 christos if (!isccc_alist_alistp(_ctrl)) {
772 1.3 christos return (false);
773 1.4 christos }
774 1.4 christos if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS) {
775 1.3 christos return (true);
776 1.4 christos }
777 1.3 christos return (false);
778 1.1 christos }
779 1.1 christos
780 1.3 christos bool
781 1.1 christos isccc_cc_isreply(isccc_sexpr_t *message) {
782 1.1 christos isccc_sexpr_t *_ctrl;
783 1.1 christos
784 1.1 christos _ctrl = isccc_alist_lookup(message, "_ctrl");
785 1.4 christos if (!isccc_alist_alistp(_ctrl)) {
786 1.3 christos return (false);
787 1.4 christos }
788 1.4 christos if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS) {
789 1.3 christos return (true);
790 1.4 christos }
791 1.3 christos return (false);
792 1.1 christos }
793 1.1 christos
794 1.1 christos isc_result_t
795 1.1 christos isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
796 1.4 christos isccc_time_t expires, isccc_sexpr_t **alistp) {
797 1.1 christos char *_frm, *_to, *type = NULL;
798 1.3 christos uint32_t serial;
799 1.1 christos isccc_sexpr_t *alist, *_ctrl, *_data;
800 1.1 christos isc_result_t result;
801 1.1 christos
802 1.1 christos REQUIRE(alistp != NULL && *alistp == NULL);
803 1.1 christos
804 1.1 christos _ctrl = isccc_alist_lookup(message, "_ctrl");
805 1.1 christos _data = isccc_alist_lookup(message, "_data");
806 1.1 christos if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
807 1.1 christos isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
808 1.1 christos isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
809 1.4 christos {
810 1.1 christos return (ISC_R_FAILURE);
811 1.4 christos }
812 1.1 christos /*
813 1.1 christos * _frm and _to are optional.
814 1.1 christos */
815 1.1 christos _frm = NULL;
816 1.1 christos (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
817 1.1 christos _to = NULL;
818 1.1 christos (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
819 1.1 christos /*
820 1.1 christos * Create the response.
821 1.1 christos */
822 1.1 christos alist = NULL;
823 1.1 christos result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
824 1.4 christos &alist);
825 1.4 christos if (result != ISC_R_SUCCESS) {
826 1.1 christos return (result);
827 1.4 christos }
828 1.1 christos
829 1.1 christos _ctrl = isccc_alist_lookup(alist, "_ctrl");
830 1.1 christos if (_ctrl == NULL) {
831 1.1 christos result = ISC_R_FAILURE;
832 1.1 christos goto bad;
833 1.1 christos }
834 1.1 christos
835 1.1 christos _data = isccc_alist_lookup(alist, "_data");
836 1.1 christos if (_data == NULL) {
837 1.1 christos result = ISC_R_FAILURE;
838 1.1 christos goto bad;
839 1.1 christos }
840 1.1 christos
841 1.1 christos if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
842 1.1 christos isccc_cc_definestring(_data, "type", type) == NULL)
843 1.1 christos {
844 1.1 christos result = ISC_R_NOMEMORY;
845 1.1 christos goto bad;
846 1.1 christos }
847 1.1 christos
848 1.1 christos *alistp = alist;
849 1.1 christos
850 1.1 christos return (ISC_R_SUCCESS);
851 1.1 christos
852 1.4 christos bad:
853 1.1 christos isccc_sexpr_free(&alist);
854 1.1 christos return (result);
855 1.1 christos }
856 1.1 christos
857 1.1 christos isccc_sexpr_t *
858 1.1 christos isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) {
859 1.1 christos size_t len;
860 1.1 christos isccc_region_t r;
861 1.1 christos
862 1.1 christos len = strlen(str);
863 1.1 christos DE_CONST(str, r.rstart);
864 1.1 christos r.rend = r.rstart + len;
865 1.1 christos
866 1.1 christos return (isccc_alist_definebinary(alist, key, &r));
867 1.1 christos }
868 1.1 christos
869 1.1 christos isccc_sexpr_t *
870 1.3 christos isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, uint32_t i) {
871 1.1 christos char b[100];
872 1.1 christos size_t len;
873 1.1 christos isccc_region_t r;
874 1.1 christos
875 1.1 christos snprintf(b, sizeof(b), "%u", i);
876 1.1 christos len = strlen(b);
877 1.1 christos r.rstart = (unsigned char *)b;
878 1.1 christos r.rend = (unsigned char *)b + len;
879 1.1 christos
880 1.1 christos return (isccc_alist_definebinary(alist, key, &r));
881 1.1 christos }
882 1.1 christos
883 1.1 christos isc_result_t
884 1.1 christos isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) {
885 1.1 christos isccc_sexpr_t *kv, *v;
886 1.1 christos
887 1.1 christos REQUIRE(strp == NULL || *strp == NULL);
888 1.1 christos
889 1.1 christos kv = isccc_alist_assq(alist, key);
890 1.1 christos if (kv != NULL) {
891 1.1 christos v = ISCCC_SEXPR_CDR(kv);
892 1.1 christos if (isccc_sexpr_binaryp(v)) {
893 1.4 christos if (strp != NULL) {
894 1.1 christos *strp = isccc_sexpr_tostring(v);
895 1.4 christos }
896 1.1 christos return (ISC_R_SUCCESS);
897 1.4 christos } else {
898 1.1 christos return (ISC_R_EXISTS);
899 1.4 christos }
900 1.1 christos }
901 1.1 christos
902 1.1 christos return (ISC_R_NOTFOUND);
903 1.1 christos }
904 1.1 christos
905 1.1 christos isc_result_t
906 1.4 christos isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key, uint32_t *uintp) {
907 1.1 christos isccc_sexpr_t *kv, *v;
908 1.1 christos
909 1.1 christos kv = isccc_alist_assq(alist, key);
910 1.1 christos if (kv != NULL) {
911 1.1 christos v = ISCCC_SEXPR_CDR(kv);
912 1.1 christos if (isccc_sexpr_binaryp(v)) {
913 1.4 christos if (uintp != NULL) {
914 1.4 christos *uintp = (uint32_t)strtoul(
915 1.4 christos isccc_sexpr_tostring(v), NULL, 10);
916 1.4 christos }
917 1.1 christos return (ISC_R_SUCCESS);
918 1.4 christos } else {
919 1.1 christos return (ISC_R_EXISTS);
920 1.4 christos }
921 1.1 christos }
922 1.1 christos
923 1.1 christos return (ISC_R_NOTFOUND);
924 1.1 christos }
925 1.1 christos
926 1.1 christos static void
927 1.1 christos symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
928 1.4 christos void *arg) {
929 1.1 christos UNUSED(type);
930 1.1 christos UNUSED(value);
931 1.1 christos UNUSED(arg);
932 1.1 christos
933 1.1 christos free(key);
934 1.1 christos }
935 1.1 christos
936 1.3 christos static bool
937 1.1 christos symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, void *arg) {
938 1.1 christos isccc_time_t *now;
939 1.1 christos
940 1.1 christos UNUSED(key);
941 1.1 christos UNUSED(type);
942 1.1 christos
943 1.1 christos now = arg;
944 1.1 christos
945 1.4 christos if (*now < value.as_uinteger) {
946 1.3 christos return (false);
947 1.4 christos }
948 1.4 christos if ((*now - value.as_uinteger) < DUP_LIFETIME) {
949 1.3 christos return (false);
950 1.4 christos }
951 1.3 christos return (true);
952 1.1 christos }
953 1.1 christos
954 1.1 christos isc_result_t
955 1.1 christos isccc_cc_createsymtab(isccc_symtab_t **symtabp) {
956 1.3 christos return (isccc_symtab_create(11897, symtab_undefine, NULL, false,
957 1.4 christos symtabp));
958 1.1 christos }
959 1.1 christos
960 1.1 christos void
961 1.1 christos isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) {
962 1.1 christos isccc_symtab_foreach(symtab, symtab_clean, &now);
963 1.1 christos }
964 1.1 christos
965 1.3 christos static bool
966 1.1 christos has_whitespace(const char *str) {
967 1.1 christos char c;
968 1.1 christos
969 1.4 christos if (str == NULL) {
970 1.3 christos return (false);
971 1.4 christos }
972 1.1 christos while ((c = *str++) != '\0') {
973 1.4 christos if (c == ' ' || c == '\t' || c == '\n') {
974 1.3 christos return (true);
975 1.4 christos }
976 1.1 christos }
977 1.3 christos return (false);
978 1.1 christos }
979 1.1 christos
980 1.1 christos isc_result_t
981 1.1 christos isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
982 1.4 christos isccc_time_t now) {
983 1.1 christos const char *_frm;
984 1.1 christos const char *_to;
985 1.1 christos char *_ser = NULL, *_tim = NULL, *tmp;
986 1.1 christos isc_result_t result;
987 1.1 christos char *key;
988 1.1 christos size_t len;
989 1.1 christos isccc_symvalue_t value;
990 1.1 christos isccc_sexpr_t *_ctrl;
991 1.1 christos
992 1.1 christos _ctrl = isccc_alist_lookup(message, "_ctrl");
993 1.1 christos if (!isccc_alist_alistp(_ctrl) ||
994 1.1 christos isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
995 1.1 christos isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
996 1.4 christos {
997 1.1 christos return (ISC_R_FAILURE);
998 1.4 christos }
999 1.1 christos
1000 1.1 christos INSIST(_ser != NULL);
1001 1.1 christos INSIST(_tim != NULL);
1002 1.1 christos
1003 1.1 christos /*
1004 1.1 christos * _frm and _to are optional.
1005 1.1 christos */
1006 1.1 christos tmp = NULL;
1007 1.4 christos if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS) {
1008 1.1 christos _frm = "";
1009 1.4 christos } else {
1010 1.1 christos _frm = tmp;
1011 1.4 christos }
1012 1.1 christos tmp = NULL;
1013 1.4 christos if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS) {
1014 1.1 christos _to = "";
1015 1.4 christos } else {
1016 1.1 christos _to = tmp;
1017 1.4 christos }
1018 1.1 christos /*
1019 1.1 christos * Ensure there is no newline in any of the strings. This is so
1020 1.1 christos * we can write them to a file later.
1021 1.1 christos */
1022 1.1 christos if (has_whitespace(_frm) || has_whitespace(_to) ||
1023 1.1 christos has_whitespace(_ser) || has_whitespace(_tim))
1024 1.4 christos {
1025 1.1 christos return (ISC_R_FAILURE);
1026 1.4 christos }
1027 1.1 christos len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
1028 1.1 christos key = malloc(len);
1029 1.4 christos if (key == NULL) {
1030 1.1 christos return (ISC_R_NOMEMORY);
1031 1.4 christos }
1032 1.1 christos snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
1033 1.1 christos value.as_uinteger = now;
1034 1.1 christos result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
1035 1.4 christos isccc_symexists_reject);
1036 1.1 christos if (result != ISC_R_SUCCESS) {
1037 1.1 christos free(key);
1038 1.1 christos return (result);
1039 1.1 christos }
1040 1.1 christos
1041 1.1 christos return (ISC_R_SUCCESS);
1042 1.1 christos }
1043