1 /*
2 fe-sasl.c : irssi
3
4 Copyright (C) 2015 The Lemon Man
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "module.h"
22 #include "misc.h"
23 #include "settings.h"
24
25 #include "irc-cap.h"
26 #include "irc-servers.h"
27 #include "sasl.h"
28
29 /*
30 * Based on IRCv3 SASL Extension Specification:
31 * http://ircv3.net/specs/extensions/sasl-3.1.html
32 */
33 #define AUTHENTICATE_CHUNK_SIZE 400 /* bytes */
34
35 /*
36 * Maximum size to allow the buffer to grow to before the next fragment comes in. Note that
37 * due to the way fragmentation works, the maximum message size will actually be:
38 * floor(AUTHENTICATE_MAX_SIZE / AUTHENTICATE_CHUNK_SIZE) + AUTHENTICATE_CHUNK_SIZE - 1
39 */
40 #define AUTHENTICATE_MAX_SIZE 8192 /* bytes */
41
42 #define SASL_TIMEOUT (20 * 1000) /* ms */
43
sasl_timeout(IRC_SERVER_REC * server)44 static gboolean sasl_timeout(IRC_SERVER_REC *server)
45 {
46 /* The authentication timed out, we can't do much beside terminating it */
47 irc_send_cmd_now(server, "AUTHENTICATE *");
48 irc_cap_finish_negotiation(server);
49
50 server->sasl_timeout = 0;
51 server->sasl_success = FALSE;
52
53 signal_emit("server sasl failure", 2, server, "The authentication timed out");
54
55 return FALSE;
56 }
57
sasl_timeout_stop(IRC_SERVER_REC * server)58 static void sasl_timeout_stop(IRC_SERVER_REC *server)
59 {
60 /* Stop any pending timeout, if any */
61 if (server->sasl_timeout != 0) {
62 g_source_remove(server->sasl_timeout);
63 server->sasl_timeout = 0;
64 }
65 }
66
sasl_start(IRC_SERVER_REC * server,const char * data,const char * from)67 static void sasl_start(IRC_SERVER_REC *server, const char *data, const char *from)
68 {
69 IRC_SERVER_CONNECT_REC *conn;
70
71 sasl_timeout_stop(server);
72
73 conn = server->connrec;
74
75 switch (conn->sasl_mechanism) {
76 case SASL_MECHANISM_PLAIN:
77 irc_send_cmd_now(server, "AUTHENTICATE PLAIN");
78 break;
79
80 case SASL_MECHANISM_EXTERNAL:
81 irc_send_cmd_now(server, "AUTHENTICATE EXTERNAL");
82 break;
83 }
84 server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
85 }
86
sasl_fail(IRC_SERVER_REC * server,const char * data,const char * from)87 static void sasl_fail(IRC_SERVER_REC *server, const char *data, const char *from)
88 {
89 char *params, *error;
90
91
92 params = event_get_params(data, 2, NULL, &error);
93
94 server->sasl_success = FALSE;
95
96 signal_emit("server sasl failure", 2, server, error);
97
98 /* Terminate the negotiation */
99 irc_cap_finish_negotiation(server);
100
101 g_free(params);
102 }
103
sasl_already(IRC_SERVER_REC * server,const char * data,const char * from)104 static void sasl_already(IRC_SERVER_REC *server, const char *data, const char *from)
105 {
106 sasl_timeout_stop(server);
107
108 server->sasl_success = TRUE;
109
110 signal_emit("server sasl success", 1, server);
111
112 /* We're already authenticated, do nothing */
113 irc_cap_finish_negotiation(server);
114 }
115
sasl_success(IRC_SERVER_REC * server,const char * data,const char * from)116 static void sasl_success(IRC_SERVER_REC *server, const char *data, const char *from)
117 {
118 sasl_timeout_stop(server);
119
120 server->sasl_success = TRUE;
121
122 signal_emit("server sasl success", 1, server);
123
124 /* The authentication succeeded, time to finish the CAP negotiation */
125 irc_cap_finish_negotiation(server);
126 }
127
128 /*
129 * Responsible for reassembling incoming SASL requests. SASL requests must be split
130 * into 400 byte requests to stay below the IRC command length limit of 512 bytes.
131 * The spec says that if there is 400 bytes, then there is expected to be a
132 * continuation in the next chunk. If a message is exactly a multiple of 400 bytes,
133 * there must be a blank message of "AUTHENTICATE +" to indicate the end.
134 *
135 * This function returns the fully reassembled and decoded AUTHENTICATION message if
136 * completed or NULL if there are more messages expected.
137 */
sasl_reassemble_incoming(IRC_SERVER_REC * server,const char * fragment,GString ** decoded)138 static gboolean sasl_reassemble_incoming(IRC_SERVER_REC *server, const char *fragment, GString **decoded)
139 {
140 GString *enc_req;
141 gsize fragment_len;
142
143 fragment_len = strlen(fragment);
144
145 /* Check if there is an existing fragment to prepend. */
146 if (server->sasl_buffer != NULL) {
147 if (g_strcmp0("+", fragment) == 0) {
148 enc_req = server->sasl_buffer;
149 } else {
150 enc_req = g_string_append_len(server->sasl_buffer, fragment, fragment_len);
151 }
152 server->sasl_buffer = NULL;
153 } else {
154 enc_req = g_string_new_len(fragment, fragment_len);
155 }
156
157 /*
158 * Fail authentication with this server. They have sent too much data.
159 */
160 if (enc_req->len > AUTHENTICATE_MAX_SIZE) {
161 g_string_free(enc_req, TRUE);
162 return FALSE;
163 }
164
165 /*
166 * If the the request is exactly the chunk size, this is a fragment
167 * and more data is expected.
168 */
169 if (fragment_len == AUTHENTICATE_CHUNK_SIZE) {
170 server->sasl_buffer = enc_req;
171 return TRUE;
172 }
173
174 if (enc_req->len == 1 && *enc_req->str == '+') {
175 *decoded = g_string_new_len("", 0);
176 } else {
177 gsize dec_len;
178 gint state = 0;
179 guint save = 0;
180
181 /* Since we're not going to use the enc_req GString anymore we
182 * can perform the decoding in place. */
183 dec_len = g_base64_decode_step(enc_req->str, enc_req->len,
184 (guchar *)enc_req->str,
185 &state, &save);
186 /* A copy of the data is made when the GString is created. */
187 *decoded = g_string_new_len(enc_req->str, dec_len);
188 }
189
190 g_string_free(enc_req, TRUE);
191 return TRUE;
192 }
193
194 /*
195 * Splits the response into appropriately sized chunks for the AUTHENTICATION
196 * command to be sent to the IRC server. If |response| is NULL, then the empty
197 * response is sent to the server.
198 */
sasl_send_response(IRC_SERVER_REC * server,GString * response)199 void sasl_send_response(IRC_SERVER_REC *server, GString *response)
200 {
201 char *enc;
202 size_t offset, enc_len, chunk_len;
203
204 if (response == NULL) {
205 irc_send_cmdv(server, "AUTHENTICATE +");
206 return;
207 }
208
209 enc = g_base64_encode((guchar *) response->str, response->len);
210 enc_len = strlen(enc);
211
212 for (offset = 0; offset < enc_len; offset += AUTHENTICATE_CHUNK_SIZE) {
213 chunk_len = enc_len - offset;
214 if (chunk_len > AUTHENTICATE_CHUNK_SIZE)
215 chunk_len = AUTHENTICATE_CHUNK_SIZE;
216
217 irc_send_cmdv(server, "AUTHENTICATE %.*s", (int) chunk_len, enc + offset);
218 }
219
220 if (offset == enc_len) {
221 irc_send_cmdv(server, "AUTHENTICATE +");
222 }
223 g_free(enc);
224 }
225
226 /*
227 * Called when the incoming SASL request is completely received.
228 */
sasl_step_complete(IRC_SERVER_REC * server,GString * data)229 static void sasl_step_complete(IRC_SERVER_REC *server, GString *data)
230 {
231 IRC_SERVER_CONNECT_REC *conn;
232 GString *resp;
233
234 conn = server->connrec;
235
236 switch (conn->sasl_mechanism) {
237 case SASL_MECHANISM_PLAIN:
238 /* At this point we assume that conn->sasl_{username, password} are non-NULL.
239 * The PLAIN mechanism expects a NULL-separated string composed by the authorization identity, the
240 * authentication identity and the password.
241 * The authorization identity field is explicitly set to the user provided username.
242 */
243
244 resp = g_string_new(NULL);
245
246 g_string_append(resp, conn->sasl_username);
247 g_string_append_c(resp, '\0');
248 g_string_append(resp, conn->sasl_username);
249 g_string_append_c(resp, '\0');
250 g_string_append(resp, conn->sasl_password);
251
252 sasl_send_response(server, resp);
253 g_string_free(resp, TRUE);
254
255 break;
256
257 case SASL_MECHANISM_EXTERNAL:
258 /* Empty response */
259 sasl_send_response(server, NULL);
260 break;
261 }
262 }
263
sasl_step_fail(IRC_SERVER_REC * server)264 static void sasl_step_fail(IRC_SERVER_REC *server)
265 {
266 irc_send_cmd_now(server, "AUTHENTICATE *");
267 irc_cap_finish_negotiation(server);
268
269 sasl_timeout_stop(server);
270
271 signal_emit("server sasl failure", 2, server, "The server sent an invalid payload");
272 }
273
sasl_step(IRC_SERVER_REC * server,const char * data,const char * from)274 static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from)
275 {
276 GString *req = NULL;
277
278 sasl_timeout_stop(server);
279
280 if (!sasl_reassemble_incoming(server, data, &req)) {
281 sasl_step_fail(server);
282 return;
283 }
284
285 if (req != NULL) {
286 sasl_step_complete(server, req);
287 g_string_free(req, TRUE);
288 }
289
290 /* We expect a response within a reasonable time */
291 server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
292 }
293
sasl_disconnected(IRC_SERVER_REC * server)294 static void sasl_disconnected(IRC_SERVER_REC *server)
295 {
296 g_return_if_fail(server != NULL);
297
298 if (!IS_IRC_SERVER(server)) {
299 return;
300 }
301
302 sasl_timeout_stop(server);
303 }
304
sig_sasl_over(IRC_SERVER_REC * server)305 static void sig_sasl_over(IRC_SERVER_REC *server)
306 {
307 if (!IS_IRC_SERVER(server))
308 return;
309
310 /* The negotiation has now been terminated, if we didn't manage to
311 * authenticate successfully with the server just disconnect. */
312 if (!server->sasl_success &&
313 server->connrec->sasl_mechanism != SASL_MECHANISM_NONE) {
314 if (server->cap_supported == NULL ||
315 !g_hash_table_lookup_extended(server->cap_supported, "sasl", NULL, NULL)) {
316 signal_emit("server sasl failure", 2, server, "The server did not offer SASL");
317 }
318
319 if (settings_get_bool("sasl_disconnect_on_failure")) {
320 /* We can't use server_disconnect() here because we'd end up
321 * freeing the 'server' object and be guilty of a slew of UaF. */
322 server->connection_lost = TRUE;
323 /* By setting connection_lost we make sure the communication is
324 * halted and when the control goes back to irc_parse_incoming
325 * the server object is safely destroyed. */
326 signal_stop();
327 }
328 }
329
330 }
331
sasl_init(void)332 void sasl_init(void)
333 {
334 settings_add_bool("server", "sasl_disconnect_on_failure", TRUE);
335
336 signal_add_first("event 001", (SIGNAL_FUNC) sig_sasl_over);
337 /* this event can get us connected on broken ircds, see irc-servers.c */
338 signal_add_first("event 375", (SIGNAL_FUNC) sig_sasl_over);
339 signal_add_first("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
340 signal_add_first("server cap end", (SIGNAL_FUNC) sig_sasl_over);
341 signal_add_first("event authenticate", (SIGNAL_FUNC) sasl_step);
342 signal_add_first("event 903", (SIGNAL_FUNC) sasl_success);
343 signal_add_first("event 902", (SIGNAL_FUNC) sasl_fail);
344 signal_add_first("event 904", (SIGNAL_FUNC) sasl_fail);
345 signal_add_first("event 905", (SIGNAL_FUNC) sasl_fail);
346 signal_add_first("event 906", (SIGNAL_FUNC) sasl_fail);
347 signal_add_first("event 907", (SIGNAL_FUNC) sasl_already);
348 signal_add_first("server disconnected", (SIGNAL_FUNC) sasl_disconnected);
349 }
350
sasl_deinit(void)351 void sasl_deinit(void)
352 {
353 signal_remove("event 001", (SIGNAL_FUNC) sig_sasl_over);
354 signal_remove("event 375", (SIGNAL_FUNC) sig_sasl_over);
355 signal_remove("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
356 signal_remove("server cap end", (SIGNAL_FUNC) sig_sasl_over);
357 signal_remove("event authenticate", (SIGNAL_FUNC) sasl_step);
358 signal_remove("event 903", (SIGNAL_FUNC) sasl_success);
359 signal_remove("event 902", (SIGNAL_FUNC) sasl_fail);
360 signal_remove("event 904", (SIGNAL_FUNC) sasl_fail);
361 signal_remove("event 905", (SIGNAL_FUNC) sasl_fail);
362 signal_remove("event 906", (SIGNAL_FUNC) sasl_fail);
363 signal_remove("event 907", (SIGNAL_FUNC) sasl_already);
364 signal_remove("server disconnected", (SIGNAL_FUNC) sasl_disconnected);
365 }
366