/* routines for state objects * Copyright (C) 1997 Angelos D. Keromytis. * Copyright (C) 1998, 1999 D. Hugh Redelmeier. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * RCSID $Id: state.c,v 1.69 2000/06/21 18:24:35 dhr Exp $ */ #include #include #include #include #include #include #include #include #include #include #include "constants.h" #include "defs.h" #include "id.h" #include "connections.h" /* needs id.h */ #include "state.h" #include "kernel.h" #include "log.h" #include "rnd.h" #include "timer.h" #include "whack.h" #include "sha1.h" #include "md5.h" #include "crypto.h" /* requires sha1.h and md5.h */ /* * Global variables: had to go somewhere, might as well be this file. */ u_int16_t pluto_port = PORT; /* Pluto's port */ struct sockaddr_in mask32; /* 255.255.255.255 */ struct sockaddr_in mask0; /* 0.0.0.0 */ /* * This file has the functions that handle the * state hash table and the Message ID list. */ #define SA_EQUAL(x, y) ((x).sa_family == (y).sa_family && \ memcmp(&((x).sa_data), &((y).sa_data), FULL_INET_ADDRESS_SIZE) == 0) /* Message-IDs * * A Message ID is contained in each IKE message header. * For Phase 1 exchanges (Main and Aggressive), it will be zero. * For other exchanges, which must be under the protection of an * ISAKMP SA, the Message ID must be unique within that ISAKMP SA. * Effectively, this labels the message as belonging to a particular * exchange. * BTW, we feel this uniqueness allows rekeying to be somewhat simpler * than specified by draft-jenkins-ipsec-rekeying-06.txt. * * A MessageID is a 32 bit unsigned number. We represent the value * internally in network order -- they are just blobs to us. * They are unsigned numbers to make hashing and comparing easy. * * The following mechanism is used to allocate message IDs. This * requires that we keep track of which numbers have already been used * so that we don't allocate one in use. */ struct msgid_list { msgid_t msgid; /* network order */ struct msgid_list *next; }; bool reserve_msgid(struct state *isakmp_sa, msgid_t msgid) { struct msgid_list *p; passert(IS_ISAKMP_SA_ESTABLISHED(isakmp_sa->st_state)); for (p = isakmp_sa->st_used_msgids; p != NULL; p = p->next) if (p->msgid == msgid) return FALSE; p = alloc_thing(struct msgid_list, "msgid"); p->msgid = msgid; p->next = isakmp_sa->st_used_msgids; isakmp_sa->st_used_msgids = p; return TRUE; } msgid_t generate_msgid(struct state *isakmp_sa) { int timeout = 100; /* only try so hard for unique msgid */ msgid_t msgid; passert(IS_ISAKMP_SA_ESTABLISHED(isakmp_sa->st_state)); for (;;) { get_rnd_bytes((void *) &msgid, sizeof(msgid)); if (msgid != 0 && reserve_msgid(isakmp_sa, msgid)) break; if (--timeout == 0) { log("gave up looking for unique msgid; using 0x%08lx", (unsigned long) msgid); break; } } return msgid; } /* state table functions */ #define STATE_TABLE_SIZE 32 static struct state *statetable[STATE_TABLE_SIZE]; static struct state ** state_hash(const u_char *icookie, const u_char *rcookie, struct in_addr peer) { u_int i = 0, j; DBG(DBG_RAW | DBG_CONTROL, DBG_dump("ICOOKIE:", icookie, COOKIE_SIZE); DBG_dump("RCOOKIE:", rcookie, COOKIE_SIZE); DBG_dump("peer:", &peer, sizeof(peer))); for (j = 0; j < COOKIE_SIZE; j++) i += icookie[j] + rcookie[j]; i = (i + peer.s_addr) % STATE_TABLE_SIZE; DBG(DBG_CONTROL, DBG_log("state hash entry %d", i)); return &statetable[i]; } /* Get a state object. * Caller must schedule an event for this object so that it doesn't leak. * Caller must insert_state(). */ struct state * new_state(void) { static struct state blank_state; /* initialized all to zero & NULL */ struct state *st; blank_state.st_serialno++; passert(blank_state.st_serialno != SOS_NOBODY); /* overflow can't happen! */ st = clone_thing(blank_state, "struct state in new_state()"); st->st_whack_sock = NULL_FD; DBG(DBG_CONTROL, DBG_log("creating state object #%lu at %p", st->st_serialno, (void *) st)); return st; } /* * Initialize the state table (and mask*). */ void init_states(void) { int i; /* initialize mask32 */ mksin(mask32, inet_addr("255.255.255.255"), 0); /* initialize mask0 */ mksin(mask0, inet_addr("0.0.0.0"), 0); for (i = 0; i < STATE_TABLE_SIZE; i++) statetable[i] = (struct state *) NULL; } /* Insert a state object in the hash table. The object is inserted * at the begining of list. * Needs cookies, connection, and msgid. */ void insert_state(struct state *st) { struct state **p = state_hash(st->st_icookie, st->st_rcookie , st->st_connection->that.host_addr); passert(st->st_hashchain_prev == NULL && st->st_hashchain_next == NULL); if (*p != NULL) { passert((*p)->st_hashchain_prev == NULL); (*p)->st_hashchain_prev = st; } st->st_hashchain_next = *p; *p = st; } /* unlink a state object from the hash table, but don't free it */ void unhash_state(struct state *st) { /* unlink from forward chain */ struct state **p = st->st_hashchain_prev == NULL ? state_hash(st->st_icookie, st->st_rcookie, st->st_connection->that.host_addr) : &st->st_hashchain_prev->st_hashchain_next; /* unlink from forward chain */ passert(*p == st); *p = st->st_hashchain_next; /* unlink from backward chain */ if (st->st_hashchain_next != NULL) { passert(st->st_hashchain_next->st_hashchain_prev == st); st->st_hashchain_next->st_hashchain_prev = st->st_hashchain_prev; } st->st_hashchain_next = st->st_hashchain_prev = NULL; } /* Free the Whack socket file descriptor. * This has the side effect of telling Whack that we're done. */ void release_whack(struct state *st) { if (st->st_whack_sock != NULL_FD) { close(st->st_whack_sock); st->st_whack_sock = NULL_FD; } } /* * delete a state object */ void delete_state(struct state *st) { /* Check that no timer event is left dangling. * We could actually delete it here, but in most * cases this is a "can't happen". */ struct connection *const c = st->st_connection; struct state *old_cur_state = cur_state == st? NULL : cur_state; cur_state = st; #ifdef DEBUG extra_debugging(c); #endif passert(st->st_event == NULL); /* effectively, this deletes any ISAKMP SA that this state represents */ unhash_state(st); /* tell kernel to delete any IPSEC SA * ??? we ought to tell peer to delete IPSEC SAs */ if (IS_IPSEC_SA_ESTABLISHED(st->st_state)) (void) delete_ipsec_sa(st, FALSE); else if (IS_ONLY_INBOUND_IPSEC_SA_ESTABLISHED(st->st_state)) (void) delete_ipsec_sa(st, TRUE); if (c->newest_ipsec_sa == st->st_serialno) c->newest_ipsec_sa = SOS_NOBODY; if (c->newest_isakmp_sa == st->st_serialno) c->newest_isakmp_sa = SOS_NOBODY; st->st_connection = NULL; /* we might be about to free it */ cur_state = old_cur_state; /* without st_connection, st isn't complete */ rw_connection_discard(c); release_whack(st); /* from here on we are just freeing RAM */ { struct msgid_list *p = st->st_used_msgids; while (p != NULL) { struct msgid_list *q = p; p = p->next; pfree(q); } } if (st->st_sec_in_use) mpz_clear(&(st->st_sec)); pfreeany(st->st_tpacket.ptr); pfreeany(st->st_rpacket.ptr); pfreeany(st->st_p1isa.ptr); pfreeany(st->st_gi.ptr); pfreeany(st->st_gr.ptr); pfreeany(st->st_shared.ptr); pfreeany(st->st_ni.ptr); pfreeany(st->st_nr.ptr); pfreeany(st->st_skeyid.ptr); pfreeany(st->st_skeyid_d.ptr); pfreeany(st->st_skeyid_a.ptr); pfreeany(st->st_skeyid_e.ptr); pfreeany(st->st_enc_key.ptr); pfreeany(st->st_ah.our_keymat); pfreeany(st->st_ah.peer_keymat); pfreeany(st->st_esp.our_keymat); pfreeany(st->st_esp.peer_keymat); pfree(st); } /* If a connection will no longer be used, and it is temporary, delete it. * We must be careful to avoid circularity: * we don't touch it if it is rwcs_going_away. */ void rw_connection_discard(struct connection *c) { if (c->rw_state == rwcs_instance) { /* are there any states still using it? */ struct state *st = NULL; int i; for (i = 0; st == NULL && i < STATE_TABLE_SIZE; i++) for (st = statetable[i] ; st != NULL && st->st_connection != c ; st = st->st_hashchain_next) ; if (st == NULL) delete_connection(c); } } void delete_states_by_connection(struct connection *c) { int i; /* this kludge avoids an n^2 algorithm */ enum rwcs rws = c->rw_state; if (rws == rwcs_instance) c->rw_state = rwcs_going_away; for (i = 0; i < STATE_TABLE_SIZE; i++) { struct state *st; for (st = statetable[i]; st != NULL; ) { struct state *this = st; st = st->st_hashchain_next; /* before this is deleted */ if (this->st_connection == c) { struct state *old_cur_state = cur_state == this? NULL : cur_state; #ifdef DEBUG unsigned int old_cur_debugging = cur_debugging; #endif cur_state = this; #ifdef DEBUG extra_debugging(this->st_connection); #endif log("deleting state (%s)" , enum_show(&state_names, this->st_state)); passert(this->st_event != NULL); delete_event(this); delete_state(this); cur_state = old_cur_state; #ifdef DEBUG cur_debugging = old_cur_debugging; #endif } } } if (rws == rwcs_instance) { c->rw_state = rws; delete_connection(c); } } /* Duplicate a Phase 1 state object, to create a Phase 2 object. * Caller must schedule an event for this object so that it doesn't leak. * Caller must insert_state(). */ struct state * duplicate_state(const struct state *st) { struct state *nst; DBG(DBG_CONTROL, DBG_log("duplicating state object #%lu", st->st_serialno)); nst = new_state(); memcpy(nst->st_icookie, st->st_icookie, COOKIE_SIZE); memcpy(nst->st_rcookie, st->st_rcookie, COOKIE_SIZE); nst->st_connection = st->st_connection; nst->st_doi = st->st_doi; nst->st_situation = st->st_situation; # define clone_chunk(ch, name) \ clonetochunk(nst->ch, st->ch.ptr, st->ch.len, name) clone_chunk(st_skeyid_d, "st_skeyid_d in duplicate_state"); clone_chunk(st_skeyid_a, "st_skeyid_a in duplicate_state"); clone_chunk(st_skeyid_e, "st_skeyid_e in duplicate_state"); clone_chunk(st_enc_key, "st_enc_key in duplicate_state"); # undef clone_chunk /* no obvious reason to copy st_peeridentity_protocol and st_peeridentity_port */ nst->st_oakley = st->st_oakley; return nst; } /* * Find a state object. */ struct state * find_state(const u_char *icookie, const u_char *rcookie, const struct in_addr peer, msgid_t /*network order*/ msgid) { struct state *st = *state_hash(icookie, rcookie, peer); while (st != (struct state *) NULL) if (same_ip(peer, st->st_connection->that.host_addr) && memcmp(icookie, st->st_icookie, COOKIE_SIZE) == 0 && memcmp(rcookie, st->st_rcookie, COOKIE_SIZE) == 0 && msgid == st->st_msgid) break; else st = st->st_hashchain_next; DBG(DBG_CONTROL, if (st == NULL) DBG_log("state object not found"); else DBG_log("state object #%lu found, in %s", st->st_serialno, enum_show(&state_names, st->st_state))); return st; } /* Find newest ISAKMP SA state object that implements connection c */ struct state * find_isakmp_sa(const struct connection *c) { struct state *st, *best = NULL; int i; for (i = 0; i < STATE_TABLE_SIZE; i++) for (st = statetable[i]; st != NULL; st = st->st_hashchain_next) if (IS_ISAKMP_SA_ESTABLISHED(st->st_state) && c->host_pair == st->st_connection->host_pair && same_peer_ids(c, st->st_connection, NULL) && (best == NULL || best->st_serialno < st->st_serialno)) best = st; return best; } void show_states_status(void) { time_t now = time((time_t *) NULL); int i; for (i = 0; i < STATE_TABLE_SIZE; i++) { struct state *st; for (st = statetable[i]; st != NULL; st = st->st_hashchain_next) { /* what the heck is interesting about a state? */ const struct connection *c = st->st_connection; long delta = st->st_event->ev_time >= now ? (long)(st->st_event->ev_time - now) : -(long)(now - st->st_event->ev_time); char him[ADDRTOA_BUF+1]; /* for RW */ const char *np1 = c->newest_isakmp_sa == st->st_serialno ? "; newest ISAKMP" : ""; const char *np2 = c->newest_ipsec_sa == st->st_serialno ? "; newest IPSEC" : ""; const char *eo = c->eroute_owner == st->st_serialno ? "; eroute owner" : ""; passert(st->st_event != 0); him[0] = '\0'; if (c->rw_state == rwcs_instance) { him[0] = ':'; addrtoa(c->that.host_addr, 0, him+1, sizeof(him)-1); } whack_log(RC_COMMENT , "#%lu: \"%s\"%s %s (%s); %s in %lds%s%s%s" , st->st_serialno , c->name , him , enum_name(&state_names, st->st_state) , state_story[st->st_state - STATE_MAIN_R0] , enum_name(&timer_event_names, st->st_event->ev_type) , delta , np1, np2, eo); /* print out SPIs if SAs are established */ if (IS_IPSEC_SA_ESTABLISHED(st->st_state)) { char buf[SATOA_BUF*6 + 1]; char *p = buf; # define add_said(adst, aspi, aproto) { \ struct sa_id s; \ \ s.dst = (adst); \ s.spi = (aspi); \ s.proto = (aproto); \ if (p < &buf[sizeof(buf)-1]) \ { \ *p++ = ' '; \ p += satoa(s, 0, p, &buf[sizeof(buf)] - p) - 1; \ } \ } *p = '\0'; if (st->st_ah.present) { add_said(c->that.host_addr, st->st_ah.attrs.spi, SA_AH); add_said(c->this.host_addr, st->st_ah.our_spi, SA_AH); } if (st->st_esp.present) { add_said(c->that.host_addr, st->st_esp.attrs.spi, SA_ESP); add_said(c->this.host_addr, st->st_esp.our_spi, SA_ESP); } #ifdef KLIPS if (st->st_ah.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL || st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL) { add_said(c->that.host_addr, st->st_tunnel_out_spi, SA_IPIP); add_said(c->this.host_addr, st->st_tunnel_in_spi, SA_IPIP); } #endif whack_log(RC_COMMENT , "#%lu: \"%s\"%s%s" , st->st_serialno , c->name , him , buf); # undef add_said } } } }