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 31struct auth_md *md_list_find(struct auth_md_head *, u_int8_t); 32 33int 34auth_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 139int 140auth_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 */ 212void 213md_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 231void 232md_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 248void 249md_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 259struct auth_md * 260md_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 271int 272md_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