1 /* $OpenBSD: auth.c,v 1.13 2023/06/22 10:38:27 claudio 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 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 56 auth_get_seq_num(struct auth_md *md) 57 { 58 return (time(NULL) + md->seq_modulator); 59 } 60 61 void 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 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 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 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 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 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 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 * 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