1*4cf3f6fdSclaudio /* $OpenBSD: auth.c,v 1.23 2023/11/07 11:29:05 claudio Exp $ */
2204df0f8Sclaudio
3204df0f8Sclaudio /*
4367f601bSnorby * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5204df0f8Sclaudio *
6204df0f8Sclaudio * Permission to use, copy, modify, and distribute this software for any
7204df0f8Sclaudio * purpose with or without fee is hereby granted, provided that the above
8204df0f8Sclaudio * copyright notice and this permission notice appear in all copies.
9204df0f8Sclaudio *
10204df0f8Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11204df0f8Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12204df0f8Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13204df0f8Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14204df0f8Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15204df0f8Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16204df0f8Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17204df0f8Sclaudio */
18204df0f8Sclaudio
19204df0f8Sclaudio #include <sys/types.h>
20204df0f8Sclaudio #include <sys/socket.h>
210491ce75Sclaudio #include <limits.h>
2203431b74Snorby #include <md5.h>
2303431b74Snorby #include <stdlib.h>
24204df0f8Sclaudio #include <string.h>
25204df0f8Sclaudio
26204df0f8Sclaudio #include "ospfd.h"
27204df0f8Sclaudio #include "ospf.h"
28204df0f8Sclaudio #include "log.h"
29204df0f8Sclaudio #include "ospfe.h"
30204df0f8Sclaudio
3176b51f83Sclaudio struct auth_md *md_list_find(struct auth_md_head *, u_int8_t);
3276b51f83Sclaudio
33204df0f8Sclaudio int
auth_validate(void * buf,u_int16_t len,struct iface * iface,struct nbr * nbr)3403431b74Snorby auth_validate(void *buf, u_int16_t len, struct iface *iface, struct nbr *nbr)
35204df0f8Sclaudio {
3603431b74Snorby MD5_CTX hash;
3703431b74Snorby u_int8_t digest[MD5_DIGEST_LENGTH];
3803431b74Snorby u_int8_t recv_digest[MD5_DIGEST_LENGTH];
3903431b74Snorby struct ospf_hdr *ospf_hdr = buf;
4003431b74Snorby struct auth_md *md;
4103431b74Snorby char *auth_data;
4203431b74Snorby
4303431b74Snorby if (ntohs(ospf_hdr->auth_type) != (u_int16_t)iface->auth_type) {
44204df0f8Sclaudio log_debug("auth_validate: wrong auth type, interface %s",
45204df0f8Sclaudio iface->name);
46204df0f8Sclaudio return (-1);
47204df0f8Sclaudio }
48204df0f8Sclaudio
49204df0f8Sclaudio switch (iface->auth_type) {
50204df0f8Sclaudio case AUTH_SIMPLE:
51171085cbSclaudio if (memcmp(ospf_hdr->auth_key.simple, iface->auth_key,
5203431b74Snorby sizeof(ospf_hdr->auth_key.simple))) {
53204df0f8Sclaudio log_debug("auth_validate: wrong password, interface %s",
54204df0f8Sclaudio iface->name);
55204df0f8Sclaudio return (-1);
56204df0f8Sclaudio }
5703431b74Snorby /* FALLTHROUGH */
5803431b74Snorby case AUTH_NONE:
59204df0f8Sclaudio /* clear the key before chksum */
6003431b74Snorby bzero(ospf_hdr->auth_key.simple,
6103431b74Snorby sizeof(ospf_hdr->auth_key.simple));
6203431b74Snorby
6303431b74Snorby if (in_cksum(ospf_hdr, ntohs(ospf_hdr->len))) {
64aeb9d7c6Sclaudio log_debug("auth_validate: invalid checksum, "
65aeb9d7c6Sclaudio "interface %s", iface->name);
66204df0f8Sclaudio return (-1);
6703431b74Snorby }
6803431b74Snorby break;
6903431b74Snorby case AUTH_CRYPT:
702ac453d1Sclaudio /*
712ac453d1Sclaudio * We must allow keys that are configured on the interface
722ac453d1Sclaudio * but not necessarily set as the transmit key
732ac453d1Sclaudio * (iface->auth_keyid). This allows for key rotation to new
742ac453d1Sclaudio * keys without taking down the network.
752ac453d1Sclaudio */
7676b51f83Sclaudio if ((md = md_list_find(&iface->auth_md_list,
7776b51f83Sclaudio ospf_hdr->auth_key.crypt.keyid)) == NULL) {
782ac453d1Sclaudio log_debug("auth_validate: keyid %d not configured, "
792ac453d1Sclaudio "interface %s", ospf_hdr->auth_key.crypt.keyid,
802ac453d1Sclaudio iface->name);
8103431b74Snorby return (-1);
8203431b74Snorby }
8303431b74Snorby
8403431b74Snorby if (nbr != NULL && ntohl(ospf_hdr->auth_key.crypt.seq_num) <
8503431b74Snorby nbr->crypt_seq_num) {
8603431b74Snorby log_debug("auth_validate: decreasing seq num, "
8703431b74Snorby "interface %s", iface->name);
8803431b74Snorby return (-1);
8903431b74Snorby }
9003431b74Snorby
9103431b74Snorby if (ospf_hdr->auth_key.crypt.len != MD5_DIGEST_LENGTH) {
9203431b74Snorby log_debug("auth_validate: invalid key length, "
9303431b74Snorby "interface %s", iface->name);
9403431b74Snorby return (-1);
9503431b74Snorby }
9603431b74Snorby
9703431b74Snorby if (len - ntohs(ospf_hdr->len) < MD5_DIGEST_LENGTH) {
9803431b74Snorby log_debug("auth_validate: invalid key length, "
9903431b74Snorby "interface %s", iface->name);
10003431b74Snorby return (-1);
10103431b74Snorby }
10203431b74Snorby
10303431b74Snorby auth_data = buf;
10403431b74Snorby auth_data += ntohs(ospf_hdr->len);
10503431b74Snorby
10603431b74Snorby /* save the received digest and clear it in the packet */
107171085cbSclaudio memcpy(recv_digest, auth_data, sizeof(recv_digest));
10803431b74Snorby bzero(auth_data, MD5_DIGEST_LENGTH);
10903431b74Snorby
11003431b74Snorby /* insert plaintext key */
11103431b74Snorby bzero(digest, MD5_DIGEST_LENGTH);
11203431b74Snorby strncpy(digest, md->key, MD5_DIGEST_LENGTH);
11303431b74Snorby
11403431b74Snorby /* calculate MD5 digest */
11503431b74Snorby MD5Init(&hash);
11603431b74Snorby MD5Update(&hash, buf, ntohs(ospf_hdr->len));
11703431b74Snorby MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
11803431b74Snorby MD5Final(digest, &hash);
11903431b74Snorby
120171085cbSclaudio if (memcmp(recv_digest, digest, sizeof(digest))) {
12103431b74Snorby log_debug("auth_validate: invalid MD5 digest, "
12203431b74Snorby "interface %s", iface->name);
12303431b74Snorby return (-1);
12403431b74Snorby }
12503431b74Snorby
12603431b74Snorby if (nbr != NULL)
12703431b74Snorby nbr->crypt_seq_num =
12803431b74Snorby ntohl(ospf_hdr->auth_key.crypt.seq_num);
12903431b74Snorby break;
13003431b74Snorby default:
13103431b74Snorby log_debug("auth_validate: unknown auth type, interface %s",
132204df0f8Sclaudio iface->name);
133204df0f8Sclaudio return (-1);
134204df0f8Sclaudio }
135204df0f8Sclaudio
136204df0f8Sclaudio return (0);
137204df0f8Sclaudio }
138204df0f8Sclaudio
139204df0f8Sclaudio int
auth_gen(struct ibuf * buf,struct iface * iface)140e39620e5Snicm auth_gen(struct ibuf *buf, struct iface *iface)
141204df0f8Sclaudio {
14203431b74Snorby MD5_CTX hash;
14303431b74Snorby u_int8_t digest[MD5_DIGEST_LENGTH];
14443e70bb4Sclaudio struct crypt crypt;
14503431b74Snorby struct auth_md *md;
14643e70bb4Sclaudio u_int16_t chksum;
147204df0f8Sclaudio
14803431b74Snorby /* update length */
149e39620e5Snicm if (ibuf_size(buf) > USHRT_MAX)
150ae61edb2Sstevesk fatalx("auth_gen: resulting ospf packet too big");
15143e70bb4Sclaudio if (ibuf_set_n16(buf, offsetof(struct ospf_hdr, len),
15243e70bb4Sclaudio ibuf_size(buf)) == -1)
15343e70bb4Sclaudio fatalx("auth_gen: ibuf_set_n16 failed");
154204df0f8Sclaudio
155204df0f8Sclaudio switch (iface->auth_type) {
156204df0f8Sclaudio case AUTH_NONE:
157a50d52cbSclaudio chksum = in_cksum(ibuf_data(buf), ibuf_size(buf));
15843e70bb4Sclaudio if (ibuf_set(buf, offsetof(struct ospf_hdr, chksum),
15943e70bb4Sclaudio &chksum, sizeof(chksum)) == -1)
16043e70bb4Sclaudio fatalx("auth_gen: ibuf_set failed");
161204df0f8Sclaudio break;
162204df0f8Sclaudio case AUTH_SIMPLE:
163a50d52cbSclaudio chksum = in_cksum(ibuf_data(buf), ibuf_size(buf));
16443e70bb4Sclaudio if (ibuf_set(buf, offsetof(struct ospf_hdr, chksum),
16543e70bb4Sclaudio &chksum, sizeof(chksum)) == -1)
16643e70bb4Sclaudio fatalx("auth_gen: ibuf_set failed");
16703431b74Snorby
16843e70bb4Sclaudio if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key),
169*4cf3f6fdSclaudio iface->auth_key, strnlen(iface->auth_key,
170*4cf3f6fdSclaudio sizeof(iface->auth_key))) == -1)
17143e70bb4Sclaudio fatalx("auth_gen: ibuf_set failed");
172204df0f8Sclaudio break;
173204df0f8Sclaudio case AUTH_CRYPT:
17443e70bb4Sclaudio bzero(&crypt, sizeof(crypt));
17543e70bb4Sclaudio crypt.keyid = iface->auth_keyid;
17643e70bb4Sclaudio crypt.seq_num = htonl(iface->crypt_seq_num);
17743e70bb4Sclaudio crypt.len = MD5_DIGEST_LENGTH;
17803431b74Snorby iface->crypt_seq_num++;
17903431b74Snorby
18043e70bb4Sclaudio if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key),
18143e70bb4Sclaudio &crypt, sizeof(crypt)) == -1)
18243e70bb4Sclaudio fatalx("auth_gen: ibuf_set failed");
18343e70bb4Sclaudio
18403431b74Snorby /* insert plaintext key */
18576b51f83Sclaudio if ((md = md_list_find(&iface->auth_md_list,
18676b51f83Sclaudio iface->auth_keyid)) == NULL) {
187e6a2be05Snorby log_debug("auth_gen: keyid %d not configured, "
18803431b74Snorby "interface %s", iface->auth_keyid, iface->name);
189204df0f8Sclaudio return (-1);
19003431b74Snorby }
19103431b74Snorby
19203431b74Snorby bzero(digest, MD5_DIGEST_LENGTH);
19303431b74Snorby strncpy(digest, md->key, MD5_DIGEST_LENGTH);
19403431b74Snorby
19503431b74Snorby /* calculate MD5 digest */
19603431b74Snorby MD5Init(&hash);
197a50d52cbSclaudio MD5Update(&hash, ibuf_data(buf), ibuf_size(buf));
19803431b74Snorby MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
19903431b74Snorby MD5Final(digest, &hash);
20003431b74Snorby
201e39620e5Snicm return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH));
202204df0f8Sclaudio default:
203204df0f8Sclaudio log_debug("auth_gen: unknown auth type, interface %s",
204204df0f8Sclaudio iface->name);
205204df0f8Sclaudio return (-1);
206204df0f8Sclaudio }
207204df0f8Sclaudio
208204df0f8Sclaudio return (0);
209204df0f8Sclaudio }
21003431b74Snorby
21103431b74Snorby /* md list */
21203431b74Snorby void
md_list_add(struct auth_md_head * head,u_int8_t keyid,char * key)21376b51f83Sclaudio md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
21403431b74Snorby {
21576b51f83Sclaudio struct auth_md *md;
21603431b74Snorby
21776b51f83Sclaudio if ((md = md_list_find(head, keyid)) != NULL) {
21803431b74Snorby /* update key */
21903431b74Snorby strncpy(md->key, key, sizeof(md->key));
22003431b74Snorby return;
22103431b74Snorby }
22203431b74Snorby
22303431b74Snorby if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
22403431b74Snorby fatalx("md_list_add");
22503431b74Snorby
22603431b74Snorby md->keyid = keyid;
22703431b74Snorby strncpy(md->key, key, sizeof(md->key));
22876b51f83Sclaudio TAILQ_INSERT_TAIL(head, md, entry);
22903431b74Snorby }
23003431b74Snorby
23103431b74Snorby void
md_list_copy(struct auth_md_head * to,struct auth_md_head * from)23276b51f83Sclaudio md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
23376b51f83Sclaudio {
23476b51f83Sclaudio struct auth_md *m, *md;
23576b51f83Sclaudio
23676b51f83Sclaudio TAILQ_INIT(to);
23776b51f83Sclaudio
23876b51f83Sclaudio TAILQ_FOREACH(m, from, entry) {
23976b51f83Sclaudio if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
240e6a2be05Snorby fatalx("md_list_copy");
24176b51f83Sclaudio
24276b51f83Sclaudio md->keyid = m->keyid;
24376b51f83Sclaudio strncpy(md->key, m->key, sizeof(md->key));
24476b51f83Sclaudio TAILQ_INSERT_TAIL(to, md, entry);
24576b51f83Sclaudio }
24676b51f83Sclaudio }
24776b51f83Sclaudio
24876b51f83Sclaudio void
md_list_clr(struct auth_md_head * head)24976b51f83Sclaudio md_list_clr(struct auth_md_head *head)
25003431b74Snorby {
25103431b74Snorby struct auth_md *m;
25203431b74Snorby
25376b51f83Sclaudio while ((m = TAILQ_FIRST(head)) != NULL) {
25476b51f83Sclaudio TAILQ_REMOVE(head, m, entry);
25503431b74Snorby free(m);
25603431b74Snorby }
25703431b74Snorby }
25803431b74Snorby
25903431b74Snorby struct auth_md *
md_list_find(struct auth_md_head * head,u_int8_t keyid)26076b51f83Sclaudio md_list_find(struct auth_md_head *head, u_int8_t keyid)
26103431b74Snorby {
26203431b74Snorby struct auth_md *m;
26303431b74Snorby
26476b51f83Sclaudio TAILQ_FOREACH(m, head, entry)
26503431b74Snorby if (m->keyid == keyid)
26603431b74Snorby return (m);
26703431b74Snorby
26803431b74Snorby return (NULL);
26903431b74Snorby }
27096c7fd34Sclaudio
27196c7fd34Sclaudio int
md_list_send(struct auth_md_head * head,struct imsgev * to)2727b4c0c10Seric md_list_send(struct auth_md_head *head, struct imsgev *to)
27396c7fd34Sclaudio {
27496c7fd34Sclaudio struct auth_md *m;
27596c7fd34Sclaudio
27696c7fd34Sclaudio TAILQ_FOREACH(m, head, entry)
277d13e54e1Spyr if (imsg_compose_event(to, IMSG_RECONF_AUTHMD, 0, 0, -1, m,
27896c7fd34Sclaudio sizeof(*m)) == -1)
27996c7fd34Sclaudio return (-1);
28096c7fd34Sclaudio
28196c7fd34Sclaudio return (0);
28296c7fd34Sclaudio }
283