Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KCM: Securely erase memory used for secrets #7062

Closed
wants to merge 3 commits into from

Conversation

aplopez
Copy link
Contributor

@aplopez aplopez commented Nov 29, 2023

Make sure all the memory blocks allocated dynamically or statically by KCM to store credentials and messages (which might include credentials) are erased by calling sss_erase_mem_securely() or
sss_erase_talloc_mem_securely() before being freed.

struct sss_iobuf is the main structure used to store secrets. It's buffer will be cleaned when freed using a destructor. The destructor is associated to the buffer itself, in case it is stolen from one structure to another.

struct kcm_ccache doesn't need to be explicitly cleaned because, in the end, it uses struct sss_iobuf.

A unit test was added to check this functionality. Valgrind's magic interfere with this test because the heap disappears. This makes the test incompatible avec Valgrind, so the test is skipped under these conditions. This added a new build dependency on the package valgrind-devel.

@aplopez aplopez force-pushed the kcm-memory branch 3 times, most recently from dfa5866 to 8ced9e3 Compare November 30, 2023 08:45
@aplopez
Copy link
Contributor Author

aplopez commented Dec 1, 2023

Failing tests seem to be failing in all the PRs, not just here.

src/util/util.h Outdated Show resolved Hide resolved
src/responder/kcm/kcmsrv_ccache.c Fixed Show fixed Hide fixed
src/responder/kcm/kcmsrv_ccache.c Fixed Show fixed Hide fixed
The structure is not modified so it is logical to receive a
`const struct sss_iobuf`.
The macro was defined in a .c module and thus unavailable to
be used on any other modules.

It was moved to common.h so that it can be used in other tests.
@aplopez aplopez force-pushed the kcm-memory branch 2 times, most recently from 49496c8 to f358b4d Compare December 1, 2023 15:51
@aplopez
Copy link
Contributor Author

aplopez commented Dec 4, 2023

@alexey-tikhonov I added a flag parameter to tell the sss_iobuf_init_[empty|readonly|steal]() functions whether they should secure the data or not. I assumed this was going to be set to false many times and thus avoid wasting time cleaning memory that didn't contain secrets. There is finally only one case where I set it to false and for a small block of memeory. In all the other cases the flag is set to true. I wonder if it is worth keeping this flag. What do you think?

/*
* @brief Returns whether the data is secured
*/
bool sss_iobuf_get_secure(const struct sss_iobuf *iobuf);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be the use?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Access to that value. It is a sibling to the other sss_iobuf_get_*() functions. Even if it is not currently used, there is no other way for a caller to access this value.

Copy link
Member

@alexey-tikhonov alexey-tikhonov Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is "what does it do", not "what would be a reason to use it".

What is the reason to store 'sss_iobuf::secure' at all?
When buffer is being created, one opts if he wants a secure cleanup.
But I can't imagine a realistic use case when one wants to know if buffer is secured.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is explained in the last sentence:

there is no other way for a caller to access this value.

The struct sss_iobuf is defined in the sss_iobuf.h header file, but declared in the sss_iobuf.c module. External users only have access to those fields by using getters. As said before, today it is not used, but we don' t know if it will be need anytime soon. Also it was created for API completeness: all the other fields have a getter, this one too.

The secure field is needed by the sss_iobuf_read_varlen() and sss_iobuf_read_iobuf() functions which create copies of the data and need to know whether to secure or not those new buffers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The secure field is needed by the sss_iobuf_read_varlen() and sss_iobuf_read_iobuf() functions which create copies of the data and need to know whether to secure or not those new buffers.

Could you please elaborate on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function sss_iobuf_read_varlen() receives an sss_iobuf, read some data from it and puts them on a brand new sss_iobuf. If the old structure were the data was read from was secured (secure is true), then the new one must also be secured. The cleanest way to know whether the original structure was secured is using the secure flag. This function is the reason the flag exists. Until I found this function, there was no flag.

Function sss_iobuf_read_iobuf() internally uses sss_iobuf_read_varlen() and sss_iobuf_init_steal() to create a new buffer, which must be secured in the same way, so the flag stored in the source structure is used to set the flag in the new structure. The flag isn't actually needed for this case, but since it exists for the other case, it must be set to the correct value.

Copy link
Member

@alexey-tikhonov alexey-tikhonov Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function sss_iobuf_read_varlen() receives an sss_iobuf, read some data from it and puts them on a brand new sss_iobuf.

Ah, this is not 'new sss_iobuf' but 'uint8_t **_out'...
This is a can of worms.
This output buffer might be copied further, and information about it's "security" is already lost anyway.
I would argue it's up to a user of sss_iobuf_read_varlen() to decide if read chunk of data has to be secured.
This is more transparent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As to talloc_steal(), I believe it keeps d-tor.

Copy link
Contributor Author

@aplopez aplopez Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This output buffer might be copied further, and information about it's "security" is already lost anyway. I would argue it's up to a user of sss_iobuf_read_varlen() to decide if read chunk of data has to be secured. This is more transparent.

No option is better than the other. I just choose the one that simplifies things by letting the original owner of the data decide about its security. "If it was originally secure, then it must remain secure."

About further copies... Sure. There is nothing we can do about that right now.

As to talloc_steal(), I believe it keeps d-tor.

Yes it does. I confirmed it. That's why I say sss_iobuf_init_steal() doesn't need the flag, but we need to keep it coherent for sss_iobuf_read_varlen().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I will not insist further, that's not too important after all (though I still think sss_iobuf_get_secure()/secure isn't really needed).

@alexey-tikhonov
Copy link
Member

@alexey-tikhonov I added a flag parameter to tell the sss_iobuf_init_[empty|readonly|steal]() functions whether they should secure the data or not. I assumed this was going to be set to false many times and thus avoid wasting time cleaning memory that didn't contain secrets. There is finally only one case where I set it to false and for a small block of memeory. In all the other cases the flag is set to true. I wonder if it is worth keeping this flag. What do you think?

I would keep it in the API. Imo, it makes this utility more versatile and thus appealing to re-use.

@aplopez aplopez marked this pull request as ready for review December 5, 2023 09:14
@andreboscatto andreboscatto requested a review from thalman December 5, 2023 13:37
@alexey-tikhonov alexey-tikhonov added no-backport This should go to target branch only. and removed backport-to-stable labels Dec 7, 2023
src/util/util.h Outdated Show resolved Hide resolved
Make sure all the memory blocks allocated dynamically or statically by
KCM to store credentials and messages (which might include credentials)
are erased by calling sss_erase_mem_securely() or
sss_erase_talloc_mem_securely() before being freed.
@alexey-tikhonov
Copy link
Member

Thanks, ACK.

Copy link
Contributor

@thalman thalman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK

@alexey-tikhonov
Copy link
Member

Pushed PR: #7062

  • master
    • 747c85f - KCM: Securely erase memory used for secrets
    • 4c159b0 - TESTS: Make the AS_STR() macro available in common.h
    • 54395cb - KCM: sss_iobuf_get_*() functions must take a const struct

@aplopez aplopez deleted the kcm-memory branch January 17, 2024 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-backport This should go to target branch only. Pushed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants