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