1 /* $OpenBSD: auth.c,v 1.14 2024/04/23 13:34:51 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
5 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <limits.h>
23 #include <md5.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "ripd.h"
29 #include "rip.h"
30 #include "log.h"
31 #include "ripe.h"
32
33 u_int32_t auth_calc_modulator(struct auth_md *md);
34 struct auth_md *md_list_find(struct auth_md_head *, u_int8_t);
35 void auth_trailer_header_gen(struct ibuf *);
36 u_int32_t auth_get_seq_num(struct auth_md*);
37
38 u_int32_t
auth_calc_modulator(struct auth_md * md)39 auth_calc_modulator(struct auth_md *md)
40 {
41 u_int32_t r;
42 MD5_CTX md5ctx;
43 u_int8_t digest[MD5_DIGEST_LENGTH];
44
45 MD5Init(&md5ctx);
46 MD5Update(&md5ctx, (void *)&md->keyid, sizeof(md->keyid));
47 MD5Update(&md5ctx, (void *)&md->key, MD5_DIGEST_LENGTH);
48 MD5Final(digest, &md5ctx);
49
50 bcopy(&digest, &r, sizeof(r));
51
52 return ((r >> 1) - time(NULL));
53 }
54
55 u_int32_t
auth_get_seq_num(struct auth_md * md)56 auth_get_seq_num(struct auth_md *md)
57 {
58 return (time(NULL) + md->seq_modulator);
59 }
60
61 void
auth_trailer_header_gen(struct ibuf * buf)62 auth_trailer_header_gen(struct ibuf *buf)
63 {
64 u_int16_t field1 = 0xFFFF;
65 u_int16_t field2 = htons(0x01);
66
67 ibuf_add(buf, &field1, sizeof(field1));
68 ibuf_add(buf, &field2, sizeof(field2));
69 }
70
71 /* XXX add the support for key lifetime and rollover */
72 int
auth_validate(u_int8_t ** buf,u_int16_t * len,struct iface * iface,struct nbr * nbr,struct nbr_failed * nbr_failed,u_int32_t * crypt_seq_num)73 auth_validate(u_int8_t **buf, u_int16_t *len, struct iface *iface,
74 struct nbr *nbr, struct nbr_failed *nbr_failed, u_int32_t *crypt_seq_num)
75 {
76 MD5_CTX hash;
77 u_int8_t digest[MD5_DIGEST_LENGTH];
78 u_int8_t recv_digest[MD5_DIGEST_LENGTH];
79 char pwd[MAX_SIMPLE_AUTH_LEN];
80 struct rip_auth *auth_head;
81 struct md5_auth *a;
82 struct auth_md *md;
83 u_int8_t *auth_data;
84 u_int8_t *b = *buf;
85
86 *buf += RIP_HDR_LEN;
87 *len -= RIP_HDR_LEN;
88
89 auth_head = (struct rip_auth *)(*buf);
90
91 if (auth_head->auth_fixed != AUTH) {
92 if (iface->auth_type != AUTH_NONE) {
93 log_debug("auth_validate: packet carrying no"
94 " authentication");
95 return (-1);
96 }
97 return (0);
98 } else {
99 if (ntohs(auth_head->auth_type) !=
100 (u_int16_t)iface->auth_type) {
101 log_debug("auth_validate: wrong auth type");
102 return (-1);
103 }
104 }
105
106 switch (iface->auth_type) {
107 case AUTH_SIMPLE:
108 bcopy(*buf+sizeof(*auth_head), pwd, MAX_SIMPLE_AUTH_LEN);
109 if (bcmp(pwd, iface->auth_key, MAX_SIMPLE_AUTH_LEN)) {
110 log_debug("auth_validate: wrong password, "
111 "interface: %s", iface->name);
112 return (-1);
113 }
114 break;
115 case AUTH_CRYPT:
116 a = (struct md5_auth *)(*buf + sizeof(*auth_head));
117
118 if ((md = md_list_find(&iface->auth_md_list,
119 a->auth_keyid)) == NULL) {
120 log_debug("auth_validate: keyid %d not configured, "
121 "interface %s", a->auth_keyid,
122 iface->name);
123 return (-1);
124 }
125
126 if (nbr != NULL) {
127 if (ntohl(a->auth_seq) < nbr->auth_seq_num) {
128 log_debug("auth_validate: decreasing seq num, "
129 "interface %s", iface->name);
130 return (-1);
131 }
132 } else if (nbr_failed != NULL) {
133 if (ntohl(a->auth_seq) < nbr_failed->auth_seq_num &&
134 ntohl(a->auth_seq)) {
135 log_debug("auth_validate: decreasing seq num, "
136 "interface %s", iface->name);
137 return (-1);
138 }
139 }
140
141 /* XXX: maybe validate also the trailer header */
142 if (a->auth_length != MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN) {
143 log_debug("auth_validate: invalid key length, "
144 "interface %s", iface->name);
145 return (-1);
146 }
147
148 if (ntohs(a->auth_offset) != *len + RIP_HDR_LEN -
149 AUTH_TRLR_HDR_LEN - MD5_DIGEST_LENGTH) {
150 log_debug("auth_validate: invalid authentication data "
151 "offset %hu, interface %s", ntohs(a->auth_offset),
152 iface->name);
153 return (-1);
154 }
155
156 auth_data = *buf;
157 auth_data += ntohs(a->auth_offset);
158
159 /* save the received digest and clear it in the packet */
160 bcopy(auth_data, recv_digest, sizeof(recv_digest));
161 bzero(auth_data, MD5_DIGEST_LENGTH);
162
163 /* insert plaintext key */
164 memcpy(digest, md->key, MD5_DIGEST_LENGTH);
165
166 /* calculate MD5 digest */
167 MD5Init(&hash);
168 MD5Update(&hash, b, ntohs(a->auth_offset) + RIP_HDR_LEN);
169 MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
170 MD5Final(digest, &hash);
171
172 if (bcmp(recv_digest, digest, sizeof(digest))) {
173 log_debug("auth_validate: invalid MD5 digest, "
174 "interface %s", iface->name);
175 return (-1);
176 }
177
178 *crypt_seq_num = ntohl(a->auth_seq);
179
180 *len -= AUTH_TRLR_HDR_LEN + MD5_DIGEST_LENGTH;
181
182 break;
183 default:
184 log_debug("auth_validate: unknown auth type, interface %s",
185 iface->name);
186 return (-1);
187 }
188
189 *buf += RIP_ENTRY_LEN;
190 *len -= RIP_ENTRY_LEN;
191
192 return (0);
193 }
194
195 int
auth_gen(struct ibuf * buf,struct iface * iface)196 auth_gen(struct ibuf *buf, struct iface *iface)
197 {
198 struct rip_auth auth_head;
199 struct md5_auth a;
200 struct auth_md *md;
201
202 auth_head.auth_fixed = AUTH;
203 auth_head.auth_type = htons(iface->auth_type);
204
205 ibuf_add(buf, &auth_head, sizeof(auth_head));
206
207 switch (iface->auth_type) {
208 case AUTH_SIMPLE:
209 ibuf_add(buf, &iface->auth_key, MAX_SIMPLE_AUTH_LEN);
210 break;
211 case AUTH_CRYPT:
212 if ((md = md_list_find(&iface->auth_md_list,
213 iface->auth_keyid)) == NULL) {
214 log_debug("auth_gen: keyid %d not configured, "
215 "interface %s", iface->auth_keyid, iface->name);
216 return (-1);
217 }
218 bzero(&a, sizeof(a));
219 a.auth_keyid = iface->auth_keyid;
220 a.auth_seq = htonl(auth_get_seq_num(md));
221 a.auth_length = MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN;
222
223 ibuf_add(buf, &a, sizeof(a));
224 break;
225 default:
226 log_debug("auth_gen: unknown auth type, interface %s",
227 iface->name);
228 return (-1);
229 }
230
231 return (0);
232 }
233
234 int
auth_add_trailer(struct ibuf * buf,struct iface * iface)235 auth_add_trailer(struct ibuf *buf, struct iface *iface)
236 {
237 MD5_CTX hash;
238 u_int8_t digest[MD5_DIGEST_LENGTH];
239 struct auth_md *md;
240 size_t pos;
241
242 pos = sizeof(struct rip_hdr) + sizeof(struct rip_auth) +
243 offsetof(struct md5_auth, auth_offset);
244
245 /* add offset to header */
246 if (ibuf_set_n16(buf, pos, ibuf_size(buf)) == -1)
247 return (-1);
248
249 /* insert plaintext key */
250 if ((md = md_list_find(&iface->auth_md_list,
251 iface->auth_keyid)) == NULL) {
252 log_debug("auth_add_trailer: keyid %d not configured, "
253 "interface %s", iface->auth_keyid, iface->name);
254 return (-1);
255 }
256
257 memcpy(digest, md->key, MD5_DIGEST_LENGTH);
258
259 auth_trailer_header_gen(buf);
260
261 /* calculate MD5 digest */
262 MD5Init(&hash);
263 MD5Update(&hash, ibuf_data(buf), ibuf_size(buf));
264 MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
265 MD5Final(digest, &hash);
266
267 return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH));
268 }
269
270 /* md list */
271 int
md_list_add(struct auth_md_head * head,u_int8_t keyid,char * key)272 md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
273 {
274 struct auth_md *md;
275
276 if (strlen(key) > MD5_DIGEST_LENGTH)
277 return (-1);
278
279 if ((md = md_list_find(head, keyid)) != NULL) {
280 /* update key */
281 bzero(md->key, sizeof(md->key));
282 memcpy(md->key, key, strlen(key));
283 return (0);
284 }
285
286 if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
287 fatalx("md_list_add");
288
289 md->keyid = keyid;
290 memcpy(md->key, key, strlen(key));
291 md->seq_modulator = auth_calc_modulator(md);
292 TAILQ_INSERT_TAIL(head, md, entry);
293
294 return (0);
295 }
296
297 void
md_list_copy(struct auth_md_head * to,struct auth_md_head * from)298 md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
299 {
300 struct auth_md *m, *md;
301
302 TAILQ_INIT(to);
303
304 TAILQ_FOREACH(m, from, entry) {
305 if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
306 fatalx("md_list_copy");
307
308 md->keyid = m->keyid;
309 memcpy(md->key, m->key, sizeof(md->key));
310 md->seq_modulator = m->seq_modulator;
311 TAILQ_INSERT_TAIL(to, md, entry);
312 }
313 }
314
315 void
md_list_clr(struct auth_md_head * head)316 md_list_clr(struct auth_md_head *head)
317 {
318 struct auth_md *m;
319
320 while ((m = TAILQ_FIRST(head)) != NULL) {
321 TAILQ_REMOVE(head, m, entry);
322 free(m);
323 }
324 }
325
326 struct auth_md *
md_list_find(struct auth_md_head * head,u_int8_t keyid)327 md_list_find(struct auth_md_head *head, u_int8_t keyid)
328 {
329 struct auth_md *m;
330
331 TAILQ_FOREACH(m, head, entry)
332 if (m->keyid == keyid)
333 return (m);
334
335 return (NULL);
336 }
337