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