strongswan/src/libtls/tls_alert.c

244 lines
5.4 KiB
C

/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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.
*/
#include "tls_alert.h"
#include <utils/debug.h>
#include <collections/linked_list.h>
ENUM_BEGIN(tls_alert_desc_names, TLS_CLOSE_NOTIFY, TLS_CLOSE_NOTIFY,
"close notify",
);
ENUM_NEXT(tls_alert_desc_names, TLS_UNEXPECTED_MESSAGE, TLS_UNEXPECTED_MESSAGE,
TLS_CLOSE_NOTIFY,
"unexpected message",
);
ENUM_NEXT(tls_alert_desc_names, TLS_BAD_RECORD_MAC, TLS_RECORD_OVERFLOW,
TLS_UNEXPECTED_MESSAGE,
"bad record mac",
"decryption failed",
"record overflow",
);
ENUM_NEXT(tls_alert_desc_names, TLS_DECOMPRESSION_FAILURE, TLS_DECOMPRESSION_FAILURE,
TLS_RECORD_OVERFLOW,
"decompression_failure",
);
ENUM_NEXT(tls_alert_desc_names, TLS_HANDSHAKE_FAILURE, TLS_DECRYPT_ERROR,
TLS_DECOMPRESSION_FAILURE,
"handshake failure",
"no certificate",
"bad certificate",
"unsupported certificate",
"certificate revoked",
"certificate expired",
"certificate unknown",
"illegal parameter",
"unknown ca",
"access denied",
"decode error",
"decrypt error",
);
ENUM_NEXT(tls_alert_desc_names, TLS_EXPORT_RESTRICTION, TLS_EXPORT_RESTRICTION,
TLS_DECRYPT_ERROR,
"export restriction",
);
ENUM_NEXT(tls_alert_desc_names, TLS_PROTOCOL_VERSION, TLS_INSUFFICIENT_SECURITY,
TLS_EXPORT_RESTRICTION,
"protocol version",
"insufficient security",
);
ENUM_NEXT(tls_alert_desc_names, TLS_INTERNAL_ERROR, TLS_INTERNAL_ERROR,
TLS_INSUFFICIENT_SECURITY,
"internal error",
);
ENUM_NEXT(tls_alert_desc_names, TLS_INAPPROPRIATE_FALLBACK,
TLS_INAPPROPRIATE_FALLBACK, TLS_INTERNAL_ERROR,
"inappropriate fallback",
);
ENUM_NEXT(tls_alert_desc_names, TLS_USER_CANCELED, TLS_USER_CANCELED,
TLS_INAPPROPRIATE_FALLBACK,
"user canceled",
);
ENUM_NEXT(tls_alert_desc_names, TLS_NO_RENEGOTIATION, TLS_NO_RENEGOTIATION,
TLS_USER_CANCELED,
"no renegotiation",
);
ENUM_NEXT(tls_alert_desc_names, TLS_MISSING_EXTENSION, TLS_CERTIFICATE_REQUIRED,
TLS_NO_RENEGOTIATION,
"missing extensions",
"unsupported extension",
"certificate unobtainable",
"recognized name",
"bad certificate status response",
"bad certificate hash value",
"unknown psk identity",
"certificate required",
);
ENUM_NEXT(tls_alert_desc_names, TLS_NO_APPLICATION_PROTOCOL,
TLS_NO_APPLICATION_PROTOCOL, TLS_CERTIFICATE_REQUIRED,
"no application protocol"
);
ENUM_END(tls_alert_desc_names, TLS_NO_APPLICATION_PROTOCOL);
typedef struct private_tls_alert_t private_tls_alert_t;
/**
* Private data of an tls_alert_t object.
*/
struct private_tls_alert_t {
/**
* Public tls_alert_t interface.
*/
tls_alert_t public;
/**
* Warning queue
*/
linked_list_t *warnings;
/**
* Do we have a fatal alert?
*/
bool fatal;
/**
* Has the fatal alert been consumed?
*/
bool consumed;
/**
* Fatal alert description
*/
tls_alert_desc_t desc;
};
METHOD(tls_alert_t, add, void,
private_tls_alert_t *this, tls_alert_level_t level,
tls_alert_desc_t desc)
{
if (level == TLS_FATAL)
{
if (!this->fatal)
{
this->desc = desc;
this->fatal = TRUE;
}
}
else
{
this->warnings->insert_last(this->warnings, (void*)(uintptr_t)desc);
}
}
METHOD(tls_alert_t, get, bool,
private_tls_alert_t *this, tls_alert_level_t *level,
tls_alert_desc_t *desc)
{
if (this->fatal && !this->consumed)
{
this->consumed = TRUE;
*level = TLS_FATAL;
*desc = this->desc;
if (this->desc == TLS_CLOSE_NOTIFY)
{
DBG1(DBG_TLS, "sending TLS close notify");
}
else
{
DBG1(DBG_TLS, "sending fatal TLS alert '%N'",
tls_alert_desc_names, this->desc);
}
return TRUE;
}
else
{
uintptr_t warning;
if (this->warnings->remove_first(this->warnings,
(void**)&warning) == SUCCESS)
{
*level = TLS_WARNING;
*desc = warning;
DBG1(DBG_TLS, "sending TLS alert warning '%N'",
tls_alert_desc_names, warning);
return TRUE;
}
}
return FALSE;
}
METHOD(tls_alert_t, fatal, bool,
private_tls_alert_t *this)
{
return this->fatal;
}
METHOD(tls_alert_t, process, status_t,
private_tls_alert_t *this, tls_alert_level_t level,
tls_alert_desc_t desc)
{
if (desc == TLS_CLOSE_NOTIFY)
{
DBG1(DBG_TLS, "received TLS close notify");
add(this, TLS_FATAL, TLS_CLOSE_NOTIFY);
return NEED_MORE;
}
switch (level)
{
case TLS_WARNING:
DBG1(DBG_TLS, "received TLS alert warning '%N'",
tls_alert_desc_names, desc);
return NEED_MORE;
case TLS_FATAL:
DBG1(DBG_TLS, "received fatal TLS alert '%N'",
tls_alert_desc_names, desc);
return FAILED;
default:
DBG1(DBG_TLS, "received unknown TLS alert '%N'",
tls_alert_desc_names, desc);
return FAILED;
}
}
METHOD(tls_alert_t, destroy, void,
private_tls_alert_t *this)
{
this->warnings->destroy(this->warnings);
free(this);
}
/**
* See header
*/
tls_alert_t *tls_alert_create()
{
private_tls_alert_t *this;
INIT(this,
.public = {
.add = _add,
.get = _get,
.fatal = _fatal,
.process = _process,
.destroy = _destroy,
},
.warnings = linked_list_create(),
);
return &this->public;
}