-
Notifications
You must be signed in to change notification settings - Fork 4
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
Disable loading of unprotected credentials from PKCS#12 files. #31
base: master
Are you sure you want to change the base?
Disable loading of unprotected credentials from PKCS#12 files. #31
Conversation
- containers with **empty password** are **rejected** if a nonempty password is provided as parameter (i.e. the empty password can no longer be used to circumvent UTA-protection) - containers that don't have private keys encrypted with the same algorithm that is used by "storage" functions (e.g. `CREDENTIALS_save_dv`) are **rejected**
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update: Meanwhile I learned from Benjamin what that background of these checks is. So okay to have them, but not by default.
Sorry to say, but I see little sense in the two restrictions added in this PR.
What does it help if an application refuses to load certain types of PKCS#12 files?
The files are in storage anyway, so an attacker can just use them and load their contents using some other software (such as vanilla OpenSSL), learn their contents, and potentially do havoc with them.
Moreover, the new checks are are not acceptable as mandatory checks because they (needlessly) restrict the general usability of PKCS#12 loading, so if you really want to keep them, make them somehow optional.
@@ -371,32 +510,37 @@ static int load_pkcs12(BIO* in, OPTIONAL const char* desc, OPTIONAL pem_password | |||
/* See if an empty password will do */ | |||
if(PKCS12_verify_mac(p12, "", 0) not_eq 0 or PKCS12_verify_mac(p12, 0, 0) not_eq 0) | |||
{ | |||
LOG(FL_WARN, "Unencrypted PKCS#12 file%s%s", for_str, desc_str); | |||
pass = ""; | |||
LOG(FL_WARN, "Rejecting unencrypted PKCS#12 file%s%s", for_str, desc_str); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FL_WARN -> FL_ERR, if this case is really considered an error, but see below.
LOG(FL_WARN, "Unencrypted PKCS#12 file%s%s", for_str, desc_str); | ||
pass = ""; | ||
LOG(FL_WARN, "Rejecting unencrypted PKCS#12 file%s%s", for_str, desc_str); | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR does not really do what is given it is description.
It rejects PKCS#12 not having a password even if the password provided by the caller is empty or no password was given by the caller. This is too much of a restriction.
In the cases just described, you can give a warning if wanted, but do not stop with an error. Otherwise, this library can no more be used to handle PKCS#12 files that (possibly for a good reason) are not password-protected.
|
||
pass = tpass; | ||
|
||
if(0 is_eq algo_allowed(p12, pass, len, NID_aes_256_cbc)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder about the strange - and not very readable - way of checking a Boolean (return) value here and at many other places. Better use if(not algo_allowed(...))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you hard-coded NID_aes_256_cbc
, but our PR description says
"the same algorithm that is used by "storage" functions (e.g. CREDENTIALS_save_dv)".
Instead, you should define a symbolic name for this algorithm and use it in the code where appropriate in order to prevent (future) inconsistencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The algo check must not be made mandatory because otherwise general usability of PKCS#12 loading is hampered.
desired algorithm. */ | ||
/* Iterates over all keybags in the file but only looks inside Shrouded keybags. Returns 1 if the desired algorithm | ||
and no other was found, 0 otherwise. */ | ||
static int algo_allowed(const PKCS12* p12, const char* pass, int passlen, int desired_algo_nid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better: static bool algo_allowed(...)
to point out that the result is Boolean
and use false
and true
rather than 0
and 1
for Boolean values below.
{ | ||
continue; | ||
} | ||
if(!bags) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better: 0 is_eq bags
found_desired_algo = 1; | ||
continue; | ||
} | ||
sk_PKCS7_pop_free(auth_safes, PKCS7_free); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simpler: goto line 482.
@@ -323,6 +323,11 @@ typedef struct pw_cb_data | |||
|
|||
static int password_callback(char* buf, int bufsiz, int verify, void* cb_tmp) | |||
{ | |||
if(0 == buf || bufsiz <= 0 || 0 == cb_tmp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why you you forbid 0 == cb_tmp
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better move the checks on buf
and bufsiz
below into if(password not_eq 0) {
because they are needed only in this case.
} | ||
|
||
|
||
static int bag_get_pkey_algo(const PKCS12_SAFEBAG *bag, const char *pass, int passlen) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pass
and passlen
are unused, so remove them from the function parameter list.
} | ||
|
||
|
||
static int bags_get_pkey_algo(const STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass, int passlen, int desired_algo_nid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pass
and passlen
are effectively unused, so remove them from the function parameter list.
return encnid; | ||
} | ||
|
||
return NID_undef; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it really justified to return ID_undef
for all other cases, i.e. pbenid != NID_pbes2
?
If you, add a comment why.
I suggest making the implementation and use of the two new checks on PKCS#12 files dependent on a compile-time flag that is not set by default but set for S2L2 Linux, e.g.
|
password is provided as parameter (i.e. the empty password can no longer
be used to circumvent UTA-protection)
that is used by "storage" functions (e.g.
CREDENTIALS_save_dv
) arerejected