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