Update isc-dhcp to 3.0.2rc3 using patch infrastructure.
[dragonfly.git] / contrib / dhcp-3.0 / minires / ns_verify.c
1 /*
2  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (c) 1999-2003 by Internet Software Consortium
4  *
5  * Permission to use, copy, modify, and 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 DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12  * ANY 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
15  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  *   Internet Systems Consortium, Inc.
18  *   950 Charter Street
19  *   Redwood City, CA 94063
20  *   <info@isc.org>
21  *   http://www.isc.org/
22  */
23
24 #ifndef lint
25 static const char rcsid[] = "$Id: ns_verify.c,v 1.5.2.3 2004/06/10 17:59:42 dhankins Exp $";
26 #endif
27
28 #define time(x)         trace_mr_time (x)
29
30 /* Import. */
31
32 #include <sys/types.h>
33 #include <sys/param.h>
34
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <sys/socket.h>
38
39 #include <errno.h>
40 #include <netdb.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <time.h>
46
47 #include "minires/minires.h"
48 #include "arpa/nameser.h"
49 #include <isc-dhcp/dst.h>
50
51 time_t trace_mr_time (time_t *);
52
53 /* Private. */
54
55 #define BOUNDS_CHECK(ptr, count) \
56         do { \
57                 if ((ptr) + (count) > eom) { \
58                         return (NS_TSIG_ERROR_FORMERR); \
59                 } \
60         } while (0)
61
62 /* Public. */
63
64 u_char *
65 ns_find_tsig(u_char *msg, u_char *eom) {
66         HEADER *hp = (HEADER *)msg;
67         int n, type;
68         u_char *cp = msg, *start;
69         isc_result_t status;
70
71         if (msg == NULL || eom == NULL || msg > eom)
72                 return (NULL);
73
74         if (cp + HFIXEDSZ >= eom)
75                 return (NULL);
76
77         if (hp->arcount == 0)
78                 return (NULL);
79
80         cp += HFIXEDSZ;
81
82         status = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount), &n);
83         if (status != ISC_R_SUCCESS)
84                 return (NULL);
85         cp += n;
86
87         status = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount), &n);
88         if (status != ISC_R_SUCCESS)
89                 return (NULL);
90         cp += n;
91
92         status = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount), &n);
93         if (status != ISC_R_SUCCESS)
94                 return (NULL);
95         cp += n;
96
97         status = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1, &n);
98         if (status != ISC_R_SUCCESS)
99                 return (NULL);
100         cp += n;
101
102         start = cp;
103         n = dn_skipname(cp, eom);
104         if (n < 0)
105                 return (NULL);
106         cp += n;
107         if (cp + INT16SZ >= eom)
108                 return (NULL);
109
110         GETSHORT(type, cp);
111         if (type != ns_t_tsig)
112                 return (NULL);
113         return (start);
114 }
115
116 /* ns_verify
117  * Parameters:
118  *      statp           res stuff
119  *      msg             received message
120  *      msglen          length of message
121  *      key             tsig key used for verifying.
122  *      querysig        (response), the signature in the query
123  *      querysiglen     (response), the length of the signature in the query
124  *      sig             (query), a buffer to hold the signature
125  *      siglen          (query), input - length of signature buffer
126  *                               output - length of signature
127  *
128  * Errors:
129  *      - bad input (-1)
130  *      - invalid dns message (NS_TSIG_ERROR_FORMERR)
131  *      - TSIG is not present (NS_TSIG_ERROR_NO_TSIG)
132  *      - key doesn't match (-ns_r_badkey)
133  *      - TSIG verification fails with BADKEY (-ns_r_badkey)
134  *      - TSIG verification fails with BADSIG (-ns_r_badsig)
135  *      - TSIG verification fails with BADTIME (-ns_r_badtime)
136  *      - TSIG verification succeeds, error set to BAKEY (ns_r_badkey)
137  *      - TSIG verification succeeds, error set to BADSIG (ns_r_badsig)
138  *      - TSIG verification succeeds, error set to BADTIME (ns_r_badtime)
139  */
140 isc_result_t
141 ns_verify(u_char *msg, unsigned *msglen, void *k,
142           const u_char *querysig, unsigned querysiglen,
143           u_char *sig, unsigned *siglen, time_t *timesigned, int nostrip)
144 {
145         HEADER *hp = (HEADER *)msg;
146         DST_KEY *key = (DST_KEY *)k;
147         u_char *cp = msg, *eom;
148         char name[MAXDNAME], alg[MAXDNAME];
149         u_char *recstart, *rdatastart;
150         u_char *sigstart, *otherstart;
151         unsigned n;
152         int error;
153         u_int16_t type, length;
154         u_int16_t fudge, sigfieldlen, id, otherfieldlen;
155
156         dst_init();
157         if (msg == NULL || msglen == NULL || *msglen < 0)
158                 return ISC_R_INVALIDARG;
159
160         eom = msg + *msglen;
161
162         recstart = ns_find_tsig(msg, eom);
163         if (recstart == NULL)
164                 return ISC_R_NO_TSIG;
165
166         cp = recstart;
167
168         /* Read the key name. */
169         n = dn_expand(msg, eom, cp, name, MAXDNAME);
170         if (n < 0)
171                 return ISC_R_FORMERR;
172         cp += n;
173
174         /* Read the type. */
175         BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ);
176         GETSHORT(type, cp);
177         if (type != ns_t_tsig)
178                 return ISC_R_NO_TSIG;
179
180         /* Skip the class and TTL, save the length. */
181         cp += INT16SZ + INT32SZ;
182         GETSHORT(length, cp);
183         if (eom - cp != length)
184                 return ISC_R_FORMERR;
185
186         /* Read the algorithm name. */
187         rdatastart = cp;
188         n = dn_expand(msg, eom, cp, alg, MAXDNAME);
189         if (n < 0)
190                 return ISC_R_FORMERR;
191         if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
192                 return ISC_R_INVALIDKEY;
193         cp += n;
194
195         /* Read the time signed and fudge. */
196         BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
197         cp += INT16SZ;
198         GETLONG((*timesigned), cp);
199         GETSHORT(fudge, cp);
200
201         /* Read the signature. */
202         BOUNDS_CHECK(cp, INT16SZ);
203         GETSHORT(sigfieldlen, cp);
204         BOUNDS_CHECK(cp, sigfieldlen);
205         sigstart = cp;
206         cp += sigfieldlen;
207
208         /* Read the original id and error. */
209         BOUNDS_CHECK(cp, 2*INT16SZ);
210         GETSHORT(id, cp);
211         GETSHORT(error, cp);
212
213         /* Parse the other data. */
214         BOUNDS_CHECK(cp, INT16SZ);
215         GETSHORT(otherfieldlen, cp);
216         BOUNDS_CHECK(cp, otherfieldlen);
217         otherstart = cp;
218         cp += otherfieldlen;
219
220         if (cp != eom)
221                 return ISC_R_FORMERR;
222
223         /* Verify that the key used is OK. */
224         if (key != NULL) {
225                 if (key->dk_alg != KEY_HMAC_MD5)
226                         return ISC_R_INVALIDKEY;
227                 if (error != ns_r_badsig && error != ns_r_badkey) {
228                         if (ns_samename(key->dk_key_name, name) != 1)
229                                 return ISC_R_INVALIDKEY;
230                 }
231         }
232
233         hp->arcount = htons(ntohs(hp->arcount) - 1);
234
235         /*
236          * Do the verification.
237          */
238
239         if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
240                 void *ctx;
241                 u_char buf[MAXDNAME];
242
243                 /* Digest the query signature, if this is a response. */
244                 dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
245                 if (querysiglen > 0 && querysig != NULL) {
246                         u_int16_t len_n = htons(querysiglen);
247                         dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
248                                         (u_char *)&len_n, INT16SZ, NULL, 0);
249                         dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
250                                         querysig, querysiglen, NULL, 0);
251                 }
252                 
253                 /* Digest the message. */
254                 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg,
255                                 (unsigned)(recstart - msg), NULL, 0);
256
257                 /* Digest the key name. */
258                 n = ns_name_ntol(recstart, buf, sizeof(buf));
259                 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
260
261                 /* Digest the class and TTL. */
262                 dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
263                                 recstart + dn_skipname(recstart, eom) + INT16SZ,
264                                 INT16SZ + INT32SZ, NULL, 0);
265
266                 /* Digest the algorithm. */
267                 n = ns_name_ntol(rdatastart, buf, sizeof(buf));
268                 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
269
270                 /* Digest the time signed and fudge. */
271                 dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
272                                 rdatastart + dn_skipname(rdatastart, eom),
273                                 INT16SZ + INT32SZ + INT16SZ, NULL, 0);
274
275                 /* Digest the error and other data. */
276                 dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
277                                 otherstart - INT16SZ - INT16SZ,
278                                 (unsigned)otherfieldlen + INT16SZ + INT16SZ,
279                                 NULL, 0);
280
281                 n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
282                                     sigstart, sigfieldlen);
283
284                 if (n < 0)
285                         return ISC_R_BADSIG;
286
287                 if (sig != NULL && siglen != NULL) {
288                         if (*siglen < sigfieldlen)
289                                 return ISC_R_NOSPACE;
290                         memcpy(sig, sigstart, sigfieldlen);
291                         *siglen = sigfieldlen;
292                 }
293         } else {
294                 if (sigfieldlen > 0)
295                         return ISC_R_FORMERR;
296                 if (sig != NULL && siglen != NULL)
297                         *siglen = 0;
298         }
299
300         /* Reset the counter, since we still need to check for badtime. */
301         hp->arcount = htons(ntohs(hp->arcount) + 1);
302
303         /* Verify the time. */
304         if (abs((*timesigned) - time(NULL)) > fudge)
305                 return ISC_R_BADTIME;
306
307         if (nostrip == 0) {
308                 *msglen = recstart - msg;
309                 hp->arcount = htons(ntohs(hp->arcount) - 1);
310         }
311
312         if (error != NOERROR)
313                 return ns_rcode_to_isc (error);
314
315         return ISC_R_SUCCESS;
316 }
317
318 #if 0
319 isc_result_t
320 ns_verify_tcp_init(void *k, const u_char *querysig, unsigned querysiglen,
321                    ns_tcp_tsig_state *state)
322 {
323         dst_init();
324         if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
325                 return ISC_R_INVALIDARG;
326         state->counter = -1;
327         state->key = k;
328         if (state->key->dk_alg != KEY_HMAC_MD5)
329                 return ISC_R_BADKEY;
330         if (querysiglen > sizeof(state->sig))
331                 return ISC_R_NOSPACE;
332         memcpy(state->sig, querysig, querysiglen);
333         state->siglen = querysiglen;
334         return ISC_R_SUCCESS;
335 }
336
337 isc_result_t
338 ns_verify_tcp(u_char *msg, unsigned *msglen, ns_tcp_tsig_state *state,
339               int required)
340 {
341         HEADER *hp = (HEADER *)msg;
342         u_char *recstart, *rdatastart, *sigstart;
343         unsigned sigfieldlen, otherfieldlen;
344         u_char *cp, *eom = msg + *msglen, *cp2;
345         char name[MAXDNAME], alg[MAXDNAME];
346         u_char buf[MAXDNAME];
347         int n, type, length, fudge, id, error;
348         time_t timesigned;
349
350         if (msg == NULL || msglen == NULL || state == NULL)
351                 return ISC_R_INVALIDARG;
352
353         state->counter++;
354         if (state->counter == 0)
355                 return (ns_verify(msg, msglen, state->key,
356                                   state->sig, state->siglen,
357                                   state->sig, &state->siglen, &timesigned, 0));
358
359         if (state->siglen > 0) {
360                 u_int16_t siglen_n = htons(state->siglen);
361
362                 dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx,
363                                 NULL, 0, NULL, 0);
364                 dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
365                                 (u_char *)&siglen_n, INT16SZ, NULL, 0);
366                 dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
367                                 state->sig, state->siglen, NULL, 0);
368                 state->siglen = 0;
369         }
370
371         cp = recstart = ns_find_tsig(msg, eom);
372
373         if (recstart == NULL) {
374                 if (required)
375                         return ISC_R_NO_TSIG;
376                 dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
377                                 msg, *msglen, NULL, 0);
378                 return ISC_R_SUCCESS;
379         }
380
381         hp->arcount = htons(ntohs(hp->arcount) - 1);
382         dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
383                         msg, (unsigned)(recstart - msg), NULL, 0);
384         
385         /* Read the key name. */
386         n = dn_expand(msg, eom, cp, name, MAXDNAME);
387         if (n < 0)
388                 return ISC_R_FORMERR;
389         cp += n;
390
391         /* Read the type. */
392         BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ);
393         GETSHORT(type, cp);
394         if (type != ns_t_tsig)
395                 return ISC_R_NO_TSIG;
396
397         /* Skip the class and TTL, save the length. */
398         cp += INT16SZ + INT32SZ;
399         GETSHORT(length, cp);
400         if (eom - cp != length)
401                 return ISC_R_FORMERR;
402
403         /* Read the algorithm name. */
404         rdatastart = cp;
405         n = dn_expand(msg, eom, cp, alg, MAXDNAME);
406         if (n < 0)
407                 return ISC_R_FORMERR;
408         if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
409                 return ISC_R_BADKEY;
410         cp += n;
411
412         /* Verify that the key used is OK. */
413         if ((ns_samename(state->key->dk_key_name, name) != 1 ||
414              state->key->dk_alg != KEY_HMAC_MD5))
415                 return ISC_R_BADKEY;
416
417         /* Read the time signed and fudge. */
418         BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
419         cp += INT16SZ;
420         GETLONG(timesigned, cp);
421         GETSHORT(fudge, cp);
422
423         /* Read the signature. */
424         BOUNDS_CHECK(cp, INT16SZ);
425         GETSHORT(sigfieldlen, cp);
426         BOUNDS_CHECK(cp, sigfieldlen);
427         sigstart = cp;
428         cp += sigfieldlen;
429
430         /* Read the original id and error. */
431         BOUNDS_CHECK(cp, 2*INT16SZ);
432         GETSHORT(id, cp);
433         GETSHORT(error, cp);
434
435         /* Parse the other data. */
436         BOUNDS_CHECK(cp, INT16SZ);
437         GETSHORT(otherfieldlen, cp);
438         BOUNDS_CHECK(cp, otherfieldlen);
439         cp += otherfieldlen;
440
441         if (cp != eom)
442                 return ISC_R_FORMERR;
443
444         /*
445          * Do the verification.
446          */
447
448         /* Digest the time signed and fudge. */
449         cp2 = buf;
450         PUTSHORT(0, cp2);       /* Top 16 bits of time. */
451         PUTLONG(timesigned, cp2);
452         PUTSHORT(NS_TSIG_FUDGE, cp2);
453
454         dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
455                         buf, (unsigned)(cp2 - buf), NULL, 0);
456
457         n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
458                             sigstart, sigfieldlen);
459         if (n < 0)
460                 return ISC_R_BADSIG;
461
462         if (sigfieldlen > sizeof(state->sig))
463                 return ISC_R_BADSIG;
464
465         if (sigfieldlen > sizeof(state->sig))
466                 return ISC_R_NOSPACE;
467
468         memcpy(state->sig, sigstart, sigfieldlen);
469         state->siglen = sigfieldlen;
470
471         /* Verify the time. */
472         if (abs(timesigned - time(NULL)) > fudge)
473                 return ISC_R_BADTIME;
474
475         *msglen = recstart - msg;
476
477         if (error != NOERROR)
478                 return ns_rcode_to_isc (error);
479
480         return ISC_R_SUCCESS;
481 }
482 #endif