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
33u_int32_t	 auth_calc_modulator(struct auth_md *md);
34struct auth_md	*md_list_find(struct auth_md_head *, u_int8_t);
35void		 auth_trailer_header_gen(struct ibuf *);
36u_int32_t	 auth_get_seq_num(struct auth_md*);
37
38u_int32_t
39auth_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
55u_int32_t
56auth_get_seq_num(struct auth_md *md)
57{
58	return (time(NULL) + md->seq_modulator);
59}
60
61void
62auth_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 */
72int
73auth_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
195int
196auth_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
234int
235auth_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 */
271int
272md_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
297void
298md_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
315void
316md_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
326struct auth_md *
327md_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