1 : /*
2 : * security.c - helpers for security-related stuff
3 : * $Id: security.c 1828 2006-12-12 13:30:59Z remi $
4 : */
5 :
6 : /***********************************************************************
7 : * Copyright © 2004-2006 Rémi Denis-Courmont. *
8 : * This program is free software; you can redistribute and/or modify *
9 : * it under the terms of the GNU General Public License as published *
10 : * by the Free Software Foundation; version 2 of the license. *
11 : * *
12 : * This program is distributed in the hope that it will be useful, *
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
15 : * See the GNU General Public License for more details. *
16 : * *
17 : * You should have received a copy of the GNU General Public License *
18 : * along with this program; if not, you can get it from: *
19 : * http://www.gnu.org/copyleft/gpl.html *
20 : ***********************************************************************/
21 :
22 : #ifdef HAVE_CONFIG_H
23 : # include <config.h>
24 : #endif
25 :
26 : #include <stdbool.h>
27 : #include <string.h>
28 : #include <inttypes.h>
29 : #include <limits.h>
30 : #include <assert.h>
31 :
32 : #include <sys/types.h>
33 : #include <fcntl.h> /* open() */
34 : #include <unistd.h> /* read(), close() */
35 : #include <pthread.h>
36 : #include <netinet/in.h> /* struct in6_addr */
37 : #include <errno.h>
38 :
39 : #include "security.h"
40 : #include "debug.h"
41 : #include "md5.h"
42 :
43 : #if defined (__OpenBSD__) || defined (__OpenBSD_kernel__)
44 : static const char randfile[] = "/dev/srandom";
45 : #else
46 : static const char randfile[] = "/dev/random";
47 : #endif
48 :
49 :
50 : /* HMAC authentication */
51 : #define LIBTEREDO_KEY_LEN 16
52 : #define HMAC_BLOCK_LEN 64 /* block size in bytes for MD5 (or SHA1) */
53 : #if LIBTEREDO_KEY_LEN > HMAC_BLOCK_LEN
54 : # error HMAC key too long.
55 : #endif
56 :
57 : static union
58 : {
59 : unsigned char key[LIBTEREDO_KEY_LEN];
60 : unsigned char ipad[HMAC_BLOCK_LEN];
61 : } inner_key;
62 :
63 : static union
64 : {
65 : unsigned char key[LIBTEREDO_KEY_LEN];
66 : unsigned char opad[HMAC_BLOCK_LEN];
67 : } outer_key;
68 :
69 : // PID cannot be zero (otherwise, have fun using fork()!)
70 : static uint16_t hmac_pid = 0;
71 :
72 : int teredo_init_HMAC (void)
73 7 : {
74 : static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
75 7 : int retval = -1;
76 :
77 : #define return YOU_DONT_MEAN_return
78 7 : pthread_mutex_lock (&mutex);
79 :
80 7 : if (hmac_pid != htons ((uint16_t)getpid ()))
81 : {
82 : /* Get a non-predictable random key from the kernel PRNG */
83 5 : int fd = open (randfile, O_RDONLY);
84 5 : if (fd == -1)
85 0 : goto error;
86 :
87 5 : memset (&inner_key, 0, sizeof (inner_key));
88 :
89 15 : for (unsigned len = 0; len < LIBTEREDO_KEY_LEN;)
90 : {
91 5 : int val = read (fd, inner_key.key + len, LIBTEREDO_KEY_LEN - len);
92 5 : if (val > 0)
93 5 : len -= val;
94 : }
95 5 : close (fd);
96 :
97 : /* Precomputes HMAC padding */
98 5 : memcpy (&outer_key, &inner_key, sizeof (outer_key));
99 :
100 325 : for (unsigned i = 0; i < sizeof (inner_key); i++)
101 : {
102 320 : inner_key.ipad[i] ^= 0x36;
103 320 : outer_key.opad[i] ^= 0x5c;
104 : }
105 :
106 5 : hmac_pid = htons ((uint16_t)getpid ());
107 : }
108 7 : retval = 0;
109 :
110 7 : error:
111 7 : pthread_mutex_unlock (&mutex);
112 : #undef return
113 :
114 7 : return retval;
115 : }
116 :
117 :
118 : void teredo_deinit_HMAC (void)
119 7 : {
120 7 : }
121 :
122 :
123 : #define LIBTEREDO_HASH_LEN 16
124 :
125 : static void
126 : teredo_hash (const void *src, size_t slen, const void *dst, size_t dlen,
127 : uint8_t *restrict hash, uint32_t timestamp)
128 153 : {
129 : /* compute hash */
130 : md5_state_t ctx;
131 153 : md5_init (&ctx);
132 153 : md5_append (&ctx, inner_key.ipad, sizeof (inner_key.ipad));
133 153 : md5_append (&ctx, (const unsigned char *)src, slen);
134 153 : md5_append (&ctx, (const unsigned char *)dst, dlen);
135 153 : md5_append (&ctx, (const unsigned char *)&hmac_pid, sizeof (hmac_pid));
136 153 : md5_append (&ctx, (const unsigned char *)×tamp, sizeof (timestamp));
137 153 : md5_finish (&ctx, hash);
138 :
139 153 : md5_init (&ctx);
140 153 : md5_append (&ctx, outer_key.opad, sizeof (outer_key.opad));
141 153 : md5_append (&ctx, hash, LIBTEREDO_HASH_LEN);
142 153 : md5_finish (&ctx, hash);
143 153 : }
144 :
145 :
146 : #ifdef MIREDO_TEREDO_CLIENT
147 : /**
148 : * Generates a cryptographically strong hash to use a payload for ping
149 : * packets. That's how we authenticate the last hop of the echo reply
150 : * (i.e. Teredo relay) before ourselves as being a legitimate first hop
151 : * toward the echo request's destination.
152 : *
153 : * The hash includes a timestamp with a lifetime of 30 units (seconds),
154 : * source and destination addresses, process ID, and a secret pseudo-random
155 : * key.
156 : */
157 : static inline void
158 : teredo_pinghash (const struct in6_addr *src, const struct in6_addr *dst,
159 : uint8_t *restrict hash, uint32_t timestamp)
160 153 : {
161 153 : teredo_hash (src, sizeof (*src), dst, sizeof (*dst), hash, timestamp);
162 153 : }
163 :
164 :
165 : #if 0
166 : typedef struct teredo_hmac
167 : {
168 : uint16_t pid; /* ICMPv6 Echo id */
169 : uint16_t time; /* ICMPv6 Echo sequence */
170 : uint16_t epoch;
171 : unint8_t hash[LIBTEREDO_HASH_LEN]; /* ICMPv6 Echo payload */
172 : } teredo_hmac;
173 : #endif
174 :
175 : #if (LIBTEREDO_HASH_LEN + 6) != LIBTEREDO_HMAC_LEN
176 : # error Inconsistent hash and HMAC length
177 : #endif
178 :
179 :
180 : void
181 : teredo_get_pinghash (uint32_t timestamp, const struct in6_addr *src,
182 : const struct in6_addr *dst, uint8_t *restrict hash)
183 6 : {
184 : /* save hash-protected data */
185 6 : memcpy (hash, &hmac_pid, sizeof (hmac_pid));
186 6 : hash += sizeof (hmac_pid);
187 :
188 6 : timestamp = htonl (timestamp);
189 6 : memcpy (hash, ((uint8_t *)×tamp) + 2, 2);
190 6 : hash += 2;
191 6 : memcpy (hash, ×tamp, 2);
192 6 : hash += 2;
193 :
194 6 : teredo_pinghash (src, dst, hash, timestamp);
195 6 : }
196 :
197 :
198 : int
199 : teredo_verify_pinghash (uint32_t now, const struct in6_addr *src,
200 : const struct in6_addr *dst,
201 : const uint8_t *restrict hash)
202 174 : {
203 : /* Check ICMPv6 ID */
204 174 : if (memcmp (hash, &hmac_pid, sizeof (hmac_pid)))
205 6 : return -1;
206 168 : hash += sizeof (hmac_pid);
207 :
208 : /* Check ICMPv6 sequence */
209 : uint32_t timestamp;
210 168 : memcpy (((uint8_t *)×tamp) + 2, hash, 2);
211 168 : hash += 2;
212 168 : memcpy (×tamp, hash, 2);
213 168 : hash += 2;
214 :
215 168 : if (((now - ntohl (timestamp)) & 0xffffffff) >= 30)
216 21 : return -1; /* replay attack */
217 :
218 : unsigned char h1[LIBTEREDO_HASH_LEN];
219 147 : teredo_pinghash (src, dst, h1, timestamp);
220 :
221 : /* compare HMAC hash */
222 147 : return memcmp (h1, hash, LIBTEREDO_HASH_LEN) ? -1 : 0;
223 : }
224 : #endif /* MIREDO_TEREDO_CLIENT */
225 :
226 :
227 : #if LIBTEREDO_HASH_LEN < LIBTEREDO_NONCE_LEN
228 : # error Inconsistent hash size
229 : #endif
230 : void
231 : teredo_get_nonce (uint32_t timestamp, uint32_t ipv4, uint16_t port,
232 : uint8_t *restrict nonce)
233 0 : {
234 : uint8_t buf[LIBTEREDO_HASH_LEN];
235 :
236 0 : teredo_hash (&ipv4, 4, &port, 2, buf, timestamp);
237 0 : memcpy (nonce, buf, LIBTEREDO_NONCE_LEN);
238 0 : }
|