1 /* $OpenBSD: connection.c,v 1.37 2014/01/22 03:09:31 deraadt Exp $ */ 2 /* $EOM: connection.c,v 1.28 2000/11/23 12:21:18 niklas Exp $ */ 3 4 /* 5 * Copyright (c) 1999, 2000, 2001 Niklas Hallqvist. All rights reserved. 6 * Copyright (c) 1999 Hakan Olsson. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * This code was written under funding by Ericsson Radio Systems. 31 */ 32 33 #include <sys/queue.h> 34 #include <sys/time.h> 35 #include <sys/socket.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include "conf.h" 40 #include "connection.h" 41 #include "doi.h" 42 #include "ipsec.h" 43 #include "pf_key_v2.h" 44 45 /* XXX isakmp.h only required for compare_ids(). */ 46 #include "isakmp.h" 47 48 #include "log.h" 49 #include "timer.h" 50 #include "ui.h" 51 #include "util.h" 52 53 /* How often should we check that connections we require to be up, are up? */ 54 #define CHECK_INTERVAL 60 55 56 static void connection_passive_teardown(char *); 57 58 struct connection { 59 TAILQ_ENTRY(connection) link; 60 char *name; 61 struct event *ev; 62 }; 63 64 struct connection_passive { 65 TAILQ_ENTRY(connection_passive) link; 66 char *name; 67 u_int8_t *local_id, *remote_id; 68 size_t local_sz, remote_sz; 69 70 #if 0 71 /* XXX Potential additions to 'connection_passive'. */ 72 char *isakmp_peer; 73 struct sa *sa; /* XXX "Soft" ref to active sa? */ 74 struct timeval sa_expiration; /* XXX *sa may expire. */ 75 #endif 76 }; 77 78 TAILQ_HEAD(connection_head, connection) connections; 79 TAILQ_HEAD(passive_head, connection_passive) connections_passive; 80 81 /* 82 * This is where we setup all the connections we want there right from the 83 * start. 84 */ 85 void 86 connection_init(void) 87 { 88 struct conf_list *conns, *attrs; 89 struct conf_list_node *conn, *attr = NULL; 90 91 /* 92 * Passive connections normally include: all "active" connections that 93 * are not flagged "Active-Only", plus all connections listed in 94 * the 'Passive-Connections' list. 95 */ 96 TAILQ_INIT(&connections); 97 TAILQ_INIT(&connections_passive); 98 99 conns = conf_get_list("Phase 2", "Connections"); 100 if (conns) { 101 for (conn = TAILQ_FIRST(&conns->fields); conn; 102 conn = TAILQ_NEXT(conn, link)) { 103 if (connection_setup(conn->field)) 104 log_print("connection_init: could not setup " 105 "\"%s\"", conn->field); 106 107 /* XXX Break/abort here if connection_setup failed? */ 108 109 /* 110 * XXX This code (i.e. the attribute lookup) seems 111 * like a likely candidate for factoring out into a 112 * function of its own. 113 */ 114 attrs = conf_get_list(conn->field, "Flags"); 115 if (attrs) 116 for (attr = TAILQ_FIRST(&attrs->fields); attr; 117 attr = TAILQ_NEXT(attr, link)) 118 if (strcasecmp("active-only", 119 attr->field) == 0) 120 break; 121 if (!attrs || (attrs && !attr)) 122 if (connection_record_passive(conn->field)) 123 log_print("connection_init: could not " 124 "record connection \"%s\"", 125 conn->field); 126 if (attrs) 127 conf_free_list(attrs); 128 129 } 130 conf_free_list(conns); 131 } 132 conns = conf_get_list("Phase 2", "Passive-Connections"); 133 if (conns) { 134 for (conn = TAILQ_FIRST(&conns->fields); conn; 135 conn = TAILQ_NEXT(conn, link)) 136 if (connection_record_passive(conn->field)) 137 log_print("connection_init: could not record " 138 "passive connection \"%s\"", conn->field); 139 conf_free_list(conns); 140 } 141 } 142 143 /* Check the connection in VCONN and schedule another check later. */ 144 static void 145 connection_checker(void *vconn) 146 { 147 struct timeval now; 148 struct connection *conn = vconn; 149 150 gettimeofday(&now, 0); 151 now.tv_sec += conf_get_num("General", "check-interval", 152 CHECK_INTERVAL); 153 conn->ev = timer_add_event("connection_checker", 154 connection_checker, conn, &now); 155 if (!conn->ev) 156 log_print("connection_checker: could not add timer event"); 157 if (!ui_daemon_passive) 158 pf_key_v2_connection_check(conn->name); 159 } 160 161 /* Find the connection named NAME. */ 162 static struct connection * 163 connection_lookup(char *name) 164 { 165 struct connection *conn; 166 167 for (conn = TAILQ_FIRST(&connections); conn; 168 conn = TAILQ_NEXT(conn, link)) 169 if (strcasecmp(conn->name, name) == 0) 170 return conn; 171 return 0; 172 } 173 174 /* Does the connection named NAME exist? */ 175 int 176 connection_exist(char *name) 177 { 178 return (connection_lookup(name) != 0); 179 } 180 181 /* Find the passive connection named NAME. */ 182 static struct connection_passive * 183 connection_passive_lookup_by_name(char *name) 184 { 185 struct connection_passive *conn; 186 187 for (conn = TAILQ_FIRST(&connections_passive); conn; 188 conn = TAILQ_NEXT(conn, link)) 189 if (strcasecmp(conn->name, name) == 0) 190 return conn; 191 return 0; 192 } 193 194 /* 195 * IDs of different types cannot be the same. 196 * XXX Rename to ipsec_compare_id, and move to ipsec.c ? 197 */ 198 static int 199 compare_ids(u_int8_t *id1, u_int8_t *id2, size_t idlen) 200 { 201 int id1_type, id2_type; 202 203 id1_type = GET_ISAKMP_ID_TYPE(id1); 204 id2_type = GET_ISAKMP_ID_TYPE(id2); 205 206 return id1_type == id2_type ? memcmp(id1 + ISAKMP_ID_DATA_OFF, 207 id2 + ISAKMP_ID_DATA_OFF, idlen - ISAKMP_ID_DATA_OFF) : -1; 208 } 209 210 /* Find the connection named with matching IDs. */ 211 char * 212 connection_passive_lookup_by_ids(u_int8_t *id1, u_int8_t *id2) 213 { 214 struct connection_passive *conn; 215 216 for (conn = TAILQ_FIRST(&connections_passive); conn; 217 conn = TAILQ_NEXT(conn, link)) { 218 if (!conn->remote_id) 219 continue; 220 221 /* 222 * If both IDs match what we have saved, return the name. 223 * Don't bother in which order they are. 224 */ 225 if ((compare_ids(id1, conn->local_id, conn->local_sz) == 0 && 226 compare_ids(id2, conn->remote_id, conn->remote_sz) == 0) || 227 (compare_ids(id1, conn->remote_id, conn->remote_sz) == 0 && 228 compare_ids(id2, conn->local_id, conn->local_sz) == 0)) { 229 LOG_DBG((LOG_MISC, 60, 230 "connection_passive_lookup_by_ids: " 231 "returned \"%s\"", conn->name)); 232 return conn->name; 233 } 234 } 235 236 /* 237 * In the road warrior case, we do not know the remote ID. In that 238 * case we will just match against the local ID. 239 */ 240 for (conn = TAILQ_FIRST(&connections_passive); conn; 241 conn = TAILQ_NEXT(conn, link)) { 242 if (!conn->remote_id) 243 continue; 244 245 if (compare_ids(id1, conn->local_id, conn->local_sz) == 0 || 246 compare_ids(id2, conn->local_id, conn->local_sz) == 0) { 247 LOG_DBG((LOG_MISC, 60, 248 "connection_passive_lookup_by_ids: returned \"%s\"" 249 " only matched local id", conn->name)); 250 return conn->name; 251 } 252 } 253 LOG_DBG((LOG_MISC, 60, 254 "connection_passive_lookup_by_ids: no match")); 255 return 0; 256 } 257 258 /* 259 * Setup NAME to be a connection that should be up "always", i.e. if it dies, 260 * for whatever reason, it should be tried to be brought up, over and over 261 * again. 262 */ 263 int 264 connection_setup(char *name) 265 { 266 struct connection *conn = 0; 267 struct timeval now; 268 269 /* Check for trials to add duplicate connections. */ 270 if (connection_lookup(name)) { 271 LOG_DBG((LOG_MISC, 10, 272 "connection_setup: cannot add \"%s\" twice", name)); 273 return 0; 274 } 275 conn = calloc(1, sizeof *conn); 276 if (!conn) { 277 log_error("connection_setup: calloc (1, %lu) failed", 278 (unsigned long)sizeof *conn); 279 goto fail; 280 } 281 conn->name = strdup(name); 282 if (!conn->name) { 283 log_error("connection_setup: strdup (\"%s\") failed", name); 284 goto fail; 285 } 286 gettimeofday(&now, 0); 287 conn->ev = timer_add_event("connection_checker", connection_checker, 288 conn, &now); 289 if (!conn->ev) { 290 log_print("connection_setup: could not add timer event"); 291 goto fail; 292 } 293 TAILQ_INSERT_TAIL(&connections, conn, link); 294 return 0; 295 296 fail: 297 if (conn) { 298 free(conn->name); 299 free(conn); 300 } 301 return -1; 302 } 303 304 int 305 connection_record_passive(char *name) 306 { 307 struct connection_passive *conn; 308 char *local_id, *remote_id; 309 310 if (connection_passive_lookup_by_name(name)) { 311 LOG_DBG((LOG_MISC, 10, 312 "connection_record_passive: cannot add \"%s\" twice", 313 name)); 314 return 0; 315 } 316 local_id = conf_get_str(name, "Local-ID"); 317 if (!local_id) { 318 log_print("connection_record_passive: " 319 "\"Local-ID\" is missing from section [%s]", name); 320 return -1; 321 } 322 /* If the remote id lookup fails we defer it to later */ 323 remote_id = conf_get_str(name, "Remote-ID"); 324 325 conn = calloc(1, sizeof *conn); 326 if (!conn) { 327 log_error("connection_record_passive: calloc (1, %lu) failed", 328 (unsigned long)sizeof *conn); 329 return -1; 330 } 331 conn->name = strdup(name); 332 if (!conn->name) { 333 log_error("connection_record_passive: strdup (\"%s\") failed", 334 name); 335 goto fail; 336 } 337 /* XXX IPsec DOI-specific. */ 338 conn->local_id = ipsec_build_id(local_id, &conn->local_sz); 339 if (!conn->local_id) 340 goto fail; 341 342 if (remote_id) { 343 conn->remote_id = ipsec_build_id(remote_id, &conn->remote_sz); 344 if (!conn->remote_id) 345 goto fail; 346 } else 347 conn->remote_id = 0; 348 349 TAILQ_INSERT_TAIL(&connections_passive, conn, link); 350 351 LOG_DBG((LOG_MISC, 60, 352 "connection_record_passive: passive connection \"%s\" added", 353 conn->name)); 354 return 0; 355 356 fail: 357 free(conn->local_id); 358 free(conn->name); 359 free(conn); 360 return -1; 361 } 362 363 /* Remove the connection named NAME. */ 364 void 365 connection_teardown(char *name) 366 { 367 struct connection *conn; 368 369 conn = connection_lookup(name); 370 if (!conn) 371 return; 372 373 TAILQ_REMOVE(&connections, conn, link); 374 timer_remove_event(conn->ev); 375 free(conn->name); 376 free(conn); 377 } 378 379 /* Remove the passive connection named NAME. */ 380 static void 381 connection_passive_teardown(char *name) 382 { 383 struct connection_passive *conn; 384 385 conn = connection_passive_lookup_by_name(name); 386 if (!conn) 387 return; 388 389 TAILQ_REMOVE(&connections_passive, conn, link); 390 free(conn->name); 391 free(conn->local_id); 392 free(conn->remote_id); 393 free(conn); 394 } 395 396 void 397 connection_report(void) 398 { 399 struct connection *conn; 400 struct timeval now; 401 struct connection_passive *pconn; 402 struct doi *doi = doi_lookup(ISAKMP_DOI_ISAKMP); 403 404 gettimeofday(&now, 0); 405 for (conn = TAILQ_FIRST(&connections); conn; 406 conn = TAILQ_NEXT(conn, link)) 407 LOG_DBG((LOG_REPORT, 0, 408 "connection_report: connection %s next check %lld seconds", 409 (conn->name ? conn->name : "<unnamed>"), 410 (long long)(conn->ev->expiration.tv_sec - now.tv_sec))); 411 for (pconn = TAILQ_FIRST(&connections_passive); pconn; 412 pconn = TAILQ_NEXT(pconn, link)) 413 LOG_DBG((LOG_REPORT, 0, 414 "connection_report: passive connection %s %s", pconn->name, 415 doi->decode_ids("local_id: %s, remote_id: %s", 416 pconn->local_id, pconn->local_sz, 417 pconn->remote_id, pconn->remote_sz, 1))); 418 } 419 420 /* Reinitialize all connections (SIGHUP handling). */ 421 void 422 connection_reinit(void) 423 { 424 struct connection *conn, *next; 425 struct connection_passive *pconn, *pnext; 426 427 LOG_DBG((LOG_MISC, 30, 428 "connection_reinit: reinitializing connection list")); 429 430 /* Remove all present connections. */ 431 for (conn = TAILQ_FIRST(&connections); conn; conn = next) { 432 next = TAILQ_NEXT(conn, link); 433 connection_teardown(conn->name); 434 } 435 436 for (pconn = TAILQ_FIRST(&connections_passive); pconn; pconn = pnext) { 437 pnext = TAILQ_NEXT(pconn, link); 438 connection_passive_teardown(pconn->name); 439 } 440 441 /* Setup new connections, as the (new) config directs. */ 442 connection_init(); 443 } 444