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