Implemented TLS Alert handling
[strongswan.git] / src / libtls / tls_alert.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "tls_alert.h"
17
18 #include <debug.h>
19 #include <utils/linked_list.h>
20
21 ENUM_BEGIN(tls_alert_desc_names, TLS_CLOSE_NOTIFY, TLS_CLOSE_NOTIFY,
22 "close notify",
23 );
24 ENUM_NEXT(tls_alert_desc_names, TLS_UNEXPECTED_MESSAGE, TLS_UNEXPECTED_MESSAGE,
25 TLS_CLOSE_NOTIFY,
26 "unexpected message",
27 );
28 ENUM_NEXT(tls_alert_desc_names, TLS_BAD_RECORD_MAC, TLS_RECORD_OVERFLOW,
29 TLS_UNEXPECTED_MESSAGE,
30 "bad record mac",
31 "decryption failed",
32 "record overflow",
33 );
34 ENUM_NEXT(tls_alert_desc_names, TLS_DECOMPRESSION_FAILURE, TLS_DECOMPRESSION_FAILURE,
35 TLS_RECORD_OVERFLOW,
36 "decompression_failure",
37 );
38 ENUM_NEXT(tls_alert_desc_names, TLS_HANDSHAKE_FAILURE, TLS_DECRYPT_ERROR,
39 TLS_DECOMPRESSION_FAILURE,
40 "handshake failure",
41 "no certificate",
42 "bad certificate",
43 "unsupported certificate",
44 "certificate revoked",
45 "certificate expired",
46 "certificate unknown",
47 "illegal parameter",
48 "unknown ca",
49 "access denied",
50 "decode error",
51 "decrypt error",
52 );
53 ENUM_NEXT(tls_alert_desc_names, TLS_EXPORT_RESTRICTION, TLS_EXPORT_RESTRICTION,
54 TLS_DECRYPT_ERROR,
55 "export restriction",
56 );
57 ENUM_NEXT(tls_alert_desc_names, TLS_PROTOCOL_VERSION, TLS_INSUFFICIENT_SECURITY,
58 TLS_EXPORT_RESTRICTION,
59 "protocol version",
60 "insufficient security",
61 );
62 ENUM_NEXT(tls_alert_desc_names, TLS_INTERNAL_ERROR, TLS_INTERNAL_ERROR,
63 TLS_INSUFFICIENT_SECURITY,
64 "internal error",
65 );
66 ENUM_NEXT(tls_alert_desc_names, TLS_USER_CANCELED, TLS_USER_CANCELED,
67 TLS_INTERNAL_ERROR,
68 "user canceled",
69 );
70 ENUM_NEXT(tls_alert_desc_names, TLS_NO_RENEGOTIATION, TLS_NO_RENEGOTIATION,
71 TLS_USER_CANCELED,
72 "no renegotiation",
73 );
74 ENUM_NEXT(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION, TLS_UNSUPPORTED_EXTENSION,
75 TLS_NO_RENEGOTIATION,
76 "unsupported extension",
77 );
78 ENUM_END(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION);
79
80
81 typedef struct private_tls_alert_t private_tls_alert_t;
82
83 /**
84 * Private data of an tls_alert_t object.
85 */
86 struct private_tls_alert_t {
87
88 /**
89 * Public tls_alert_t interface.
90 */
91 tls_alert_t public;
92
93 /**
94 * Warning queue
95 */
96 linked_list_t *warnings;
97
98 /**
99 * Do we have a fatal alert?
100 */
101 bool fatal;
102
103 /**
104 * Has the fatal alert been consumed?
105 */
106 bool consumed;
107
108 /**
109 * Fatal alert discription
110 */
111 tls_alert_desc_t desc;
112 };
113
114 METHOD(tls_alert_t, add, void,
115 private_tls_alert_t *this, tls_alert_level_t level,
116 tls_alert_desc_t desc)
117 {
118 if (level == TLS_FATAL)
119 {
120 if (!this->fatal)
121 {
122 this->desc = desc;
123 this->fatal = TRUE;
124 }
125 }
126 else
127 {
128 this->warnings->insert_last(this->warnings, (void*)(uintptr_t)desc);
129 }
130 }
131
132 METHOD(tls_alert_t, get, bool,
133 private_tls_alert_t *this, tls_alert_level_t *level,
134 tls_alert_desc_t *desc)
135 {
136 if (this->fatal && !this->consumed)
137 {
138 this->consumed = TRUE;
139 *level = TLS_FATAL;
140 *desc = this->desc;
141 DBG1(DBG_TLS, "sending fatal TLS alert '%N'",
142 tls_alert_desc_names, this->desc);
143 return TRUE;
144 }
145 else
146 {
147 uintptr_t warning;
148
149 if (this->warnings->remove_first(this->warnings,
150 (void**)&warning) == SUCCESS)
151 {
152 *level = TLS_WARNING;
153 *desc = warning;
154 DBG1(DBG_TLS, "sending TLS alert warning '%N'",
155 tls_alert_desc_names, warning);
156 return TRUE;
157 }
158 }
159 return FALSE;
160 }
161
162 METHOD(tls_alert_t, fatal, bool,
163 private_tls_alert_t *this)
164 {
165 return this->fatal;
166 }
167
168 METHOD(tls_alert_t, process, status_t,
169 private_tls_alert_t *this, tls_alert_level_t level,
170 tls_alert_desc_t desc)
171 {
172 if (desc == TLS_CLOSE_NOTIFY)
173 {
174 DBG1(DBG_TLS, "received TLS close notify");
175 add(this, TLS_FATAL, TLS_CLOSE_NOTIFY);
176 return NEED_MORE;
177 }
178 switch (level)
179 {
180 case TLS_WARNING:
181 DBG1(DBG_TLS, "received TLS alert warning '%N'",
182 tls_alert_desc_names, desc);
183 return NEED_MORE;
184 case TLS_FATAL:
185 DBG1(DBG_TLS, "received fatal TLS alert '%N'",
186 tls_alert_desc_names, desc);
187 return FAILED;
188 default:
189 DBG1(DBG_TLS, "received unknown TLS alert '%N'",
190 tls_alert_desc_names, desc);
191 return FAILED;
192 }
193 }
194
195 METHOD(tls_alert_t, destroy, void,
196 private_tls_alert_t *this)
197 {
198 this->warnings->destroy(this->warnings);
199 free(this);
200 }
201
202 /**
203 * See header
204 */
205 tls_alert_t *tls_alert_create()
206 {
207 private_tls_alert_t *this;
208
209 INIT(this,
210 .public = {
211 .add = _add,
212 .get = _get,
213 .fatal = _fatal,
214 .process = _process,
215 .destroy = _destroy,
216 },
217 .warnings = linked_list_create(),
218 );
219
220 return &this->public;
221 }