diff --git a/cf-serverd/server_common.c b/cf-serverd/server_common.c index 6322a1a9d2..295a139f93 100644 --- a/cf-serverd/server_common.c +++ b/cf-serverd/server_common.c @@ -403,7 +403,7 @@ static void FailedTransfer(ConnectionInfo *connection) void CfGetFile(ServerFileGetState *args) { int fd; - off_t n_read, total = 0, sendlen = 0, count = 0; + off_t n_read, total = 0, sendlen = 0; char sendbuffer[CF_BUFSIZE + 256], filename[CF_BUFSIZE - 128]; struct stat sb; int blocksize = 2048; @@ -459,13 +459,6 @@ void CfGetFile(ServerFileGetState *args) } else { - int div = 3; - - if (sb.st_size > 10485760L) /* File larger than 10 MB, checks every 64kB */ - { - div = 32; - } - while (true) { memset(sendbuffer, 0, CF_BUFSIZE); @@ -488,14 +481,11 @@ void CfGetFile(ServerFileGetState *args) /* check the file is not changing at source */ - if (count++ % div == 0) /* Don't do this too often */ + if (stat(filename, &sb) == -1) { - if (stat(filename, &sb)) - { - Log(LOG_LEVEL_ERR, "Cannot stat file '%s'. (stat: %s)", - filename, GetErrorStr()); - break; - } + Log(LOG_LEVEL_ERR, "Cannot stat file '%s'. (stat: %s)", + filename, GetErrorStr()); + break; } if (sb.st_size != savedlen) diff --git a/libcfnet/client_code.c b/libcfnet/client_code.c index 7dccb88324..030fbe8246 100644 --- a/libcfnet/client_code.c +++ b/libcfnet/client_code.c @@ -568,23 +568,32 @@ bool CompareHashNet(const char *file1, const char *file2, bool encrypt, AgentCon /*********************************************************************/ +static void FlushFileStream(int sd, int toget) +{ + int i; + char buffer[2]; + + Log(LOG_LEVEL_VERBOSE, "Flushing rest of file...%d bytes", toget); + + for (i = 0; i < toget; i++) + { + recv(sd, buffer, 1, 0); /* flush to end of current file */ + } +} static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_t size, AgentConnection *conn) { + assert(conn != NULL); + int blocksize = 2048, n_read = 0, plainlen, more = true, finlen; int tosend, cipherlen = 0; - char *buf, in[CF_BUFSIZE], out[CF_BUFSIZE], workbuf[CF_BUFSIZE], cfchangedstr[265]; + // TODO: CFE-4449 + char buf[CF_BUFSIZE + sizeof(int)], in[CF_BUFSIZE], out[CF_BUFSIZE], workbuf[CF_BUFSIZE], cfchangedstr[265]; unsigned char iv[32] = { 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 }; snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2); - if ((strlen(dest) > CF_BUFSIZE - 20)) - { - Log(LOG_LEVEL_ERR, "Filename too long"); - return false; - } - unlink(dest); /* To avoid link attacks */ int dd = safe_open_create_perms(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, CF_PERMS_DEFAULT); @@ -604,9 +613,8 @@ static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_ return true; } - workbuf[0] = '\0'; - - snprintf(in, CF_BUFSIZE - CF_PROTO_OFFSET, "GET dummykey %s", source); + NDEBUG_UNUSED int rc = snprintf(in, CF_BUFSIZE - CF_PROTO_OFFSET, "GET dummykey %s", source); + assert(rc >= 0 && rc < sizeof(in)); cipherlen = EncryptString(out, sizeof(out), in, strlen(in) + 1, conn->encryption_type, conn->session_key); tosend = cipherlen + CF_PROTO_OFFSET; @@ -621,7 +629,8 @@ static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_ tosend, sizeof(workbuf)); } - snprintf(workbuf, CF_BUFSIZE, "SGET %4d %4d", cipherlen, blocksize); + rc = snprintf(workbuf, CF_BUFSIZE, "SGET %4d %4d", cipherlen, blocksize); + assert(rc >= 0 && rc < sizeof(workbuf)); memcpy(workbuf + CF_PROTO_OFFSET, out, cipherlen); /* Send proposition C0 - query */ @@ -642,8 +651,6 @@ static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_ return false; } - buf = xmalloc(CF_BUFSIZE + sizeof(int)); - bool last_write_made_hole = false; size_t n_wrote_total = 0; @@ -652,7 +659,6 @@ static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_ if ((cipherlen = ReceiveTransaction(conn->conn_info, buf, &more)) == -1) { close(dd); - free(buf); EVP_CIPHER_CTX_free(crypto_ctx); return false; } @@ -665,7 +671,6 @@ static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_ { Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, source); close(dd); - free(buf); EVP_CIPHER_CTX_free(crypto_ctx); return false; } @@ -674,7 +679,6 @@ static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_ { Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", conn->this_server, source); close(dd); - free(buf); EVP_CIPHER_CTX_free(crypto_ctx); return false; } @@ -683,16 +687,16 @@ static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_ if (!EVP_DecryptUpdate(crypto_ctx, (unsigned char *) workbuf, &plainlen, (unsigned char *) buf, cipherlen)) { + FlushFileStream(conn->conn_info->sd, size - n_wrote_total - n_read); close(dd); - free(buf); EVP_CIPHER_CTX_free(crypto_ctx); return false; } if (!EVP_DecryptFinal_ex(crypto_ctx, (unsigned char *) workbuf + plainlen, &finlen)) { + FlushFileStream(conn->conn_info->sd, size - n_wrote_total - n_read); close(dd); - free(buf); EVP_CIPHER_CTX_free(crypto_ctx); return false; } @@ -701,20 +705,27 @@ static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_ bool w_ok = FileSparseWrite(dd, workbuf, n_read, &last_write_made_hole); - if (!w_ok) + n_wrote_total += n_read; + if (!w_ok || n_wrote_total > size) { - Log(LOG_LEVEL_ERR, - "Local disk write failed copying '%s:%s' to '%s'", - conn->this_server, source, dest); - free(buf); + if (!w_ok) + { + Log(LOG_LEVEL_ERR, + "Local disk write failed copying '%s:%s' to '%s'", + conn->this_server, source, dest); + } + else // if (n_wrote_total > size) + { + Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", + conn->this_server, source); + } + FlushFileStream(conn->conn_info->sd, size - n_wrote_total); unlink(dest); close(dd); conn->error = true; EVP_CIPHER_CTX_free(crypto_ctx); return false; } - - n_wrote_total += n_read; } const bool do_sync = false; @@ -724,35 +735,24 @@ static bool EncryptCopyRegularFileNet(const char *source, const char *dest, off_ if (!ret) { unlink(dest); - free(buf); EVP_CIPHER_CTX_free(crypto_ctx); return false; } - free(buf); EVP_CIPHER_CTX_free(crypto_ctx); return true; } -static void FlushFileStream(int sd, int toget) -{ - int i; - char buffer[2]; - - Log(LOG_LEVEL_VERBOSE, "Flushing rest of file...%d bytes", toget); - - for (i = 0; i < toget; i++) - { - recv(sd, buffer, 1, 0); /* flush to end of current file */ - } -} - -/* TODO finalise socket or TLS session in all cases that this function fails +/* TODO finalize socket or TLS session in all cases that this function fails * and the transaction protocol is out of sync. */ bool CopyRegularFileNet(const char *source, const char *dest, off_t size, bool encrypt, AgentConnection *conn, mode_t mode) { - char *buf, workbuf[CF_BUFSIZE], cfchangedstr[265]; + assert(conn != NULL); + + // TODO: CFE-4449 + char buf[CF_BUFSIZE + sizeof(int)]; /* Note CF_BUFSIZE not buf_size !! */ + char workbuf[CF_BUFSIZE], cfchangedstr[265]; const int buf_size = 2048; /* We encrypt only for CLASSIC protocol. The TLS protocol is always over @@ -764,13 +764,8 @@ bool CopyRegularFileNet(const char *source, const char *dest, off_t size, return EncryptCopyRegularFileNet(source, dest, size, conn); } - snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2); - - if ((strlen(dest) > CF_BUFSIZE - 20)) - { - Log(LOG_LEVEL_ERR, "Filename too long"); - return false; - } + NDEBUG_UNUSED int rc = snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2); + assert(rc >= 0 && rc < sizeof(cfchangedstr)); unlink(dest); /* To avoid link attacks */ @@ -784,7 +779,6 @@ bool CopyRegularFileNet(const char *source, const char *dest, off_t size, return false; } - workbuf[0] = '\0'; int tosend = snprintf(workbuf, CF_BUFSIZE, "GET %d %s", buf_size, source); if (tosend <= 0 || tosend >= CF_BUFSIZE) { @@ -803,8 +797,6 @@ bool CopyRegularFileNet(const char *source, const char *dest, off_t size, return false; } - buf = xmalloc(CF_BUFSIZE + sizeof(int)); /* Note CF_BUFSIZE not buf_size !! */ - Log(LOG_LEVEL_VERBOSE, "Copying remote file '%s:%s', expecting %jd bytes", conn->this_server, source, (intmax_t)size); @@ -848,7 +840,6 @@ bool CopyRegularFileNet(const char *source, const char *dest, off_t size, conn->this_server, source, n_read); close(dd); - free(buf); return false; } @@ -860,7 +851,6 @@ bool CopyRegularFileNet(const char *source, const char *dest, off_t size, Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, source); close(dd); - free(buf); return false; } @@ -869,40 +859,48 @@ bool CopyRegularFileNet(const char *source, const char *dest, off_t size, Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", conn->this_server, source); close(dd); - free(buf); return false; } /* Check for mismatch between encryption here and on server. */ int value = -1; - sscanf(buf, "t %d", &value); + rc = sscanf(buf, "t %d", &value); + if (rc != 1) + { + value = -1; + } if ((value > 0) && (strncmp(buf + CF_INBAND_OFFSET, "BAD: ", 5) == 0)) { Log(LOG_LEVEL_INFO, "Network access to cleartext '%s:%s' denied", conn->this_server, source); close(dd); - free(buf); return false; } bool w_ok = FileSparseWrite(dd, buf, n_read, &last_write_made_hole); - if (!w_ok) + n_wrote_total += n_read; + if (!w_ok || n_wrote_total > size) { - Log(LOG_LEVEL_ERR, - "Local disk write failed copying '%s:%s' to '%s'", - conn->this_server, source, dest); - free(buf); + if (!w_ok) + { + Log(LOG_LEVEL_ERR, + "Local disk write failed copying '%s:%s' to '%s'", + conn->this_server, source, dest); + } + else // if (n_wrote_total > size) + { + Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", + conn->this_server, source); + } unlink(dest); close(dd); - FlushFileStream(conn->conn_info->sd, size - n_wrote_total - n_read); + FlushFileStream(conn->conn_info->sd, size - n_wrote_total); conn->error = true; return false; } - - n_wrote_total += n_read; } const bool do_sync = false; @@ -912,11 +910,9 @@ bool CopyRegularFileNet(const char *source, const char *dest, off_t size, if (!ret) { unlink(dest); - free(buf); FlushFileStream(conn->conn_info->sd, size - n_wrote_total); return false; } - free(buf); return true; } diff --git a/libcfnet/client_code.h b/libcfnet/client_code.h index 4aaaa5ff4c..6543a1b7c6 100644 --- a/libcfnet/client_code.h +++ b/libcfnet/client_code.h @@ -47,8 +47,24 @@ AgentConnection *ServerConnection(const char *server, const char *port, const Rl void DisconnectServer(AgentConnection *conn); bool CompareHashNet(const char *file1, const char *file2, bool encrypt, AgentConnection *conn); + +/** + * @brief Copy regular file over network. + * @param source The source filename. + * @param dest The destination filename. + * @param size The source file size. + * @param encrypt Whether or not to encrypt the transmission. + * @param conn The connection object to communicate with server. + * @param mode The source file mode (perms) to be used when creating the + * destination file. + * @return %true on success, %false on error. + * @note Note that the #encrypt parameter only has an effect on the CLASSIC + * protocol (used in CFEngine Community). The TLS protocol (used in + * CFEngine Enterprise) is always encrypted. + */ bool CopyRegularFileNet(const char *source, const char *dest, off_t size, bool encrypt, AgentConnection *conn, mode_t mode); + Item *RemoteDirList(const char *dirname, bool encrypt, AgentConnection *conn); int TLSConnectCallCollect(ConnectionInfo *conn_info, const char *username);