NMSetting8021x has various utility functions to set
the certificate:
- nm_setting_802_1x_set_ca_cert()
- nm_setting_802_1x_set_client_cert()
- nm_setting_802_1x_set_private_key()
- nm_setting_802_1x_set_phase2_ca_cert()
- nm_setting_802_1x_set_phase2_client_cert()
- nm_setting_802_1x_set_phase2_private_key()
They support:
- accepting a plain PKCS11 URI, with scheme set to
NM_SETTING_802_1X_CK_SCHEME_PKCS11.
- accepting a filename, with scheme set to
NM_SETTING_802_1X_CK_SCHEME_BLOB or
NM_SETTING_802_1X_CK_SCHEME_PATH.
In the latter case, the function tries to load the file and verify it.
In case of the private-key setters, this also involves accepting a
password. Depending on whether the scheme is BLOB or PATH, the function
will either set the certificate to a PATH blob, or take the blob that
was read from file.
The functions seem misdesigned to me, because their behavior is
rather obscure. E.g. they behave fundamentally different, depending
on whether scheme is PKCS11 or BLOB/PATH.
Anyway, improve them:
- refactor the common code into a function _cert_impl_set(). Previously,
their non-trivial implementations were copy+pasted several times,
now they all use the same implementation.
- if the function is going to fail, don't touch the setting. Previously,
the functions would first clear the certificate before trying to
validate the input. It's more logical, that if a functions is going
to fail to check for failure first and don't modify the settings.
- not every blob can be represented. For example, if we have a blob
which starts with "file://", then there is no way to set it, simply
because we don't support a prefix for blobs (like "data:;base64,").
This means, if we try to set the certificate to a particular binary,
we must check that the binary is interpreted with the expected scheme.
Add this check.
We need to (and already did) define our own identifier for ciphers,
because the gnutls/nss identifiers must be abstracted.
Don't use a string for that. The number of supported ciphers
is not generic but fixed and known at compiler time. An enum
is better suited.
@key is directly passed to nm_crypto_md5_hash(), which cannot (by API design)
fail. No need to initialize it.
Also, no need to allocate an additional trailing NUL byte. The key is
binary, every attempt to use it as a string will horribly fail.
- avoid "const gsize" as type for function arguments.
- consistently use "guint8 *" type for binary data, instead
of "char *", which indicates a NUL terminated C string.
- drop nm_crypto_encrypt(). It's not actually used outside of
"nm-crypto.c".
- rename internal _nm_crypto_*() functions that are only used
in tests. It's so much nicer to visually recognize functions
that are used for testing only.
file_to_secure_bytes() tried to load the file from disk and ensure that
the data will be cleared. It did so poorely, because g_file_get_contents()
cannot be used for that.
Add a helper function nm_crypto_read_file() to get this right.
g_file_get_contents() may use re-alloc to load the file. Each time
it re-allocated the buffer, it does not bother clearing the loaded
buffer from memory.
Alternatively, g_file_get_contents() may use stat() and only allocate
one buffer. But also in this mode, without realloc(), it does not
clear the buffer if reading the file fails with IO error later.
Use nm_utils_file_get_contents() which does that.
While at it, don't load files larger that 100 MB.
It's only used for testing, so this change is not very relevant.
Anyway, I think our crypto code should succeed in not leaving
key material in memory. Refactor the code to do that, though,
how the pem file gets composed is quite a hack (for tests good
enough though).
nm_utils_rsa_key_encrypt() is internal API which is only uesd for testing.
Move it to nm-crypto.h (where it fits better) and rename it to make the
testing-aspect obvious.
In nm-crypto.c we have functions that are only called from tests.
Maybe these functions should move away from libnm-core to the
test.
Leave it, but at least rename them to make it clear that these
functions are not relevant for libnm's actual usage. For a
reviewer that makes a big difference as crypto functions in libnm
have a significantly higher requirement for quality.
There is nothing new here. We already have other *nmtst* functions
beside our regular code. The concention is, that functions that
are only for testing are named explicitly ("nmtst"), and that they
can only be called by test functions themselves.
The GBytes has a suitable cleanup function, which zeros the certificate
from memory.
Also, all callers that require the certificate, actually later converted
it into a GBytes anyway. This way, they can re-used the same instance
(avoiding an additionaly copying of the data), and they will properly
clear the memory when freed.
Rename _nm_crypto_verify_cert() to _nm_crypto_verify_x509().
Also, don't let it return a NMCryptoFileFormat result. This
function only checks for a particular format, hence it
should only return true/false.
Also, fix setting error output argument when the function fails.
Follow our convention, that items in headers are all named with
an "NM" prefix.
Also, "nm-crypto-impl.h" contains internal functions that are to be implemented
by the corresponding crypto backends. Distinguish their names as well.
There are two aspects: the public crypto API that is provided by
"nm-crypto.h" header, and the internal header which crypto backends
need to implement. Split them.