dpd.c revision 1.11
1/* $OpenBSD: dpd.c,v 1.11 2005/04/08 19:40:02 deraadt 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#include <string.h> 30 31#include "sysdep.h" 32 33#include "conf.h" 34#include "dpd.h" 35#include "exchange.h" 36#include "hash.h" 37#include "ipsec.h" 38#include "isakmp_fld.h" 39#include "log.h" 40#include "message.h" 41#include "sa.h" 42#include "timer.h" 43#include "transport.h" 44#include "util.h" 45 46/* From RFC 3706. */ 47#define DPD_MAJOR 0x01 48#define DPD_MINOR 0x00 49#define DPD_SEQNO_SZ 4 50 51static const char dpd_vendor_id[] = { 52 0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, /* RFC 3706 */ 53 0xC9, 0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 54 DPD_MAJOR, 55 DPD_MINOR 56}; 57 58#define DPD_RETRANS_MAX 5 /* max number of retries. */ 59#define DPD_RETRANS_WAIT 5 /* seconds between retries. */ 60 61/* DPD Timer State */ 62enum dpd_tstate { DPD_TIMER_NORMAL, DPD_TIMER_CHECK }; 63 64static void dpd_check_event(void *); 65static void dpd_event(void *); 66static u_int32_t dpd_timer_interval(u_int32_t); 67static void dpd_timer_reset(struct sa *, u_int32_t, enum dpd_tstate); 68 69/* Add the DPD VENDOR ID payload. */ 70int 71dpd_add_vendor_payload(struct message *msg) 72{ 73 u_int8_t *buf; 74 size_t buflen = sizeof dpd_vendor_id + ISAKMP_GEN_SZ; 75 76 buf = malloc(buflen); 77 if (!buf) { 78 log_error("dpd_add_vendor_payload: malloc(%lu) failed", 79 (unsigned long)buflen); 80 return -1; 81 } 82 83 SET_ISAKMP_GEN_LENGTH(buf, buflen); 84 memcpy(buf + ISAKMP_VENDOR_ID_OFF, dpd_vendor_id, 85 sizeof dpd_vendor_id); 86 if (message_add_payload(msg, ISAKMP_PAYLOAD_VENDOR, buf, buflen, 1)) { 87 free(buf); 88 return -1; 89 } 90 91 return 0; 92} 93 94/* 95 * Check an incoming message for DPD capability markers. 96 */ 97void 98dpd_check_vendor_payload(struct message *msg, struct payload *p) 99{ 100 u_int8_t *pbuf = p->p; 101 size_t vlen; 102 103 /* Already checked? */ 104 if (msg->exchange->flags & EXCHANGE_FLAG_DPD_CAP_PEER) { 105 /* Just mark it as handled and return. */ 106 p->flags |= PL_MARK; 107 return; 108 } 109 110 vlen = GET_ISAKMP_GEN_LENGTH(pbuf) - ISAKMP_GEN_SZ; 111 if (vlen != sizeof dpd_vendor_id) { 112 LOG_DBG((LOG_EXCHANGE, 90, 113 "dpd_check_vendor_payload: bad size %lu != %lu", 114 (unsigned long)vlen, (unsigned long)sizeof dpd_vendor_id)); 115 return; 116 } 117 118 if (memcmp(dpd_vendor_id, pbuf + ISAKMP_GEN_SZ, vlen) == 0) { 119 /* This peer is DPD capable. */ 120 if (msg->isakmp_sa) { 121 msg->exchange->flags |= EXCHANGE_FLAG_DPD_CAP_PEER; 122 LOG_DBG((LOG_EXCHANGE, 10, "dpd_check_vendor_payload: " 123 "DPD capable peer detected")); 124 if (dpd_timer_interval(0) != 0) { 125 LOG_DBG((LOG_EXCHANGE, 10, 126 "dpd_check_vendor_payload: enabling")); 127 msg->isakmp_sa->flags |= SA_FLAG_DPD; 128 dpd_timer_reset(msg->isakmp_sa, 0, 129 DPD_TIMER_NORMAL); 130 } 131 } 132 p->flags |= PL_MARK; 133 } 134} 135 136/* 137 * All incoming DPD Notify messages enter here. Message has been validated. 138 */ 139void 140dpd_handle_notify(struct message *msg, struct payload *p) 141{ 142 struct sa *isakmp_sa = msg->isakmp_sa; 143 u_int16_t notify = GET_ISAKMP_NOTIFY_MSG_TYPE(p->p); 144 u_int32_t p_seq; 145 146 /* Extract the sequence number. */ 147 memcpy(&p_seq, p->p + ISAKMP_NOTIFY_SPI_OFF + ISAKMP_HDR_COOKIES_LEN, 148 sizeof p_seq); 149 p_seq = ntohl(p_seq); 150 151 LOG_DBG((LOG_MESSAGE, 40, "dpd_handle_notify: got %s seq %u", 152 constant_name(isakmp_notify_cst, notify), p_seq)); 153 154 switch (notify) { 155 case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE: 156 /* The other peer wants to know we're alive. */ 157 if (p_seq < isakmp_sa->dpd_rseq || 158 (p_seq == isakmp_sa->dpd_rseq && 159 ++isakmp_sa->dpd_rdupcount >= DPD_RETRANS_MAX)) { 160 log_print("dpd_handle_notify: bad R_U_THERE seqno " 161 "%u <= %u", p_seq, isakmp_sa->dpd_rseq); 162 return; 163 } 164 if (isakmp_sa->dpd_rseq != p_seq) { 165 isakmp_sa->dpd_rdupcount = 0; 166 isakmp_sa->dpd_rseq = p_seq; 167 } 168 message_send_dpd_notify(isakmp_sa, 169 ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK, p_seq); 170 break; 171 172 case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK: 173 /* This should be a response to a R_U_THERE we've sent. */ 174 if (isakmp_sa->dpd_seq != p_seq) { 175 log_print("dpd_handle_notify: got bad ACK seqno %u, " 176 "expected %u", p_seq, isakmp_sa->dpd_seq); 177 /* XXX Give up? Retry? */ 178 return; 179 } 180 break; 181 default: 182 break; 183 } 184 185 /* Mark handled. */ 186 p->flags |= PL_MARK; 187 188 /* The other peer is alive, so we can safely wait a while longer. */ 189 if (isakmp_sa->flags & SA_FLAG_DPD) 190 dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_NORMAL); 191} 192 193/* Calculate the time until next DPD exchange. */ 194static u_int32_t 195dpd_timer_interval(u_int32_t offset) 196{ 197 int32_t v = 0; 198 199#ifdef notyet 200 v = ...; /* XXX Per-peer specified DPD intervals? */ 201#endif 202 if (!v) 203 v = conf_get_num("General", "DPD-check-interval", 0); 204 if (v < 1) 205 return 0; /* DPD-Check-Interval < 1 means disable DPD */ 206 207 v -= offset; 208 return v < 1 ? 1 : v; 209} 210 211static void 212dpd_timer_reset(struct sa *sa, u_int32_t time_passed, enum dpd_tstate mode) 213{ 214 struct timeval tv; 215 216 if (sa->dpd_event) 217 timer_remove_event(sa->dpd_event); 218 219 gettimeofday(&tv, 0); 220 switch (mode) { 221 case DPD_TIMER_NORMAL: 222 sa->dpd_failcount = 0; 223 tv.tv_sec += dpd_timer_interval(time_passed); 224 sa->dpd_event = timer_add_event("dpd_event", dpd_event, sa, 225 &tv); 226 break; 227 case DPD_TIMER_CHECK: 228 tv.tv_sec += DPD_RETRANS_WAIT; 229 sa->dpd_event = timer_add_event("dpd_check_event", 230 dpd_check_event, sa, &tv); 231 break; 232 default: 233 break; 234 } 235 if (!sa->dpd_event) 236 log_print("dpd_timer_reset: timer_add_event failed"); 237} 238 239/* Helper function for dpd_exchange_finalization(). */ 240static int 241dpd_find_sa(struct sa *sa, void *v_sa) 242{ 243 struct sa *isakmp_sa = v_sa; 244 245 if (!isakmp_sa->id_i || !isakmp_sa->id_r) 246 return (0); 247 return (sa->phase == 2 && (sa->flags & SA_FLAG_READY) && 248 memcmp(sa->id_i, isakmp_sa->id_i, sa->id_i_len) == 0 && 249 memcmp(sa->id_r, isakmp_sa->id_r, sa->id_r_len) == 0); 250} 251 252struct dpd_args { 253 struct sa *isakmp_sa; 254 u_int32_t interval; 255}; 256 257/* Helper function for dpd_event(). */ 258static int 259dpd_check_time(struct sa *sa, void *v_arg) 260{ 261 struct dpd_args *args = v_arg; 262 struct sockaddr *dst; 263 struct proto *proto; 264 struct sa_kinfo *ksa; 265 struct timeval tv; 266 267 if (sa->phase == 1 || (args->isakmp_sa->flags & SA_FLAG_DPD) == 0 || 268 dpd_find_sa(sa, args->isakmp_sa) == 0) 269 return 0; 270 271 proto = TAILQ_FIRST(&sa->protos); 272 if (!proto || !proto->data) 273 return 0; 274 sa->transport->vtbl->get_src(sa->transport, &dst); 275 276 gettimeofday(&tv, 0); 277 ksa = sysdep_ipsec_get_kernel_sa(proto->spi[1], proto->spi_sz[1], 278 proto->proto, dst); 279 280 if (!ksa || !ksa->last_used) 281 return 0; 282 283 LOG_DBG((LOG_MESSAGE, 80, "dpd_check_time: " 284 "SA %p last use %u second(s) ago", sa, 285 (u_int32_t)(tv.tv_sec - ksa->last_used))); 286 287 if ((u_int32_t)(tv.tv_sec - ksa->last_used) < args->interval) { 288 args->interval = (u_int32_t)(tv.tv_sec - ksa->last_used); 289 return 1; 290 } 291 return 0; 292} 293 294/* Called by the timer. */ 295static void 296dpd_event(void *v_sa) 297{ 298 struct sa *isakmp_sa = v_sa; 299 struct dpd_args args; 300 struct sockaddr *dst; 301 char *addr; 302 303 isakmp_sa->dpd_event = 0; 304 305 /* Check if there's been any incoming SA activity since last time. */ 306 args.isakmp_sa = isakmp_sa; 307 args.interval = dpd_timer_interval(0); 308 if (sa_find(dpd_check_time, &args)) { 309 if (args.interval > dpd_timer_interval(0)) 310 args.interval = 0; 311 dpd_timer_reset(isakmp_sa, args.interval, DPD_TIMER_NORMAL); 312 return; 313 } 314 315 /* No activity seen, do a DPD exchange. */ 316 if (isakmp_sa->dpd_seq == 0) { 317 /* 318 * RFC 3706: first seq# should be random, with MSB zero, 319 * otherwise we just increment it. 320 */ 321 getrandom((u_int8_t *)&isakmp_sa->dpd_seq, 322 sizeof isakmp_sa->dpd_seq); 323 isakmp_sa->dpd_seq &= 0x7FFF; 324 } else 325 isakmp_sa->dpd_seq++; 326 327 isakmp_sa->transport->vtbl->get_dst(isakmp_sa->transport, &dst); 328 if (sockaddr2text(dst, &addr, 0) == -1) 329 addr = 0; 330 LOG_DBG((LOG_MESSAGE, 30, "dpd_event: sending R_U_THERE to %s seq %u", 331 addr ? addr : "<unknown>", isakmp_sa->dpd_seq)); 332 if (addr) 333 free(addr); 334 message_send_dpd_notify(isakmp_sa, ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE, 335 isakmp_sa->dpd_seq); 336 337 /* And set the short timer. */ 338 dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_CHECK); 339} 340 341/* 342 * Called by the timer. If this function is called, it means we did not 343 * recieve any R_U_THERE_ACK confirmation from the other peer. 344 */ 345static void 346dpd_check_event(void *v_sa) 347{ 348 struct sa *isakmp_sa = v_sa; 349 struct sa *sa; 350 351 isakmp_sa->dpd_event = 0; 352 353 if (++isakmp_sa->dpd_failcount < DPD_RETRANS_MAX) { 354 LOG_DBG((LOG_MESSAGE, 10, "dpd_check_event: " 355 "peer not responding, retry %u of %u", 356 isakmp_sa->dpd_failcount, DPD_RETRANS_MAX)); 357 message_send_dpd_notify(isakmp_sa, 358 ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE, isakmp_sa->dpd_seq); 359 dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_CHECK); 360 return; 361 } 362 363 /* 364 * Peer is considered dead. Delete all SAs created under isakmp_sa. 365 */ 366 LOG_DBG((LOG_MESSAGE, 10, "dpd_check_event: peer is dead, " 367 "deleting all SAs connected to SA %p", isakmp_sa)); 368 while ((sa = sa_find(dpd_find_sa, isakmp_sa)) != 0) { 369 LOG_DBG((LOG_MESSAGE, 30, "dpd_check_event: deleting SA %p", 370 sa)); 371 sa_delete(sa, 0); 372 } 373 LOG_DBG((LOG_MESSAGE, 30, "dpd_check_event: deleting ISAKMP SA %p", 374 isakmp_sa)); 375 sa_delete(isakmp_sa, 0); 376} 377