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