Discussion:
[fuse-devel] AES encrypted filesystem.
Irad K
2017-06-11 16:52:16 UTC
Permalink
My case is rather simple to explain, and I'm struggling to find in which
layer should I implement it.

Basically I've got files that are rather sensitive so I keep them
encrypted, and I'd like to let only certain processes the ability to
interact with them as they were regular files (all other processes who
attempt to touch those files, will see them as encrypted).

For that I'm willing to use AES256 which is a simple encryption method with
symmetric key.

Now, for every access to a file contents (either read/write or map) I want
to be able to perform the following flow :

1. READ: after reading the raw data (encrypted), it will get decrypted with
the symmetric key that the process has.
2. WRITE: before writing, the contents get encrypted with the symmetric key
that the process has.
3. MMAP: the same for file mmap, I wish to decrypt the file contents before
they mapped to memory.

I'm asking is just some direction about where to start from .. which layer
should I apply to implement these ideas disregarding the OS (osx or linux).

After I read the manuals I found out that the only way to control file
actions is using the API in fuse_lowlevel.h or fuse.h, both let you set a
vector of file operations called fuse_operation or fuse_lowlevel_ops.

I've tried to play with these api's, but when I tested it, it seems that
when I read or write or map the file, it doesn't goes through the operation
I just defined in the vector I mentioned above.

thanks
Irad
Nikolaus Rath
2017-06-13 02:24:36 UTC
Permalink
Post by Irad K
My case is rather simple to explain, and I'm struggling to find in which
layer should I implement it.
Now, for every access to a file contents (either read/write or map) I want
1. READ: after reading the raw data (encrypted), it will get decrypted with
the symmetric key that the process has.
2. WRITE: before writing, the contents get encrypted with the symmetric key
that the process has.
3. MMAP: the same for file mmap, I wish to decrypt the file contents before
they mapped to memory.
I'm asking is just some direction about where to start from .. which layer
should I apply to implement these ideas disregarding the OS (osx or linux).
FUSE is able to do what you outline here.
Post by Irad K
Basically I've got files that are rather sensitive so I keep them
encrypted, and I'd like to let only certain processes the ability to
interact with them as they were regular files (all other processes who
attempt to touch those files, will see them as encrypted).
Keep in mind that there are a lot of ways for the sensitive information
to leak once it is in memory unencrypted, even if you restrict access to
some processes.
Post by Irad K
After I read the manuals I found out that the only way to control file
actions is using the API in fuse_lowlevel.h or fuse.h, both let you set a
vector of file operations called fuse_operation or fuse_lowlevel_ops.
I've tried to play with these api's, but when I tested it, it seems that
when I read or write or map the file, it doesn't goes through the operation
I just defined in the vector I mentioned above.
You'll need to be a little more specific than that, otherwise there's
very little we can do to help. Did you take a look at the example
filesystems? Are they working for you?

Best,
-Nikolaus
--
GPG Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F

»Time flies like an arrow, fruit flies like a Banana.«
Irad K
2017-06-15 07:20:35 UTC
Permalink
Hi Nikolaus and thank your for your response.

Here are the specific challenges I'm currently facing with :

1. *How to pass the AES key for encrypting/decrypting*. I want to be able
to allow certain processes read / write the data after encryption. However,
when calling the system call read / write /mmap, none of them contain extra
argument for supplying the AES block key (the symmetric key). Therefore, I
need to communicate the filesystem about the key and whose process does it
belong. I also want that this channel to be secure and make it hard to
eavesdrop by unwanted entities. Do you have any idea how to do it ?

*ssize_t read(int **fd**, void ***buf**, size_t **count**);*

*ssize_t write(int **fd**, const void ***buf**, size_t **count**);*

*void *mmap(void ***addr**, size_t **length**, int **prot**, int
**flags**, **int **fd**, off_t **offset**);*

2. *On which software layer to perform the decryption/encryption.* the
Osxfuse API contain 2 APIs as explain in the documentation : a
"high-level", synchronous API, and a "low-level" asynchronous API. I've
read the documentation, but I'm still not sure I fully understand the
difference between them. which I/F did you use in your implementation and
what was the motivation for choosing it upon the other one ? in addition, I
assumed the the encryption layer should be reside on the driver, because it
needs to be secure and untraceable as much as possible. do you know if
there's an API for osxfuse kernel extension (kext).

3. *Understanding the data flow.* I wish to base my solution on the
loopback filesystem, where I can easily add my encryption layer on top of
root filesystem. If I'm looking at the read command in struct fuse
operation, it seems that the only thing that it does is calling 'pread'
(see code below).

So as I see the flow, when I invoke read sys call, it reaches the fuse
driver in kernel layer, which pass it back to user-space to the callback
fuse_operations.read() in fuse daemon, and then it return back to the
kernel using 'pread' sys call which perform the actual task of reading the
file.
The Question here's is how does the kernel knows to pass the first sys call
to daemon, and to do the read on the second sys call.


static int

loopback_read(const char *path, char *buf, size_t size, off_t offset,

struct fuse_file_info *fi)

{

int res;



(void)path;

res = pread(fi->fh, buf, size, offset);

if (res == -1) {

res = -errno;

}



return res;

}

Thanks in advance,
Irad
Post by Irad K
Post by Irad K
My case is rather simple to explain, and I'm struggling to find in which
layer should I implement it.
Now, for every access to a file contents (either read/write or map) I
want
Post by Irad K
1. READ: after reading the raw data (encrypted), it will get decrypted
with
Post by Irad K
the symmetric key that the process has.
2. WRITE: before writing, the contents get encrypted with the symmetric
key
Post by Irad K
that the process has.
3. MMAP: the same for file mmap, I wish to decrypt the file contents
before
Post by Irad K
they mapped to memory.
I'm asking is just some direction about where to start from .. which
layer
Post by Irad K
should I apply to implement these ideas disregarding the OS (osx or
linux).
FUSE is able to do what you outline here.
Post by Irad K
Basically I've got files that are rather sensitive so I keep them
encrypted, and I'd like to let only certain processes the ability to
interact with them as they were regular files (all other processes who
attempt to touch those files, will see them as encrypted).
Keep in mind that there are a lot of ways for the sensitive information
to leak once it is in memory unencrypted, even if you restrict access to
some processes.
Post by Irad K
After I read the manuals I found out that the only way to control file
actions is using the API in fuse_lowlevel.h or fuse.h, both let you set a
vector of file operations called fuse_operation or fuse_lowlevel_ops.
I've tried to play with these api's, but when I tested it, it seems that
when I read or write or map the file, it doesn't goes through the
operation
Post by Irad K
I just defined in the vector I mentioned above.
You'll need to be a little more specific than that, otherwise there's
very little we can do to help. Did you take a look at the example
filesystems? Are they working for you?
Best,
-Nikolaus
--
GPG Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F
»Time flies like an arrow, fruit flies like a Banana.«
------------------------------------------------------------
------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
--
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/
lists/listinfo/fuse-devel
Michael Theall
2017-06-15 15:23:05 UTC
Permalink
Hi Irad,

1) The only way you'd be able to do this through fuse is via ioctl(2) or
setxattr(2). If you're really worried someone could intercept the keys
(like via gdb breaking on these syscalls and looking at the arguments)
you'll need to secure this message passing too. An alternative would be
your fuse implementation listening on a socket or something and
communicating over that, but this will be much more complicated to
correlate the file/process. Of course, all of these ways means that your
end applications need to know about your filesystem and how to trade
encryption keys with it.

2) This is irrelevant to encryption. Use the high-level interface if you
want path-based operations or for simplicity. Use the low-level interface
for inode-based operations or you need more advanced functionality.

3) pread(2) is used here because it directly takes an offset. It just makes
it easier to implement in the example code. You could use read(2),
fread(3), mmap(2), or really any way you are able to read the file at all.
Depending on what you use, you might need to do it in some type of critical
section for consistency if you have simultaneous access.

PLEASE NOTE: Once you have sent unencrypted data to the kernel, then when
some other process tries to read that file it may read the unencrypted data
directly from the kernel, bypassing your fuse implementation. If you want
to avoid this, you need to ensure that the kernel never caches your data.

The only truly secure way to do what you want is to do all
encryption/decryption at the application layer where the kernel and your
fuse implementation (which would become unnecessary) never see unencrypted
data.

Regards,
Michael Theall
Post by Irad K
Hi Nikolaus and thank your for your response.
1. *How to pass the AES key for encrypting/decrypting*. I want to be able
to allow certain processes read / write the data after encryption. However,
when calling the system call read / write /mmap, none of them contain extra
argument for supplying the AES block key (the symmetric key). Therefore, I
need to communicate the filesystem about the key and whose process does it
belong. I also want that this channel to be secure and make it hard to
eavesdrop by unwanted entities. Do you have any idea how to do it ?
*ssize_t read(int **fd**, void ***buf**, size_t **count**);*
*ssize_t write(int **fd**, const void ***buf**, size_t **count**);*
*void *mmap(void ***addr**, size_t **length**, int **prot**, int **flags**, **int **fd**, off_t **offset**);*
2. *On which software layer to perform the decryption/encryption.* the
Osxfuse API contain 2 APIs as explain in the documentation : a
"high-level", synchronous API, and a "low-level" asynchronous API. I've
read the documentation, but I'm still not sure I fully understand the
difference between them. which I/F did you use in your implementation and
what was the motivation for choosing it upon the other one ? in addition, I
assumed the the encryption layer should be reside on the driver, because it
needs to be secure and untraceable as much as possible. do you know if
there's an API for osxfuse kernel extension (kext).
3. *Understanding the data flow.* I wish to base my solution on the
loopback filesystem, where I can easily add my encryption layer on top of
root filesystem. If I'm looking at the read command in struct fuse
operation, it seems that the only thing that it does is calling 'pread'
(see code below).
So as I see the flow, when I invoke read sys call, it reaches the fuse
driver in kernel layer, which pass it back to user-space to the callback
fuse_operations.read() in fuse daemon, and then it return back to the
kernel using 'pread' sys call which perform the actual task of reading the
file.
The Question here's is how does the kernel knows to pass the first sys
call to daemon, and to do the read on the second sys call.
static int
loopback_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void)path;
res = pread(fi->fh, buf, size, offset);
if (res == -1) {
res = -errno;
}
return res;
}
Thanks in advance,
Irad
On Jun 11 2017, Irad K <
Post by Irad K
My case is rather simple to explain, and I'm struggling to find in which
layer should I implement it.
Now, for every access to a file contents (either read/write or map) I
want
Post by Irad K
1. READ: after reading the raw data (encrypted), it will get decrypted
with
Post by Irad K
the symmetric key that the process has.
2. WRITE: before writing, the contents get encrypted with the symmetric
key
Post by Irad K
that the process has.
3. MMAP: the same for file mmap, I wish to decrypt the file contents
before
Post by Irad K
they mapped to memory.
I'm asking is just some direction about where to start from .. which
layer
Post by Irad K
should I apply to implement these ideas disregarding the OS (osx or
linux).
FUSE is able to do what you outline here.
Post by Irad K
Basically I've got files that are rather sensitive so I keep them
encrypted, and I'd like to let only certain processes the ability to
interact with them as they were regular files (all other processes who
attempt to touch those files, will see them as encrypted).
Keep in mind that there are a lot of ways for the sensitive information
to leak once it is in memory unencrypted, even if you restrict access to
some processes.
Post by Irad K
After I read the manuals I found out that the only way to control file
actions is using the API in fuse_lowlevel.h or fuse.h, both let you set
a
Post by Irad K
vector of file operations called fuse_operation or fuse_lowlevel_ops.
I've tried to play with these api's, but when I tested it, it seems that
when I read or write or map the file, it doesn't goes through the
operation
Post by Irad K
I just defined in the vector I mentioned above.
You'll need to be a little more specific than that, otherwise there's
very little we can do to help. Did you take a look at the example
filesystems? Are they working for you?
Best,
-Nikolaus
--
GPG Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F
»Time flies like an arrow, fruit flies like a Banana.«
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
--
fuse-devel mailing list
To unsubscribe or subscribe, visit
https://lists.sourceforge.net/lists/listinfo/fuse-devel
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot--
fuse-devel mailing list
To unsubscribe or subscribe, visit
https://lists.sourceforge.net/lists/listinfo/fuse-devel
Nikolaus Rath
2017-06-15 16:27:36 UTC
Permalink
Post by Irad K
Hi Nikolaus and thank your for your response.
1. *How to pass the AES key for encrypting/decrypting*. I want to be able
to allow certain processes read / write the data after encryption. However,
when calling the system call read / write /mmap, none of them contain extra
argument for supplying the AES block key (the symmetric key). Therefore, I
need to communicate the filesystem about the key and whose process does it
belong. I also want that this channel to be secure and make it hard to
eavesdrop by unwanted entities. Do you have any idea how to do it ?
That seems pointless to me. If, in the ideal case, you want the process
to supply the key in the read/write call, why can't the process simply
do the encryption itself? That will be faster and less complex to
implement. For example, you could write a small library that provides
read_encrypted() and write_encrypted() functions with exactly the
semantics that you want.
Post by Irad K
2. *On which software layer to perform the decryption/encryption.* the
Osxfuse API contain 2 APIs as explain in the documentation : a
"high-level", synchronous API, and a "low-level" asynchronous API. I've
read the documentation, but I'm still not sure I fully understand the
difference between them.
which I/F did you use in your implementation
What implementation do you mean? Did you take a look at the examples/
directory in libfuse? It contains a passthrough filesystem implemented
once using the high- and once using the low-level API. That might help.


Best,
-Nikolaus
--
GPG Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F

»Time flies like an arrow, fruit flies like a Banana.«
Loading...