1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <cfnet.h> /* struct ConnectionInfo */
26 #include <client_code.h>
27 #include <communication.h>
28 #include <connection_info.h>
29 #include <classic.h> /* RecvSocketStream */
30 #include <net.h> /* SendTransaction,ReceiveTransaction */
31 #include <openssl/err.h> /* ERR_get_error */
32 #include <protocol.h> /* ProtocolIsUndefined() */
33 #include <libcrypto-compat.h>
34 #include <tls_client.h> /* TLSTry */
35 #include <tls_generic.h> /* TLSVerifyPeer */
36 #include <dir.h>
37 #include <unix.h>
38 #include <dir_priv.h> /* AllocateDirentForFilename */
39 #include <client_protocol.h>
40 #include <crypto.h> /* CryptoInitialize,SavePublicKey,EncryptString */
41 #include <logging.h>
42 #include <hash.h> /* HashFile */
43 #include <mutex.h> /* ThreadLock */
44 #include <files_lib.h> /* FullWrite,safe_open */
45 #include <string_lib.h> /* MemSpan,MemSpanInverse */
46 #include <misc_lib.h> /* ProgrammingError */
47 #include <printsize.h> /* PRINTSIZE */
48 #include <lastseen.h> /* LastSaw */
49
50
51 #define CFENGINE_SERVICE "cfengine"
52
53
54 /**
55 * Initialize client's network library.
56 */
cfnet_init(const char * tls_min_version,const char * ciphers)57 bool cfnet_init(const char *tls_min_version, const char *ciphers)
58 {
59 CryptoInitialize();
60 return TLSClientInitialize(tls_min_version, ciphers);
61 }
62
cfnet_shut()63 void cfnet_shut()
64 {
65 TLSDeInitialize();
66 CryptoDeInitialize();
67 }
68
cfnet_IsInitialized()69 bool cfnet_IsInitialized()
70 {
71 return TLSClientIsInitialized();
72 }
73
74 #define MAX_PORT_NUMBER 65535 /* 2^16 - 1 */
75
76 /* These should only be modified by the two functions below! */
77 int CFENGINE_PORT = 5308;
78 char CFENGINE_PORT_STR[16] = "5308";
79
DetermineCfenginePort()80 void DetermineCfenginePort()
81 {
82 nt_static_assert(sizeof(CFENGINE_PORT_STR) >= PRINTSIZE(CFENGINE_PORT));
83 /* ... but we leave more space, as service names can be longer */
84
85 errno = 0;
86 struct servent *server = getservbyname(CFENGINE_SERVICE, "tcp");
87 if (server != NULL)
88 {
89 CFENGINE_PORT = ntohs(server->s_port);
90 snprintf(CFENGINE_PORT_STR, sizeof(CFENGINE_PORT_STR),
91 "%d", CFENGINE_PORT);
92 }
93 else if (errno == 0)
94 {
95 Log(LOG_LEVEL_VERBOSE,
96 "No registered cfengine service, using default");
97 }
98 else
99 {
100 Log(LOG_LEVEL_VERBOSE,
101 "Unable to query services database, using default. (getservbyname: %s)",
102 GetErrorStr());
103 }
104 Log(LOG_LEVEL_VERBOSE, "Default port for cfengine is %d", CFENGINE_PORT);
105 }
106
SetCfenginePort(const char * port_str)107 bool SetCfenginePort(const char *port_str)
108 {
109 /* parse and store the new value (use the string representation of the
110 * parsed value because it may potentially be better/nicer/more
111 * canonical) */
112 long int port;
113 int ret = StringToLong(port_str, &port);
114 if (ret != 0)
115 {
116 LogStringToLongError(port_str, "CFENGINE_PORT", ret);
117 return false;
118 }
119 if (port > MAX_PORT_NUMBER)
120 {
121 Log(LOG_LEVEL_ERR, "Invalid port number given, must be <= %d", MAX_PORT_NUMBER);
122 return false;
123 }
124
125 CFENGINE_PORT = port;
126 Log(LOG_LEVEL_VERBOSE, "Setting default port number to %d",
127 CFENGINE_PORT);
128 xsnprintf(CFENGINE_PORT_STR, sizeof(CFENGINE_PORT_STR),
129 "%d", CFENGINE_PORT);
130 return true;
131 }
132
133 /**
134 * @return 1 success, 0 auth/ID error, -1 other error
135 */
TLSConnect(ConnectionInfo * conn_info,bool trust_server,const char * ipaddr,const char * username)136 int TLSConnect(ConnectionInfo *conn_info, bool trust_server,
137 const char *ipaddr, const char *username)
138 {
139 int ret;
140
141 ret = TLSTry(conn_info);
142 if (ret == -1)
143 {
144 return -1;
145 }
146
147 /* TODO username is local, fix. */
148 ret = TLSVerifyPeer(conn_info, ipaddr, username);
149
150 if (ret == -1) /* error */
151 {
152 return -1;
153 }
154
155 const char *key_hash = KeyPrintableHash(conn_info->remote_key);
156
157 if (ret == 1)
158 {
159 Log(LOG_LEVEL_VERBOSE,
160 "Server is TRUSTED, received key '%s' MATCHES stored one.",
161 key_hash);
162 }
163 else /* ret == 0 */
164 {
165 if (trust_server) /* We're most probably bootstrapping. */
166 {
167 Log(LOG_LEVEL_NOTICE, "Trusting new key: %s", key_hash);
168 SavePublicKey(username, KeyPrintableHash(conn_info->remote_key),
169 KeyRSA(conn_info->remote_key));
170 }
171 else
172 {
173 Log(LOG_LEVEL_ERR,
174 "TRUST FAILED, server presented untrusted key: %s", key_hash);
175 return -1;
176 }
177 }
178
179 /* TLS CONNECTION IS ESTABLISHED, negotiate protocol version and send
180 * identification data. */
181 ret = TLSClientIdentificationDialog(conn_info, username);
182
183 return ret;
184 }
185
186 /**
187 * @NOTE if #flags.protocol_version is CF_PROTOCOL_UNDEFINED, then latest
188 * protocol is used by default.
189 */
ServerConnection(const char * server,const char * port,unsigned int connect_timeout,ConnectionFlags flags,int * err)190 AgentConnection *ServerConnection(const char *server, const char *port,
191 unsigned int connect_timeout,
192 ConnectionFlags flags, int *err)
193 {
194 AgentConnection *conn = NULL;
195 int ret;
196 *err = 0;
197
198 conn = NewAgentConn(server, port, flags);
199
200 #if !defined(__MINGW32__) && !defined(__ANDROID__)
201 signal(SIGPIPE, SIG_IGN);
202
203 sigset_t signal_mask;
204 sigemptyset(&signal_mask);
205 sigaddset(&signal_mask, SIGPIPE);
206 pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
207
208 /* FIXME: username is local */
209 GetCurrentUserName(conn->username, sizeof(conn->username));
210 #else
211 /* Always say "root" if windows or termux. */
212 strlcpy(conn->username, "root", sizeof(conn->username));
213 #endif
214
215 if (port == NULL || *port == '\0')
216 {
217 port = CFENGINE_PORT_STR;
218 }
219
220 char txtaddr[CF_MAX_IP_LEN] = "";
221 conn->conn_info->sd = SocketConnect(server, port, connect_timeout,
222 flags.force_ipv4,
223 txtaddr, sizeof(txtaddr));
224 if (conn->conn_info->sd == -1)
225 {
226 Log(LOG_LEVEL_INFO, "No server is responding on port: %s",
227 port);
228 DisconnectServer(conn);
229 *err = -1;
230 return NULL;
231 }
232
233 nt_static_assert(sizeof(conn->remoteip) >= sizeof(txtaddr));
234 strcpy(conn->remoteip, txtaddr);
235
236 ProtocolVersion protocol_version = flags.protocol_version;
237 if (ProtocolIsUndefined(protocol_version))
238 {
239 protocol_version = CF_PROTOCOL_LATEST;
240 }
241
242 if (ProtocolIsTLS(protocol_version))
243 {
244 /* Set the version to request during protocol negotiation. After
245 * TLSConnect() it will have the version we finally ended up with. */
246 conn->conn_info->protocol = protocol_version;
247
248 ret = TLSConnect(conn->conn_info, flags.trust_server,
249 conn->remoteip, conn->username);
250
251 if (ret == -1) /* Error */
252 {
253 DisconnectServer(conn);
254 *err = -1;
255 return NULL;
256 }
257 else if (ret == 0) /* Auth/ID error */
258 {
259 DisconnectServer(conn);
260 errno = EPERM;
261 *err = -2;
262 return NULL;
263 }
264 assert(ret == 1);
265
266 conn->conn_info->status = CONNECTIONINFO_STATUS_ESTABLISHED;
267 if (!flags.off_the_record)
268 {
269 LastSaw1(conn->remoteip, KeyPrintableHash(conn->conn_info->remote_key),
270 LAST_SEEN_ROLE_CONNECT);
271 }
272 }
273 else if (ProtocolIsClassic(protocol_version))
274 {
275 conn->conn_info->protocol = CF_PROTOCOL_CLASSIC;
276 conn->encryption_type = CfEnterpriseOptions();
277
278 if (!IdentifyAgent(conn->conn_info))
279 {
280 Log(LOG_LEVEL_ERR, "Id-authentication for '%s' failed", VFQNAME);
281 errno = EPERM;
282 DisconnectServer(conn);
283 *err = -2; // auth err
284 return NULL;
285 }
286
287 if (!AuthenticateAgent(conn, flags.trust_server))
288 {
289 Log(LOG_LEVEL_ERR, "Authentication dialogue with '%s' failed", server);
290 errno = EPERM;
291 DisconnectServer(conn);
292 *err = -2; // auth err
293 return NULL;
294 }
295 conn->conn_info->status = CONNECTIONINFO_STATUS_ESTABLISHED;
296 }
297 else
298 {
299 ProgrammingError("ServerConnection: ProtocolVersion %d!",
300 protocol_version);
301 }
302
303 conn->authenticated = true;
304 return conn;
305 }
306
307 /*********************************************************************/
308
DisconnectServer(AgentConnection * conn)309 void DisconnectServer(AgentConnection *conn)
310 {
311 /* Socket needs to be closed even after SSL_shutdown. */
312 if (conn->conn_info->sd >= 0) /* Not INVALID or OFFLINE */
313 {
314 if (conn->conn_info->protocol >= CF_PROTOCOL_TLS &&
315 conn->conn_info->ssl != NULL)
316 {
317 SSL_shutdown(conn->conn_info->ssl);
318 }
319
320 cf_closesocket(conn->conn_info->sd);
321 conn->conn_info->sd = SOCKET_INVALID;
322 Log(LOG_LEVEL_VERBOSE, "Connection to %s is closed", conn->remoteip);
323 }
324 DeleteAgentConn(conn);
325 }
326
327 /*********************************************************************/
328
329 /* Returning NULL (an empty list) does not mean empty directory but ERROR,
330 * since every directory has to contain at least . and .. */
RemoteDirList(const char * dirname,bool encrypt,AgentConnection * conn)331 Item *RemoteDirList(const char *dirname, bool encrypt, AgentConnection *conn)
332 {
333 char sendbuffer[CF_BUFSIZE];
334 char recvbuffer[CF_BUFSIZE];
335 char in[CF_BUFSIZE];
336 char out[CF_BUFSIZE];
337 int cipherlen = 0, tosend;
338
339 if (strlen(dirname) > CF_BUFSIZE - 20)
340 {
341 Log(LOG_LEVEL_ERR, "Directory name too long");
342 return NULL;
343 }
344
345 /* We encrypt only for CLASSIC protocol. The TLS protocol is always over
346 * encrypted layer, so it does not support encrypted (S*) commands. */
347 encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC;
348
349 if (encrypt)
350 {
351 if (conn->session_key == NULL)
352 {
353 Log(LOG_LEVEL_ERR, "Cannot do encrypted copy without keys (use cf-key)");
354 return NULL;
355 }
356
357 snprintf(in, CF_BUFSIZE, "OPENDIR %s", dirname);
358 cipherlen = EncryptString(out, sizeof(out), in, strlen(in) + 1, conn->encryption_type, conn->session_key);
359
360 tosend = cipherlen + CF_PROTO_OFFSET;
361
362 if (tosend < 0)
363 {
364 ProgrammingError("RemoteDirList: tosend (%d) < 0", tosend);
365 }
366 else if ((unsigned long) tosend > sizeof(sendbuffer))
367 {
368 ProgrammingError("RemoteDirList: tosend (%d) > sendbuffer (%zd)",
369 tosend, sizeof(sendbuffer));
370 }
371
372 snprintf(sendbuffer, CF_BUFSIZE - 1, "SOPENDIR %d", cipherlen);
373 memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen);
374 }
375 else
376 {
377 snprintf(sendbuffer, CF_BUFSIZE, "OPENDIR %s", dirname);
378 tosend = strlen(sendbuffer);
379 }
380
381 if (SendTransaction(conn->conn_info, sendbuffer, tosend, CF_DONE) == -1)
382 {
383 return NULL;
384 }
385
386 Item *start = NULL, *end = NULL; /* NULL is empty list */
387 while (true)
388 {
389 /* TODO check the CF_MORE flag, no need for CFD_TERMINATOR. */
390 int nbytes = ReceiveTransaction(conn->conn_info, recvbuffer, NULL);
391
392 /* If recv error or socket closed before receiving CFD_TERMINATOR. */
393 if (nbytes == -1)
394 {
395 /* TODO mark connection in the cache as closed. */
396 goto err;
397 }
398
399 if (encrypt)
400 {
401 memcpy(in, recvbuffer, nbytes);
402 DecryptString(recvbuffer, sizeof(recvbuffer), in, nbytes,
403 conn->encryption_type, conn->session_key);
404 }
405
406 if (recvbuffer[0] == '\0')
407 {
408 Log(LOG_LEVEL_ERR,
409 "Empty%s server packet when listing directory '%s'!",
410 (start == NULL) ? " first" : "",
411 dirname);
412 goto err;
413 }
414
415 if (FailedProtoReply(recvbuffer))
416 {
417 Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, dirname);
418 goto err;
419 }
420
421 if (BadProtoReply(recvbuffer))
422 {
423 Log(LOG_LEVEL_INFO, "%s", recvbuffer + strlen("BAD: "));
424 goto err;
425 }
426
427 /* Double '\0' means end of packet. */
428 for (char *sp = recvbuffer; *sp != '\0'; sp += strlen(sp) + 1)
429 {
430 if (strcmp(sp, CFD_TERMINATOR) == 0) /* end of all packets */
431 {
432 return start;
433 }
434
435 Item *ip = xcalloc(1, sizeof(Item));
436 ip->name = (char *) AllocateDirentForFilename(sp);
437
438 if (start == NULL) /* First element */
439 {
440 start = ip;
441 end = ip;
442 }
443 else
444 {
445 end->next = ip;
446 end = ip;
447 }
448 }
449 }
450
451 return start;
452
453 err: /* free list */
454 for (Item *ip = start; ip != NULL; ip = start)
455 {
456 start = ip->next;
457 free(ip->name);
458 free(ip);
459 }
460
461 return NULL;
462 }
463
464 /*********************************************************************/
465
CompareHashNet(const char * file1,const char * file2,bool encrypt,AgentConnection * conn)466 bool CompareHashNet(const char *file1, const char *file2, bool encrypt, AgentConnection *conn)
467 {
468 unsigned char d[EVP_MAX_MD_SIZE + 1];
469 char *sp;
470 char sendbuffer[CF_BUFSIZE] = {0};
471 char recvbuffer[CF_BUFSIZE] = {0};
472 int i, tosend, cipherlen;
473
474 HashFile(file2, d, CF_DEFAULT_DIGEST, false);
475
476 memset(recvbuffer, 0, CF_BUFSIZE);
477
478 /* We encrypt only for CLASSIC protocol. The TLS protocol is always over
479 * encrypted layer, so it does not support encrypted (S*) commands. */
480 encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC;
481
482 if (encrypt)
483 {
484 char in[CF_BUFSIZE] = {0};
485 char out[CF_BUFSIZE] = {0};
486 snprintf(in, CF_BUFSIZE, "MD5 %s", file1);
487
488 sp = in + strlen(in) + CF_SMALL_OFFSET;
489
490 for (i = 0; i < CF_DEFAULT_DIGEST_LEN; i++)
491 {
492 *sp++ = d[i];
493 }
494
495 cipherlen =
496 EncryptString(out, sizeof(out), in,
497 strlen(in) + CF_SMALL_OFFSET + CF_DEFAULT_DIGEST_LEN,
498 conn->encryption_type, conn->session_key);
499
500 tosend = cipherlen + CF_PROTO_OFFSET;
501
502 if (tosend < 0)
503 {
504 ProgrammingError("CompareHashNet: tosend (%d) < 0", tosend);
505 }
506 else if ((unsigned long) tosend > sizeof(sendbuffer))
507 {
508 ProgrammingError("CompareHashNet: tosend (%d) > sendbuffer (%zd)",
509 tosend, sizeof(sendbuffer));
510 }
511
512 snprintf(sendbuffer, CF_BUFSIZE, "SMD5 %d", cipherlen);
513 memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen);
514 }
515 else
516 {
517 snprintf(sendbuffer, CF_BUFSIZE, "MD5 %s", file1);
518 sp = sendbuffer + strlen(sendbuffer) + CF_SMALL_OFFSET;
519
520 for (i = 0; i < CF_DEFAULT_DIGEST_LEN; i++)
521 {
522 *sp++ = d[i];
523 }
524
525 tosend = strlen(sendbuffer) + CF_SMALL_OFFSET + CF_DEFAULT_DIGEST_LEN;
526 }
527
528 if (SendTransaction(conn->conn_info, sendbuffer, tosend, CF_DONE) == -1)
529 {
530 Log(LOG_LEVEL_ERR, "Failed send. (SendTransaction: %s)", GetErrorStr());
531 Log(LOG_LEVEL_VERBOSE, "Networking error, assuming different checksum");
532 return true;
533 }
534
535 if (ReceiveTransaction(conn->conn_info, recvbuffer, NULL) == -1)
536 {
537 /* TODO mark connection in the cache as closed. */
538 Log(LOG_LEVEL_ERR, "Failed receive. (ReceiveTransaction: %s)", GetErrorStr());
539 Log(LOG_LEVEL_VERBOSE, "No answer from host, assuming different checksum");
540 return true;
541 }
542
543 if (strcmp(CFD_TRUE, recvbuffer) == 0)
544 {
545 return true; /* mismatch */
546 }
547 else
548 {
549 return false;
550 }
551
552 /* Not reached */
553 }
554
555 /*********************************************************************/
556
557
EncryptCopyRegularFileNet(const char * source,const char * dest,off_t size,AgentConnection * conn)558 static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_t size, AgentConnection *conn)
559 {
560 int blocksize = 2048, n_read = 0, plainlen, more = true, finlen, cnt = 0;
561 int tosend, cipherlen = 0;
562 char *buf, in[CF_BUFSIZE], out[CF_BUFSIZE], workbuf[CF_BUFSIZE], cfchangedstr[265];
563 unsigned char iv[32] =
564 { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
565
566 snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2);
567
568 if ((strlen(dest) > CF_BUFSIZE - 20))
569 {
570 Log(LOG_LEVEL_ERR, "Filename too long");
571 return false;
572 }
573
574 unlink(dest); /* To avoid link attacks */
575
576 int dd = safe_open_create_perms(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, CF_PERMS_DEFAULT);
577 if (dd == -1)
578 {
579 Log(LOG_LEVEL_ERR,
580 "Copy from server '%s' to destination '%s' failed (open: %s)",
581 conn->this_server, dest, GetErrorStr());
582 unlink(dest);
583 return false;
584 }
585
586 if (size == 0)
587 {
588 // No sense in copying an empty file
589 close(dd);
590 return true;
591 }
592
593 workbuf[0] = '\0';
594
595 snprintf(in, CF_BUFSIZE - CF_PROTO_OFFSET, "GET dummykey %s", source);
596 cipherlen = EncryptString(out, sizeof(out), in, strlen(in) + 1, conn->encryption_type, conn->session_key);
597
598 tosend = cipherlen + CF_PROTO_OFFSET;
599
600 if (tosend < 0)
601 {
602 ProgrammingError("EncryptCopyRegularFileNet: tosend (%d) < 0", tosend);
603 }
604 else if ((unsigned long) tosend > sizeof(workbuf))
605 {
606 ProgrammingError("EncryptCopyRegularFileNet: tosend (%d) > workbuf (%zd)",
607 tosend, sizeof(workbuf));
608 }
609
610 snprintf(workbuf, CF_BUFSIZE, "SGET %4d %4d", cipherlen, blocksize);
611 memcpy(workbuf + CF_PROTO_OFFSET, out, cipherlen);
612
613 /* Send proposition C0 - query */
614
615 if (SendTransaction(conn->conn_info, workbuf, tosend, CF_DONE) == -1)
616 {
617 Log(LOG_LEVEL_ERR, "Couldn't send data. (SendTransaction: %s)", GetErrorStr());
618 close(dd);
619 return false;
620 }
621
622 EVP_CIPHER_CTX *crypto_ctx = EVP_CIPHER_CTX_new();
623 if (crypto_ctx == NULL)
624 {
625 Log(LOG_LEVEL_ERR, "Failed to allocate cipher: %s",
626 TLSErrorString(ERR_get_error()));
627 close(dd);
628 return false;
629 }
630
631 buf = xmalloc(CF_BUFSIZE + sizeof(int));
632
633 bool last_write_made_hole = false;
634 size_t n_wrote_total = 0;
635
636 while (more)
637 {
638 if ((cipherlen = ReceiveTransaction(conn->conn_info, buf, &more)) == -1)
639 {
640 close(dd);
641 free(buf);
642 EVP_CIPHER_CTX_free(crypto_ctx);
643 return false;
644 }
645
646 cnt++;
647
648 /* If the first thing we get is an error message, break. */
649
650 if (n_wrote_total == 0 &&
651 strncmp(buf + CF_INBAND_OFFSET, CF_FAILEDSTR, strlen(CF_FAILEDSTR)) == 0)
652 {
653 Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, source);
654 close(dd);
655 free(buf);
656 EVP_CIPHER_CTX_free(crypto_ctx);
657 return false;
658 }
659
660 if (strncmp(buf + CF_INBAND_OFFSET, cfchangedstr, strlen(cfchangedstr)) == 0)
661 {
662 Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", conn->this_server, source);
663 close(dd);
664 free(buf);
665 EVP_CIPHER_CTX_free(crypto_ctx);
666 return false;
667 }
668
669 EVP_DecryptInit_ex(crypto_ctx, CfengineCipher(CfEnterpriseOptions()), NULL, conn->session_key, iv);
670
671 if (!EVP_DecryptUpdate(crypto_ctx, (unsigned char *) workbuf, &plainlen, (unsigned char *) buf, cipherlen))
672 {
673 close(dd);
674 free(buf);
675 EVP_CIPHER_CTX_free(crypto_ctx);
676 return false;
677 }
678
679 if (!EVP_DecryptFinal_ex(crypto_ctx, (unsigned char *) workbuf + plainlen, &finlen))
680 {
681 close(dd);
682 free(buf);
683 EVP_CIPHER_CTX_free(crypto_ctx);
684 return false;
685 }
686
687 n_read = plainlen + finlen;
688
689 bool w_ok = FileSparseWrite(dd, workbuf, n_read,
690 &last_write_made_hole);
691 if (!w_ok)
692 {
693 Log(LOG_LEVEL_ERR,
694 "Local disk write failed copying '%s:%s' to '%s'",
695 conn->this_server, source, dest);
696 free(buf);
697 unlink(dest);
698 close(dd);
699 conn->error = true;
700 EVP_CIPHER_CTX_free(crypto_ctx);
701 return false;
702 }
703
704 n_wrote_total += n_read;
705 }
706
707 const bool do_sync = false;
708
709 bool ret = FileSparseClose(dd, dest, do_sync,
710 n_wrote_total, last_write_made_hole);
711 if (!ret)
712 {
713 unlink(dest);
714 free(buf);
715 EVP_CIPHER_CTX_free(crypto_ctx);
716 return false;
717 }
718
719 free(buf);
720 EVP_CIPHER_CTX_free(crypto_ctx);
721 return true;
722 }
723
FlushFileStream(int sd,int toget)724 static void FlushFileStream(int sd, int toget)
725 {
726 int i;
727 char buffer[2];
728
729 Log(LOG_LEVEL_VERBOSE, "Flushing rest of file...%d bytes", toget);
730
731 for (i = 0; i < toget; i++)
732 {
733 recv(sd, buffer, 1, 0); /* flush to end of current file */
734 }
735 }
736
737 /* TODO finalise socket or TLS session in all cases that this function fails
738 * and the transaction protocol is out of sync. */
CopyRegularFileNet(const char * source,const char * dest,off_t size,bool encrypt,AgentConnection * conn)739 bool CopyRegularFileNet(const char *source, const char *dest, off_t size,
740 bool encrypt, AgentConnection *conn)
741 {
742 char *buf, workbuf[CF_BUFSIZE], cfchangedstr[265];
743 const int buf_size = 2048;
744
745 /* We encrypt only for CLASSIC protocol. The TLS protocol is always over
746 * encrypted layer, so it does not support encrypted (S*) commands. */
747 encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC;
748
749 if (encrypt)
750 {
751 return EncryptCopyRegularFileNet(source, dest, size, conn);
752 }
753
754 snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2);
755
756 if ((strlen(dest) > CF_BUFSIZE - 20))
757 {
758 Log(LOG_LEVEL_ERR, "Filename too long");
759 return false;
760 }
761
762 unlink(dest); /* To avoid link attacks */
763
764 int dd = safe_open_create_perms(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, CF_PERMS_DEFAULT);
765 if (dd == -1)
766 {
767 Log(LOG_LEVEL_ERR,
768 "Copy from server '%s' to destination '%s' failed (open: %s)",
769 conn->this_server, dest, GetErrorStr());
770 unlink(dest);
771 return false;
772 }
773
774 workbuf[0] = '\0';
775 int tosend = snprintf(workbuf, CF_BUFSIZE, "GET %d %s", buf_size, source);
776 if (tosend <= 0 || tosend >= CF_BUFSIZE)
777 {
778 Log(LOG_LEVEL_ERR, "Failed to compose GET command for file %s",
779 source);
780 close(dd);
781 return false;
782 }
783
784 /* Send proposition C0 */
785
786 if (SendTransaction(conn->conn_info, workbuf, tosend, CF_DONE) == -1)
787 {
788 Log(LOG_LEVEL_ERR, "Couldn't send GET command");
789 close(dd);
790 return false;
791 }
792
793 buf = xmalloc(CF_BUFSIZE + sizeof(int)); /* Note CF_BUFSIZE not buf_size !! */
794
795 Log(LOG_LEVEL_VERBOSE, "Copying remote file '%s:%s', expecting %jd bytes",
796 conn->this_server, source, (intmax_t)size);
797
798 int n_wrote_total = 0;
799 bool last_write_made_hole = false;
800 while (n_wrote_total < size)
801 {
802 int toget = MIN(size - n_wrote_total, buf_size);
803
804 assert(toget > 0);
805
806 /* Stage C1 - receive */
807 int n_read;
808
809 const ProtocolVersion version = conn->conn_info->protocol;
810
811 if (ProtocolIsClassic(version))
812 {
813 n_read = RecvSocketStream(conn->conn_info->sd, buf, toget);
814 }
815 else if (ProtocolIsTLS(version))
816 {
817 n_read = TLSRecv(conn->conn_info->ssl, buf, toget);
818 }
819 else
820 {
821 UnexpectedError("CopyRegularFileNet: ProtocolVersion %d!",
822 conn->conn_info->protocol);
823 n_read = -1;
824 }
825
826 /* TODO what if 0 < n_read < toget? Might happen with TLS. */
827
828 if (n_read <= 0)
829 {
830 /* This may happen on race conditions, where the file has shrunk
831 * since we asked for its size in SYNCH ... STAT source */
832
833 Log(LOG_LEVEL_ERR,
834 "Error in client-server stream, has %s:%s shrunk? (code %d)",
835 conn->this_server, source, n_read);
836
837 close(dd);
838 free(buf);
839 return false;
840 }
841
842 /* If the first thing we get is an error message, break. */
843
844 if (n_wrote_total == 0
845 && strncmp(buf, CF_FAILEDSTR, strlen(CF_FAILEDSTR)) == 0)
846 {
847 Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied",
848 conn->this_server, source);
849 close(dd);
850 free(buf);
851 return false;
852 }
853
854 if (strncmp(buf, cfchangedstr, strlen(cfchangedstr)) == 0)
855 {
856 Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying",
857 conn->this_server, source);
858 close(dd);
859 free(buf);
860 return false;
861 }
862
863 /* Check for mismatch between encryption here and on server. */
864
865 int value = -1;
866 sscanf(buf, "t %d", &value);
867
868 if ((value > 0) && (strncmp(buf + CF_INBAND_OFFSET, "BAD: ", 5) == 0))
869 {
870 Log(LOG_LEVEL_INFO, "Network access to cleartext '%s:%s' denied",
871 conn->this_server, source);
872 close(dd);
873 free(buf);
874 return false;
875 }
876
877 bool w_ok = FileSparseWrite(dd, buf, n_read,
878 &last_write_made_hole);
879 if (!w_ok)
880 {
881 Log(LOG_LEVEL_ERR,
882 "Local disk write failed copying '%s:%s' to '%s'",
883 conn->this_server, source, dest);
884 free(buf);
885 unlink(dest);
886 close(dd);
887 FlushFileStream(conn->conn_info->sd, size - n_wrote_total - n_read);
888 conn->error = true;
889 return false;
890 }
891
892 n_wrote_total += n_read;
893 }
894
895 const bool do_sync = false;
896
897 bool ret = FileSparseClose(dd, dest, do_sync,
898 n_wrote_total, last_write_made_hole);
899 if (!ret)
900 {
901 unlink(dest);
902 free(buf);
903 FlushFileStream(conn->conn_info->sd, size - n_wrote_total);
904 return false;
905 }
906
907 free(buf);
908 return true;
909 }
910