dpd.c revision 1.2
1/* $OpenBSD: dpd.c,v 1.2 2004/06/20 17:17:34 ho Exp $ */ 2 3/* 4 * Copyright (c) 2004 H�kan Olsson. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/types.h> 28#include <stdlib.h> 29 30#include "sysdep.h" 31 32#include "dpd.h" 33#include "exchange.h" 34#include "ipsec.h" 35#include "isakmp_fld.h" 36#include "log.h" 37#include "message.h" 38#include "sa.h" 39#include "timer.h" 40#include "util.h" 41 42/* From RFC 3706. */ 43#define DPD_MAJOR 0x01 44#define DPD_MINOR 0x00 45#define DPD_SEQNO_SZ 4 46 47static const char dpd_vendor_id[] = { 48 0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, /* RFC 3706 */ 49 0xC9, 0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 50 DPD_MAJOR, 51 DPD_MINOR 52}; 53 54int16_t script_dpd[] = { 55 ISAKMP_PAYLOAD_NOTIFY, /* Initiator -> responder. */ 56 ISAKMP_PAYLOAD_HASH, 57 EXCHANGE_SCRIPT_SWITCH, 58 ISAKMP_PAYLOAD_NOTIFY, /* Responder -> initiator. */ 59 ISAKMP_PAYLOAD_HASH, 60 EXCHANGE_SCRIPT_END 61}; 62 63static int dpd_initiator_send_notify(struct message *); 64static int dpd_initiator_recv_ack(struct message *); 65static int dpd_responder_recv_notify(struct message *); 66static int dpd_responder_send_ack(struct message *); 67static void dpd_event(void *); 68 69int (*isakmp_dpd_initiator[])(struct message *) = { 70 dpd_initiator_send_notify, 71 dpd_initiator_recv_ack 72}; 73 74int (*isakmp_dpd_responder[])(struct message *) = { 75 dpd_responder_recv_notify, 76 dpd_responder_send_ack 77}; 78 79/* Add the DPD VENDOR ID payload. */ 80int 81dpd_add_vendor_payload(struct message *msg) 82{ 83 u_int8_t *buf; 84 size_t buflen = sizeof dpd_vendor_id + ISAKMP_GEN_SZ; 85 86 buf = malloc(buflen); 87 if (!buf) { 88 log_error("dpd_add_vendor_payload: malloc(%lu) failed", 89 (unsigned long)buflen); 90 return -1; 91 } 92 93 SET_ISAKMP_GEN_LENGTH(buf, buflen); 94 memcpy(buf + ISAKMP_VENDOR_ID_OFF, dpd_vendor_id, 95 sizeof dpd_vendor_id); 96 if (message_add_payload(msg, ISAKMP_PAYLOAD_VENDOR, buf, buflen, 1)) { 97 free(buf); 98 return -1; 99 } 100 101 return 0; 102} 103 104/* 105 * Check an incoming message for DPD capability markers. 106 */ 107void 108dpd_check_vendor_payload(struct message *msg, struct payload *p) 109{ 110 u_int8_t *pbuf = p->p; 111 size_t vlen; 112 113 /* Already checked? */ 114 if (msg->exchange->flags & EXCHANGE_FLAG_DPD_CAP_PEER) { 115 /* Just mark it as handled and return. */ 116 p->flags |= PL_MARK; 117 return; 118 } 119 120 vlen = GET_ISAKMP_GEN_LENGTH(pbuf) - ISAKMP_GEN_SZ; 121 if (vlen != sizeof dpd_vendor_id) { 122 LOG_DBG((LOG_EXCHANGE, 90, 123 "dpd_check_vendor_payload: bad size %d != %d", vlen, 124 sizeof dpd_vendor_id)); 125 return; 126 } 127 128 if (memcmp(dpd_vendor_id, pbuf + ISAKMP_GEN_SZ, vlen) == 0) { 129 /* This peer is DPD capable. */ 130 msg->exchange->flags |= EXCHANGE_FLAG_DPD_CAP_PEER; 131 LOG_DBG((LOG_EXCHANGE, 10, "dpd_check_vendor_payload: " 132 "DPD capable peer detected")); 133 p->flags |= PL_MARK; 134 return; 135 } 136 137 return; 138} 139 140static int 141dpd_add_notify(struct message *msg, u_int16_t type, u_int32_t seqno) 142{ 143 struct sa *isakmp_sa = msg->isakmp_sa; 144 char *buf; 145 u_int32_t buflen; 146 147 if (!isakmp_sa) { 148 log_print("dpd_add_notify: no isakmp_sa"); 149 return -1; 150 } 151 152 buflen = ISAKMP_NOTIFY_SZ + ISAKMP_HDR_COOKIES_LEN + DPD_SEQNO_SZ; 153 buf = malloc(buflen); 154 if (!buf) { 155 log_error("dpd_add_notify: malloc(%d) failed", 156 ISAKMP_NOTIFY_SZ + DPD_SEQNO_SZ); 157 return -1; 158 } 159 160 SET_ISAKMP_NOTIFY_DOI(buf, IPSEC_DOI_IPSEC); 161 SET_ISAKMP_NOTIFY_PROTO(buf, ISAKMP_PROTO_ISAKMP); 162 SET_ISAKMP_NOTIFY_SPI_SZ(buf, ISAKMP_HDR_COOKIES_LEN); 163 SET_ISAKMP_NOTIFY_MSG_TYPE(buf, type); 164 memcpy(buf + ISAKMP_NOTIFY_SPI_OFF, isakmp_sa->cookies, 165 ISAKMP_HDR_COOKIES_LEN); 166 167 memcpy(buf + ISAKMP_NOTIFY_SPI_OFF + ISAKMP_HDR_COOKIES_LEN, &seqno, 168 sizeof (u_int32_t)); 169 170 if (message_add_payload(msg, ISAKMP_PAYLOAD_NOTIFY, buf, buflen, 1)) { 171 free(buf); 172 return -1; 173 } 174 175 return 0; 176} 177 178static int 179dpd_initiator_send_notify(struct message *msg) 180{ 181 if (!msg->isakmp_sa) { 182 log_print("dpd_initiator_send_notify: no isakmp_sa"); 183 return -1; 184 } 185 186 if (msg->isakmp_sa->dpd_seq == 0) { 187 /* RFC 3706: first seq# should be random, with MSB zero. */ 188 getrandom((u_int8_t *)&msg->isakmp_sa->seq, 189 sizeof msg->isakmp_sa->seq); 190 msg->isakmp_sa->dpd_seq &= 0x7FFF; 191 } else 192 msg->isakmp_sa->dpd_seq++; 193 194 return dpd_add_notify(msg, ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE, 195 msg->isakmp_sa->dpd_seq); 196} 197 198static int 199dpd_initiator_recv_ack(struct message *msg) 200{ 201 struct payload *p = payload_first(msg, ISAKMP_PAYLOAD_NOTIFY); 202 struct sa *isakmp_sa = msg->isakmp_sa; 203 struct timeval tv; 204 u_int32_t rseq; 205 206 if (msg->exchange->phase != 2) { 207 message_drop(msg, ISAKMP_NOTIFY_INVALID_EXCHANGE_TYPE, 0, 1, 208 0); 209 return -1; 210 } 211 212 if (GET_ISAKMP_NOTIFY_MSG_TYPE(p->p) 213 != ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK) { 214 message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); 215 return -1; 216 } 217 218 /* Presumably, we've been through message_validate_notify(). */ 219 220 /* Validate the SPI. Perhaps move to message_validate_notify(). */ 221 if (memcmp(p->p + ISAKMP_NOTIFY_SPI_OFF, isakmp_sa->cookies, 222 ISAKMP_HDR_COOKIES_LEN) != 0) { 223 log_print("dpd_initiator_recv_ack: bad cookies"); 224 message_drop(msg, ISAKMP_NOTIFY_INVALID_SPI, 0, 1, 0); 225 return -1; 226 } 227 228 /* Check the seqno. */ 229 memcpy(p->p + ISAKMP_NOTIFY_SPI_OFF + ISAKMP_HDR_COOKIES_LEN, &rseq, 230 sizeof rseq); 231 rseq = ntohl(rseq); 232 233 if (isakmp_sa->seq != rseq) { 234 log_print("dpd_initiator_recv_ack: bad seqno %u, expected %u", 235 rseq, isakmp_sa->seq); 236 message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); 237 return -1; 238 } 239 240 /* Peer is alive. Reset timer. */ 241 gettimeofday(&tv, 0); 242 tv.tv_sec += DPD_DEFAULT_WORRY_METRIC; /* XXX Configurable */ 243 244 isakmp_sa->dpd_nextev = timer_add_event("dpd_event", dpd_event, 245 isakmp_sa, &tv); 246 if (!isakmp_sa->dpd_nextev) 247 log_print("dpd_initiator_recv_ack: timer_add_event " 248 "failed"); 249 else 250 sa_reference(isakmp_sa); 251 252 /* Mark handled. */ 253 p->flags |= PL_MARK; 254 255 return 0; 256} 257 258static int 259dpd_responder_recv_notify(struct message *msg) 260{ 261 struct payload *p = payload_first(msg, ISAKMP_PAYLOAD_NOTIFY); 262 struct sa *isakmp_sa = msg->isakmp_sa; 263 struct timeval tv; 264 u_int32_t rseq; 265 266 if (msg->exchange->phase != 2) { 267 message_drop(msg, ISAKMP_NOTIFY_INVALID_EXCHANGE_TYPE, 0, 1, 268 0); 269 return -1; 270 } 271 272 if (GET_ISAKMP_NOTIFY_MSG_TYPE(p->p) != 273 ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE) { 274 message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); 275 return -1; 276 } 277 278 /* Presumably, we've gone through message_validate_notify(). */ 279 /* XXX */ 280 281 /* Validate the SPI. Perhaps move to message_validate_notify(). */ 282 if (memcmp(p->p + ISAKMP_NOTIFY_SPI_OFF, isakmp_sa->cookies, 283 ISAKMP_HDR_COOKIES_LEN) != 0) { 284 log_print("dpd_initiator_recv_notify: bad cookies"); 285 message_drop(msg, ISAKMP_NOTIFY_INVALID_SPI, 0, 1, 0); 286 return -1; 287 } 288 289 /* Get the seqno. */ 290 memcpy(p->p + ISAKMP_NOTIFY_SPI_OFF + ISAKMP_HDR_COOKIES_LEN, &rseq, 291 sizeof rseq); 292 rseq = ntohl(rseq); 293 294 /* Check increasing seqno. */ 295 if (rseq <= isakmp_sa->dpd_rseq) { 296 log_print("dpd_initiator_recv_notify: bad seqno (%u <= %u)", 297 rseq, isakmp_sa->dpd_rseq); 298 message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); 299 return -1; 300 } 301 isakmp_sa->dpd_rseq = rseq; 302 303 /* 304 * Ok, now we know the peer is alive, in case we're wondering. 305 * If so, reset timers, etc... here. 306 */ 307 if (isakmp_sa->dpd_nextev) { 308 timer_remove_event(isakmp_sa->dpd_nextev); 309 sa_release(isakmp_sa); 310 311 gettimeofday(&tv, 0); 312 tv.tv_sec += DPD_DEFAULT_WORRY_METRIC; /* XXX Configurable */ 313 314 isakmp_sa->dpd_nextev = timer_add_event("dpd_event", dpd_event, 315 isakmp_sa, &tv); 316 if (!isakmp_sa->dpd_nextev) 317 log_print("dpd_responder_recv_notify: timer_add_event " 318 "failed"); 319 else 320 sa_reference(isakmp_sa); 321 } 322 323 /* Mark handled. */ 324 p->flags |= PL_MARK; 325 326 return 0; 327} 328 329static int 330dpd_responder_send_ack(struct message *msg) 331{ 332 if (!msg->isakmp_sa) 333 return -1; 334 335 return dpd_add_notify(msg, ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK, 336 msg->isakmp_sa->dpd_rseq); 337} 338 339static void 340dpd_event(void *v_sa) 341{ 342 struct sa *sa = v_sa; 343 344 sa->dpd_nextev = 0; 345 sa_release(sa); 346 347 if ((sa->flags & SA_FLAG_DPD) == 0) 348 return; 349 350 /* Create a new DPD exchange. XXX */ 351} 352 353