diff options
Diffstat (limited to 'src/libtls/tls_alert.c')
-rw-r--r-- | src/libtls/tls_alert.c | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/src/libtls/tls_alert.c b/src/libtls/tls_alert.c new file mode 100644 index 000000000..8a4fa7d77 --- /dev/null +++ b/src/libtls/tls_alert.c @@ -0,0 +1,228 @@ +/* + * 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 <debug.h> +#include <utils/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_USER_CANCELED, TLS_USER_CANCELED, + TLS_INTERNAL_ERROR, + "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_UNSUPPORTED_EXTENSION, TLS_UNSUPPORTED_EXTENSION, + TLS_NO_RENEGOTIATION, + "unsupported extension", +); +ENUM_END(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION); + + +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 discription + */ + 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; +} |