*/
u_int32_t hard_lifetime;
+ /**
+ * Use full reauthentication instead of rekeying
+ */
+ bool reauth;
+
/**
* Time, which specifies the range of a random value
* substracted from soft_lifetime.
}
/**
- * Implementation of connection_t.get_hard_lifetime
+ * Implementation of connection_t.get_hard_lifetime.
*/
static u_int32_t get_hard_lifetime(private_connection_t *this)
{
return this->hard_lifetime;
}
+/**
+ * Implementation of connection_t.get_reauth.
+ */
+static bool get_reauth(private_connection_t *this)
+{
+ return this->reauth;
+}
+
/**
* Implementation of connection_t.get_ref.
*/
cert_policy_t cert_policy,
cert_policy_t certreq_policy,
host_t *my_host, host_t *other_host,
- u_int32_t dpd_delay,
+ u_int32_t dpd_delay, bool reauth,
u_int32_t retrans_sequences,
u_int32_t hard_lifetime,
u_int32_t soft_lifetime, u_int32_t jitter)
this->public.select_proposal = (proposal_t*(*)(connection_t*,linked_list_t*))select_proposal;
this->public.add_proposal = (void(*)(connection_t*, proposal_t*)) add_proposal;
this->public.get_dpd_delay = (u_int32_t(*)(connection_t*)) get_dpd_delay;
+ this->public.get_reauth = (bool(*)(connection_t*)) get_reauth;
this->public.get_retrans_seq = (u_int32_t(*)(connection_t*)) get_retrans_seq;
this->public.get_dh_group = (diffie_hellman_group_t(*)(connection_t*)) get_dh_group;
this->public.check_dh_group = (bool(*)(connection_t*,diffie_hellman_group_t)) check_dh_group;
this->my_host = my_host;
this->other_host = other_host;
this->dpd_delay = dpd_delay;
+ this->reauth = reauth;
this->retrans_sequences = retrans_sequences;
this->hard_lifetime = hard_lifetime;
this->soft_lifetime = soft_lifetime;
*/
u_int32_t (*get_dpd_delay) (connection_t *this);
+ /**
+ * @brief Should a full reauthentication be done instead of rekeying?
+ *
+ * @param this calling object
+ * @return TRUE to use full reauthentication
+ */
+ bool (*get_reauth) (connection_t *this);
+
/**
* @brief Get the max number of retransmission sequences.
*
* @param my_host host_t representing local address
* @param other_host host_t representing remote address
* @param dpd_delay interval of DPD liveness checks
+ * @param reauth use full reauthentication instead of rekeying
* @param retrans_sequences number of retransmit sequences to use
* @param hard_lifetime lifetime before deleting an IKE_SA
* @param soft_lifetime lifetime before rekeying an IKE_SA
connection_t * connection_create(char *name, bool ikev2,
cert_policy_t cert_pol, cert_policy_t req_pol,
host_t *my_host, host_t *other_host,
- u_int32_t dpd_delay, u_int32_t retrans_sequences,
+ u_int32_t dpd_delay, bool reauth,
+ u_int32_t retrans_sequences,
u_int32_t hard_lifetime, u_int32_t soft_lifetime,
u_int32_t jitter);
* ID of the IKE_SA to rekey
*/
ike_sa_id_t *ike_sa_id;
+
+ /**
+ * force reauthentication of the peer (full IKE_SA setup)
+ */
+ bool reauth;
};
/**
static status_t execute(private_rekey_ike_sa_job_t *this)
{
ike_sa_t *ike_sa;
+ status_t status;
ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
this->ike_sa_id);
DBG2(DBG_JOB, "IKE_SA %J to rekey not found", this->ike_sa_id);
return DESTROY_ME;
}
- ike_sa->rekey(ike_sa);
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+ if (this->reauth)
+ {
+ status = ike_sa->reauth(ike_sa);
+ }
+ else
+ {
+ status = ike_sa->rekey(ike_sa);
+ }
+
+ if (status == DESTROY_ME)
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
+ }
+ else
+ {
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+ }
return DESTROY_ME;
}
/*
* Described in header
*/
-rekey_ike_sa_job_t *rekey_ike_sa_job_create(ike_sa_id_t *ike_sa_id)
+rekey_ike_sa_job_t *rekey_ike_sa_job_create(ike_sa_id_t *ike_sa_id, bool reauth)
{
private_rekey_ike_sa_job_t *this = malloc_thing(private_rekey_ike_sa_job_t);
/* private variables */
this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
+ this->reauth = reauth;
return &(this->public);
}
* @brief Creates a job of type REKEY_IKE_SA.
*
* @param ike_sa_id ID of the IKE_SA to rekey
+ * @param reauth TRUE to reauthenticate peer, FALSE for rekeying only
* @return rekey_ike_sa_job_t object
*
* @ingroup jobs
*/
-rekey_ike_sa_job_t *rekey_ike_sa_job_create(ike_sa_id_t *ike_sa_id);
+rekey_ike_sa_job_t *rekey_ike_sa_job_create(ike_sa_id_t *ike_sa_id, bool reauth);
#endif /* REKEY_IKE_SA_JOB_H_ */
/**
* Implementation of ike_sa_t.set_lifetimes.
*/
-static void set_lifetimes(private_ike_sa_t *this,
+static void set_lifetimes(private_ike_sa_t *this, bool reauth,
u_int32_t soft_lifetime, u_int32_t hard_lifetime)
{
job_t *job;
if (soft_lifetime)
{
this->time.rekey = this->time.established + soft_lifetime;
- job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id);
+ job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth);
charon->event_queue->add_relative(charon->event_queue, job,
soft_lifetime * 1000);
}
}
}
+/**
+ * Implementation of public_ike_sa_t.delete.
+ */
+static status_t delete_(private_ike_sa_t *this)
+{
+ switch (this->state)
+ {
+ case IKE_CONNECTING:
+ {
+ /* this may happen if a half open IKE_SA gets closed after a
+ * timeout. We signal here UP_FAILED to complete the SIG schema */
+ SIG(IKE_UP_FAILED, "half open IKE_SA deleted after timeout");
+ return DESTROY_ME;
+ }
+ case IKE_ESTABLISHED:
+ {
+ delete_ike_sa_t *delete_ike_sa;
+ if (this->transaction_out)
+ {
+ /* already a transaction in progress. As this may hang
+ * around a while, we don't inform the other peer. */
+ return DESTROY_ME;
+ }
+ delete_ike_sa = delete_ike_sa_create(&this->public);
+ return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE);
+ }
+ case IKE_CREATED:
+ case IKE_DELETING:
+ default:
+ {
+ SIG(IKE_DOWN_START, "closing IKE_SA");
+ SIG(IKE_DOWN_SUCCESS, "IKE_SA closed between %H[%D]...%H[%D]",
+ this->my_host, this->my_id, this->other_host, this->other_id);
+ return DESTROY_ME;
+ }
+ }
+}
+
/**
* Implementation of ike_sa_t.rekey.
*/
return queue_transaction(this, (transaction_t*)rekey_ike_sa, FALSE);
}
+/**
+ * Implementation of ike_sa_t.reauth.
+ */
+static status_t reauth(private_ike_sa_t *this)
+{
+ connection_t *connection;
+ child_sa_t *child_sa;
+ iterator_t *iterator;
+
+ DBG1(DBG_IKE, "reauthenticating IKE_SA between %H[%D]..%H[%D]",
+ this->my_host, this->my_id, this->other_host, this->other_id);
+
+ /* get a connection to initiate */
+ connection = charon->connections->get_connection_by_hosts(charon->connections,
+ this->my_host, this->other_host);
+ if (connection == NULL)
+ {
+ DBG1(DBG_IKE, "no connection found to reauthenticate");
+ return FAILED;
+ }
+
+ /* queue CREATE_CHILD_SA transactions to set up all CHILD_SAs */
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ job_t *job;
+ policy_t *policy;
+ linked_list_t *my_ts, *other_ts;
+ my_ts = child_sa->get_my_traffic_selectors(child_sa);
+ other_ts = child_sa->get_other_traffic_selectors(child_sa);
+ policy = charon->policies->get_policy(charon->policies,
+ this->my_id, this->other_id, my_ts, other_ts,
+ this->my_host, this->other_host, NULL);
+ if (policy == NULL)
+ {
+ DBG1(DBG_IKE, "policy not found to recreate CHILD_SA, skipped");
+ continue;
+ }
+
+ connection->get_ref(connection);
+ job = (job_t*)initiate_job_create(connection, policy);
+ charon->job_queue->add(charon->job_queue, job);
+ }
+ iterator->destroy(iterator);
+ connection->destroy(connection);
+
+ /* delete the old IKE_SA
+ * TODO: we should delay the delete to avoid connectivity gaps?! */
+ return delete_(this);
+}
+
/**
* Implementation of ike_sa_t.get_rekeying_transaction.
*/
}
}
-/**
- * Implementation of public_ike_sa_t.delete.
- */
-static status_t delete_(private_ike_sa_t *this)
-{
- switch (this->state)
- {
- case IKE_CONNECTING:
- {
- /* this may happen if a half open IKE_SA gets closed after a
- * timeout. We signal here UP_FAILED to complete the SIG schema */
- SIG(IKE_UP_FAILED, "half open IKE_SA deleted after timeout");
- return DESTROY_ME;
- }
- case IKE_ESTABLISHED:
- {
- delete_ike_sa_t *delete_ike_sa;
- if (this->transaction_out)
- {
- /* already a transaction in progress. As this may hang
- * around a while, we don't inform the other peer. */
- return DESTROY_ME;
- }
- delete_ike_sa = delete_ike_sa_create(&this->public);
- return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE);
- }
- case IKE_CREATED:
- case IKE_DELETING:
- default:
- {
- SIG(IKE_DOWN_START, "closing IKE_SA");
- SIG(IKE_DOWN_SUCCESS, "IKE_SA closed between %H[%D]...%H[%D]",
- this->my_host, this->my_id, this->other_host, this->other_id);
- return DESTROY_ME;
- }
- }
-}
-
/**
* Implementation of ike_sa_t.get_next_message_id.
*/
this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa;
this->public.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt;
this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled;
- this->public.set_lifetimes = (void(*)(ike_sa_t*,u_int32_t,u_int32_t))set_lifetimes;
+ this->public.set_lifetimes = (void(*)(ike_sa_t*,bool,u_int32_t,u_int32_t))set_lifetimes;
this->public.apply_connection = (void(*)(ike_sa_t*,connection_t*))apply_connection;
this->public.rekey = (status_t(*)(ike_sa_t*))rekey;
+ this->public.reauth = (status_t(*)(ike_sa_t*))reauth;
this->public.get_rekeying_transaction = (transaction_t*(*)(ike_sa_t*))get_rekeying_transaction;
this->public.set_rekeying_transaction = (void(*)(ike_sa_t*,transaction_t*))set_rekeying_transaction;
this->public.adopt_children = (void(*)(ike_sa_t*,ike_sa_t*))adopt_children;
* hard_lifetime is only reached when rekeying at soft_lifetime fails.
*
* @param this calling object
+ * @param reauth use full reauthentication instead of rekeying.
* @param soft_lifetime soft_lifetime
* @param hard_lifetime hard_lifetime
*/
- void (*set_lifetimes) (ike_sa_t *this,
+ void (*set_lifetimes) (ike_sa_t *this, bool reauth,
u_int32_t soft_lifetime, u_int32_t hard_lifetime);
/**
*/
status_t (*rekey) (ike_sa_t *this);
+ /**
+ * @brief Reauthentication the IKE_SA.
+ *
+ * Create a completely new IKE_SA with authentication, recreates all children
+ * within the IKE_SA and shuts the old SA down.
+ *
+ * @param this calling object
+ * @return - SUCCESS, if IKE_SA rekeying initiated
+ */
+ status_t (*reauth) (ike_sa_t *this);
+
/**
* @brief Get the transaction which rekeys this IKE_SA.
*
continue;
}
+ if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING)
+ {
+ /* skip IKE_SA which are not useable */
+ continue;
+ }
+
found_my_id = entry->ike_sa->get_my_id(entry->ike_sa);
found_other_id = entry->ike_sa->get_other_id(entry->ike_sa);
found_my_host = entry->ike_sa->get_my_host(entry->ike_sa);
* Allows the lookup of an IKE_SA by user IDs and hosts. It returns the
* first found occurence, if there are multiple candidates. Supplied IDs
* may contain wildcards, hosts may be %any.
- * If no IKE_SA is found, a new one is created.
+ * If no IKE_SA is found, a new one is created. This is also the case when
+ * the found IKE_SA is in the DELETING state.
*
* @param this the manager object
* @param my_host address of our host
return DESTROY_ME;
}
- this->ike_sa->set_lifetimes(this->ike_sa,
+ this->ike_sa->set_lifetimes(this->ike_sa,
+ this->connection->get_reauth(this->connection),
this->connection->get_soft_lifetime(this->connection),
this->connection->get_hard_lifetime(this->connection));
}
this->ike_sa->set_lifetimes(this->ike_sa,
+ this->connection->get_reauth(this->connection),
this->connection->get_soft_lifetime(this->connection),
this->connection->get_hard_lifetime(this->connection));
this->new_sa->apply_connection(this->new_sa, this->connection);
this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
this->new_sa->set_lifetimes(this->new_sa,
+ this->connection->get_reauth(this->connection),
this->connection->get_soft_lifetime(this->connection),
this->connection->get_hard_lifetime(this->connection));
return SUCCESS;
msg->add_conn.other.sendcert,
my_host, other_host,
msg->add_conn.dpd.delay,
+ msg->add_conn.rekey.reauth,
msg->add_conn.rekey.tries,
msg->add_conn.rekey.ike_lifetime,
msg->add_conn.rekey.ike_lifetime - msg->add_conn.rekey.margin,
#define POLICY_XAUTH_PSK LELEM(18) /* do we support XAUTH????PreShared? */
#define POLICY_XAUTH_RSASIG LELEM(19) /* do we support XAUTH????RSA? */
#define POLICY_XAUTH_SERVER LELEM(20) /* are we an XAUTH server? */
+#define POLICY_REAUTH LELEM(21) /* reauthenticate on rekeying, IKEv2 */
/* Any IPsec policy? If not, a connection description
* is only for ISAKMP SA, not IPSEC SA. (A pun, I admit.)
{ ARG_ULNG, offsetof(starter_conn_t, sa_keying_tries), NULL },
{ ARG_PCNT, offsetof(starter_conn_t, sa_rekey_fuzz), NULL },
{ ARG_MISC, 0, NULL /* KW_REKEY */ },
+ { ARG_MISC, 0, NULL /* KW_REAUTH */ },
{ ARG_STR, offsetof(starter_conn_t, ike), NULL },
{ ARG_STR, offsetof(starter_conn_t, esp), NULL },
{ ARG_STR, offsetof(starter_conn_t, pfsgroup), LST_pfsgroup },
cfg->conn_default.seen = LEMPTY;
cfg->conn_default.startup = STARTUP_NO;
cfg->conn_default.state = STATE_IGNORE;
- cfg->conn_default.policy = POLICY_ENCRYPT | POLICY_TUNNEL | POLICY_RSASIG | POLICY_PFS;
+ cfg->conn_default.policy = POLICY_ENCRYPT | POLICY_TUNNEL | POLICY_RSASIG |
+ POLICY_PFS | POLICY_REAUTH;
cfg->conn_default.ike = clone_str(ike_defaults, "ike_defaults");
cfg->conn_default.esp = clone_str(esp_defaults, "esp_defaults");
case KW_REKEY:
KW_POLICY_FLAG("no", "yes", POLICY_DONT_REKEY)
break;
+ case KW_REAUTH:
+ KW_POLICY_FLAG("yes", "no", POLICY_REAUTH)
+ break;
case KW_MODECONFIG:
KW_POLICY_FLAG("push", "pull", POLICY_MODECFG_PUSH)
break;
The two ends need not agree,
but while a value of
.B no
-prevents Pluto from requesting renegotiation,
+prevents Pluto/Charon from requesting renegotiation,
it does not prevent responding to renegotiation requested from the other end,
so
.B no
will be largely ineffective unless both ends agree on it.
.TP
+.B reauth
+whether rekeying of an IKE_SA should also reauthenticate the peer. In IKEv1,
+reauthentication is always done. In IKEv2, a value of
+.B no
+rekeys without uninstalling the IPsec SAs, a value of
+.B yes
+(the default) creates a new IKE_SA from scratch and tries to recreate
+all IPsec SAs.
+.TP
.B rekeyfuzz
maximum percentage by which
.B rekeymargin
KW_KEYINGTRIES,
KW_REKEYFUZZ,
KW_REKEY,
+ KW_REAUTH,
KW_IKE,
KW_ESP,
KW_PFSGROUP,
keyingtries, KW_KEYINGTRIES
rekeyfuzz, KW_REKEYFUZZ
rekey, KW_REKEY
+reauth, KW_REAUTH
esp, KW_ESP
ike, KW_IKE
pfsgroup, KW_PFSGROUP
}
else
{
+ msg.add_conn.rekey.reauth = (conn->policy & POLICY_REAUTH);
msg.add_conn.rekey.ipsec_lifetime = conn->sa_ipsec_life_seconds;
msg.add_conn.rekey.ike_lifetime = conn->sa_ike_life_seconds;
msg.add_conn.rekey.margin = conn->sa_rekey_margin;
msg.add_conn.name = push_string(&msg, name);
msg.add_conn.ikev2 = 1;
+ msg.add_conn.rekey.reauth = 0;
msg.add_conn.rekey.ipsec_lifetime = 0;
msg.add_conn.rekey.ike_lifetime = 0;
msg.add_conn.rekey.margin = 0;
char *esp;
} algorithms;
struct {
+ int reauth;
time_t ipsec_lifetime;
time_t ike_lifetime;
time_t margin;