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

Disable loading of unprotected credentials from PKCS#12 files. #31

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

martin-barta-sie
Copy link
Contributor

  • 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

- 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**
Copy link
Member

@DDvO DDvO left a 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);
Copy link
Member

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;
Copy link
Member

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))
Copy link
Member

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(...))

Copy link
Member

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.

Copy link
Member

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)
Copy link
Member

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)
Copy link
Member

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);
Copy link
Member

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)
Copy link
Member

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 ?

Copy link
Member

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)
Copy link
Member

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)
Copy link
Member

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;
Copy link
Member

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.

@DDvO
Copy link
Member

DDvO commented Aug 29, 2022

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.

#ifdef SECUTILS_PKCS12_STRICT

@DDvO DDvO changed the title Disable loading of unprotected credentials. Disable loading of unprotected credentials from PKCS#12 files. Sep 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants