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