Browse Source

Resolve: "CLI file put loads whole file into memory"

Pflanzer, Jonas 4 years ago
parent
commit
357fd03c2b
4 changed files with 108 additions and 57 deletions
  1. 2 3
      cli/include/fileman.h
  2. 102 50
      cli/src/fileman.cpp
  3. 3 3
      daemon/src/main.cpp
  4. 1 1
      gui/src/climanager.cpp

+ 2 - 3
cli/include/fileman.h

@@ -27,10 +27,10 @@ private:
 	 * Boolean replacement for filestreams being open for list
 	 *
 	 */
-	std::vector<std::vector<char>> putdata;
+	std::ifstream putfile;
 	std::fstream getfile;
 	std::vector<Json::Value> listdata;
-	std::string getpath, getname, putpath, putname;
+	std::string getpath, getname, putpath, putname, cipherpath;
 	const unsigned int max_read_len = 4096;
 	int putsize;
 	int putchunks;
@@ -64,7 +64,6 @@ private:
 	void deinitCryptoE();
 	void deinitCryptoD();
 
-	std::vector<std::vector<char>> chunkify(char *data, unsigned int size);
 	void writeEnc(const std::vector<char> data);
 	const char signature[4] = {'C', 'C', 'A', 'T'};
 

+ 102 - 50
cli/src/fileman.cpp

@@ -36,21 +36,7 @@ bool FileMan::isListingExtended() { return islisting && extendedListing; }
 
 bool FileMan::isEncrypted() { return keyenabled; }
 
-vector<vector<char>> FileMan::chunkify(char *data, unsigned int size) {
-	vector<vector<char>> ret;
-	vector<char> chunk;
-	unsigned int i;
-	for (i = 0; (i + max_read_len) < size; i += max_read_len) {
-		chunk = vector<char>(data + i, data + i + max_read_len);
-		ret.push_back(chunk);
-	}
-	chunk = vector<char>(data + i, data + size);
-	ret.push_back(chunk);
-	return ret;
-}
-
 bool FileMan::openPut(const string &path) {
-	std::ifstream putfile;
 	std::fstream keyfile;
 	putpath = path;
 	putname = pathToFilename(path);
@@ -68,32 +54,71 @@ bool FileMan::openPut(const string &path) {
 		}
 		putfile.seekg(0);
 
-		// read into memory and chunkify
+		// if crypto enabled, encrypt now
+		if (keyenabled) {
+			cipherpath = path + ".tmp";
+
+			if (!!std::ifstream(cipherpath)) {
+				// close put if tmp file already exists
+				closePut();
+				return false;
+			}
 
-		char *temp = new char[size + sizeof(signature)];
-		memcpy(temp, signature, sizeof(signature));
-		putfile.read(temp + sizeof(signature), size);
+			std::ofstream cipherFile(cipherpath, std::ios::binary);
+			if (!cipherFile) {
+				closePut();
+				return false;
+			}
 
-		size += sizeof(signature);
+			// resize buffer to also fit IV and tag
+			size_t additionalsize = sizeof(iv) + sizeof(tag);
+
+			// skip first 32 bytes. Those are needed for the header
+			cipherFile.seekp(additionalsize);
 
-		// have all file in memory prepended with signature
-		// if crypto enabled, encrypt now
-		if (keyenabled) {
 			if (!initCryptoE()) {
 				// failed to init crypto, do not continue
-				delete[] temp;
 				return false;
 			}
-			// resize buffer to also fit IV and tag
-			size_t additionalsize = sizeof(iv) + sizeof(tag);
-			unsigned char *cipher = new unsigned char[size];
+
+			unsigned char *plain = new unsigned char[max_read_len];
+			unsigned char *cipher = new unsigned char[max_read_len];
 			int cipherlen;
 
-			if (!EVP_EncryptUpdate(cryptctxe, cipher, &cipherlen, (unsigned char *)temp, size)) {
+			// prepend signature
+			memcpy(plain, signature, sizeof(signature));
+
+			if (!EVP_EncryptUpdate(cryptctxe, cipher, &cipherlen, plain, sizeof(signature))) {
 				setOpensslError();
 				std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
 			}
-			if (!EVP_EncryptFinal_ex(cryptctxe, cipher + cipherlen, &cipherlen)) {
+
+			cipherFile.write((char *)cipher, cipherlen);
+
+			while ((std::size_t)(putfile.tellg()) + max_read_len < size) {
+				putfile.read((char *)plain, max_read_len);
+
+				if (!EVP_EncryptUpdate(cryptctxe, cipher, &cipherlen, plain, max_read_len)) {
+					setOpensslError();
+					std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
+				}
+
+				cipherFile.write((char *)cipher, cipherlen);
+			}
+
+			if (putfile.tellg() < size - 1) {
+				int rest = size - putfile.tellg();
+				putfile.read((char *)plain, rest);
+
+				if (!EVP_EncryptUpdate(cryptctxe, cipher, &cipherlen, plain, rest)) {
+					setOpensslError();
+					std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
+				}
+
+				cipherFile.write((char *)cipher, cipherlen);
+			}
+
+			if (!EVP_EncryptFinal_ex(cryptctxe, cipher, &cipherlen)) {
 				setOpensslError();
 				std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
 			}
@@ -103,29 +128,35 @@ bool FileMan::openPut(const string &path) {
 				std::cerr << __PRETTY_FUNCTION__ << " failed to get tag " << getOpensslError() << std::endl;
 			}
 
-			delete[] temp;
-			temp = new char[size + additionalsize];
-
 			// prepend IV and tag
-			memcpy(temp, iv, sizeof(iv));
-			memcpy(temp + sizeof(iv), tag, sizeof(tag));
-			memcpy(temp + additionalsize, cipher, size);
+			cipherFile.seekp(0);
+			cipherFile.write((char *)iv, sizeof(iv));
+			cipherFile.write((char *)tag, sizeof(tag));
+
+			cipherFile.close();
+			putfile.close();
 
 			delete[] cipher;
+			delete[] plain;
+
+			// now open cipher file as put file
+			putfile.open(cipherpath, std::ios::ate | std::ios::binary);
+			if (putfile.is_open()) {
+				size = putfile.tellg();
+				putfile.seekg(0);
+			} else {
+				closePut();
+				return false;
+			}
 
-			// increase size to also include IV and tag
-			size += sizeof(iv) + sizeof(tag);
 			deinitCryptoE();
+		} else {
+			size += sizeof(signature);
 		}
 
-		// chunkify
-		putdata = chunkify(temp, size);
+		// calculate chunks
 		putsize = size;
-		putchunksRemaining = putchunks = putdata.size();
-
-		delete[] temp;
-
-		// end read into memory and chunkify
+		putchunksRemaining = putchunks = size / max_read_len + (size % max_read_len > 0 ? 1 : 0);
 
 		isputting = true;
 
@@ -181,7 +212,7 @@ bool FileMan::openKey(const string &path) {
 }
 
 void FileMan::closePut() {
-	putdata = vector<vector<char>>();
+	putfile.close();
 	putname = "";
 	putpath = "";
 	putchunks = 0;
@@ -189,6 +220,11 @@ void FileMan::closePut() {
 	isputting = false;
 	memset(iv, 0, sizeof(iv));
 	memset(tag, 0, sizeof(tag));
+
+	if (keyenabled && !!std::ifstream(cipherpath)) {
+		// delete tmp cipher file if it exists
+		std::remove(cipherpath.c_str());
+	}
 }
 
 void FileMan::closeGet() {
@@ -303,7 +339,23 @@ void FileMan::setListChunks(int chunks) {
 }
 
 vector<char> FileMan::readPut() {
-	vector<char> data = putdata[putchunks - putchunksRemaining];
+	int chunk = putchunks - putchunksRemaining;
+	vector<char> data;
+
+	int toread = max_read_len;
+	if (putchunksRemaining == 1) {
+		toread = putsize - (putchunks - 1) * max_read_len;
+	}
+
+	data.resize(toread);
+
+	if (chunk == 0 && !keyenabled) {
+		memcpy(&data.front(), signature, sizeof(signature));
+		putfile.read(&data.front() + sizeof(signature), toread - sizeof(signature));
+	} else {
+		putfile.read(&data.front(), toread);
+	}
+
 	putchunksRemaining--;
 	return data;
 }
@@ -362,7 +414,7 @@ void FileMan::setOpensslError() {
 bool FileMan::initCryptoE() {
 	// try to initialize crypto context
 	if (!cryptoreadye) {
-		std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
+		//~ std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
 		if (!(cryptctxe = EVP_CIPHER_CTX_new())) {
 			setOpensslError();
 			return false;
@@ -379,7 +431,7 @@ bool FileMan::initCryptoE() {
 bool FileMan::initCryptoD() {
 	// try to initialize crypto context
 	if (!cryptoreadyd) {
-		std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
+		//~ std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
 		if (!(cryptctxd = EVP_CIPHER_CTX_new())) {
 			setOpensslError();
 			return false;
@@ -437,15 +489,15 @@ void FileMan::writeEnc(const vector<char> data) {
 				setOpensslError();
 				std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
 			}
-			std::cerr << __PRETTY_FUNCTION__ << " decrypted" << std::endl;
+			//~ std::cerr << __PRETTY_FUNCTION__ << " decrypted" << std::endl;
 			if (!EVP_CIPHER_CTX_ctrl(cryptctxd, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
 				setOpensslError();
 				std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
 			}
-			std::cerr << __PRETTY_FUNCTION__ << " set tag" << std::endl;
+			//~ std::cerr << __PRETTY_FUNCTION__ << " set tag" << std::endl;
 			if (0 < EVP_DecryptFinal_ex(cryptctxd, plain + plainlen, &finallen)) {
 				plainlen += finallen;
-				std::cerr << __PRETTY_FUNCTION__ << " finalized with len " << plainlen << std::endl;
+				//~ std::cerr << __PRETTY_FUNCTION__ << " finalized with len " << plainlen << std::endl;
 				getfile.close();
 				// check signature
 				if (memcmp(plain, signature, 4)) {

+ 3 - 3
daemon/src/main.cpp

@@ -18,7 +18,7 @@ using namespace std;
  *
  * @return covert channel pointer | nullptr if no channel
  */
-CovertChannel * createChannel() {
+CovertChannel *createChannel() {
 	CovertChannel *covertchannel = nullptr;
 
 	const std::string covertChannelMode = Config::getValue("covertChannelMode");
@@ -71,8 +71,8 @@ CovertChannel * createChannel() {
 int main(int argc, char *argv[]) {
 
 	std::string configFile = "config.txt";
-	if(argc >= 2) {
-		if(std::string(argv[1]) == "help") {
+	if (argc >= 2) {
+		if (std::string(argv[1]) == "help") {
 			std::cout << "Usage " << argv[0] << " [config-file]" << std::endl;
 			return 0;
 		}

+ 1 - 1
gui/src/climanager.cpp

@@ -73,7 +73,7 @@ void CliManager::init() {
 		// Child
 		dup2(outpipefd[0], STDIN_FILENO);
 		dup2(inpipefd[1], STDOUT_FILENO);
-		 dup2(inpipefd[1], STDERR_FILENO);
+		// dup2(inpipefd[1], STDERR_FILENO);
 
 		// ask kernel to deliver SIGTERM in case the parent dies
 		prctl(PR_SET_PDEATHSIG, SIGTERM);