1 /* $OpenBSD: auth.c,v 1.23 2023/11/07 11:29:05 claudio 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
auth_validate(void * buf,u_int16_t len,struct iface * iface,struct nbr * nbr)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
auth_gen(struct ibuf * buf,struct iface * iface)140 auth_gen(struct ibuf *buf, struct iface *iface)
141 {
142 MD5_CTX hash;
143 u_int8_t digest[MD5_DIGEST_LENGTH];
144 struct crypt crypt;
145 struct auth_md *md;
146 u_int16_t chksum;
147
148 /* update length */
149 if (ibuf_size(buf) > USHRT_MAX)
150 fatalx("auth_gen: resulting ospf packet too big");
151 if (ibuf_set_n16(buf, offsetof(struct ospf_hdr, len),
152 ibuf_size(buf)) == -1)
153 fatalx("auth_gen: ibuf_set_n16 failed");
154
155 switch (iface->auth_type) {
156 case AUTH_NONE:
157 chksum = in_cksum(ibuf_data(buf), ibuf_size(buf));
158 if (ibuf_set(buf, offsetof(struct ospf_hdr, chksum),
159 &chksum, sizeof(chksum)) == -1)
160 fatalx("auth_gen: ibuf_set failed");
161 break;
162 case AUTH_SIMPLE:
163 chksum = in_cksum(ibuf_data(buf), ibuf_size(buf));
164 if (ibuf_set(buf, offsetof(struct ospf_hdr, chksum),
165 &chksum, sizeof(chksum)) == -1)
166 fatalx("auth_gen: ibuf_set failed");
167
168 if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key),
169 iface->auth_key, strnlen(iface->auth_key,
170 sizeof(iface->auth_key))) == -1)
171 fatalx("auth_gen: ibuf_set failed");
172 break;
173 case AUTH_CRYPT:
174 bzero(&crypt, sizeof(crypt));
175 crypt.keyid = iface->auth_keyid;
176 crypt.seq_num = htonl(iface->crypt_seq_num);
177 crypt.len = MD5_DIGEST_LENGTH;
178 iface->crypt_seq_num++;
179
180 if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key),
181 &crypt, sizeof(crypt)) == -1)
182 fatalx("auth_gen: ibuf_set failed");
183
184 /* insert plaintext key */
185 if ((md = md_list_find(&iface->auth_md_list,
186 iface->auth_keyid)) == NULL) {
187 log_debug("auth_gen: keyid %d not configured, "
188 "interface %s", iface->auth_keyid, iface->name);
189 return (-1);
190 }
191
192 bzero(digest, MD5_DIGEST_LENGTH);
193 strncpy(digest, md->key, MD5_DIGEST_LENGTH);
194
195 /* calculate MD5 digest */
196 MD5Init(&hash);
197 MD5Update(&hash, ibuf_data(buf), ibuf_size(buf));
198 MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
199 MD5Final(digest, &hash);
200
201 return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH));
202 default:
203 log_debug("auth_gen: unknown auth type, interface %s",
204 iface->name);
205 return (-1);
206 }
207
208 return (0);
209 }
210
211 /* md list */
212 void
md_list_add(struct auth_md_head * head,u_int8_t keyid,char * key)213 md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
214 {
215 struct auth_md *md;
216
217 if ((md = md_list_find(head, keyid)) != NULL) {
218 /* update key */
219 strncpy(md->key, key, sizeof(md->key));
220 return;
221 }
222
223 if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
224 fatalx("md_list_add");
225
226 md->keyid = keyid;
227 strncpy(md->key, key, sizeof(md->key));
228 TAILQ_INSERT_TAIL(head, md, entry);
229 }
230
231 void
md_list_copy(struct auth_md_head * to,struct auth_md_head * from)232 md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
233 {
234 struct auth_md *m, *md;
235
236 TAILQ_INIT(to);
237
238 TAILQ_FOREACH(m, from, entry) {
239 if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
240 fatalx("md_list_copy");
241
242 md->keyid = m->keyid;
243 strncpy(md->key, m->key, sizeof(md->key));
244 TAILQ_INSERT_TAIL(to, md, entry);
245 }
246 }
247
248 void
md_list_clr(struct auth_md_head * head)249 md_list_clr(struct auth_md_head *head)
250 {
251 struct auth_md *m;
252
253 while ((m = TAILQ_FIRST(head)) != NULL) {
254 TAILQ_REMOVE(head, m, entry);
255 free(m);
256 }
257 }
258
259 struct auth_md *
md_list_find(struct auth_md_head * head,u_int8_t keyid)260 md_list_find(struct auth_md_head *head, u_int8_t keyid)
261 {
262 struct auth_md *m;
263
264 TAILQ_FOREACH(m, head, entry)
265 if (m->keyid == keyid)
266 return (m);
267
268 return (NULL);
269 }
270
271 int
md_list_send(struct auth_md_head * head,struct imsgev * to)272 md_list_send(struct auth_md_head *head, struct imsgev *to)
273 {
274 struct auth_md *m;
275
276 TAILQ_FOREACH(m, head, entry)
277 if (imsg_compose_event(to, IMSG_RECONF_AUTHMD, 0, 0, -1, m,
278 sizeof(*m)) == -1)
279 return (-1);
280
281 return (0);
282 }
283