6 * Copyright (C) 2010-2015 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
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"
41 #include "sipe-ft-tftp.h"
43 #include "sipe-utils.h"
45 #define BUFFER_SIZE 50
46 #define SIPE_FT_CHUNK_HEADER_LENGTH 3
49 write_exact(struct sipe_file_transfer_private
*ft_private
, const guchar
*data
,
52 gssize bytes_written
= sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC
,
54 if ((bytes_written
< 0) || ((gsize
) bytes_written
!= size
))
60 read_exact(struct sipe_file_transfer_private
*ft_private
, guchar
*data
,
63 const gulong READ_TIMEOUT
= 10000000;
64 gulong time_spent
= 0;
67 gssize bytes_read
= sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC
,
69 if (bytes_read
== 0) {
72 } else if (bytes_read
< 0 || time_spent
> READ_TIMEOUT
) {
84 read_line(struct sipe_file_transfer_private
*ft_private
, guchar
*data
,
89 if (size
< 2) return FALSE
;
91 memset(data
, 0, size
--);
93 if (!read_exact(ft_private
, data
+ pos
, 1))
95 } while ((data
[pos
] != '\n') && (++pos
< size
));
97 /* Buffer too short? */
98 if ((pos
== size
) && (data
[pos
- 1] != '\n')) {
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"));
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"));
119 sipe_cipher_context_init(const guchar
*enc_key
)
122 * Decryption of file from SIPE file transfer
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
];
132 sipe_digest_sha1(enc_key
, SIPE_FT_KEY_LENGTH
, k2
);
134 /* 2.) RC4 decryption */
135 return sipe_crypt_ft_start(k2
);
139 sipe_hmac_context_init(const guchar
*hash_key
)
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
];
152 sipe_digest_sha1(hash_key
, SIPE_FT_KEY_LENGTH
, k2
);
154 /* 2.) HMAC (initialization only) */
155 return sipe_digest_ft_start(k2
);
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
));
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
];
181 if (!write_exact(ft_private
, VER
, sizeof(VER
) - 1)) {
182 raise_ft_socket_read_error_and_cancel(ft_private
);
185 if (!read_line(ft_private
, buf
, BUFFER_SIZE
)) {
186 raise_ft_socket_read_error_and_cancel(ft_private
);
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
);
200 if (!read_line(ft_private
, buf
, BUFFER_SIZE
)) {
201 raise_ft_socket_read_error_and_cancel(ft_private
);
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."));
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
);
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
);
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
];
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
);
239 if (!read_line(ft_private
, (guchar
*) buffer
, BUFFER_SIZE
)) {
240 raise_ft_socket_read_error_and_cancel(ft_private
);
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"));
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
)) {
257 sipe_ft_raise_error_and_cancel(ft_private
,
258 _("Received file is corrupted"));
268 sipe_ft_tftp_start_sending(struct sipe_file_transfer
*ft
, gsize total_size
)
270 static const guchar VER
[] = "VER MSN_SECURE_FTP\r\n";
272 struct sipe_file_transfer_private
*ft_private
= SIPE_FILE_TRANSFER_PRIVATE
;
273 guchar buf
[BUFFER_SIZE
];
275 unsigned auth_cookie_received
;
276 gboolean users_match
;
278 if (!read_line(ft_private
, buf
, BUFFER_SIZE
)) {
279 raise_ft_socket_read_error_and_cancel(ft_private
);
283 if (!sipe_strequal((gchar
*)buf
, (gchar
*)VER
)) {
284 sipe_ft_raise_error_and_cancel(ft_private
,
285 _("File transfer initialization failed."));
286 SIPE_DEBUG_INFO("File transfer VER string incorrect, received: %s expected: %s",
291 if (!write_exact(ft_private
, VER
, sizeof(VER
) - 1)) {
292 raise_ft_socket_write_error_and_cancel(ft_private
);
296 if (!read_line(ft_private
, buf
, BUFFER_SIZE
)) {
297 raise_ft_socket_read_error_and_cancel(ft_private
);
301 parts
= g_strsplit((gchar
*)buf
, " ", 3);
302 auth_cookie_received
= g_ascii_strtoull(parts
[2], NULL
, 10);
303 /* dialog->with has 'sip:' prefix, skip these four characters */
304 users_match
= sipe_strcase_equal(parts
[1],
305 (ft_private
->dialog
->with
+ 4));
308 SIPE_DEBUG_INFO("File transfer authentication: %s Expected: USR %s %u",
310 ft_private
->dialog
->with
+ 4,
311 ft_private
->auth_cookie
);
314 (ft_private
->auth_cookie
!= auth_cookie_received
)) {
315 sipe_ft_raise_error_and_cancel(ft_private
,
316 _("File transfer authentication failed."));
320 g_sprintf((gchar
*)buf
, "FIL %" G_GSIZE_FORMAT
"\r\n", total_size
);
321 if (!write_exact(ft_private
, buf
, strlen((gchar
*)buf
))) {
322 raise_ft_socket_write_error_and_cancel(ft_private
);
327 if (!read_line(ft_private
,buf
, BUFFER_SIZE
)) {
328 raise_ft_socket_read_error_and_cancel(ft_private
);
332 ft_private
->bytes_remaining_chunk
= 0;
333 ft_private
->cipher_context
= sipe_cipher_context_init(ft_private
->encryption_key
);
334 ft_private
->hmac_context
= sipe_hmac_context_init(ft_private
->hash_key
);
338 sipe_ft_tftp_stop_sending(struct sipe_file_transfer
*ft
)
340 struct sipe_file_transfer_private
*ft_private
= SIPE_FILE_TRANSFER_PRIVATE
;
341 guchar buffer
[BUFFER_SIZE
];
346 if (!read_line(ft_private
, buffer
, BUFFER_SIZE
)) {
347 raise_ft_socket_read_error_and_cancel(ft_private
);
351 mac
= sipe_hmac_finalize(ft_private
->hmac_context
);
352 g_sprintf((gchar
*)buffer
, "MAC %s \r\n", mac
);
355 mac_len
= strlen((gchar
*)buffer
);
356 /* There must be this zero byte between mac and \r\n */
357 buffer
[mac_len
- 3] = 0;
359 if (!write_exact(ft_private
, buffer
, mac_len
)) {
360 raise_ft_socket_write_error_and_cancel(ft_private
);
367 static void raise_ft_error(struct sipe_file_transfer_private
*ft_private
,
370 gchar
*tmp
= g_strdup_printf("%s: %s", errmsg
,
371 sipe_backend_ft_get_error(SIPE_FILE_TRANSFER_PUBLIC
));
372 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC
, tmp
);
377 sipe_ft_tftp_read(struct sipe_file_transfer
*ft
, guchar
**buffer
,
378 gsize bytes_remaining
, gsize bytes_available
)
380 struct sipe_file_transfer_private
*ft_private
= SIPE_FILE_TRANSFER_PRIVATE
;
384 if (ft_private
->bytes_remaining_chunk
== 0) {
385 guchar hdr_buf
[SIPE_FT_CHUNK_HEADER_LENGTH
];
387 /* read chunk header */
388 if (!read_exact(ft_private
, hdr_buf
, sizeof(hdr_buf
))) {
389 raise_ft_error(ft_private
, _("Socket read failed"));
393 /* chunk header format:
395 * 0: 00 unknown (always zero?)
396 * 1: LL chunk size in bytes (low byte)
397 * 2: HH chunk size in bytes (high byte)
399 * Convert size from little endian to host order
401 ft_private
->bytes_remaining_chunk
=
402 hdr_buf
[1] + (hdr_buf
[2] << 8);
405 bytes_to_read
= MIN(bytes_remaining
, bytes_available
);
406 bytes_to_read
= MIN(bytes_to_read
, ft_private
->bytes_remaining_chunk
);
408 *buffer
= g_malloc(bytes_to_read
);
410 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC
, _("Out of memory"));
411 SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for receive buffer",
416 bytes_read
= sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC
, *buffer
, bytes_to_read
);
417 if (bytes_read
< 0) {
418 raise_ft_error(ft_private
, _("Socket read failed"));
424 if (bytes_read
> 0) {
425 guchar
*decrypted
= g_malloc(bytes_read
);
428 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC
, _("Out of memory"));
429 SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for decryption buffer",
435 sipe_crypt_ft_stream(ft_private
->cipher_context
,
436 *buffer
, bytes_read
, decrypted
);
440 sipe_digest_ft_update(ft_private
->hmac_context
,
441 decrypted
, bytes_read
);
443 ft_private
->bytes_remaining_chunk
-= bytes_read
;
450 sipe_ft_tftp_write(struct sipe_file_transfer
*ft
, const guchar
*buffer
,
453 struct sipe_file_transfer_private
*ft_private
= SIPE_FILE_TRANSFER_PRIVATE
;
454 gssize bytes_written
;
456 /* When sending data via server with ForeFront installed, block bigger than
457 * this default causes ending of transmission. Hard limit block to this value
458 * when libpurple sends us more data. */
459 const gsize DEFAULT_BLOCK_SIZE
= 2045;
460 if (size
> DEFAULT_BLOCK_SIZE
)
461 size
= DEFAULT_BLOCK_SIZE
;
463 if (ft_private
->bytes_remaining_chunk
== 0) {
465 guchar local_buf
[16 + 1]; /* space for string terminator */
466 guchar hdr_buf
[SIPE_FT_CHUNK_HEADER_LENGTH
];
468 /* Check if receiver did not cancel the transfer
469 before it is finished */
470 bytes_read
= sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC
,
472 sizeof(local_buf
) - 1);
473 local_buf
[sizeof(local_buf
) - 1] = '\0';
475 if (bytes_read
< 0) {
476 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC
,
477 _("Socket read failed"));
479 } else if ((bytes_read
> 0) &&
480 (g_str_has_prefix((gchar
*)local_buf
, "CCL\r\n") ||
481 g_str_has_prefix((gchar
*)local_buf
, "BYE 2164261682\r\n"))) {
485 if (ft_private
->outbuf_size
< size
) {
486 g_free(ft_private
->encrypted_outbuf
);
487 ft_private
->outbuf_size
= size
;
488 ft_private
->encrypted_outbuf
= g_malloc(ft_private
->outbuf_size
);
489 if (!ft_private
->encrypted_outbuf
) {
490 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC
,
492 SIPE_DEBUG_ERROR("sipe_core_ft_write: can't allocate %" G_GSIZE_FORMAT
" bytes for send buffer",
493 ft_private
->outbuf_size
);
498 ft_private
->bytes_remaining_chunk
= size
;
499 ft_private
->outbuf_ptr
= ft_private
->encrypted_outbuf
;
500 sipe_crypt_ft_stream(ft_private
->cipher_context
,
502 ft_private
->encrypted_outbuf
);
503 sipe_digest_ft_update(ft_private
->hmac_context
,
506 /* chunk header format:
508 * 0: 00 unknown (always zero?)
509 * 1: LL chunk size in bytes (low byte)
510 * 2: HH chunk size in bytes (high byte)
512 * Convert size from host order to little endian
515 hdr_buf
[1] = (ft_private
->bytes_remaining_chunk
& 0x00FF);
516 hdr_buf
[2] = (ft_private
->bytes_remaining_chunk
& 0xFF00) >> 8;
518 /* write chunk header */
519 if (sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC
, hdr_buf
, sizeof(hdr_buf
)) != sizeof(hdr_buf
)) {
520 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC
,
521 _("Socket write failed"));
526 bytes_written
= sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC
,
527 ft_private
->outbuf_ptr
,
528 ft_private
->bytes_remaining_chunk
);
529 if (bytes_written
< 0) {
530 raise_ft_error(ft_private
, _("Socket write failed"));
531 } else if (bytes_written
> 0) {
532 ft_private
->bytes_remaining_chunk
-= bytes_written
;
533 ft_private
->outbuf_ptr
+= bytes_written
;
536 return bytes_written
;