1 /* $OpenBSD: auth.c,v 1.22 2023/07/03 09:40:47 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 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 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, strlen(iface->auth_key)) == -1) 170 fatalx("auth_gen: ibuf_set failed"); 171 break; 172 case AUTH_CRYPT: 173 bzero(&crypt, sizeof(crypt)); 174 crypt.keyid = iface->auth_keyid; 175 crypt.seq_num = htonl(iface->crypt_seq_num); 176 crypt.len = MD5_DIGEST_LENGTH; 177 iface->crypt_seq_num++; 178 179 if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key), 180 &crypt, sizeof(crypt)) == -1) 181 fatalx("auth_gen: ibuf_set failed"); 182 183 /* insert plaintext key */ 184 if ((md = md_list_find(&iface->auth_md_list, 185 iface->auth_keyid)) == NULL) { 186 log_debug("auth_gen: keyid %d not configured, " 187 "interface %s", iface->auth_keyid, iface->name); 188 return (-1); 189 } 190 191 bzero(digest, MD5_DIGEST_LENGTH); 192 strncpy(digest, md->key, MD5_DIGEST_LENGTH); 193 194 /* calculate MD5 digest */ 195 MD5Init(&hash); 196 MD5Update(&hash, ibuf_data(buf), ibuf_size(buf)); 197 MD5Update(&hash, digest, MD5_DIGEST_LENGTH); 198 MD5Final(digest, &hash); 199 200 return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH)); 201 default: 202 log_debug("auth_gen: unknown auth type, interface %s", 203 iface->name); 204 return (-1); 205 } 206 207 return (0); 208 } 209 210 /* md list */ 211 void 212 md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key) 213 { 214 struct auth_md *md; 215 216 if ((md = md_list_find(head, keyid)) != NULL) { 217 /* update key */ 218 strncpy(md->key, key, sizeof(md->key)); 219 return; 220 } 221 222 if ((md = calloc(1, sizeof(struct auth_md))) == NULL) 223 fatalx("md_list_add"); 224 225 md->keyid = keyid; 226 strncpy(md->key, key, sizeof(md->key)); 227 TAILQ_INSERT_TAIL(head, md, entry); 228 } 229 230 void 231 md_list_copy(struct auth_md_head *to, struct auth_md_head *from) 232 { 233 struct auth_md *m, *md; 234 235 TAILQ_INIT(to); 236 237 TAILQ_FOREACH(m, from, entry) { 238 if ((md = calloc(1, sizeof(struct auth_md))) == NULL) 239 fatalx("md_list_copy"); 240 241 md->keyid = m->keyid; 242 strncpy(md->key, m->key, sizeof(md->key)); 243 TAILQ_INSERT_TAIL(to, md, entry); 244 } 245 } 246 247 void 248 md_list_clr(struct auth_md_head *head) 249 { 250 struct auth_md *m; 251 252 while ((m = TAILQ_FIRST(head)) != NULL) { 253 TAILQ_REMOVE(head, m, entry); 254 free(m); 255 } 256 } 257 258 struct auth_md * 259 md_list_find(struct auth_md_head *head, u_int8_t keyid) 260 { 261 struct auth_md *m; 262 263 TAILQ_FOREACH(m, head, entry) 264 if (m->keyid == keyid) 265 return (m); 266 267 return (NULL); 268 } 269 270 int 271 md_list_send(struct auth_md_head *head, struct imsgev *to) 272 { 273 struct auth_md *m; 274 275 TAILQ_FOREACH(m, head, entry) 276 if (imsg_compose_event(to, IMSG_RECONF_AUTHMD, 0, 0, -1, m, 277 sizeof(*m)) == -1) 278 return (-1); 279 280 return (0); 281 } 282