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