mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-12 13:30:35 +01:00
2008-11-13 Dan Williams <dcbw@redhat.com>
Add support for PKCS#12 private keys (bgo #558982) * libnm-util/crypto.c libnm-util/crypto.h - (parse_old_openssl_key_file): rename from parse_key_file(); adapt to take a GByteArray instead of a filename - (file_to_g_byte_array): handle private key files too - (decrypt_key): take a GByteArray rather than data + len - (crypto_get_private_key_data): refactor crypto_get_private_key() into one function that takes a filename, and one that takes raw data; detect pkcs#12 files as well - (crypto_load_and_verify_certificate): detect file type - (crypto_is_pkcs12_data, crypto_is_pkcs12_file): add pkcs#12 detection functions * libnm-util/crypto_gnutls.c - (crypto_decrypt): take GByteArray rather than data + len; fix a bug whereby tail padding was incorrectly handled, leading to erroneous successes when trying to decrypt the data - (crypto_verify_cert): rework somewhat - (crypto_verify_pkcs12): validate pkcs#12 keys * libnm-util/crypto_nss.c - (crypto_init): enable various pkcs#12 ciphers - (crypto_decrypt): take a GByteArray rather than data + len - (crypto_verify_cert): clean up - (crypto_verify_pkcs12): validate pkcs#12 keys * libnm-util/test-crypto.c - Handle pkcs#12 keys * libnm-util/nm-setting-8021x.c libnm-util/nm-setting-8021x.h libnm-util/libnm-util.ver - Add two new properties, 'private-key-password' and 'phase2-private-key-password', to be used in conjunction with pkcs#12 keys - (nm_setting_802_1x_set_ca_cert_from_file, nm_setting_802_1x_set_client_cert_from_file, nm_setting_802_1x_set_phase2_ca_cert_from_file, nm_setting_802_1x_set_phase2_client_from_file): return certificate type - (nm_setting_802_1x_get_private_key_password, nm_setting_802_1x_get_phase2_private_key_password): return private key passwords - (nm_setting_802_1x_set_private_key_from_file, nm_setting_802_1x_set_phase2_private_key_from_file): set the private key from a file, and update the private key password at the same time - (nm_setting_802_1x_get_private_key_type, nm_setting_802_1x_get_phase2_private_key_type): return the private key type * src/supplicant-manager/nm-supplicant-settings-verify.c - Whitelist private key passwords * src/supplicant-manager/nm-supplicant-config.c - (nm_supplicant_config_add_setting_8021x): for pkcs#12 private keys, add the private key password to the supplicant config, but do not add the client certificate (as required by wpa_supplicant) git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@4280 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
This commit is contained in:
parent
e4ae149b37
commit
e2f65ce12a
11 changed files with 879 additions and 128 deletions
62
ChangeLog
62
ChangeLog
|
|
@ -1,3 +1,65 @@
|
|||
2008-11-13 Dan Williams <dcbw@redhat.com>
|
||||
|
||||
Add support for PKCS#12 private keys (bgo #558982)
|
||||
|
||||
* libnm-util/crypto.c
|
||||
libnm-util/crypto.h
|
||||
- (parse_old_openssl_key_file): rename from parse_key_file(); adapt to
|
||||
take a GByteArray instead of a filename
|
||||
- (file_to_g_byte_array): handle private key files too
|
||||
- (decrypt_key): take a GByteArray rather than data + len
|
||||
- (crypto_get_private_key_data): refactor crypto_get_private_key() into
|
||||
one function that takes a filename, and one that takes raw data;
|
||||
detect pkcs#12 files as well
|
||||
- (crypto_load_and_verify_certificate): detect file type
|
||||
- (crypto_is_pkcs12_data, crypto_is_pkcs12_file): add pkcs#12 detection
|
||||
functions
|
||||
|
||||
* libnm-util/crypto_gnutls.c
|
||||
- (crypto_decrypt): take GByteArray rather than data + len; fix a bug
|
||||
whereby tail padding was incorrectly handled, leading to erroneous
|
||||
successes when trying to decrypt the data
|
||||
- (crypto_verify_cert): rework somewhat
|
||||
- (crypto_verify_pkcs12): validate pkcs#12 keys
|
||||
|
||||
* libnm-util/crypto_nss.c
|
||||
- (crypto_init): enable various pkcs#12 ciphers
|
||||
- (crypto_decrypt): take a GByteArray rather than data + len
|
||||
- (crypto_verify_cert): clean up
|
||||
- (crypto_verify_pkcs12): validate pkcs#12 keys
|
||||
|
||||
* libnm-util/test-crypto.c
|
||||
- Handle pkcs#12 keys
|
||||
|
||||
* libnm-util/nm-setting-8021x.c
|
||||
libnm-util/nm-setting-8021x.h
|
||||
libnm-util/libnm-util.ver
|
||||
- Add two new properties, 'private-key-password' and
|
||||
'phase2-private-key-password', to be used in conjunction with
|
||||
pkcs#12 keys
|
||||
- (nm_setting_802_1x_set_ca_cert_from_file,
|
||||
nm_setting_802_1x_set_client_cert_from_file,
|
||||
nm_setting_802_1x_set_phase2_ca_cert_from_file,
|
||||
nm_setting_802_1x_set_phase2_client_from_file): return certificate
|
||||
type
|
||||
- (nm_setting_802_1x_get_private_key_password,
|
||||
nm_setting_802_1x_get_phase2_private_key_password): return private
|
||||
key passwords
|
||||
- (nm_setting_802_1x_set_private_key_from_file,
|
||||
nm_setting_802_1x_set_phase2_private_key_from_file): set the private
|
||||
key from a file, and update the private key password at the same time
|
||||
- (nm_setting_802_1x_get_private_key_type,
|
||||
nm_setting_802_1x_get_phase2_private_key_type): return the private
|
||||
key type
|
||||
|
||||
* src/supplicant-manager/nm-supplicant-settings-verify.c
|
||||
- Whitelist private key passwords
|
||||
|
||||
* src/supplicant-manager/nm-supplicant-config.c
|
||||
- (nm_supplicant_config_add_setting_8021x): for pkcs#12 private keys,
|
||||
add the private key password to the supplicant config, but do not
|
||||
add the client certificate (as required by wpa_supplicant)
|
||||
|
||||
2008-11-12 Tambet Ingo <tambet@gmail.com>
|
||||
|
||||
* system-settings/plugins/keyfile/nm-keyfile-connection.c (copy_one_secret)
|
||||
|
|
|
|||
|
|
@ -68,26 +68,24 @@ find_tag (const char *tag, const char *buf, gsize len)
|
|||
#define DEK_INFO_TAG "DEK-Info: "
|
||||
#define PROC_TYPE_TAG "Proc-Type: "
|
||||
|
||||
static char *
|
||||
parse_key_file (const char *filename,
|
||||
int key_type,
|
||||
gsize *out_length,
|
||||
char **out_cipher,
|
||||
char **out_iv,
|
||||
GError **error)
|
||||
static GByteArray *
|
||||
parse_old_openssl_key_file (GByteArray *contents,
|
||||
int key_type,
|
||||
char **out_cipher,
|
||||
char **out_iv,
|
||||
GError **error)
|
||||
{
|
||||
char *contents = NULL;
|
||||
GByteArray *bindata = NULL;
|
||||
char **lines = NULL;
|
||||
char **ln = NULL;
|
||||
gsize length = 0;
|
||||
const char *pos;
|
||||
const char *end;
|
||||
GString *str = NULL;
|
||||
int enc_tags = 0;
|
||||
char *iv = NULL;
|
||||
char *cipher = NULL;
|
||||
char *bindata = NULL;
|
||||
gsize bindata_len = 0;
|
||||
unsigned char *tmp = NULL;
|
||||
gsize tmp_len = 0;
|
||||
const char *start_tag;
|
||||
const char *end_tag;
|
||||
|
||||
|
|
@ -109,19 +107,16 @@ parse_key_file (const char *filename,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (!g_file_get_contents (filename, &contents, &length, error))
|
||||
return NULL;
|
||||
|
||||
pos = find_tag (start_tag, contents, length);
|
||||
pos = find_tag (start_tag, (const char *) contents->data, contents->len);
|
||||
if (!pos)
|
||||
goto parse_error;
|
||||
|
||||
pos += strlen (start_tag);
|
||||
|
||||
end = find_tag (end_tag, pos, contents + length - pos);
|
||||
end = find_tag (end_tag, pos, (const char *) contents->data + contents->len - pos);
|
||||
if (end == NULL) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("PEM key file had no end tag '%s'."),
|
||||
end_tag);
|
||||
goto parse_error;
|
||||
|
|
@ -131,7 +126,7 @@ parse_key_file (const char *filename,
|
|||
lines = g_strsplit (pos, "\n", 0);
|
||||
if (!lines || g_strv_length (lines) <= 1) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("Doesn't look like a PEM private key file."));
|
||||
goto parse_error;
|
||||
}
|
||||
|
|
@ -155,7 +150,7 @@ parse_key_file (const char *filename,
|
|||
if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) {
|
||||
if (enc_tags++ != 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("Malformed PEM file: Proc-Type was not first tag."));
|
||||
goto parse_error;
|
||||
}
|
||||
|
|
@ -163,7 +158,7 @@ parse_key_file (const char *filename,
|
|||
p += strlen (PROC_TYPE_TAG);
|
||||
if (strcmp (p, "4,ENCRYPTED")) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("Malformed PEM file: unknown Proc-Type tag '%s'."),
|
||||
p);
|
||||
goto parse_error;
|
||||
|
|
@ -173,7 +168,7 @@ parse_key_file (const char *filename,
|
|||
|
||||
if (enc_tags++ != 1) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("Malformed PEM file: DEK-Info was not the second tag."));
|
||||
goto parse_error;
|
||||
}
|
||||
|
|
@ -184,14 +179,14 @@ parse_key_file (const char *filename,
|
|||
comma = strchr (p, ',');
|
||||
if (!comma || (*(comma + 1) == '\0')) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("Malformed PEM file: no IV found in DEK-Info tag."));
|
||||
goto parse_error;
|
||||
}
|
||||
*comma++ = '\0';
|
||||
if (!g_ascii_isxdigit (*comma)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("Malformed PEM file: invalid format of IV in DEK-Info tag."));
|
||||
goto parse_error;
|
||||
}
|
||||
|
|
@ -212,7 +207,7 @@ parse_key_file (const char *filename,
|
|||
} else {
|
||||
if ((enc_tags != 0) && (enc_tags != 2)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
"Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
|
||||
goto parse_error;
|
||||
}
|
||||
|
|
@ -220,8 +215,8 @@ parse_key_file (const char *filename,
|
|||
}
|
||||
}
|
||||
|
||||
bindata = (char *) g_base64_decode (str->str, &bindata_len);
|
||||
if (bindata == NULL || !bindata_len) {
|
||||
tmp = g_base64_decode (str->str, &tmp_len);
|
||||
if (tmp == NULL || !tmp_len) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_DECODE_FAILED,
|
||||
_("Could not decode private key."));
|
||||
|
|
@ -230,36 +225,37 @@ parse_key_file (const char *filename,
|
|||
|
||||
if (lines)
|
||||
g_strfreev (lines);
|
||||
g_free (contents);
|
||||
|
||||
bindata = g_byte_array_sized_new (tmp_len);
|
||||
g_byte_array_append (bindata, tmp, tmp_len);
|
||||
*out_iv = iv;
|
||||
*out_cipher = cipher;
|
||||
*out_length = bindata_len;
|
||||
return bindata;
|
||||
|
||||
parse_error:
|
||||
g_free (bindata);
|
||||
g_free (cipher);
|
||||
g_free (iv);
|
||||
if (lines)
|
||||
g_strfreev (lines);
|
||||
g_free (contents);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GByteArray *
|
||||
file_to_g_byte_array (const char *filename,
|
||||
gboolean privkey,
|
||||
GError **error)
|
||||
{
|
||||
char *contents, *der = NULL;
|
||||
GByteArray *array = NULL;
|
||||
gsize length = 0;
|
||||
const char *pos;
|
||||
const char *pos = NULL;
|
||||
|
||||
if (!g_file_get_contents (filename, &contents, &length, error))
|
||||
return NULL;
|
||||
|
||||
pos = find_tag (pem_cert_begin, contents, length);
|
||||
if (!privkey)
|
||||
pos = find_tag (pem_cert_begin, contents, length);
|
||||
|
||||
if (pos) {
|
||||
const char *end;
|
||||
|
||||
|
|
@ -267,7 +263,7 @@ file_to_g_byte_array (const char *filename,
|
|||
end = find_tag (pem_cert_end, pos, contents + length - pos);
|
||||
if (end == NULL) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("PEM certificate '%s' had no end tag '%s'."),
|
||||
filename, pem_cert_end);
|
||||
goto done;
|
||||
|
|
@ -295,7 +291,7 @@ file_to_g_byte_array (const char *filename,
|
|||
if (array->len != length) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_OUT_OF_MEMORY,
|
||||
_("Not enough memory to store certificate data."));
|
||||
_("Not enough memory to store file data."));
|
||||
g_byte_array_free (array, TRUE);
|
||||
array = NULL;
|
||||
}
|
||||
|
|
@ -420,8 +416,7 @@ error:
|
|||
static char *
|
||||
decrypt_key (const char *cipher,
|
||||
int key_type,
|
||||
const char *data,
|
||||
gsize data_len,
|
||||
GByteArray *data,
|
||||
const char *iv,
|
||||
const char *password,
|
||||
gsize *out_len,
|
||||
|
|
@ -443,7 +438,7 @@ decrypt_key (const char *cipher,
|
|||
goto out;
|
||||
|
||||
output = crypto_decrypt (cipher, key_type,
|
||||
data, data_len,
|
||||
data,
|
||||
bin_iv, bin_iv_len,
|
||||
key, key_len,
|
||||
out_len,
|
||||
|
|
@ -467,38 +462,60 @@ out:
|
|||
return output;
|
||||
}
|
||||
|
||||
|
||||
GByteArray *
|
||||
crypto_get_private_key (const char *file,
|
||||
const char *password,
|
||||
guint32 *out_key_type,
|
||||
GError **error)
|
||||
crypto_get_private_key_data (GByteArray *contents,
|
||||
const char *password,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
NMCryptoFileFormat *out_file_type,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *array = NULL;
|
||||
guint32 key_type = NM_CRYPTO_KEY_TYPE_RSA;
|
||||
char *data = NULL;
|
||||
gsize data_len = 0;
|
||||
NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_RSA;
|
||||
GByteArray *data;
|
||||
char *iv = NULL;
|
||||
char *cipher = NULL;
|
||||
char *decrypted = NULL;
|
||||
gsize decrypted_len = 0;
|
||||
|
||||
/* Try RSA first */
|
||||
data = parse_key_file (file, key_type, &data_len, &cipher, &iv, error);
|
||||
g_return_val_if_fail (contents != NULL, NULL);
|
||||
g_return_val_if_fail (password != NULL, NULL);
|
||||
g_return_val_if_fail (out_key_type != NULL, NULL);
|
||||
g_return_val_if_fail (out_key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, NULL);
|
||||
g_return_val_if_fail (out_file_type != NULL, NULL);
|
||||
g_return_val_if_fail (out_file_type == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
|
||||
|
||||
/* Try PKCS#12 first */
|
||||
if (crypto_verify_pkcs12 (contents, password, NULL)) {
|
||||
*out_key_type = NM_CRYPTO_KEY_TYPE_ENCRYPTED;
|
||||
*out_file_type = NM_CRYPTO_FILE_FORMAT_PKCS12;
|
||||
|
||||
array = g_byte_array_sized_new (contents->len);
|
||||
g_byte_array_append (array, contents->data, contents->len);
|
||||
return array;
|
||||
}
|
||||
|
||||
/* OpenSSL non-standard legacy PEM files */
|
||||
|
||||
/* Try RSA keys first */
|
||||
data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
|
||||
if (!data) {
|
||||
g_clear_error (error);
|
||||
|
||||
/* DSA next */
|
||||
key_type = NM_CRYPTO_KEY_TYPE_DSA;
|
||||
data = parse_key_file (file, key_type, &data_len, &cipher, &iv, error);
|
||||
if (!data)
|
||||
data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
|
||||
if (!data) {
|
||||
g_clear_error (error);
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("Unable to determine private key type."));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
decrypted = decrypt_key (cipher,
|
||||
key_type,
|
||||
data,
|
||||
data_len,
|
||||
iv,
|
||||
password,
|
||||
&decrypted_len,
|
||||
|
|
@ -516,6 +533,7 @@ crypto_get_private_key (const char *file,
|
|||
|
||||
g_byte_array_append (array, (const guint8 *) decrypted, decrypted_len);
|
||||
*out_key_type = key_type;
|
||||
*out_file_type = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
|
||||
|
||||
out:
|
||||
if (decrypted) {
|
||||
|
|
@ -523,27 +541,96 @@ out:
|
|||
memset (decrypted, 0, decrypted_len);
|
||||
g_free (decrypted);
|
||||
}
|
||||
g_free (data);
|
||||
if (data)
|
||||
g_byte_array_free (data, TRUE);
|
||||
g_free (cipher);
|
||||
g_free (iv);
|
||||
return array;
|
||||
}
|
||||
|
||||
GByteArray *
|
||||
crypto_get_private_key (const char *file,
|
||||
const char *password,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
NMCryptoFileFormat *out_file_type,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *contents;
|
||||
GByteArray *key = NULL;
|
||||
|
||||
contents = file_to_g_byte_array (file, TRUE, error);
|
||||
if (contents) {
|
||||
key = crypto_get_private_key_data (contents, password, out_key_type, out_file_type, error);
|
||||
g_byte_array_free (contents, TRUE);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
GByteArray *
|
||||
crypto_load_and_verify_certificate (const char *file,
|
||||
NMCryptoFileFormat *out_file_format,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *array;
|
||||
|
||||
array = file_to_g_byte_array (file, error);
|
||||
g_return_val_if_fail (file != NULL, NULL);
|
||||
g_return_val_if_fail (out_file_format != NULL, NULL);
|
||||
g_return_val_if_fail (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
|
||||
|
||||
array = file_to_g_byte_array (file, FALSE, error);
|
||||
if (!array)
|
||||
return NULL;
|
||||
|
||||
if (!crypto_verify_cert (array->data, array->len, error)) {
|
||||
g_byte_array_free (array, TRUE);
|
||||
array = NULL;
|
||||
*out_file_format = crypto_verify_cert (array->data, array->len, error);
|
||||
if (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
|
||||
/* Try PKCS#12 */
|
||||
if (crypto_is_pkcs12_data (array)) {
|
||||
*out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12;
|
||||
g_clear_error (error);
|
||||
} else {
|
||||
g_byte_array_free (array, TRUE);
|
||||
array = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_is_pkcs12_data (const GByteArray *data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
gboolean success;
|
||||
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
success = crypto_verify_pkcs12 (data, NULL, &error);
|
||||
if (success)
|
||||
return TRUE;
|
||||
|
||||
/* If the error was just a decryption error, then it's pkcs#12 */
|
||||
if (error) {
|
||||
if (g_error_matches (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED))
|
||||
success = TRUE;
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_is_pkcs12_file (const char *file)
|
||||
{
|
||||
GByteArray *contents;
|
||||
gboolean success = FALSE;
|
||||
|
||||
g_return_val_if_fail (file != NULL, FALSE);
|
||||
|
||||
contents = file_to_g_byte_array (file, TRUE, NULL);
|
||||
if (contents) {
|
||||
success = crypto_is_pkcs12_data (contents);
|
||||
g_byte_array_free (contents, TRUE);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ enum {
|
|||
NM_CRYPTO_ERR_NONE = 0,
|
||||
NM_CRYPTO_ERR_INIT_FAILED,
|
||||
NM_CRYPTO_ERR_CANT_READ_FILE,
|
||||
NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
|
||||
NM_CRYPTO_ERR_DECODE_FAILED,
|
||||
NM_CRYPTO_ERR_OUT_OF_MEMORY,
|
||||
|
|
@ -43,14 +43,22 @@ enum {
|
|||
NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
|
||||
NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
|
||||
NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
|
||||
NM_CRYPTO_ERR_INVALID_PASSWORD,
|
||||
};
|
||||
|
||||
enum {
|
||||
typedef enum {
|
||||
NM_CRYPTO_KEY_TYPE_UNKNOWN = 0,
|
||||
NM_CRYPTO_KEY_TYPE_RSA,
|
||||
NM_CRYPTO_KEY_TYPE_DSA,
|
||||
};
|
||||
NM_CRYPTO_KEY_TYPE_ENCRYPTED
|
||||
} NMCryptoKeyType;
|
||||
|
||||
typedef enum {
|
||||
NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0,
|
||||
NM_CRYPTO_FILE_FORMAT_X509,
|
||||
NM_CRYPTO_FILE_FORMAT_RAW_KEY,
|
||||
NM_CRYPTO_FILE_FORMAT_PKCS12
|
||||
} NMCryptoFileFormat;
|
||||
|
||||
#define NM_CRYPTO_ERROR _nm_crypto_error_quark ()
|
||||
GQuark _nm_crypto_error_quark (void);
|
||||
|
|
@ -59,14 +67,27 @@ gboolean crypto_init (GError **error);
|
|||
|
||||
void crypto_deinit (void);
|
||||
|
||||
GByteArray * crypto_get_private_key_data (GByteArray *contents,
|
||||
const char *password,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
NMCryptoFileFormat *out_file_format,
|
||||
GError **error);
|
||||
|
||||
GByteArray * crypto_get_private_key (const char *file,
|
||||
const char *password,
|
||||
guint32 *out_key_type,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
NMCryptoFileFormat *out_file_format,
|
||||
GError **error);
|
||||
|
||||
GByteArray * crypto_load_and_verify_certificate (const char *file,
|
||||
NMCryptoFileFormat *out_file_format,
|
||||
GError **error);
|
||||
|
||||
gboolean crypto_is_pkcs12_file (const char *file);
|
||||
|
||||
gboolean crypto_is_pkcs12_data (const GByteArray *data);
|
||||
|
||||
|
||||
/* Internal utils API bits for crypto providers */
|
||||
|
||||
gboolean crypto_md5_hash (const char *salt,
|
||||
|
|
@ -79,8 +100,7 @@ gboolean crypto_md5_hash (const char *salt,
|
|||
|
||||
char * crypto_decrypt (const char *cipher,
|
||||
int key_type,
|
||||
const char *data,
|
||||
gsize data_len,
|
||||
GByteArray *data,
|
||||
const char *iv,
|
||||
const gsize iv_len,
|
||||
const char *key,
|
||||
|
|
@ -88,8 +108,11 @@ char * crypto_decrypt (const char *cipher,
|
|||
gsize *out_len,
|
||||
GError **error);
|
||||
|
||||
gboolean crypto_verify_cert (const unsigned char *data,
|
||||
gsize len,
|
||||
GError **error);
|
||||
NMCryptoFileFormat crypto_verify_cert (const unsigned char *data,
|
||||
gsize len,
|
||||
GError **error);
|
||||
|
||||
gboolean crypto_verify_pkcs12 (const GByteArray *data,
|
||||
const char *password,
|
||||
GError **error);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <gcrypt.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/x509.h>
|
||||
#include <gnutls/pkcs12.h>
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
|
|
@ -117,8 +118,7 @@ crypto_md5_hash (const char *salt,
|
|||
char *
|
||||
crypto_decrypt (const char *cipher,
|
||||
int key_type,
|
||||
const char *data,
|
||||
gsize data_len,
|
||||
GByteArray *data,
|
||||
const char *iv,
|
||||
const gsize iv_len,
|
||||
const char *key,
|
||||
|
|
@ -128,10 +128,10 @@ crypto_decrypt (const char *cipher,
|
|||
{
|
||||
gcry_cipher_hd_t ctx;
|
||||
gcry_error_t err;
|
||||
int cipher_mech;
|
||||
int cipher_mech, i;
|
||||
char *output = NULL;
|
||||
gboolean success = FALSE;
|
||||
gsize len;
|
||||
gsize pad_len;
|
||||
|
||||
if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
|
||||
cipher_mech = GCRY_CIPHER_3DES;
|
||||
|
|
@ -145,7 +145,7 @@ crypto_decrypt (const char *cipher,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
output = g_malloc0 (data_len + 1);
|
||||
output = g_malloc0 (data->len + 1);
|
||||
if (!output) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_OUT_OF_MEMORY,
|
||||
|
|
@ -180,7 +180,7 @@ crypto_decrypt (const char *cipher,
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = gcry_cipher_decrypt (ctx, output, data_len, data, data_len);
|
||||
err = gcry_cipher_decrypt (ctx, output, data->len, data->data, data->len);
|
||||
if (err) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
|
||||
|
|
@ -188,11 +188,21 @@ crypto_decrypt (const char *cipher,
|
|||
gcry_strsource (err), gcry_strerror (err));
|
||||
goto out;
|
||||
}
|
||||
len = data_len - output[data_len - 1];
|
||||
if (len > data_len)
|
||||
goto out;
|
||||
pad_len = output[data->len - 1];
|
||||
|
||||
*out_len = len;
|
||||
/* Validate tail padding; last byte is the padding size, and all pad bytes
|
||||
* should contain the padding size.
|
||||
*/
|
||||
for (i = 1; i <= pad_len; ++i) {
|
||||
if (output[data->len - i] != pad_len) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
|
||||
_("Failed to decrypt the private key."));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = data->len - pad_len;
|
||||
output[*out_len] = '\0';
|
||||
success = TRUE;
|
||||
|
||||
|
|
@ -200,7 +210,7 @@ out:
|
|||
if (!success) {
|
||||
if (output) {
|
||||
/* Don't expose key material */
|
||||
memset (output, 0, data_len);
|
||||
memset (output, 0, data->len);
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
}
|
||||
|
|
@ -209,37 +219,96 @@ out:
|
|||
return output;
|
||||
}
|
||||
|
||||
gboolean
|
||||
NMCryptoFileFormat
|
||||
crypto_verify_cert (const unsigned char *data,
|
||||
gsize len,
|
||||
GError **error)
|
||||
{
|
||||
gnutls_x509_crt_t crt;
|
||||
gnutls_x509_crt_t der;
|
||||
gnutls_datum dt;
|
||||
int err;
|
||||
|
||||
err = gnutls_x509_crt_init (&crt);
|
||||
err = gnutls_x509_crt_init (&der);
|
||||
if (err < 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
|
||||
_("Error initializing certificate data: %s"),
|
||||
gnutls_strerror (err));
|
||||
return FALSE;
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
/* Try DER first */
|
||||
dt.data = (unsigned char *) data;
|
||||
dt.size = len;
|
||||
err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_DER);
|
||||
if (err == GNUTLS_E_SUCCESS) {
|
||||
gnutls_x509_crt_deinit (der);
|
||||
return NM_CRYPTO_FILE_FORMAT_X509;
|
||||
}
|
||||
|
||||
err = gnutls_x509_crt_import (crt, &dt, GNUTLS_X509_FMT_DER);
|
||||
/* And PEM next */
|
||||
err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_PEM);
|
||||
gnutls_x509_crt_deinit (der);
|
||||
if (err == GNUTLS_E_SUCCESS)
|
||||
return NM_CRYPTO_FILE_FORMAT_X509;
|
||||
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
|
||||
_("Couldn't decode certificate: %s"),
|
||||
gnutls_strerror (err));
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_verify_pkcs12 (const GByteArray *data,
|
||||
const char *password,
|
||||
GError **error)
|
||||
{
|
||||
gnutls_pkcs12_t p12;
|
||||
gnutls_datum dt;
|
||||
gboolean success = FALSE;
|
||||
int err;
|
||||
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
dt.data = (unsigned char *) data->data;
|
||||
dt.size = data->len;
|
||||
|
||||
err = gnutls_pkcs12_init (&p12);
|
||||
if (err < 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
|
||||
_("Couldn't decode certificate: %s"),
|
||||
NM_CRYPTO_ERR_DECODE_FAILED,
|
||||
_("Couldn't initialize PKCS#12 decoder: %s"),
|
||||
gnutls_strerror (err));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gnutls_x509_crt_deinit (crt);
|
||||
return TRUE;
|
||||
/* DER first */
|
||||
err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_DER, 0);
|
||||
if (err < 0) {
|
||||
/* PEM next */
|
||||
err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_PEM, 0);
|
||||
if (err < 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("Couldn't decode PKCS#12 file: %s"),
|
||||
gnutls_strerror (err));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
err = gnutls_pkcs12_verify_mac (p12, password);
|
||||
if (err == GNUTLS_E_SUCCESS)
|
||||
success = TRUE;
|
||||
else {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
|
||||
_("Couldn't verify PKCS#12 file: %s"),
|
||||
gnutls_strerror (err));
|
||||
}
|
||||
|
||||
out:
|
||||
gnutls_pkcs12_deinit (p12);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
* (C) Copyright 2007 - 2008 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
|
|
@ -30,6 +32,9 @@
|
|||
#include <pkcs11t.h>
|
||||
#include <cert.h>
|
||||
#include <prerror.h>
|
||||
#include <p12.h>
|
||||
#include <ciferfam.h>
|
||||
#include <p12plcy.h>
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
|
|
@ -54,6 +59,14 @@ crypto_init (GError **error)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
|
||||
SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
|
||||
|
||||
initialized = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -125,8 +138,7 @@ crypto_md5_hash (const char *salt,
|
|||
char *
|
||||
crypto_decrypt (const char *cipher,
|
||||
int key_type,
|
||||
const char *data,
|
||||
gsize data_len,
|
||||
GByteArray *data,
|
||||
const char *iv,
|
||||
const gsize iv_len,
|
||||
const char *key,
|
||||
|
|
@ -159,7 +171,7 @@ crypto_decrypt (const char *cipher,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
output = g_malloc0 (data_len + 1);
|
||||
output = g_malloc0 (data->len + 1);
|
||||
if (!output) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_OUT_OF_MEMORY,
|
||||
|
|
@ -206,9 +218,9 @@ crypto_decrypt (const char *cipher,
|
|||
s = PK11_CipherOp (ctx,
|
||||
(unsigned char *) output,
|
||||
&tmp1_len,
|
||||
data_len,
|
||||
(unsigned char *) data,
|
||||
data_len);
|
||||
data->len,
|
||||
data->data,
|
||||
data->len);
|
||||
if (s != SECSuccess) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
|
||||
|
|
@ -220,7 +232,7 @@ crypto_decrypt (const char *cipher,
|
|||
s = PK11_DigestFinal (ctx,
|
||||
(unsigned char *) (output + tmp1_len),
|
||||
&tmp2_len,
|
||||
data_len - tmp1_len);
|
||||
data->len - tmp1_len);
|
||||
if (s != SECSuccess) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
|
||||
|
|
@ -229,7 +241,7 @@ crypto_decrypt (const char *cipher,
|
|||
goto out;
|
||||
}
|
||||
len = tmp1_len + tmp2_len;
|
||||
if (len > data_len)
|
||||
if (len > data->len)
|
||||
goto out;
|
||||
|
||||
*out_len = len;
|
||||
|
|
@ -249,7 +261,7 @@ out:
|
|||
if (!success) {
|
||||
if (output) {
|
||||
/* Don't expose key material */
|
||||
memset (output, 0, data_len);
|
||||
memset (output, 0, data->len);
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
}
|
||||
|
|
@ -257,23 +269,114 @@ out:
|
|||
return output;
|
||||
}
|
||||
|
||||
gboolean
|
||||
NMCryptoFileFormat
|
||||
crypto_verify_cert (const unsigned char *data,
|
||||
gsize len,
|
||||
GError **error)
|
||||
{
|
||||
CERTCertificate *cert;
|
||||
|
||||
/* Try DER/PEM first */
|
||||
cert = CERT_DecodeCertFromPackage ((char *) data, len);
|
||||
if (!cert) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
|
||||
_("Couldn't decode certificate: %d"),
|
||||
PORT_GetError());
|
||||
return FALSE;
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
CERT_DestroyCertificate (cert);
|
||||
return TRUE;
|
||||
CERT_DestroyCertificate (cert);
|
||||
return NM_CRYPTO_FILE_FORMAT_X509;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_verify_pkcs12 (const GByteArray *data,
|
||||
const char *password,
|
||||
GError **error)
|
||||
{
|
||||
SEC_PKCS12DecoderContext *p12ctx = NULL;
|
||||
SECItem pw = { 0 };
|
||||
PK11SlotInfo *slot = NULL;
|
||||
SECStatus s;
|
||||
char *ucs2_password;
|
||||
glong ucs2_chars = 0;
|
||||
guint16 *p;
|
||||
|
||||
if (error)
|
||||
g_return_val_if_fail (*error == NULL, FALSE);
|
||||
|
||||
/* PKCS#12 passwords are apparently UCS2 BIG ENDIAN, and NSS doesn't do
|
||||
* any conversions for us.
|
||||
*/
|
||||
if (password && strlen (password)) {
|
||||
ucs2_password = (char *) g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL);
|
||||
if (!ucs2_password || !ucs2_chars) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_INVALID_PASSWORD,
|
||||
_("Couldn't convert password to UCS2: %d"),
|
||||
PORT_GetError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ucs2_chars *= 2; /* convert # UCS2 characters -> bytes */
|
||||
pw.data = PORT_ZAlloc(ucs2_chars + 2);
|
||||
memcpy (pw.data, ucs2_password, ucs2_chars);
|
||||
pw.len = ucs2_chars + 2; /* include terminating NULL */
|
||||
|
||||
memset (ucs2_password, 0, ucs2_chars);
|
||||
g_free (ucs2_password);
|
||||
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++)
|
||||
*p = GUINT16_SWAP_LE_BE (*p);
|
||||
#endif
|
||||
} else {
|
||||
/* NULL password */
|
||||
pw.data = NULL;
|
||||
pw.len = 0;
|
||||
}
|
||||
|
||||
slot = PK11_GetInternalKeySlot();
|
||||
p12ctx = SEC_PKCS12DecoderStart (&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (!p12ctx) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_DECODE_FAILED,
|
||||
_("Couldn't initialize PKCS#12 decoder: %d"),
|
||||
PORT_GetError());
|
||||
goto error;
|
||||
}
|
||||
|
||||
s = SEC_PKCS12DecoderUpdate (p12ctx, data->data, data->len);
|
||||
if (s != SECSuccess) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
|
||||
_("Couldn't decode PKCS#12 file: %d"),
|
||||
PORT_GetError());
|
||||
goto error;
|
||||
}
|
||||
|
||||
s = SEC_PKCS12DecoderVerify (p12ctx);
|
||||
if (s != SECSuccess) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
|
||||
_("Couldn't verify PKCS#12 file: %d"),
|
||||
PORT_GetError());
|
||||
goto error;
|
||||
}
|
||||
|
||||
SEC_PKCS12DecoderFinish (p12ctx);
|
||||
SECITEM_ZfreeItem (&pw, PR_FALSE);
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
if (p12ctx)
|
||||
SEC_PKCS12DecoderFinish (p12ctx);
|
||||
|
||||
if (slot)
|
||||
PK11_FreeSlot(slot);
|
||||
|
||||
SECITEM_ZfreeItem (&pw, PR_FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,9 +52,13 @@ global:
|
|||
nm_setting_802_1x_set_phase2_client_cert_from_file;
|
||||
nm_setting_802_1x_get_phase2_private_key;
|
||||
nm_setting_802_1x_set_phase2_private_key_from_file;
|
||||
nm_setting_802_1x_get_phase2_private_key_password;
|
||||
nm_setting_802_1x_get_phase2_private_key_type;
|
||||
nm_setting_802_1x_get_pin;
|
||||
nm_setting_802_1x_get_private_key;
|
||||
nm_setting_802_1x_set_private_key_from_file;
|
||||
nm_setting_802_1x_get_private_key_password;
|
||||
nm_setting_802_1x_get_private_key_type;
|
||||
nm_setting_802_1x_get_psk;
|
||||
nm_setting_802_1x_get_type;
|
||||
nm_setting_802_1x_new;
|
||||
|
|
|
|||
|
|
@ -89,7 +89,9 @@ typedef struct {
|
|||
char *pin;
|
||||
char *psk;
|
||||
GByteArray *private_key;
|
||||
char *private_key_password;
|
||||
GByteArray *phase2_private_key;
|
||||
char *phase2_private_key_password;
|
||||
} NMSetting8021xPrivate;
|
||||
|
||||
enum {
|
||||
|
|
@ -110,7 +112,9 @@ enum {
|
|||
PROP_PHASE2_CLIENT_CERT,
|
||||
PROP_PASSWORD,
|
||||
PROP_PRIVATE_KEY,
|
||||
PROP_PRIVATE_KEY_PASSWORD,
|
||||
PROP_PHASE2_PRIVATE_KEY,
|
||||
PROP_PHASE2_PRIVATE_KEY_PASSWORD,
|
||||
PROP_PIN,
|
||||
PROP_PSK,
|
||||
|
||||
|
|
@ -226,18 +230,39 @@ nm_setting_802_1x_get_ca_path (NMSetting8021x *setting)
|
|||
gboolean
|
||||
nm_setting_802_1x_set_ca_cert_from_file (NMSetting8021x *self,
|
||||
const char *filename,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err)
|
||||
{
|
||||
NMSetting8021xPrivate *priv;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
if (out_ck_type)
|
||||
g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
|
||||
|
||||
priv = NM_SETTING_802_1X_GET_PRIVATE (self);
|
||||
if (priv->ca_cert)
|
||||
g_byte_array_free (priv->ca_cert, TRUE);
|
||||
|
||||
priv->ca_cert = crypto_load_and_verify_certificate (filename, err);
|
||||
priv->ca_cert = crypto_load_and_verify_certificate (filename, &format, err);
|
||||
if (priv->ca_cert) {
|
||||
/* wpa_supplicant can only use raw x509 CA certs */
|
||||
switch (format) {
|
||||
case NM_CRYPTO_FILE_FORMAT_X509:
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_X509;
|
||||
break;
|
||||
default:
|
||||
g_byte_array_free (priv->ca_cert, TRUE);
|
||||
priv->ca_cert = NULL;
|
||||
g_set_error (err,
|
||||
NM_SETTING_802_1X_ERROR,
|
||||
NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
|
||||
NM_SETTING_802_1X_CA_CERT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return priv->ca_cert != NULL;
|
||||
}
|
||||
|
|
@ -253,18 +278,42 @@ nm_setting_802_1x_get_client_cert (NMSetting8021x *setting)
|
|||
gboolean
|
||||
nm_setting_802_1x_set_client_cert_from_file (NMSetting8021x *self,
|
||||
const char *filename,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err)
|
||||
{
|
||||
NMSetting8021xPrivate *priv;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
if (out_ck_type)
|
||||
g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
|
||||
|
||||
priv = NM_SETTING_802_1X_GET_PRIVATE (self);
|
||||
if (priv->client_cert)
|
||||
g_byte_array_free (priv->client_cert, TRUE);
|
||||
|
||||
priv->client_cert = crypto_load_and_verify_certificate (filename, err);
|
||||
priv->client_cert = crypto_load_and_verify_certificate (filename, &format, err);
|
||||
if (priv->client_cert) {
|
||||
switch (format) {
|
||||
case NM_CRYPTO_FILE_FORMAT_X509:
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_X509;
|
||||
break;
|
||||
case NM_CRYPTO_FILE_FORMAT_PKCS12:
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
|
||||
break;
|
||||
default:
|
||||
g_byte_array_free (priv->client_cert, TRUE);
|
||||
priv->client_cert = NULL;
|
||||
g_set_error (err,
|
||||
NM_SETTING_802_1X_ERROR,
|
||||
NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
|
||||
NM_SETTING_802_1X_CLIENT_CERT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return priv->client_cert != NULL;
|
||||
}
|
||||
|
|
@ -328,21 +377,41 @@ nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting)
|
|||
gboolean
|
||||
nm_setting_802_1x_set_phase2_ca_cert_from_file (NMSetting8021x *self,
|
||||
const char *filename,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err)
|
||||
{
|
||||
NMSetting8021xPrivate *priv;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
if (out_ck_type)
|
||||
g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
|
||||
|
||||
priv = NM_SETTING_802_1X_GET_PRIVATE (self);
|
||||
if (priv->phase2_ca_cert)
|
||||
g_byte_array_free (priv->phase2_ca_cert, TRUE);
|
||||
|
||||
priv->phase2_ca_cert = crypto_load_and_verify_certificate (filename, err);
|
||||
priv->phase2_ca_cert = crypto_load_and_verify_certificate (filename, &format, err);
|
||||
if (priv->phase2_ca_cert) {
|
||||
/* wpa_supplicant can only use X509 CA certs */
|
||||
switch (format) {
|
||||
case NM_CRYPTO_FILE_FORMAT_X509:
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_X509;
|
||||
break;
|
||||
default:
|
||||
g_byte_array_free (priv->phase2_ca_cert, TRUE);
|
||||
priv->phase2_ca_cert = NULL;
|
||||
g_set_error (err,
|
||||
NM_SETTING_802_1X_ERROR,
|
||||
NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
|
||||
NM_SETTING_802_1X_PHASE2_CA_CERT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return priv->phase2_ca_cert != NULL;
|
||||
|
||||
}
|
||||
|
||||
const GByteArray *
|
||||
|
|
@ -356,18 +425,43 @@ nm_setting_802_1x_get_phase2_client_cert (NMSetting8021x *setting)
|
|||
gboolean
|
||||
nm_setting_802_1x_set_phase2_client_cert_from_file (NMSetting8021x *self,
|
||||
const char *filename,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err)
|
||||
{
|
||||
NMSetting8021xPrivate *priv;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
if (out_ck_type)
|
||||
g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
|
||||
|
||||
priv = NM_SETTING_802_1X_GET_PRIVATE (self);
|
||||
if (priv->phase2_client_cert)
|
||||
g_byte_array_free (priv->phase2_client_cert, TRUE);
|
||||
|
||||
priv->phase2_client_cert = crypto_load_and_verify_certificate (filename, err);
|
||||
priv->phase2_client_cert = crypto_load_and_verify_certificate (filename, &format, err);
|
||||
if (priv->phase2_client_cert) {
|
||||
/* Only X509 client certs should be used; not pkcs#12 */
|
||||
switch (format) {
|
||||
case NM_CRYPTO_FILE_FORMAT_X509:
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_X509;
|
||||
break;
|
||||
case NM_CRYPTO_FILE_FORMAT_PKCS12:
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
|
||||
break;
|
||||
default:
|
||||
g_byte_array_free (priv->phase2_client_cert, TRUE);
|
||||
priv->phase2_client_cert = NULL;
|
||||
g_set_error (err,
|
||||
NM_SETTING_802_1X_ERROR,
|
||||
NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
|
||||
NM_SETTING_802_1X_CLIENT_CERT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return priv->phase2_client_cert != NULL;
|
||||
}
|
||||
|
|
@ -404,18 +498,29 @@ nm_setting_802_1x_get_private_key (NMSetting8021x *setting)
|
|||
return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key;
|
||||
}
|
||||
|
||||
const char *
|
||||
nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting)
|
||||
{
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
|
||||
|
||||
return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key_password;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_setting_802_1x_set_private_key_from_file (NMSetting8021x *self,
|
||||
const char *filename,
|
||||
const char *password,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err)
|
||||
{
|
||||
NMSetting8021xPrivate *priv;
|
||||
guint32 ignore;
|
||||
NMCryptoKeyType ignore = NM_CRYPTO_KEY_TYPE_UNKNOWN;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
g_return_val_if_fail (password != NULL, FALSE);
|
||||
if (out_ck_type)
|
||||
g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
|
||||
|
||||
priv = NM_SETTING_802_1X_GET_PRIVATE (self);
|
||||
if (priv->private_key) {
|
||||
|
|
@ -424,11 +529,61 @@ nm_setting_802_1x_set_private_key_from_file (NMSetting8021x *self,
|
|||
g_byte_array_free (priv->private_key, TRUE);
|
||||
}
|
||||
|
||||
priv->private_key = crypto_get_private_key (filename, password, &ignore, err);
|
||||
g_free (priv->private_key_password);
|
||||
priv->private_key_password = NULL;
|
||||
|
||||
priv->private_key = crypto_get_private_key (filename, password, &ignore, &format, err);
|
||||
if (priv->private_key) {
|
||||
switch (format) {
|
||||
case NM_CRYPTO_FILE_FORMAT_RAW_KEY:
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_RAW_KEY;
|
||||
break;
|
||||
case NM_CRYPTO_FILE_FORMAT_PKCS12:
|
||||
// FIXME: use secure memory
|
||||
priv->private_key_password = g_strdup (password);
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
/* As required by NM, set the client-cert property to the same PKCS#12 data */
|
||||
if (priv->client_cert)
|
||||
g_byte_array_free (priv->client_cert, TRUE);
|
||||
|
||||
priv->client_cert = g_byte_array_sized_new (priv->private_key->len);
|
||||
g_byte_array_append (priv->client_cert, priv->private_key->data, priv->private_key->len);
|
||||
} else {
|
||||
/* As a special case for private keys, even if the decrypt fails,
|
||||
* return the key's file type.
|
||||
*/
|
||||
if (out_ck_type && crypto_is_pkcs12_file (filename))
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
|
||||
}
|
||||
|
||||
return priv->private_key != NULL;
|
||||
}
|
||||
|
||||
NMSetting8021xCKType
|
||||
nm_setting_802_1x_get_private_key_type (NMSetting8021x *setting)
|
||||
{
|
||||
NMSetting8021xPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_TYPE_UNKNOWN);
|
||||
priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
|
||||
|
||||
if (!priv->private_key)
|
||||
return NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
|
||||
|
||||
if (crypto_is_pkcs12_data (priv->private_key))
|
||||
return NM_SETTING_802_1X_CK_TYPE_PKCS12;
|
||||
|
||||
return NM_SETTING_802_1X_CK_TYPE_X509;
|
||||
}
|
||||
|
||||
const GByteArray *
|
||||
nm_setting_802_1x_get_phase2_private_key (NMSetting8021x *setting)
|
||||
{
|
||||
|
|
@ -437,18 +592,29 @@ nm_setting_802_1x_get_phase2_private_key (NMSetting8021x *setting)
|
|||
return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key;
|
||||
}
|
||||
|
||||
const char *
|
||||
nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting)
|
||||
{
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
|
||||
|
||||
return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key_password;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_setting_802_1x_set_phase2_private_key_from_file (NMSetting8021x *self,
|
||||
const char *filename,
|
||||
const char *password,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err)
|
||||
{
|
||||
NMSetting8021xPrivate *priv;
|
||||
guint32 ignore;
|
||||
NMCryptoKeyType ignore = NM_CRYPTO_KEY_TYPE_UNKNOWN;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
g_return_val_if_fail (password != NULL, FALSE);
|
||||
if (out_ck_type)
|
||||
g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
|
||||
|
||||
priv = NM_SETTING_802_1X_GET_PRIVATE (self);
|
||||
if (priv->phase2_private_key) {
|
||||
|
|
@ -457,11 +623,61 @@ nm_setting_802_1x_set_phase2_private_key_from_file (NMSetting8021x *self,
|
|||
g_byte_array_free (priv->phase2_private_key, TRUE);
|
||||
}
|
||||
|
||||
priv->phase2_private_key = crypto_get_private_key (filename, password, &ignore, err);
|
||||
g_free (priv->phase2_private_key_password);
|
||||
priv->phase2_private_key_password = NULL;
|
||||
|
||||
priv->phase2_private_key = crypto_get_private_key (filename, password, &ignore, &format, err);
|
||||
if (priv->phase2_private_key) {
|
||||
switch (format) {
|
||||
case NM_CRYPTO_FILE_FORMAT_RAW_KEY:
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_RAW_KEY;
|
||||
break;
|
||||
case NM_CRYPTO_FILE_FORMAT_PKCS12:
|
||||
// FIXME: use secure memory
|
||||
priv->phase2_private_key_password = g_strdup (password);
|
||||
if (out_ck_type)
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
/* As required by NM, set the client-cert property to the same PKCS#12 data */
|
||||
if (priv->phase2_client_cert)
|
||||
g_byte_array_free (priv->phase2_client_cert, TRUE);
|
||||
|
||||
priv->phase2_client_cert = g_byte_array_sized_new (priv->phase2_private_key->len);
|
||||
g_byte_array_append (priv->phase2_client_cert, priv->phase2_private_key->data, priv->phase2_private_key->len);
|
||||
} else {
|
||||
/* As a special case for private keys, even if the decrypt fails,
|
||||
* return the key's file type.
|
||||
*/
|
||||
if (out_ck_type && crypto_is_pkcs12_file (filename))
|
||||
*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
|
||||
}
|
||||
|
||||
return priv->phase2_private_key != NULL;
|
||||
}
|
||||
|
||||
NMSetting8021xCKType
|
||||
nm_setting_802_1x_get_phase2_private_key_type (NMSetting8021x *setting)
|
||||
{
|
||||
NMSetting8021xPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_TYPE_UNKNOWN);
|
||||
priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
|
||||
|
||||
if (!priv->phase2_private_key)
|
||||
return NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
|
||||
|
||||
if (crypto_is_pkcs12_data (priv->phase2_private_key))
|
||||
return NM_SETTING_802_1X_CK_TYPE_PKCS12;
|
||||
|
||||
return NM_SETTING_802_1X_CK_TYPE_X509;
|
||||
}
|
||||
|
||||
static void
|
||||
need_secrets_password (NMSetting8021x *self,
|
||||
GPtrArray *secrets,
|
||||
|
|
@ -484,6 +700,30 @@ need_secrets_sim (NMSetting8021x *self,
|
|||
g_ptr_array_add (secrets, NM_SETTING_802_1X_PIN);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
need_private_key_password (GByteArray *key, const char *password)
|
||||
{
|
||||
GError *error = NULL;
|
||||
gboolean needed = TRUE;
|
||||
|
||||
/* See if a private key password is needed, which basically is whether
|
||||
* or not the private key is a PKCS#12 file or not, since PKCS#1 files
|
||||
* are decrypted by the settings service.
|
||||
*/
|
||||
if (!crypto_is_pkcs12_data (key))
|
||||
return FALSE;
|
||||
|
||||
if (crypto_verify_pkcs12 (key, password, &error))
|
||||
return FALSE; /* pkcs#12 validation successful */
|
||||
|
||||
/* If the error was a decryption error then a password is needed */
|
||||
if (!error || g_error_matches (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED))
|
||||
needed = TRUE;
|
||||
|
||||
g_clear_error (&error);
|
||||
return needed;
|
||||
}
|
||||
|
||||
static void
|
||||
need_secrets_tls (NMSetting8021x *self,
|
||||
GPtrArray *secrets,
|
||||
|
|
@ -492,13 +732,15 @@ need_secrets_tls (NMSetting8021x *self,
|
|||
NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
|
||||
|
||||
if (phase2) {
|
||||
if ( priv->phase2_client_cert
|
||||
&& (!priv->phase2_private_key || !priv->phase2_private_key->len))
|
||||
if (!priv->phase2_private_key || !priv->phase2_private_key->len)
|
||||
g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
|
||||
else if (need_private_key_password (priv->phase2_private_key, priv->phase2_private_key_password))
|
||||
g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
|
||||
} else {
|
||||
if (priv->client_cert
|
||||
&& (!priv->private_key || !priv->private_key->len))
|
||||
if (!priv->private_key || !priv->private_key->len)
|
||||
g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY);
|
||||
else if (need_private_key_password (priv->private_key, priv->private_key_password))
|
||||
g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -521,6 +763,27 @@ verify_tls (NMSetting8021x *self, gboolean phase2, GError **error)
|
|||
NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* If the private key is PKCS#12, check that it matches the client cert */
|
||||
if (priv->phase2_private_key && crypto_is_pkcs12_data (priv->phase2_private_key)) {
|
||||
if (priv->phase2_private_key->len != priv->phase2_client_cert->len) {
|
||||
g_set_error (error,
|
||||
NM_SETTING_802_1X_ERROR,
|
||||
NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
|
||||
NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (memcmp (priv->phase2_private_key->data,
|
||||
priv->phase2_client_cert->data,
|
||||
priv->phase2_private_key->len)) {
|
||||
g_set_error (error,
|
||||
NM_SETTING_802_1X_ERROR,
|
||||
NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
|
||||
NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!priv->client_cert) {
|
||||
g_set_error (error,
|
||||
|
|
@ -535,6 +798,27 @@ verify_tls (NMSetting8021x *self, gboolean phase2, GError **error)
|
|||
NM_SETTING_802_1X_CLIENT_CERT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* If the private key is PKCS#12, check that it matches the client cert */
|
||||
if (priv->private_key && crypto_is_pkcs12_data (priv->private_key)) {
|
||||
if (priv->private_key->len != priv->client_cert->len) {
|
||||
g_set_error (error,
|
||||
NM_SETTING_802_1X_ERROR,
|
||||
NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
|
||||
NM_SETTING_802_1X_CLIENT_CERT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (memcmp (priv->private_key->data,
|
||||
priv->client_cert->data,
|
||||
priv->private_key->len)) {
|
||||
g_set_error (error,
|
||||
NM_SETTING_802_1X_ERROR,
|
||||
NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
|
||||
NM_SETTING_802_1X_CLIENT_CERT);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
|
@ -856,12 +1140,14 @@ finalize (GObject *object)
|
|||
g_byte_array_free (priv->client_cert, TRUE);
|
||||
if (priv->private_key)
|
||||
g_byte_array_free (priv->private_key, TRUE);
|
||||
g_free (priv->private_key_password);
|
||||
if (priv->phase2_ca_cert)
|
||||
g_byte_array_free (priv->phase2_ca_cert, TRUE);
|
||||
if (priv->phase2_client_cert)
|
||||
g_byte_array_free (priv->phase2_client_cert, TRUE);
|
||||
if (priv->phase2_private_key)
|
||||
g_byte_array_free (priv->phase2_private_key, TRUE);
|
||||
g_free (priv->phase2_private_key_password);
|
||||
|
||||
G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object);
|
||||
}
|
||||
|
|
@ -943,11 +1229,19 @@ set_property (GObject *object, guint prop_id,
|
|||
g_byte_array_free (priv->private_key, TRUE);
|
||||
priv->private_key = g_value_dup_boxed (value);
|
||||
break;
|
||||
case PROP_PRIVATE_KEY_PASSWORD:
|
||||
g_free (priv->private_key_password);
|
||||
priv->private_key_password = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_PHASE2_PRIVATE_KEY:
|
||||
if (priv->phase2_private_key)
|
||||
g_byte_array_free (priv->phase2_private_key, TRUE);
|
||||
priv->phase2_private_key = g_value_dup_boxed (value);
|
||||
break;
|
||||
case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
|
||||
g_free (priv->phase2_private_key_password);
|
||||
priv->phase2_private_key_password = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
|
@ -1010,9 +1304,15 @@ get_property (GObject *object, guint prop_id,
|
|||
case PROP_PRIVATE_KEY:
|
||||
g_value_set_boxed (value, priv->private_key);
|
||||
break;
|
||||
case PROP_PRIVATE_KEY_PASSWORD:
|
||||
g_value_set_string (value, priv->private_key_password);
|
||||
break;
|
||||
case PROP_PHASE2_PRIVATE_KEY:
|
||||
g_value_set_boxed (value, priv->phase2_private_key);
|
||||
break;
|
||||
case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
|
||||
g_value_set_string (value, priv->phase2_private_key_password);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
|
@ -1165,6 +1465,14 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
|
|||
DBUS_TYPE_G_UCHAR_ARRAY,
|
||||
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
|
||||
|
||||
g_object_class_install_property
|
||||
(object_class, PROP_PRIVATE_KEY_PASSWORD,
|
||||
g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
|
||||
"Private key password",
|
||||
"Private key password",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
|
||||
|
||||
g_object_class_install_property
|
||||
(object_class, PROP_PHASE2_PRIVATE_KEY,
|
||||
_nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
|
||||
|
|
@ -1173,6 +1481,14 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
|
|||
DBUS_TYPE_G_UCHAR_ARRAY,
|
||||
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
|
||||
|
||||
g_object_class_install_property
|
||||
(object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD,
|
||||
g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
|
||||
"Phase2 private key password",
|
||||
"Phase2 private key password",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
|
||||
|
||||
/* Initialize crypto lbrary. */
|
||||
if (!nm_utils_init (&error)) {
|
||||
g_warning ("Couldn't initilize nm-utils/crypto system: %d %s",
|
||||
|
|
|
|||
|
|
@ -30,6 +30,13 @@
|
|||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum {
|
||||
NM_SETTING_802_1X_CK_TYPE_UNKNOWN = 0,
|
||||
NM_SETTING_802_1X_CK_TYPE_X509,
|
||||
NM_SETTING_802_1X_CK_TYPE_RAW_KEY,
|
||||
NM_SETTING_802_1X_CK_TYPE_PKCS12
|
||||
} NMSetting8021xCKType;
|
||||
|
||||
#define NM_TYPE_SETTING_802_1X (nm_setting_802_1x_get_type ())
|
||||
#define NM_SETTING_802_1X(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_802_1X, NMSetting8021x))
|
||||
#define NM_SETTING_802_1X_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_802_1X, NMSetting8021xClass))
|
||||
|
|
@ -69,7 +76,9 @@ GQuark nm_setting_802_1x_error_quark (void);
|
|||
#define NM_SETTING_802_1X_PHASE2_CLIENT_CERT "phase2-client-cert"
|
||||
#define NM_SETTING_802_1X_PASSWORD "password"
|
||||
#define NM_SETTING_802_1X_PRIVATE_KEY "private-key"
|
||||
#define NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD "private-key-password"
|
||||
#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY "phase2-private-key"
|
||||
#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD "phase2-private-key-password"
|
||||
#define NM_SETTING_802_1X_PIN "pin"
|
||||
#define NM_SETTING_802_1X_PSK "psk"
|
||||
|
||||
|
|
@ -99,11 +108,13 @@ const GByteArray *nm_setting_802_1x_get_ca_cert (NMSetting8
|
|||
const char * nm_setting_802_1x_get_ca_path (NMSetting8021x *setting);
|
||||
gboolean nm_setting_802_1x_set_ca_cert_from_file (NMSetting8021x *setting,
|
||||
const char *filename,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err);
|
||||
|
||||
const GByteArray *nm_setting_802_1x_get_client_cert (NMSetting8021x *setting);
|
||||
gboolean nm_setting_802_1x_set_client_cert_from_file (NMSetting8021x *setting,
|
||||
const char *filename,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err);
|
||||
|
||||
const char * nm_setting_802_1x_get_phase1_peapver (NMSetting8021x *setting);
|
||||
|
|
@ -120,11 +131,13 @@ const GByteArray *nm_setting_802_1x_get_phase2_ca_cert (NMSetting8
|
|||
const char * nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting);
|
||||
gboolean nm_setting_802_1x_set_phase2_ca_cert_from_file (NMSetting8021x *setting,
|
||||
const char *filename,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err);
|
||||
|
||||
const GByteArray *nm_setting_802_1x_get_phase2_client_cert (NMSetting8021x *setting);
|
||||
gboolean nm_setting_802_1x_set_phase2_client_cert_from_file (NMSetting8021x *setting,
|
||||
const char *filename,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err);
|
||||
|
||||
const char * nm_setting_802_1x_get_password (NMSetting8021x *setting);
|
||||
|
|
@ -133,17 +146,39 @@ const char * nm_setting_802_1x_get_pin (NMSetting8
|
|||
|
||||
const char * nm_setting_802_1x_get_psk (NMSetting8021x *setting);
|
||||
|
||||
/* PRIVATE KEY NOTE: when PKCS#12 private keys are used, the PKCS#12 data must
|
||||
* be passed to NetworkManager as PKCS#12 (ie, shrouded). In this case, the
|
||||
* private key password must also be passed to NetworkManager, and the
|
||||
* appropriate "client-cert" (or "phase2-client-cert") property of the
|
||||
* NMSetting8021x object must also contain the exact same PKCS#12 data that the
|
||||
* private key will when NetworkManager requests secrets.
|
||||
*
|
||||
* When OpenSSL-derived "traditional" format (ie S/MIME style, not PKCS#8) RSA
|
||||
* and DSA keys are used, they must passed to NetworkManager completely
|
||||
* decrypted because the OpenSSL "traditional" format is non-standard and is not
|
||||
* complete enough for all crypto libraries to use. Thus, for OpenSSL
|
||||
* "traditional" format keys, the private key password is not passed to
|
||||
* NetworkManager, and the appropriate "client-cert" (or "phase2-client-cert")
|
||||
* property of the NMSetting8021x object must be a valid client certificate.
|
||||
*/
|
||||
|
||||
const GByteArray *nm_setting_802_1x_get_private_key (NMSetting8021x *setting);
|
||||
const char * nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting);
|
||||
gboolean nm_setting_802_1x_set_private_key_from_file (NMSetting8021x *setting,
|
||||
const char *filename,
|
||||
const char *password,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err);
|
||||
NMSetting8021xCKType nm_setting_802_1x_get_private_key_type (NMSetting8021x *setting);
|
||||
|
||||
const GByteArray *nm_setting_802_1x_get_phase2_private_key (NMSetting8021x *setting);
|
||||
const char * nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting);
|
||||
gboolean nm_setting_802_1x_set_phase2_private_key_from_file (NMSetting8021x *setting,
|
||||
const char *filename,
|
||||
const char *password,
|
||||
NMSetting8021xCKType *out_ck_type,
|
||||
GError **err);
|
||||
NMSetting8021xCKType nm_setting_802_1x_get_phase2_private_key_type (NMSetting8021x *setting);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,6 @@ usage (const char *prgname)
|
|||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
guint32 key_type = 0;
|
||||
int mode = 0;
|
||||
const char *file;
|
||||
GError *error = NULL;
|
||||
|
|
@ -140,26 +139,54 @@ int main (int argc, char **argv)
|
|||
|
||||
if (mode == MODE_CERT) {
|
||||
GByteArray *array;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
array = crypto_load_and_verify_certificate (file, &error);
|
||||
array = crypto_load_and_verify_certificate (file, &format, &error);
|
||||
if (!array) {
|
||||
g_warning ("Couldn't read certificate file '%s': %d %s",
|
||||
file, error->code, error->message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case NM_CRYPTO_FILE_FORMAT_X509:
|
||||
g_message ("Format: pkcs#1");
|
||||
break;
|
||||
case NM_CRYPTO_FILE_FORMAT_PKCS12:
|
||||
g_message ("Format: pkcs#12");
|
||||
break;
|
||||
default:
|
||||
g_message ("Format: unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
g_byte_array_free (array, TRUE);
|
||||
} else if (mode == MODE_KEY) {
|
||||
NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
const char *password = argv[3];
|
||||
GByteArray *array;
|
||||
|
||||
array = crypto_get_private_key (file, password, &key_type, &error);
|
||||
array = crypto_get_private_key (file, password, &key_type, &format, &error);
|
||||
if (!array) {
|
||||
g_warning ("Couldn't read key file '%s': %d %s",
|
||||
file, error->code, error->message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dump_key_to_pem ((const char *) array->data, array->len, key_type);
|
||||
switch (format) {
|
||||
case NM_CRYPTO_FILE_FORMAT_RAW_KEY:
|
||||
g_message ("Original format: pkcs#1\n");
|
||||
dump_key_to_pem ((const char *) array->data, array->len, key_type);
|
||||
break;
|
||||
case NM_CRYPTO_FILE_FORMAT_PKCS12:
|
||||
g_message ("Original format: pkcs#12");
|
||||
break;
|
||||
default:
|
||||
g_message ("Original format: unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
g_byte_array_free (array, TRUE);
|
||||
} else {
|
||||
g_assert_not_reached ();
|
||||
|
|
|
|||
|
|
@ -561,10 +561,10 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
|
|||
gboolean wired)
|
||||
{
|
||||
NMSupplicantConfigPrivate *priv;
|
||||
char * value;
|
||||
char *value, *tmp;
|
||||
gboolean success;
|
||||
GString *phase1, *phase2;
|
||||
char *tmp;
|
||||
const GByteArray *array;
|
||||
|
||||
g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
|
||||
g_return_val_if_fail (setting != NULL, FALSE);
|
||||
|
|
@ -620,18 +620,41 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
|
|||
ADD_STRING_VAL (phase2->str, "phase2", FALSE, FALSE, FALSE);
|
||||
g_string_free (phase2, TRUE);
|
||||
|
||||
/* Private key passwords are never passed to wpa_supplicant because the
|
||||
* user agent is responsible for decoding and decrypting the private key,
|
||||
* and file paths are never passed to wpa_supplicant to ensure that
|
||||
* the supplicant can be locked down and doesn't try to read stuff from
|
||||
* all over the drive.
|
||||
*/
|
||||
ADD_BLOB_VAL (nm_setting_802_1x_get_ca_cert (setting), "ca_cert", connection_uid);
|
||||
ADD_BLOB_VAL (nm_setting_802_1x_get_client_cert (setting), "client_cert", connection_uid);
|
||||
ADD_BLOB_VAL (nm_setting_802_1x_get_private_key (setting), "private_key", connection_uid);
|
||||
|
||||
array = nm_setting_802_1x_get_private_key (setting);
|
||||
if (array) {
|
||||
ADD_BLOB_VAL (array, "private_key", connection_uid);
|
||||
|
||||
switch (nm_setting_802_1x_get_private_key_type (setting)) {
|
||||
case NM_SETTING_802_1X_CK_TYPE_PKCS12:
|
||||
/* Only add the private key password for PKCS#12 keys */
|
||||
ADD_STRING_VAL (nm_setting_802_1x_get_private_key_password (setting), "private_key_passwd", FALSE, FALSE, TRUE);
|
||||
break;
|
||||
default:
|
||||
/* Only add the client cert if the private key is not PKCS#12 */
|
||||
ADD_BLOB_VAL (nm_setting_802_1x_get_client_cert (setting), "client_cert", connection_uid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_ca_cert (setting), "ca_cert2", connection_uid);
|
||||
ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_client_cert (setting), "client_cert2", connection_uid);
|
||||
ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_private_key (setting), "private_key2", connection_uid);
|
||||
|
||||
array = nm_setting_802_1x_get_phase2_private_key (setting);
|
||||
if (array) {
|
||||
ADD_BLOB_VAL (array, "private_key2", connection_uid);
|
||||
|
||||
switch (nm_setting_802_1x_get_phase2_private_key_type (setting)) {
|
||||
case NM_SETTING_802_1X_CK_TYPE_PKCS12:
|
||||
/* Only add the private key password for PKCS#12 keys */
|
||||
ADD_STRING_VAL (nm_setting_802_1x_get_phase2_private_key_password (setting), "private_key2_passwd", FALSE, FALSE, TRUE);
|
||||
break;
|
||||
default:
|
||||
/* Only add the client cert if the private key is not PKCS#12 */
|
||||
ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_client_cert (setting), "client_cert2", connection_uid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ADD_STRING_VAL (nm_setting_802_1x_get_identity (setting), "identity", FALSE, FALSE, FALSE);
|
||||
ADD_STRING_VAL (nm_setting_802_1x_get_anonymous_identity (setting), "anonymous_identity", FALSE, FALSE, FALSE);
|
||||
|
|
|
|||
|
|
@ -104,12 +104,14 @@ static const struct Opt opt_table[] = {
|
|||
{ "ca_cert", TYPE_BYTES, 0, 65536, FALSE, NULL },
|
||||
{ "client_cert", TYPE_BYTES, 0, 65536, FALSE, NULL },
|
||||
{ "private_key", TYPE_BYTES, 0, 65536, FALSE, NULL },
|
||||
{ "private_key_passwd", TYPE_BYTES, 0, 1024, FALSE, NULL },
|
||||
{ "phase1", TYPE_KEYWORD, 0, 0, TRUE, phase1_allowed },
|
||||
{ "phase2", TYPE_KEYWORD, 0, 0, TRUE, phase2_allowed },
|
||||
{ "anonymous_identity", TYPE_BYTES, 0, 0, FALSE, NULL },
|
||||
{ "ca_cert2", TYPE_BYTES, 0, 65536, FALSE, NULL },
|
||||
{ "client_cert2", TYPE_BYTES, 0, 65536, FALSE, NULL },
|
||||
{ "private_key2", TYPE_BYTES, 0, 65536, FALSE, NULL },
|
||||
{ "private_key2_passwd",TYPE_BYTES, 0, 1024, FALSE, NULL },
|
||||
{ "pin", TYPE_BYTES, 0, 0, FALSE, NULL },
|
||||
{ "pcsc", TYPE_BYTES, 0, 0, FALSE, NULL },
|
||||
{ "nai", TYPE_BYTES, 0, 0, FALSE, NULL },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue