digest: add support for OpenSSL 1.1.0
[siplcs.git] / src / core / sipe-ft-tftp.c
blob2fb8565e0f639d0c0704c36e9c8beea4c1c63a1a
1 /**
2 * @file sipe-ft-tftp.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2016 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 Jakub Adam <jakub.adam@ktknet.cz>
8 * Copyright (C) 2010 Tomáš Hrabčík <tomas.hrabcik@tieto.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
29 #include <string.h>
31 #include <glib.h>
32 #include <glib/gprintf.h>
34 #include "sipe-backend.h"
35 #include "sipe-core.h"
36 #include "sipe-core-private.h"
37 #include "sipe-crypt.h"
38 #include "sipe-dialog.h"
39 #include "sipe-digest.h"
40 #include "sipe-ft.h"
41 #include "sipe-ft-tftp.h"
42 #include "sipe-nls.h"
43 #include "sipe-utils.h"
45 #define BUFFER_SIZE 50
46 #define SIPE_FT_CHUNK_HEADER_LENGTH 3
48 static gboolean
49 write_exact(struct sipe_file_transfer_private *ft_private, const guchar *data,
50 gsize size)
52 gssize bytes_written = sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC,
53 data, size);
54 if ((bytes_written < 0) || ((gsize) bytes_written != size))
55 return FALSE;
56 return TRUE;
59 static gboolean
60 read_exact(struct sipe_file_transfer_private *ft_private, guchar *data,
61 gsize size)
63 const gulong READ_TIMEOUT = 10000000;
64 gulong time_spent = 0;
66 while (size) {
67 gssize bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC,
68 data, size);
69 if (bytes_read == 0) {
70 g_usleep(100000);
71 time_spent += 100000;
72 } else if (bytes_read < 0 || time_spent > READ_TIMEOUT) {
73 return FALSE;
74 } else {
75 size -= bytes_read;
76 data += bytes_read;
77 time_spent = 0;
80 return TRUE;
83 static gboolean
84 read_line(struct sipe_file_transfer_private *ft_private, guchar *data,
85 gsize size)
87 gsize pos = 0;
89 if (size < 2) return FALSE;
91 memset(data, 0, size--);
92 do {
93 if (!read_exact(ft_private, data + pos, 1))
94 return FALSE;
95 } while ((data[pos] != '\n') && (++pos < size));
97 /* Buffer too short? */
98 if ((pos == size) && (data[pos - 1] != '\n')) {
99 return FALSE;
102 return TRUE;
106 static void
107 raise_ft_socket_read_error_and_cancel(struct sipe_file_transfer_private *ft_private)
109 sipe_ft_raise_error_and_cancel(ft_private, _("Socket read failed"));
112 static void
113 raise_ft_socket_write_error_and_cancel(struct sipe_file_transfer_private *ft_private)
115 sipe_ft_raise_error_and_cancel(ft_private, _("Socket write failed"));
118 static gpointer
119 sipe_cipher_context_init(const guchar *enc_key)
122 * Decryption of file from SIPE file transfer
124 * Decryption:
125 * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key.
126 * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key;
129 guchar k2[SIPE_DIGEST_SHA1_LENGTH];
131 /* 1.) SHA1 sum */
132 sipe_digest_sha1(enc_key, SIPE_FT_KEY_LENGTH, k2);
134 /* 2.) RC4 decryption */
135 return sipe_crypt_ft_start(k2);
138 static gpointer
139 sipe_hmac_context_init(const guchar *hash_key)
142 * Count MAC digest
144 * HMAC digest:
145 * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key.
146 * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes)
149 guchar k2[SIPE_DIGEST_SHA1_LENGTH];
151 /* 1.) SHA1 sum */
152 sipe_digest_sha1(hash_key, SIPE_FT_KEY_LENGTH, k2);
154 /* 2.) HMAC (initialization only) */
155 return sipe_digest_ft_start(k2);
158 static gchar *
159 sipe_hmac_finalize(gpointer hmac_context)
161 guchar hmac_digest[SIPE_DIGEST_FILETRANSFER_LENGTH];
163 /* MAC = Digest of decrypted file and SHA1-Key (used again only 16 bytes) */
164 sipe_digest_ft_end(hmac_context, hmac_digest);
166 return g_base64_encode(hmac_digest, sizeof (hmac_digest));
169 void
170 sipe_ft_tftp_start_receiving(struct sipe_file_transfer *ft, gsize total_size)
172 static const guchar VER[] = "VER MSN_SECURE_FTP\r\n";
173 static const guchar TFR[] = "TFR\r\n";
174 const gsize FILE_SIZE_OFFSET = 4;
176 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
177 guchar buf[BUFFER_SIZE];
178 gchar *request;
179 gsize file_size;
181 if (!write_exact(ft_private, VER, sizeof(VER) - 1)) {
182 raise_ft_socket_read_error_and_cancel(ft_private);
183 return;
185 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
186 raise_ft_socket_read_error_and_cancel(ft_private);
187 return;
190 request = g_strdup_printf("USR %s %u\r\n",
191 ft_private->sipe_private->username,
192 ft_private->auth_cookie);
193 if (!write_exact(ft_private, (guchar *)request, strlen(request))) {
194 raise_ft_socket_write_error_and_cancel(ft_private);
195 g_free(request);
196 return;
198 g_free(request);
200 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
201 raise_ft_socket_read_error_and_cancel(ft_private);
202 return;
205 file_size = g_ascii_strtoull((gchar *) buf + FILE_SIZE_OFFSET, NULL, 10);
206 if (file_size != total_size) {
207 sipe_ft_raise_error_and_cancel(ft_private,
208 _("File size is different from the advertised value."));
209 return;
212 if (sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, TFR, sizeof(TFR) - 1) != (sizeof(TFR) - 1)) {
213 raise_ft_socket_write_error_and_cancel(ft_private);
214 return;
217 ft_private->bytes_remaining_chunk = 0;
218 ft_private->cipher_context = sipe_cipher_context_init(ft_private->encryption_key);
219 ft_private->hmac_context = sipe_hmac_context_init(ft_private->hash_key);
222 gboolean
223 sipe_ft_tftp_stop_receiving(struct sipe_file_transfer *ft)
225 static const guchar BYE[] = "BYE 16777989\r\n";
226 const gsize MAC_OFFSET = 4;
228 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
229 gchar buffer[BUFFER_SIZE];
230 gsize mac_len;
231 gchar *mac;
232 gchar *mac1;
234 if (sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, BYE, sizeof(BYE) - 1) != (sizeof(BYE) - 1)) {
235 raise_ft_socket_write_error_and_cancel(ft_private);
236 return FALSE;
239 if (!read_line(ft_private, (guchar *) buffer, BUFFER_SIZE)) {
240 raise_ft_socket_read_error_and_cancel(ft_private);
241 return FALSE;
244 mac_len = strlen(buffer);
245 if (mac_len < (MAC_OFFSET)) {
246 sipe_ft_raise_error_and_cancel(ft_private,
247 _("Received MAC is corrupted"));
248 return FALSE;
251 /* Check MAC */
252 mac = g_strndup(buffer + MAC_OFFSET, mac_len - MAC_OFFSET);
253 mac1 = sipe_hmac_finalize(ft_private->hmac_context);
254 if (!sipe_strequal(mac, mac1)) {
255 g_free(mac1);
256 g_free(mac);
257 sipe_ft_raise_error_and_cancel(ft_private,
258 _("Received file is corrupted"));
259 return(FALSE);
261 g_free(mac1);
262 g_free(mac);
264 sipe_ft_free(ft);
266 return(TRUE);
269 void
270 sipe_ft_tftp_start_sending(struct sipe_file_transfer *ft, gsize total_size)
272 static const guchar VER[] = "VER MSN_SECURE_FTP\r\n";
274 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
275 guchar buf[BUFFER_SIZE];
276 gchar **parts;
277 unsigned auth_cookie_received;
278 gboolean users_match;
280 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
281 raise_ft_socket_read_error_and_cancel(ft_private);
282 return;
285 if (!sipe_strequal((gchar *)buf, (gchar *)VER)) {
286 sipe_ft_raise_error_and_cancel(ft_private,
287 _("File transfer initialization failed."));
288 SIPE_DEBUG_INFO("File transfer VER string incorrect, received: %s expected: %s",
289 buf, VER);
290 return;
293 if (!write_exact(ft_private, VER, sizeof(VER) - 1)) {
294 raise_ft_socket_write_error_and_cancel(ft_private);
295 return;
298 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
299 raise_ft_socket_read_error_and_cancel(ft_private);
300 return;
303 parts = g_strsplit((gchar *)buf, " ", 3);
304 auth_cookie_received = g_ascii_strtoull(parts[2], NULL, 10);
305 /* dialog->with has 'sip:' prefix, skip these four characters */
306 users_match = sipe_strcase_equal(parts[1],
307 (ft_private->dialog->with + 4));
308 g_strfreev(parts);
310 SIPE_DEBUG_INFO("File transfer authentication: %s Expected: USR %s %u",
311 buf,
312 ft_private->dialog->with + 4,
313 ft_private->auth_cookie);
315 if (!users_match ||
316 (ft_private->auth_cookie != auth_cookie_received)) {
317 sipe_ft_raise_error_and_cancel(ft_private,
318 _("File transfer authentication failed."));
319 return;
322 g_sprintf((gchar *)buf, "FIL %" G_GSIZE_FORMAT "\r\n", total_size);
323 if (!write_exact(ft_private, buf, strlen((gchar *)buf))) {
324 raise_ft_socket_write_error_and_cancel(ft_private);
325 return;
328 /* TFR */
329 if (!read_line(ft_private ,buf, BUFFER_SIZE)) {
330 raise_ft_socket_read_error_and_cancel(ft_private);
331 return;
334 ft_private->bytes_remaining_chunk = 0;
335 ft_private->cipher_context = sipe_cipher_context_init(ft_private->encryption_key);
336 ft_private->hmac_context = sipe_hmac_context_init(ft_private->hash_key);
339 gboolean
340 sipe_ft_tftp_stop_sending(struct sipe_file_transfer *ft)
342 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
343 guchar buffer[BUFFER_SIZE];
344 gchar *mac;
345 gsize mac_len;
347 /* BYE */
348 if (!read_line(ft_private, buffer, BUFFER_SIZE)) {
349 raise_ft_socket_read_error_and_cancel(ft_private);
350 return FALSE;
353 mac = sipe_hmac_finalize(ft_private->hmac_context);
354 g_sprintf((gchar *)buffer, "MAC %s \r\n", mac);
355 g_free(mac);
357 mac_len = strlen((gchar *)buffer);
358 /* There must be this zero byte between mac and \r\n */
359 buffer[mac_len - 3] = 0;
361 if (!write_exact(ft_private, buffer, mac_len)) {
362 raise_ft_socket_write_error_and_cancel(ft_private);
363 return FALSE;
366 sipe_ft_free(ft);
368 return TRUE;
371 static void raise_ft_error(struct sipe_file_transfer_private *ft_private,
372 const gchar *errmsg)
374 gchar *tmp = g_strdup_printf("%s: %s", errmsg,
375 sipe_backend_ft_get_error(SIPE_FILE_TRANSFER_PUBLIC));
376 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, tmp);
377 g_free(tmp);
380 gssize
381 sipe_ft_tftp_read(struct sipe_file_transfer *ft, guchar **buffer,
382 gsize bytes_remaining, gsize bytes_available)
384 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
385 gsize bytes_to_read;
386 gssize bytes_read;
388 if (ft_private->bytes_remaining_chunk == 0) {
389 guchar hdr_buf[SIPE_FT_CHUNK_HEADER_LENGTH];
391 /* read chunk header */
392 if (!read_exact(ft_private, hdr_buf, sizeof(hdr_buf))) {
393 raise_ft_error(ft_private, _("Socket read failed"));
394 return -1;
397 /* chunk header format:
399 * 0: 00 unknown (always zero?)
400 * 1: LL chunk size in bytes (low byte)
401 * 2: HH chunk size in bytes (high byte)
403 * Convert size from little endian to host order
405 ft_private->bytes_remaining_chunk =
406 hdr_buf[1] + (hdr_buf[2] << 8);
409 bytes_to_read = MIN(bytes_remaining, bytes_available);
410 bytes_to_read = MIN(bytes_to_read, ft_private->bytes_remaining_chunk);
412 *buffer = g_malloc(bytes_to_read);
413 if (!*buffer) {
414 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Out of memory"));
415 SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT " bytes for receive buffer",
416 bytes_to_read);
417 return -1;
420 bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC, *buffer, bytes_to_read);
421 if (bytes_read < 0) {
422 raise_ft_error(ft_private, _("Socket read failed"));
423 g_free(*buffer);
424 *buffer = NULL;
425 return -1;
428 if (bytes_read > 0) {
429 guchar *decrypted = g_malloc(bytes_read);
431 if (!decrypted) {
432 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Out of memory"));
433 SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT " bytes for decryption buffer",
434 (gsize)bytes_read);
435 g_free(*buffer);
436 *buffer = NULL;
437 return -1;
439 sipe_crypt_ft_stream(ft_private->cipher_context,
440 *buffer, bytes_read, decrypted);
441 g_free(*buffer);
442 *buffer = decrypted;
444 sipe_digest_ft_update(ft_private->hmac_context,
445 decrypted, bytes_read);
447 ft_private->bytes_remaining_chunk -= bytes_read;
450 return(bytes_read);
453 gssize
454 sipe_ft_tftp_write(struct sipe_file_transfer *ft, const guchar *buffer,
455 gsize size)
457 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
458 gssize bytes_written;
460 /* When sending data via server with ForeFront installed, block bigger than
461 * this default causes ending of transmission. Hard limit block to this value
462 * when libpurple sends us more data. */
463 const gsize DEFAULT_BLOCK_SIZE = 2045;
464 if (size > DEFAULT_BLOCK_SIZE)
465 size = DEFAULT_BLOCK_SIZE;
467 if (ft_private->bytes_remaining_chunk == 0) {
468 gssize bytes_read;
469 guchar local_buf[16 + 1]; /* space for string terminator */
470 guchar hdr_buf[SIPE_FT_CHUNK_HEADER_LENGTH];
472 /* Check if receiver did not cancel the transfer
473 before it is finished */
474 bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC,
475 local_buf,
476 sizeof(local_buf) - 1);
477 local_buf[sizeof(local_buf) - 1] = '\0';
479 if (bytes_read < 0) {
480 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC,
481 _("Socket read failed"));
482 return -1;
483 } else if ((bytes_read > 0) &&
484 (g_str_has_prefix((gchar *)local_buf, "CCL\r\n") ||
485 g_str_has_prefix((gchar *)local_buf, "BYE 2164261682\r\n"))) {
486 return -1;
489 if (ft_private->outbuf_size < size) {
490 g_free(ft_private->encrypted_outbuf);
491 ft_private->outbuf_size = size;
492 ft_private->encrypted_outbuf = g_malloc(ft_private->outbuf_size);
493 if (!ft_private->encrypted_outbuf) {
494 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC,
495 _("Out of memory"));
496 SIPE_DEBUG_ERROR("sipe_core_ft_write: can't allocate %" G_GSIZE_FORMAT " bytes for send buffer",
497 ft_private->outbuf_size);
498 return -1;
502 ft_private->bytes_remaining_chunk = size;
503 ft_private->outbuf_ptr = ft_private->encrypted_outbuf;
504 sipe_crypt_ft_stream(ft_private->cipher_context,
505 buffer, size,
506 ft_private->encrypted_outbuf);
507 sipe_digest_ft_update(ft_private->hmac_context,
508 buffer, size);
510 /* chunk header format:
512 * 0: 00 unknown (always zero?)
513 * 1: LL chunk size in bytes (low byte)
514 * 2: HH chunk size in bytes (high byte)
516 * Convert size from host order to little endian
518 hdr_buf[0] = 0;
519 hdr_buf[1] = (ft_private->bytes_remaining_chunk & 0x00FF);
520 hdr_buf[2] = (ft_private->bytes_remaining_chunk & 0xFF00) >> 8;
522 /* write chunk header */
523 if (sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, hdr_buf, sizeof(hdr_buf)) != sizeof(hdr_buf)) {
524 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC,
525 _("Socket write failed"));
526 return -1;
530 bytes_written = sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC,
531 ft_private->outbuf_ptr,
532 ft_private->bytes_remaining_chunk);
533 if (bytes_written < 0) {
534 raise_ft_error(ft_private, _("Socket write failed"));
535 } else if (bytes_written > 0) {
536 ft_private->bytes_remaining_chunk -= bytes_written;
537 ft_private->outbuf_ptr += bytes_written;
540 return bytes_written;
544 Local Variables:
545 mode: c
546 c-file-style: "bsd"
547 indent-tabs-mode: t
548 tab-width: 8
549 End: