cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

client_code.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 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  */
57 bool cfnet_init(const char *tls_min_version, const char *ciphers)
58 {
60  return TLSClientInitialize(tls_min_version, ciphers);
61 }
62 
63 void cfnet_shut()
64 {
67 }
68 
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 
81 {
82  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  {
96  "No registered cfengine service, using default");
97  }
98  else
99  {
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 
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);
129  "%d", CFENGINE_PORT);
130  return true;
131 }
132 
133 /**
134  * @return 1 success, 0 auth/ID error, -1 other error
135  */
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  {
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  {
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  */
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__)
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" as username from windows. */
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  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 
267  if (!flags.off_the_record)
268  {
271  }
272  }
273  else if (ProtocolIsClassic(protocol_version))
274  {
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  }
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 
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 .. */
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 > sizeof(sendbuffer))
363  {
364  ProgrammingError("RemoteDirList: tosend (%d) > sendbuffer (%zd)",
365  tosend, sizeof(sendbuffer));
366  }
367 
368  snprintf(sendbuffer, CF_BUFSIZE - 1, "SOPENDIR %d", cipherlen);
369  memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen);
370  }
371  else
372  {
373  snprintf(sendbuffer, CF_BUFSIZE, "OPENDIR %s", dirname);
374  tosend = strlen(sendbuffer);
375  }
376 
377  if (SendTransaction(conn->conn_info, sendbuffer, tosend, CF_DONE) == -1)
378  {
379  return NULL;
380  }
381 
382  Item *start = NULL, *end = NULL; /* NULL is empty list */
383  while (true)
384  {
385  /* TODO check the CF_MORE flag, no need for CFD_TERMINATOR. */
386  int nbytes = ReceiveTransaction(conn->conn_info, recvbuffer, NULL);
387 
388  /* If recv error or socket closed before receiving CFD_TERMINATOR. */
389  if (nbytes == -1)
390  {
391  /* TODO mark connection in the cache as closed. */
392  goto err;
393  }
394 
395  if (encrypt)
396  {
397  memcpy(in, recvbuffer, nbytes);
398  DecryptString(recvbuffer, sizeof(recvbuffer), in, nbytes,
399  conn->encryption_type, conn->session_key);
400  }
401 
402  if (recvbuffer[0] == '\0')
403  {
405  "Empty%s server packet when listing directory '%s'!",
406  (start == NULL) ? " first" : "",
407  dirname);
408  goto err;
409  }
410 
411  if (FailedProtoReply(recvbuffer))
412  {
413  Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, dirname);
414  goto err;
415  }
416 
417  if (BadProtoReply(recvbuffer))
418  {
419  Log(LOG_LEVEL_INFO, "%s", recvbuffer + strlen("BAD: "));
420  goto err;
421  }
422 
423  /* Double '\0' means end of packet. */
424  for (char *sp = recvbuffer; *sp != '\0'; sp += strlen(sp) + 1)
425  {
426  if (strcmp(sp, CFD_TERMINATOR) == 0) /* end of all packets */
427  {
428  return start;
429  }
430 
431  Item *ip = xcalloc(1, sizeof(Item));
432  ip->name = (char *) AllocateDirentForFilename(sp);
433 
434  if (start == NULL) /* First element */
435  {
436  start = ip;
437  end = ip;
438  }
439  else
440  {
441  end->next = ip;
442  end = ip;
443  }
444  }
445  }
446 
447  return start;
448 
449  err: /* free list */
450  for (Item *ip = start; ip != NULL; ip = start)
451  {
452  start = ip->next;
453  free(ip->name);
454  free(ip);
455  }
456 
457  return NULL;
458 }
459 
460 /*********************************************************************/
461 
462 bool CompareHashNet(const char *file1, const char *file2, bool encrypt, AgentConnection *conn)
463 {
464  unsigned char d[EVP_MAX_MD_SIZE + 1];
465  char *sp;
466  char sendbuffer[CF_BUFSIZE] = {0};
467  char recvbuffer[CF_BUFSIZE] = {0};
468  int i, tosend, cipherlen;
469 
470  HashFile(file2, d, CF_DEFAULT_DIGEST, false);
471 
472  memset(recvbuffer, 0, CF_BUFSIZE);
473 
474  /* We encrypt only for CLASSIC protocol. The TLS protocol is always over
475  * encrypted layer, so it does not support encrypted (S*) commands. */
476  encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC;
477 
478  if (encrypt)
479  {
480  char in[CF_BUFSIZE] = {0};
481  char out[CF_BUFSIZE] = {0};
482  snprintf(in, CF_BUFSIZE, "MD5 %s", file1);
483 
484  sp = in + strlen(in) + CF_SMALL_OFFSET;
485 
486  for (i = 0; i < CF_DEFAULT_DIGEST_LEN; i++)
487  {
488  *sp++ = d[i];
489  }
490 
491  cipherlen =
492  EncryptString(out, sizeof(out), in,
493  strlen(in) + CF_SMALL_OFFSET + CF_DEFAULT_DIGEST_LEN,
494  conn->encryption_type, conn->session_key);
495 
496  tosend = cipherlen + CF_PROTO_OFFSET;
497 
498  if(tosend > sizeof(sendbuffer))
499  {
500  ProgrammingError("CompareHashNet: tosend (%d) > sendbuffer (%zd)",
501  tosend, sizeof(sendbuffer));
502  }
503 
504  snprintf(sendbuffer, CF_BUFSIZE, "SMD5 %d", cipherlen);
505  memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen);
506  }
507  else
508  {
509  snprintf(sendbuffer, CF_BUFSIZE, "MD5 %s", file1);
510  sp = sendbuffer + strlen(sendbuffer) + CF_SMALL_OFFSET;
511 
512  for (i = 0; i < CF_DEFAULT_DIGEST_LEN; i++)
513  {
514  *sp++ = d[i];
515  }
516 
517  tosend = strlen(sendbuffer) + CF_SMALL_OFFSET + CF_DEFAULT_DIGEST_LEN;
518  }
519 
520  if (SendTransaction(conn->conn_info, sendbuffer, tosend, CF_DONE) == -1)
521  {
522  Log(LOG_LEVEL_ERR, "Failed send. (SendTransaction: %s)", GetErrorStr());
523  Log(LOG_LEVEL_VERBOSE, "Networking error, assuming different checksum");
524  return true;
525  }
526 
527  if (ReceiveTransaction(conn->conn_info, recvbuffer, NULL) == -1)
528  {
529  /* TODO mark connection in the cache as closed. */
530  Log(LOG_LEVEL_ERR, "Failed receive. (ReceiveTransaction: %s)", GetErrorStr());
531  Log(LOG_LEVEL_VERBOSE, "No answer from host, assuming different checksum");
532  return true;
533  }
534 
535  if (strcmp(CFD_TRUE, recvbuffer) == 0)
536  {
537  return true; /* mismatch */
538  }
539  else
540  {
541  return false;
542  }
543 
544 /* Not reached */
545 }
546 
547 /*********************************************************************/
548 
549 
550 static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_t size, AgentConnection *conn)
551 {
552  int blocksize = 2048, n_read = 0, plainlen, more = true, finlen, cnt = 0;
553  int tosend, cipherlen = 0;
554  char *buf, in[CF_BUFSIZE], out[CF_BUFSIZE], workbuf[CF_BUFSIZE], cfchangedstr[265];
555  unsigned char iv[32] =
556  { 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 };
557 
558  snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2);
559 
560  if ((strlen(dest) > CF_BUFSIZE - 20))
561  {
562  Log(LOG_LEVEL_ERR, "Filename too long");
563  return false;
564  }
565 
566  unlink(dest); /* To avoid link attacks */
567 
568  int dd = safe_open_create_perms(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, CF_PERMS_DEFAULT);
569  if (dd == -1)
570  {
572  "Copy from server '%s' to destination '%s' failed (open: %s)",
573  conn->this_server, dest, GetErrorStr());
574  unlink(dest);
575  return false;
576  }
577 
578  if (size == 0)
579  {
580  // No sense in copying an empty file
581  close(dd);
582  return true;
583  }
584 
585  workbuf[0] = '\0';
586 
587  snprintf(in, CF_BUFSIZE - CF_PROTO_OFFSET, "GET dummykey %s", source);
588  cipherlen = EncryptString(out, sizeof(out), in, strlen(in) + 1, conn->encryption_type, conn->session_key);
589 
590  tosend = cipherlen + CF_PROTO_OFFSET;
591 
592  if(tosend > sizeof(workbuf))
593  {
594  ProgrammingError("EncryptCopyRegularFileNet: tosend (%d) > workbuf (%zd)",
595  tosend, sizeof(workbuf));
596  }
597 
598  snprintf(workbuf, CF_BUFSIZE, "SGET %4d %4d", cipherlen, blocksize);
599  memcpy(workbuf + CF_PROTO_OFFSET, out, cipherlen);
600 
601 /* Send proposition C0 - query */
602 
603  if (SendTransaction(conn->conn_info, workbuf, tosend, CF_DONE) == -1)
604  {
605  Log(LOG_LEVEL_ERR, "Couldn't send data. (SendTransaction: %s)", GetErrorStr());
606  close(dd);
607  return false;
608  }
609 
610  EVP_CIPHER_CTX *crypto_ctx = EVP_CIPHER_CTX_new();
611  if (crypto_ctx == NULL)
612  {
613  Log(LOG_LEVEL_ERR, "Failed to allocate cipher: %s",
614  TLSErrorString(ERR_get_error()));
615  close(dd);
616  return false;
617  }
618 
619  buf = xmalloc(CF_BUFSIZE + sizeof(int));
620 
621  bool last_write_made_hole = false;
622  size_t n_wrote_total = 0;
623 
624  while (more)
625  {
626  if ((cipherlen = ReceiveTransaction(conn->conn_info, buf, &more)) == -1)
627  {
628  close(dd);
629  free(buf);
630  EVP_CIPHER_CTX_free(crypto_ctx);
631  return false;
632  }
633 
634  cnt++;
635 
636  /* If the first thing we get is an error message, break. */
637 
638  if (n_wrote_total == 0 &&
639  strncmp(buf + CF_INBAND_OFFSET, CF_FAILEDSTR, strlen(CF_FAILEDSTR)) == 0)
640  {
641  Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, source);
642  close(dd);
643  free(buf);
644  EVP_CIPHER_CTX_free(crypto_ctx);
645  return false;
646  }
647 
648  if (strncmp(buf + CF_INBAND_OFFSET, cfchangedstr, strlen(cfchangedstr)) == 0)
649  {
650  Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", conn->this_server, source);
651  close(dd);
652  free(buf);
653  EVP_CIPHER_CTX_free(crypto_ctx);
654  return false;
655  }
656 
657  EVP_DecryptInit_ex(crypto_ctx, CfengineCipher(CfEnterpriseOptions()), NULL, conn->session_key, iv);
658 
659  if (!EVP_DecryptUpdate(crypto_ctx, workbuf, &plainlen, buf, cipherlen))
660  {
661  close(dd);
662  free(buf);
663  EVP_CIPHER_CTX_free(crypto_ctx);
664  return false;
665  }
666 
667  if (!EVP_DecryptFinal_ex(crypto_ctx, workbuf + plainlen, &finlen))
668  {
669  close(dd);
670  free(buf);
671  EVP_CIPHER_CTX_free(crypto_ctx);
672  return false;
673  }
674 
675  n_read = plainlen + finlen;
676 
677  bool w_ok = FileSparseWrite(dd, workbuf, n_read,
678  &last_write_made_hole);
679  if (!w_ok)
680  {
682  "Local disk write failed copying '%s:%s' to '%s'",
683  conn->this_server, source, dest);
684  free(buf);
685  unlink(dest);
686  close(dd);
687  conn->error = true;
688  EVP_CIPHER_CTX_free(crypto_ctx);
689  return false;
690  }
691 
692  n_wrote_total += n_read;
693  }
694 
695  const bool do_sync = false;
696 
697  bool ret = FileSparseClose(dd, dest, do_sync,
698  n_wrote_total, last_write_made_hole);
699  if (!ret)
700  {
701  unlink(dest);
702  free(buf);
703  EVP_CIPHER_CTX_free(crypto_ctx);
704  return false;
705  }
706 
707  free(buf);
708  EVP_CIPHER_CTX_free(crypto_ctx);
709  return true;
710 }
711 
712 static void FlushFileStream(int sd, int toget)
713 {
714  int i;
715  char buffer[2];
716 
717  Log(LOG_LEVEL_VERBOSE, "Flushing rest of file...%d bytes", toget);
718 
719  for (i = 0; i < toget; i++)
720  {
721  recv(sd, buffer, 1, 0); /* flush to end of current file */
722  }
723 }
724 
725 /* TODO finalise socket or TLS session in all cases that this function fails
726  * and the transaction protocol is out of sync. */
727 bool CopyRegularFileNet(const char *source, const char *dest, off_t size,
728  bool encrypt, AgentConnection *conn)
729 {
730  char *buf, workbuf[CF_BUFSIZE], cfchangedstr[265];
731  const int buf_size = 2048;
732 
733  /* We encrypt only for CLASSIC protocol. The TLS protocol is always over
734  * encrypted layer, so it does not support encrypted (S*) commands. */
735  encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC;
736 
737  if (encrypt)
738  {
739  return EncryptCopyRegularFileNet(source, dest, size, conn);
740  }
741 
742  snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2);
743 
744  if ((strlen(dest) > CF_BUFSIZE - 20))
745  {
746  Log(LOG_LEVEL_ERR, "Filename too long");
747  return false;
748  }
749 
750  unlink(dest); /* To avoid link attacks */
751 
752  int dd = safe_open_create_perms(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, CF_PERMS_DEFAULT);
753  if (dd == -1)
754  {
756  "Copy from server '%s' to destination '%s' failed (open: %s)",
757  conn->this_server, dest, GetErrorStr());
758  unlink(dest);
759  return false;
760  }
761 
762  workbuf[0] = '\0';
763  int tosend = snprintf(workbuf, CF_BUFSIZE, "GET %d %s", buf_size, source);
764  if (tosend <= 0 || tosend >= CF_BUFSIZE)
765  {
766  Log(LOG_LEVEL_ERR, "Failed to compose GET command for file %s",
767  source);
768  close(dd);
769  return false;
770  }
771 
772  /* Send proposition C0 */
773 
774  if (SendTransaction(conn->conn_info, workbuf, tosend, CF_DONE) == -1)
775  {
776  Log(LOG_LEVEL_ERR, "Couldn't send GET command");
777  close(dd);
778  return false;
779  }
780 
781  buf = xmalloc(CF_BUFSIZE + sizeof(int)); /* Note CF_BUFSIZE not buf_size !! */
782 
783  Log(LOG_LEVEL_VERBOSE, "Copying remote file '%s:%s', expecting %jd bytes",
784  conn->this_server, source, (intmax_t)size);
785 
786  size_t n_wrote_total = 0;
787  bool last_write_made_hole = false;
788  while (n_wrote_total < size)
789  {
790  int toget = MIN(size - n_wrote_total, buf_size);
791 
792  assert(toget > 0);
793 
794  /* Stage C1 - receive */
795  int n_read;
796 
797  const ProtocolVersion version = conn->conn_info->protocol;
798 
799  if (ProtocolIsClassic(version))
800  {
801  n_read = RecvSocketStream(conn->conn_info->sd, buf, toget);
802  }
803  else if (ProtocolIsTLS(version))
804  {
805  n_read = TLSRecv(conn->conn_info->ssl, buf, toget);
806  }
807  else
808  {
809  UnexpectedError("CopyRegularFileNet: ProtocolVersion %d!",
810  conn->conn_info->protocol);
811  n_read = -1;
812  }
813 
814  /* TODO what if 0 < n_read < toget? Might happen with TLS. */
815 
816  if (n_read <= 0)
817  {
818  /* This may happen on race conditions, where the file has shrunk
819  * since we asked for its size in SYNCH ... STAT source */
820 
822  "Error in client-server stream, has %s:%s shrunk? (code %d)",
823  conn->this_server, source, n_read);
824 
825  close(dd);
826  free(buf);
827  return false;
828  }
829 
830  /* If the first thing we get is an error message, break. */
831 
832  if (n_wrote_total == 0
833  && strncmp(buf, CF_FAILEDSTR, strlen(CF_FAILEDSTR)) == 0)
834  {
835  Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied",
836  conn->this_server, source);
837  close(dd);
838  free(buf);
839  return false;
840  }
841 
842  if (strncmp(buf, cfchangedstr, strlen(cfchangedstr)) == 0)
843  {
844  Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying",
845  conn->this_server, source);
846  close(dd);
847  free(buf);
848  return false;
849  }
850 
851  /* Check for mismatch between encryption here and on server. */
852 
853  int value = -1;
854  sscanf(buf, "t %d", &value);
855 
856  if ((value > 0) && (strncmp(buf + CF_INBAND_OFFSET, "BAD: ", 5) == 0))
857  {
858  Log(LOG_LEVEL_INFO, "Network access to cleartext '%s:%s' denied",
859  conn->this_server, source);
860  close(dd);
861  free(buf);
862  return false;
863  }
864 
865  bool w_ok = FileSparseWrite(dd, buf, n_read,
866  &last_write_made_hole);
867  if (!w_ok)
868  {
870  "Local disk write failed copying '%s:%s' to '%s'",
871  conn->this_server, source, dest);
872  free(buf);
873  unlink(dest);
874  close(dd);
875  FlushFileStream(conn->conn_info->sd, size - n_wrote_total - n_read);
876  conn->error = true;
877  return false;
878  }
879 
880  n_wrote_total += n_read;
881  }
882 
883  const bool do_sync = false;
884 
885  bool ret = FileSparseClose(dd, dest, do_sync,
886  n_wrote_total, last_write_made_hole);
887  if (!ret)
888  {
889  unlink(dest);
890  free(buf);
891  FlushFileStream(conn->conn_info->sd, size - n_wrote_total);
892  return false;
893  }
894 
895  free(buf);
896  return true;
897 }
void * xmalloc(size_t size)
Definition: alloc-mini.c:46
void * xcalloc(size_t nmemb, size_t size)
Definition: alloc-mini.c:51
#define CF_CHANGEDSTR1
Definition: cf3.defs.h:136
#define CFD_TERMINATOR
Definition: cf3.defs.h:102
#define CF_CHANGEDSTR2
Definition: cf3.defs.h:137
#define CF_SMALL_OFFSET
Definition: cf3.defs.h:107
#define CFD_TRUE
Definition: cf3.defs.h:103
#define CF_FAILEDSTR
Definition: cf3.defs.h:135
HashMethod CF_DEFAULT_DIGEST
Definition: cf3globals.c:88
int CF_DEFAULT_DIGEST_LEN
Definition: cf3globals.c:89
void free(void *)
#define CF_DONE
Definition: cfnet.h:45
#define CF_PROTO_OFFSET
Definition: cfnet.h:50
#define CF_MAX_IP_LEN
Definition: cfnet.h:39
#define SOCKET_INVALID
Definition: cfnet.h:47
#define CF_INBAND_OFFSET
Definition: cfnet.h:51
int cf_closesocket(int sd)
Definition: misc.c:31
int RecvSocketStream(int sd, char *buffer, int toget)
Receive up to #toget bytes, plus a '\0', into buffer from sd.
Definition: classic.c:64
#define CFENGINE_SERVICE
Definition: client_code.c:51
Item * RemoteDirList(const char *dirname, bool encrypt, AgentConnection *conn)
Definition: client_code.c:331
bool CompareHashNet(const char *file1, const char *file2, bool encrypt, AgentConnection *conn)
Definition: client_code.c:462
bool CopyRegularFileNet(const char *source, const char *dest, off_t size, bool encrypt, AgentConnection *conn)
Definition: client_code.c:727
int TLSConnect(ConnectionInfo *conn_info, bool trust_server, const char *ipaddr, const char *username)
Definition: client_code.c:136
static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_t size, AgentConnection *conn)
Definition: client_code.c:550
bool SetCfenginePort(const char *port_str)
Definition: client_code.c:107
bool cfnet_IsInitialized()
Definition: client_code.c:69
#define MAX_PORT_NUMBER
Definition: client_code.c:74
int CFENGINE_PORT
Definition: client_code.c:77
AgentConnection * ServerConnection(const char *server, const char *port, unsigned int connect_timeout, ConnectionFlags flags, int *err)
Definition: client_code.c:190
bool cfnet_init(const char *tls_min_version, const char *ciphers)
Definition: client_code.c:57
void DisconnectServer(AgentConnection *conn)
Definition: client_code.c:309
static void FlushFileStream(int sd, int toget)
Definition: client_code.c:712
void cfnet_shut()
Definition: client_code.c:63
void DetermineCfenginePort()
Definition: client_code.c:80
char CFENGINE_PORT_STR[16]
Definition: client_code.c:78
char VFQNAME[]
Definition: cf3globals.c:58
bool FailedProtoReply(char *buf)
bool AuthenticateAgent(AgentConnection *conn, bool trust_key)
bool BadProtoReply(char *buf)
bool IdentifyAgent(ConnectionInfo *conn_info)
void DeleteAgentConn(AgentConnection *conn)
Destroys an AgentConnection.
Definition: communication.c:51
AgentConnection * NewAgentConn(const char *server, const char *port, ConnectionFlags flags)
Allocates a new AgentConnection (stores a connection from Agent to Server).
Definition: communication.c:38
@ CONNECTIONINFO_STATUS_ESTABLISHED
int EncryptString(char *out, size_t out_size, const char *in, int plainlen, char type, unsigned char *key)
Definition: crypto.c:593
bool SavePublicKey(const char *user, const char *digest, const RSA *key)
Definition: crypto.c:429
void CryptoInitialize()
Definition: crypto.c:71
void CryptoDeInitialize()
Definition: crypto.c:86
int DecryptString(char *out, size_t out_size, const char *in, int cipherlen, char type, unsigned char *key)
Definition: crypto.c:675
#define CF_PERMS_DEFAULT
Definition: definitions.h:58
#define CF_BUFSIZE
Definition: definitions.h:50
struct dirent * AllocateDirentForFilename(const char *filename)
Definition: unix_dir.c:201
const EVP_CIPHER * CfengineCipher(char type)
char CfEnterpriseOptions()
bool FileSparseClose(int fd, const char *filename, bool do_sync, size_t total_bytes_written, bool last_write_was_hole)
Definition: file_lib.c:1426
bool FileSparseWrite(int fd, const void *buf, size_t count, bool *wrote_hole)
Definition: file_lib.c:1316
int safe_open_create_perms(const char *const pathname, int flags, const mode_t create_perms)
Definition: file_lib.c:541
int errno
#define NULL
Definition: getopt1.c:56
void HashFile(const char *const filename, unsigned char digest[EVP_MAX_MD_SIZE+1], HashMethod type, bool text_mode)
Definition: hash.c:443
const char * KeyPrintableHash(const Key *key)
Printable hash of the key.
Definition: key.c:84
RSA * KeyRSA(const Key *key)
Constant pointer to the key data.
Definition: key.c:70
void LastSaw1(const char *ipaddress, const char *hashstr, LastSeenRole role)
Same as LastSaw() but the digest parameter is the hash as a "SHA=..." string, to avoid converting twi...
Definition: lastseen.c:83
@ LAST_SEEN_ROLE_CONNECT
Definition: lastseen.h:38
const char * GetErrorStr(void)
Definition: logging.c:275
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_NOTICE
Definition: logging.h:44
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_INFO
Definition: logging.h:45
void xsnprintf(char *str, size_t str_size, const char *format,...)
Definition: misc_lib.c:114
#define ProgrammingError(...)
Definition: misc_lib.h:33
#define UnexpectedError(...)
Definition: misc_lib.h:38
int SocketConnect(const char *host, const char *port, unsigned int connect_timeout, bool force_ipv4, char *txtaddr, size_t txtaddr_size)
Definition: net.c:413
int ReceiveTransaction(ConnectionInfo *conn_info, char *buffer, int *more)
Definition: net.c:149
int SendTransaction(ConnectionInfo *conn_info, const char *buffer, int len, char status)
Definition: net.c:61
#define O_BINARY
Definition: platform.h:1001
#define MIN(a, b)
Definition: platform.h:478
#define PRINTSIZE(what)
Definition: printsize.h:66
static bool ProtocolIsTLS(const ProtocolVersion p)
ProtocolVersion
@ CF_PROTOCOL_CLASSIC
@ CF_PROTOCOL_TLS
#define CF_PROTOCOL_LATEST
static bool ProtocolIsUndefined(const ProtocolVersion p)
static bool ProtocolIsClassic(const ProtocolVersion p)
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)
int StringToLong(const char *str, long *value_out)
Converts a string of numerals in base 10 to a long integer.
Definition: string_lib.c:485
void LogStringToLongError(const char *str_attempted, const char *id, int error_code)
Log StringToLong conversion error with an identifier for debugging.
Definition: string_lib.c:529
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:34
int authenticated
Definition: cfnet.h:99
ConnectionInfo * conn_info
Definition: cfnet.h:98
unsigned char * session_key
Definition: cfnet.h:104
char username[128]
Definition: cfnet.h:100
short error
Definition: cfnet.h:106
char * this_server
Definition: cfnet.h:112
char encryption_type
Definition: cfnet.h:105
char remoteip[64]
Definition: cfnet.h:103
bool trust_server
Definition: cfnet.h:59
bool off_the_record
Definition: cfnet.h:60
ProtocolVersion protocol_version
Definition: cfnet.h:56
bool force_ipv4
Definition: cfnet.h:58
ProtocolVersion protocol
ConnectionStatus status
Definition: item_lib.h:33
Item * next
Definition: item_lib.h:38
char * name
Definition: item_lib.h:34
int TLSClientIdentificationDialog(ConnectionInfo *conn_info, const char *username)
Definition: tls_client.c:192
bool TLSClientIsInitialized()
Definition: tls_client.c:60
bool TLSClientInitialize(const char *tls_min_version, const char *ciphers)
Definition: tls_client.c:74
int TLSTry(ConnectionInfo *conn_info)
Definition: tls_client.c:307
void TLSDeInitialize()
Definition: tls_client.c:153
int TLSVerifyPeer(ConnectionInfo *conn_info, const char *remoteip, const char *username)
Definition: tls_generic.c:325
int TLSRecv(SSL *ssl, char *buffer, int toget)
Receives at most #length bytes of data from the SSL session and stores it in the buffer.
Definition: tls_generic.c:737
const char * TLSErrorString(intmax_t errcode)
Definition: tls_generic.c:87
bool GetCurrentUserName(char *userName, int userNameLen)
Definition: unix.c:107