VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Driver/Fuse/FuseService.cpp
blob: 026883d12b6b2a2ea60eb0deef611418beb2c785 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>VeraCrypt - Free Open source disk encryption with strong security for the Paranoid</title>
<meta name="description" content="VeraCrypt is free open-source disk encryption software for Windows, Mac OS X and Linux. In case an attacker forces you to reveal the password, VeraCrypt provides plausible deniability. In contrast to file encryption, data encryption performed by VeraCrypt is real-time (on-the-fly), automatic, transparent, needs very little memory, and does not involve temporary unencrypted files."/>
<meta name="keywords" content="encryption, security"/>
<link href="styles.css" rel="stylesheet" type="text/css" />
</head>
<body>

<div>                      
<a href="https://www.veracrypt.fr/en/Home.html"><img src="VeraCrypt128x128.png" alt="VeraCrypt"/></a>
</div>

<div id="menu">
	<ul>
	  <li><a href="Home.html">Home</a></li>
	  <li><a href="/code/">Source Code</a></li>
	  <li><a href="Downloads.html">Downloads</a></li>
	  <li><a class="active" href="Documentation.html">Documentation</a></li>
	  <li><a href="Donation.html">Donate</a></li>
	  <li><a href="https://sourceforge.net/p/veracrypt/discussion/" target="_blank">Forums</a></li>
	</ul>
</div>

<div>
<p>
<a href="Documentation.html">Documentation</a>           
<img src="arrow_right.gif" alt=">>" style="margin-top: 5px">
<a href="VeraCrypt%20Volume.html">VeraCrypt Volume</a>
</p></div>

<div class="wikidoc">
<h1>VeraCrypt Volume</h1>
<p>There are two types of VeraCrypt volumes:</p>
<ul>
<li>File-hosted (container) </li><li>Partition/device-hosted (non-system) </li></ul>
<p>Note: In addition to creating the above types of virtual volumes, VeraCrypt can encrypt a physical partition/drive where Windows is installed (for more information, see the chapter
<a href="System%20Encryption.html"><em>System Encryption</em></a>).<br>
<br>
A VeraCrypt file-hosted volume is a normal file, which can reside on any type of storage device. It contains (hosts) a completely independent encrypted virtual disk device.<br>
<br>
A VeraCrypt partition is a hard disk partition encrypted using VeraCrypt. You can also encrypt entire hard disks, USB hard disks, USB memory sticks, and other types of storage devices.</p>
<p><a href="Creating%20New%20Volumes.html" style="text-align:left; color:#0080c0; text-decoration:none; font-weight:bold.html">Next Section &gt;&gt;</a></p>
</div>
</body></html>
20'>520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
/*
 Derived from source code of TrueCrypt 7.1a, which is
 Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed
 by the TrueCrypt License 3.0.

 Modifications and additions to the original source code (contained in this file)
 and all other portions of this file are Copyright (c) 2013-2017 IDRIX
 and are governed by the Apache License 2.0 the full text of which is
 contained in the file License.txt included in VeraCrypt binary and source
 code distribution packages.
*/

#define FUSE_USE_VERSION  25
#include <errno.h>
#include <fcntl.h>
#include <fuse.h>
#include <iostream>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/wait.h>

#include "FuseService.h"
#include "Platform/FileStream.h"
#include "Platform/MemoryStream.h"
#include "Platform/Serializable.h"
#include "Platform/SystemLog.h"
#include "Platform/Unix/Pipe.h"
#include "Platform/Unix/Poller.h"
#include "Volume/EncryptionThreadPool.h"
#include "Core/Core.h"

namespace VeraCrypt
{
	static int fuse_service_access (const char *path, int mask)
	{
		try
		{
			if (!FuseService::CheckAccessRights())
				return -EACCES;
		}
		catch (...)
		{
			return FuseService::ExceptionToErrorCode();
		}

		return 0;
	}

	static void *fuse_service_init ()
	{
		try
		{
			// Termination signals are handled by a separate process to allow clean dismount on shutdown
			struct sigaction action;
			Memory::Zero (&action, sizeof (action));
			action.sa_handler = SIG_IGN;

			sigaction (SIGINT, &action, nullptr);
			sigaction (SIGQUIT, &action, nullptr);
			sigaction (SIGTERM, &action, nullptr);

			if (!EncryptionThreadPool::IsRunning())
				EncryptionThreadPool::Start();
		}
		catch (exception &e)
		{
			SystemLog::WriteException (e);
		}
		catch (...)
		{
			SystemLog::WriteException (UnknownException (SRC_POS));
		}

		return nullptr;
	}

	static void fuse_service_destroy (void *userdata)
	{
		try
		{
			FuseService::Dismount();
		}
		catch (exception &e)
		{
			SystemLog::WriteException (e);
		}
		catch (...)
		{
			SystemLog::WriteException (UnknownException (SRC_POS));
		}
	}

	static int fuse_service_getattr (const char *path, struct stat *statData)
	{
		try
		{
			Memory::Zero (statData, sizeof(*statData));

			statData->st_uid = FuseService::GetUserId();
			statData->st_gid = FuseService::GetGroupId();
			statData->st_atime = time (NULL);
			statData->st_ctime = time (NULL);
			statData->st_mtime = time (NULL);

			if (strcmp (path, "/") == 0)
			{
				statData->st_mode = S_IFDIR | 0500;
				statData->st_nlink = 2;
			}
			else
			{
				if (!FuseService::CheckAccessRights())
					return -EACCES;

				if (strcmp (path, FuseService::GetVolumeImagePath()) == 0)
				{
					statData->st_mode = S_IFREG | 0600;
					statData->st_nlink = 1;
					statData->st_size = FuseService::GetVolumeSize();
				}
				else if (strcmp (path, FuseService::GetControlPath()) == 0)
				{
					statData->st_mode = S_IFREG | 0600;
					statData->st_nlink = 1;
					statData->st_size = FuseService::GetVolumeInfo()->Size();
				}
				else
				{
					return -ENOENT;
				}
			}
		}
		catch (...)
		{
			return FuseService::ExceptionToErrorCode();
		}

		return 0;
	}

	static int fuse_service_opendir (const char *path, struct fuse_file_info *fi)
	{
		try
		{
			if (!FuseService::CheckAccessRights())
				return -EACCES;

			if (strcmp (path, "/") != 0)
				return -ENOENT;
		}
		catch (...)
		{
			return FuseService::ExceptionToErrorCode();
		}

		return 0;
	}

	static int fuse_service_open (const char *path, struct fuse_file_info *fi)
	{
		try
		{
			if (!FuseService::CheckAccessRights())
				return -EACCES;

			if (strcmp (path, FuseService::GetVolumeImagePath()) == 0)
				return 0;

			if (strcmp (path, FuseService::GetControlPath()) == 0)
			{
				fi->direct_io = 1;
				return 0;
			}
		}
		catch (...)
		{
			return FuseService::ExceptionToErrorCode();
		}
		return -ENOENT;
	}

	static int fuse_service_read (const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
	{
		try
		{
			if (!FuseService::CheckAccessRights())
				return -EACCES;

			if (strcmp (path, FuseService::GetVolumeImagePath()) == 0)
			{
				try
				{
					// Test for read beyond the end of the volume
					if ((uint64) offset + size > FuseService::GetVolumeSize())
						size = FuseService::GetVolumeSize() - offset;

					size_t sectorSize = FuseService::GetVolumeSectorSize();
					if (size % sectorSize != 0 || offset % sectorSize != 0)
					{
						// Support for non-sector-aligned read operations is required by some loop device tools
						// which may analyze the volume image before attaching it as a device

						uint64 alignedOffset = offset - (offset % sectorSize);
						uint64 alignedSize = size + (offset % sectorSize);

						if (alignedSize % sectorSize != 0)
							alignedSize += sectorSize - (alignedSize % sectorSize);

						SecureBuffer alignedBuffer (alignedSize);

						FuseService::ReadVolumeSectors (alignedBuffer, alignedOffset);
						BufferPtr ((byte *) buf, size).CopyFrom (alignedBuffer.GetRange (offset % sectorSize, size));
					}
					else
					{
						FuseService::ReadVolumeSectors (BufferPtr ((byte *) buf, size), offset);
					}
				}
				catch (MissingVolumeData)
				{
					return 0;
				}

				return size;
			}

			if (strcmp (path, FuseService::GetControlPath()) == 0)
			{
				shared_ptr <Buffer> infoBuf = FuseService::GetVolumeInfo();
				BufferPtr outBuf ((byte *)buf, size);

				if (offset >= (off_t) infoBuf->Size())
					return 0;

				if (offset + size > infoBuf->Size())
					size = infoBuf->Size () - offset;

				outBuf.CopyFrom (infoBuf->GetRange (offset, size));
				return size;
			}
		}
		catch (...)
		{
			return FuseService::ExceptionToErrorCode();
		}

		return -ENOENT;
	}

	static int fuse_service_readdir (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
	{
		try
		{
			if (!FuseService::CheckAccessRights())
				return -EACCES;

			if (strcmp (path, "/") != 0)
				return -ENOENT;

			filler (buf, ".", NULL, 0);
			filler (buf, "..", NULL, 0);
			filler (buf, FuseService::GetVolumeImagePath() + 1, NULL, 0);
			filler (buf, FuseService::GetControlPath() + 1, NULL, 0);
		}
		catch (...)
		{
			return FuseService::ExceptionToErrorCode();
		}

		return 0;
	}

	static int fuse_service_write (const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
	{
		try
		{
			if (!FuseService::CheckAccessRights())
				return -EACCES;

			if (strcmp (path, FuseService::GetVolumeImagePath()) == 0)
			{
				FuseService::WriteVolumeSectors (BufferPtr ((byte *) buf, size), offset);
				return size;
			}

			if (strcmp (path, FuseService::GetControlPath()) == 0)
			{
				if (FuseService::AuxDeviceInfoReceived())
					return -EACCES;

				FuseService::ReceiveAuxDeviceInfo (ConstBufferPtr ((const byte *)buf, size));
				return size;
			}
		}
#ifdef TC_FREEBSD
		// FreeBSD apparently retries failed write operations forever, which may lead to a system crash.
		catch (VolumeReadOnly&)
		{
			return size;
		}
		catch (VolumeProtected&)
		{
			return size;
		}
#endif
		catch (...)
		{
			return FuseService::ExceptionToErrorCode();
		}

		return -ENOENT;
	}

	bool FuseService::CheckAccessRights ()
	{
		return fuse_get_context()->uid == 0 || fuse_get_context()->uid == UserId;
	}

	void FuseService::CloseMountedVolume ()
	{
		if (MountedVolume)
		{
			// This process will exit before the use count of MountedVolume reaches zero
			if (MountedVolume->GetFile().use_count() > 1)
				MountedVolume->GetFile()->Close();

			if (MountedVolume.use_count() > 1)
				delete MountedVolume.get();

			MountedVolume.reset();
		}
	}

	void FuseService::Dismount ()
	{
		CloseMountedVolume();

		if (EncryptionThreadPool::IsRunning())
			EncryptionThreadPool::Stop();
	}

	int FuseService::ExceptionToErrorCode ()
	{
		try
		{
			throw;
		}
		catch (std::bad_alloc)
		{
			return -ENOMEM;
		}
		catch (ParameterIncorrect &e)
		{
			SystemLog::WriteException (e);
			return -EINVAL;
		}
		catch (VolumeProtected&)
		{
			return -EPERM;
		}
		catch (VolumeReadOnly&)
		{
			return -EPERM;
		}
		catch (SystemException &e)
		{
			SystemLog::WriteException (e);
			return -static_cast <int> (e.GetErrorCode());
		}
		catch (std::exception &e)
		{
			SystemLog::WriteException (e);
			return -EINTR;
		}
		catch (...)
		{
			SystemLog::WriteException (UnknownException (SRC_POS));
			return -EINTR;
		}
	}

	shared_ptr <Buffer> FuseService::GetVolumeInfo ()
	{
		shared_ptr <Stream> stream (new MemoryStream);

		{
			ScopeLock lock (OpenVolumeInfoMutex);

			OpenVolumeInfo.Set (*MountedVolume);
			OpenVolumeInfo.SlotNumber = SlotNumber;

			OpenVolumeInfo.Serialize (stream);
		}

		ConstBufferPtr infoBuf = dynamic_cast <MemoryStream&> (*stream);
		shared_ptr <Buffer> outBuf (new Buffer (infoBuf.Size()));
		outBuf->CopyFrom (infoBuf);

		return outBuf;
	}

	const char *FuseService::GetVolumeImagePath ()
	{
#ifdef TC_MACOSX
		return "/volume.dmg";
#else
		return "/volume";
#endif
	}

	uint64 FuseService::GetVolumeSize ()
	{
		if (!MountedVolume)
			throw NotInitialized (SRC_POS);

		return MountedVolume->GetSize();
	}

	void FuseService::Mount (shared_ptr <Volume> openVolume, VolumeSlotNumber slotNumber, const string &fuseMountPoint)
	{
		list <string> args;
		args.push_back (FuseService::GetDeviceType());
		args.push_back (fuseMountPoint);

#ifdef TC_MACOSX
		args.push_back ("-o");
		args.push_back ("noping_diskarb");
		args.push_back ("-o");
		args.push_back ("nobrowse");

		if (getuid() == 0 || geteuid() == 0)
#endif
		{
			args.push_back ("-o");
			args.push_back ("allow_other");
		}

		ExecFunctor execFunctor (openVolume, slotNumber);
		Process::Execute ("fuse", args, -1, &execFunctor);

		for (int t = 0; true; t++)
		{
			try
			{
				if (FilesystemPath (fuseMountPoint + FuseService::GetControlPath()).GetType() == FilesystemPathType::File)
					break;
			}
			catch (...)
			{
				if (t > 50)
					throw;

				Thread::Sleep (100);
			}
		}
	}

	void FuseService::ReadVolumeSectors (const BufferPtr &buffer, uint64 byteOffset)
	{
		if (!MountedVolume)
			throw NotInitialized (SRC_POS);

		MountedVolume->ReadSectors (buffer, byteOffset);
	}

	void FuseService::ReceiveAuxDeviceInfo (const ConstBufferPtr &buffer)
	{
		shared_ptr <Stream> stream (new MemoryStream (buffer));
		Serializer sr (stream);

		ScopeLock lock (OpenVolumeInfoMutex);
		OpenVolumeInfo.VirtualDevice = sr.DeserializeString ("VirtualDevice");
		OpenVolumeInfo.LoopDevice = sr.DeserializeString ("LoopDevice");
	}

	void FuseService::SendAuxDeviceInfo (const DirectoryPath &fuseMountPoint, const DevicePath &virtualDevice, const DevicePath &loopDevice)
	{
		File fuseServiceControl;
		fuseServiceControl.Open (string (fuseMountPoint) + GetControlPath(), File::OpenWrite);

		shared_ptr <Stream> stream (new MemoryStream);
		Serializer sr (stream);

		sr.Serialize ("VirtualDevice", string (virtualDevice));
		sr.Serialize ("LoopDevice", string (loopDevice));
		fuseServiceControl.Write (dynamic_cast <MemoryStream&> (*stream));
	}

	void FuseService::WriteVolumeSectors (const ConstBufferPtr &buffer, uint64 byteOffset)
	{
		if (!MountedVolume)
			throw NotInitialized (SRC_POS);

		MountedVolume->WriteSectors (buffer, byteOffset);
	}

	void FuseService::OnSignal (int signal)
	{
		try
		{
			shared_ptr <VolumeInfo> volume = Core->GetMountedVolume (SlotNumber);

			if (volume)
				Core->DismountVolume (volume, true);
		}
		catch (...) { }

		_exit (0);
	}

	void FuseService::ExecFunctor::operator() (int argc, char *argv[])
	{
		struct timeval tv;
		gettimeofday (&tv, NULL);
		FuseService::OpenVolumeInfo.SerialInstanceNumber = (uint64)tv.tv_sec * 1000000ULL + tv.tv_usec;

		FuseService::MountedVolume = MountedVolume;
		FuseService::SlotNumber = SlotNumber;

		FuseService::UserId = getuid();
		FuseService::GroupId = getgid();

		if (getenv ("SUDO_UID"))
		{
			try
			{
				string s (getenv ("SUDO_UID"));
				FuseService::UserId = static_cast <uid_t> (StringConverter::ToUInt64 (s));

				if (getenv ("SUDO_GID"))
				{
					s = getenv ("SUDO_GID");
					FuseService::GroupId = static_cast <gid_t> (StringConverter::ToUInt64 (s));
				}
			}
			catch (...) { }
		}

		static fuse_operations fuse_service_oper;

		fuse_service_oper.access = fuse_service_access;
		fuse_service_oper.destroy = fuse_service_destroy;
		fuse_service_oper.getattr = fuse_service_getattr;
		fuse_service_oper.init = fuse_service_init;
		fuse_service_oper.open = fuse_service_open;
		fuse_service_oper.opendir = fuse_service_opendir;
		fuse_service_oper.read = fuse_service_read;
		fuse_service_oper.readdir = fuse_service_readdir;
		fuse_service_oper.write = fuse_service_write;

		// Create a new session
		setsid ();

		// Fork handler of termination signals
		SignalHandlerPipe.reset (new Pipe);

		int forkedPid = fork();
		throw_sys_if (forkedPid == -1);

		if (forkedPid == 0)
		{
			CloseMountedVolume();

			struct sigaction action;
			Memory::Zero (&action, sizeof (action));
			action.sa_handler = OnSignal;

			sigaction (SIGINT, &action, nullptr);
			sigaction (SIGQUIT, &action, nullptr);
			sigaction (SIGTERM, &action, nullptr);

			// Wait for the exit of the main service
			byte buf[1];
			if (read (SignalHandlerPipe->GetReadFD(), buf, sizeof (buf))) { } // Errors ignored

			_exit (0);
		}

		SignalHandlerPipe->GetWriteFD();

		_exit (fuse_main (argc, argv, &fuse_service_oper));
	}

	VolumeInfo FuseService::OpenVolumeInfo;
	Mutex FuseService::OpenVolumeInfoMutex;
	shared_ptr <Volume> FuseService::MountedVolume;
	VolumeSlotNumber FuseService::SlotNumber;
	uid_t FuseService::UserId;
	gid_t FuseService::GroupId;
	auto_ptr <Pipe> FuseService::SignalHandlerPipe;
}