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