1 |
|
|
/* $OpenBSD: auth.c,v 1.20 2015/05/05 01:26:37 jsg Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
#include <sys/types.h> |
20 |
|
|
#include <sys/socket.h> |
21 |
|
|
#include <limits.h> |
22 |
|
|
#include <md5.h> |
23 |
|
|
#include <stdlib.h> |
24 |
|
|
#include <string.h> |
25 |
|
|
|
26 |
|
|
#include "ospfd.h" |
27 |
|
|
#include "ospf.h" |
28 |
|
|
#include "log.h" |
29 |
|
|
#include "ospfe.h" |
30 |
|
|
|
31 |
|
|
struct auth_md *md_list_find(struct auth_md_head *, u_int8_t); |
32 |
|
|
|
33 |
|
|
int |
34 |
|
|
auth_validate(void *buf, u_int16_t len, struct iface *iface, struct nbr *nbr) |
35 |
|
|
{ |
36 |
|
|
MD5_CTX hash; |
37 |
|
|
u_int8_t digest[MD5_DIGEST_LENGTH]; |
38 |
|
|
u_int8_t recv_digest[MD5_DIGEST_LENGTH]; |
39 |
|
|
struct ospf_hdr *ospf_hdr = buf; |
40 |
|
|
struct auth_md *md; |
41 |
|
|
char *auth_data; |
42 |
|
|
|
43 |
|
|
if (ntohs(ospf_hdr->auth_type) != (u_int16_t)iface->auth_type) { |
44 |
|
|
log_debug("auth_validate: wrong auth type, interface %s", |
45 |
|
|
iface->name); |
46 |
|
|
return (-1); |
47 |
|
|
} |
48 |
|
|
|
49 |
|
|
switch (iface->auth_type) { |
50 |
|
|
case AUTH_SIMPLE: |
51 |
|
|
if (memcmp(ospf_hdr->auth_key.simple, iface->auth_key, |
52 |
|
|
sizeof(ospf_hdr->auth_key.simple))) { |
53 |
|
|
log_debug("auth_validate: wrong password, interface %s", |
54 |
|
|
iface->name); |
55 |
|
|
return (-1); |
56 |
|
|
} |
57 |
|
|
/* FALLTHROUGH */ |
58 |
|
|
case AUTH_NONE: |
59 |
|
|
/* clear the key before chksum */ |
60 |
|
|
bzero(ospf_hdr->auth_key.simple, |
61 |
|
|
sizeof(ospf_hdr->auth_key.simple)); |
62 |
|
|
|
63 |
|
|
if (in_cksum(ospf_hdr, ntohs(ospf_hdr->len))) { |
64 |
|
|
log_debug("auth_validate: invalid checksum, " |
65 |
|
|
"interface %s", iface->name); |
66 |
|
|
return (-1); |
67 |
|
|
} |
68 |
|
|
break; |
69 |
|
|
case AUTH_CRYPT: |
70 |
|
|
/* |
71 |
|
|
* We must allow keys that are configured on the interface |
72 |
|
|
* but not necessarily set as the transmit key |
73 |
|
|
* (iface->auth_keyid). This allows for key rotation to new |
74 |
|
|
* keys without taking down the network. |
75 |
|
|
*/ |
76 |
|
|
if ((md = md_list_find(&iface->auth_md_list, |
77 |
|
|
ospf_hdr->auth_key.crypt.keyid)) == NULL) { |
78 |
|
|
log_debug("auth_validate: keyid %d not configured, " |
79 |
|
|
"interface %s", ospf_hdr->auth_key.crypt.keyid, |
80 |
|
|
iface->name); |
81 |
|
|
return (-1); |
82 |
|
|
} |
83 |
|
|
|
84 |
|
|
if (nbr != NULL && ntohl(ospf_hdr->auth_key.crypt.seq_num) < |
85 |
|
|
nbr->crypt_seq_num) { |
86 |
|
|
log_debug("auth_validate: decreasing seq num, " |
87 |
|
|
"interface %s", iface->name); |
88 |
|
|
return (-1); |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
if (ospf_hdr->auth_key.crypt.len != MD5_DIGEST_LENGTH) { |
92 |
|
|
log_debug("auth_validate: invalid key length, " |
93 |
|
|
"interface %s", iface->name); |
94 |
|
|
return (-1); |
95 |
|
|
} |
96 |
|
|
|
97 |
|
|
if (len - ntohs(ospf_hdr->len) < MD5_DIGEST_LENGTH) { |
98 |
|
|
log_debug("auth_validate: invalid key length, " |
99 |
|
|
"interface %s", iface->name); |
100 |
|
|
return (-1); |
101 |
|
|
} |
102 |
|
|
|
103 |
|
|
auth_data = buf; |
104 |
|
|
auth_data += ntohs(ospf_hdr->len); |
105 |
|
|
|
106 |
|
|
/* save the received digest and clear it in the packet */ |
107 |
|
|
memcpy(recv_digest, auth_data, sizeof(recv_digest)); |
108 |
|
|
bzero(auth_data, MD5_DIGEST_LENGTH); |
109 |
|
|
|
110 |
|
|
/* insert plaintext key */ |
111 |
|
|
bzero(digest, MD5_DIGEST_LENGTH); |
112 |
|
|
strncpy(digest, md->key, MD5_DIGEST_LENGTH); |
113 |
|
|
|
114 |
|
|
/* calculate MD5 digest */ |
115 |
|
|
MD5Init(&hash); |
116 |
|
|
MD5Update(&hash, buf, ntohs(ospf_hdr->len)); |
117 |
|
|
MD5Update(&hash, digest, MD5_DIGEST_LENGTH); |
118 |
|
|
MD5Final(digest, &hash); |
119 |
|
|
|
120 |
|
|
if (memcmp(recv_digest, digest, sizeof(digest))) { |
121 |
|
|
log_debug("auth_validate: invalid MD5 digest, " |
122 |
|
|
"interface %s", iface->name); |
123 |
|
|
return (-1); |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
if (nbr != NULL) |
127 |
|
|
nbr->crypt_seq_num = |
128 |
|
|
ntohl(ospf_hdr->auth_key.crypt.seq_num); |
129 |
|
|
break; |
130 |
|
|
default: |
131 |
|
|
log_debug("auth_validate: unknown auth type, interface %s", |
132 |
|
|
iface->name); |
133 |
|
|
return (-1); |
134 |
|
|
} |
135 |
|
|
|
136 |
|
|
return (0); |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
int |
140 |
|
|
auth_gen(struct ibuf *buf, struct iface *iface) |
141 |
|
|
{ |
142 |
|
|
MD5_CTX hash; |
143 |
|
|
u_int8_t digest[MD5_DIGEST_LENGTH]; |
144 |
|
|
struct ospf_hdr *ospf_hdr; |
145 |
|
|
struct auth_md *md; |
146 |
|
|
|
147 |
|
|
if ((ospf_hdr = ibuf_seek(buf, 0, sizeof(*ospf_hdr))) == NULL) |
148 |
|
|
fatalx("auth_gen: buf_seek failed"); |
149 |
|
|
|
150 |
|
|
/* update length */ |
151 |
|
|
if (ibuf_size(buf) > USHRT_MAX) |
152 |
|
|
fatalx("auth_gen: resulting ospf packet too big"); |
153 |
|
|
ospf_hdr->len = htons(ibuf_size(buf)); |
154 |
|
|
/* clear auth_key field */ |
155 |
|
|
bzero(ospf_hdr->auth_key.simple, sizeof(ospf_hdr->auth_key.simple)); |
156 |
|
|
|
157 |
|
|
switch (iface->auth_type) { |
158 |
|
|
case AUTH_NONE: |
159 |
|
|
ospf_hdr->chksum = in_cksum(buf->buf, ibuf_size(buf)); |
160 |
|
|
break; |
161 |
|
|
case AUTH_SIMPLE: |
162 |
|
|
ospf_hdr->chksum = in_cksum(buf->buf, ibuf_size(buf)); |
163 |
|
|
|
164 |
|
|
strncpy(ospf_hdr->auth_key.simple, iface->auth_key, |
165 |
|
|
sizeof(ospf_hdr->auth_key.simple)); |
166 |
|
|
break; |
167 |
|
|
case AUTH_CRYPT: |
168 |
|
|
ospf_hdr->chksum = 0; |
169 |
|
|
ospf_hdr->auth_key.crypt.keyid = iface->auth_keyid; |
170 |
|
|
ospf_hdr->auth_key.crypt.seq_num = htonl(iface->crypt_seq_num); |
171 |
|
|
ospf_hdr->auth_key.crypt.len = MD5_DIGEST_LENGTH; |
172 |
|
|
iface->crypt_seq_num++; |
173 |
|
|
|
174 |
|
|
/* insert plaintext key */ |
175 |
|
|
if ((md = md_list_find(&iface->auth_md_list, |
176 |
|
|
iface->auth_keyid)) == NULL) { |
177 |
|
|
log_debug("auth_gen: keyid %d not configured, " |
178 |
|
|
"interface %s", iface->auth_keyid, iface->name); |
179 |
|
|
return (-1); |
180 |
|
|
} |
181 |
|
|
|
182 |
|
|
bzero(digest, MD5_DIGEST_LENGTH); |
183 |
|
|
strncpy(digest, md->key, MD5_DIGEST_LENGTH); |
184 |
|
|
|
185 |
|
|
/* calculate MD5 digest */ |
186 |
|
|
MD5Init(&hash); |
187 |
|
|
MD5Update(&hash, buf->buf, ibuf_size(buf)); |
188 |
|
|
MD5Update(&hash, digest, MD5_DIGEST_LENGTH); |
189 |
|
|
MD5Final(digest, &hash); |
190 |
|
|
|
191 |
|
|
return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH)); |
192 |
|
|
default: |
193 |
|
|
log_debug("auth_gen: unknown auth type, interface %s", |
194 |
|
|
iface->name); |
195 |
|
|
return (-1); |
196 |
|
|
} |
197 |
|
|
|
198 |
|
|
return (0); |
199 |
|
|
} |
200 |
|
|
|
201 |
|
|
/* md list */ |
202 |
|
|
void |
203 |
|
|
md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key) |
204 |
|
|
{ |
205 |
|
|
struct auth_md *md; |
206 |
|
|
|
207 |
|
|
if ((md = md_list_find(head, keyid)) != NULL) { |
208 |
|
|
/* update key */ |
209 |
|
|
strncpy(md->key, key, sizeof(md->key)); |
210 |
|
|
return; |
211 |
|
|
} |
212 |
|
|
|
213 |
|
|
if ((md = calloc(1, sizeof(struct auth_md))) == NULL) |
214 |
|
|
fatalx("md_list_add"); |
215 |
|
|
|
216 |
|
|
md->keyid = keyid; |
217 |
|
|
strncpy(md->key, key, sizeof(md->key)); |
218 |
|
|
TAILQ_INSERT_TAIL(head, md, entry); |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
void |
222 |
|
|
md_list_copy(struct auth_md_head *to, struct auth_md_head *from) |
223 |
|
|
{ |
224 |
|
|
struct auth_md *m, *md; |
225 |
|
|
|
226 |
|
96 |
TAILQ_INIT(to); |
227 |
|
|
|
228 |
✗✓ |
96 |
TAILQ_FOREACH(m, from, entry) { |
229 |
|
|
if ((md = calloc(1, sizeof(struct auth_md))) == NULL) |
230 |
|
|
fatalx("md_list_copy"); |
231 |
|
|
|
232 |
|
|
md->keyid = m->keyid; |
233 |
|
|
strncpy(md->key, m->key, sizeof(md->key)); |
234 |
|
|
TAILQ_INSERT_TAIL(to, md, entry); |
235 |
|
|
} |
236 |
|
48 |
} |
237 |
|
|
|
238 |
|
|
void |
239 |
|
|
md_list_clr(struct auth_md_head *head) |
240 |
|
|
{ |
241 |
|
|
struct auth_md *m; |
242 |
|
|
|
243 |
✗✓ |
192 |
while ((m = TAILQ_FIRST(head)) != NULL) { |
244 |
|
|
TAILQ_REMOVE(head, m, entry); |
245 |
|
|
free(m); |
246 |
|
|
} |
247 |
|
64 |
} |
248 |
|
|
|
249 |
|
|
struct auth_md * |
250 |
|
|
md_list_find(struct auth_md_head *head, u_int8_t keyid) |
251 |
|
|
{ |
252 |
|
|
struct auth_md *m; |
253 |
|
|
|
254 |
|
|
TAILQ_FOREACH(m, head, entry) |
255 |
|
|
if (m->keyid == keyid) |
256 |
|
|
return (m); |
257 |
|
|
|
258 |
|
|
return (NULL); |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
int |
262 |
|
|
md_list_send(struct auth_md_head *head, struct imsgev *to) |
263 |
|
|
{ |
264 |
|
|
struct auth_md *m; |
265 |
|
|
|
266 |
|
|
TAILQ_FOREACH(m, head, entry) |
267 |
|
|
if (imsg_compose_event(to, IMSG_RECONF_AUTHMD, 0, 0, -1, m, |
268 |
|
|
sizeof(*m)) == -1) |
269 |
|
|
return (-1); |
270 |
|
|
|
271 |
|
|
return (0); |
272 |
|
|
} |