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

Add feature for loading the chained certificate into Certificate array #361

Closed
wants to merge 3 commits into from

Conversation

sikuan
Copy link
Contributor

@sikuan sikuan commented Apr 19, 2020

Load the chained certificate into Certificate array

New feature proposal for issue #288

Create two functions:

  • OpenSSL::X509::Certificate#load_file(path)
    • Input: string (File path)
    • Output: array (Array of Certificate objects)
    • Related files:
    • Proposed features:
      • Load PEM file
      • Load DER(ASN1) file
      • Return the array of Certificate objects, which are the chained certificates
    • Proposed way to solve:
      • Create a BIO wrapper in ext/openssl/ossl_x509cert.c
      • (proposing in progress)
  • OpenSSL::X509::Certificate#read_certificates(cert_content)
    • Input: certificate content (DER(ASN1) or PEM)
    • Output: array (Array of Certificate objects)
    • Related files:
    • Proposed features:
      • Load PEM
      • Load DER(ASN1)
      • Return the array of Certificate objects, which are the chained certificates
    • Proposed way to solve:
      • (proposing in progress)

Comment on lines 717 to 776
in = BIO_new(BIO_s_file());
if (in == NULL) {
SSL_CTX_free(ctx);
ossl_raise(eX509CertError, NULL);
}

if (BIO_read_filename(in, StringValueCStr(path)) <= 0) {
SSL_CTX_free(ctx);
ossl_raise(eX509CertError, NULL);
}

x509 = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
if (x509 == NULL) {
BIO_free(in);
SSL_CTX_free(ctx);
ossl_raise(eX509CertError, NULL);
}

SSL_CTX_use_certificate(ctx, x509);
if (ERR_peek_error() != 0) {
X509_free(x509);
BIO_free(in);
SSL_CTX_free(ctx);
ossl_raise(eX509CertError, NULL); /* Key/certificate mismatch doesn't imply */
}

/*
* If we could set up our certificate, now proceed to the CA
* certificates.
*/
SSL_CTX_clear_chain_certs(ctx);
while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL) {
int r = SSL_CTX_add0_chain_cert(ctx, ca);
/*
* Note that we must not free ca if it was successfully added to
* the chain (while we must free the main certificate, since its
* reference count is increased by SSL_CTX_use_certificate).
*/

if (!r) {
X509_free(ca);
X509_free(x509);
BIO_free(in);
SSL_CTX_free(ctx);
ossl_raise(eX509CertError, NULL);
}
}

/* When the while loop ends, it's usually just EOF. */
err = ERR_peek_last_error();
if (! (ERR_GET_LIB(err) == ERR_LIB_PEM
&& ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
/* some real error */
X509_free(x509);
BIO_free(in);
SSL_CTX_free(ctx);
ossl_raise(eX509CertError, NULL);
}

ERR_clear_error();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sikuan
Copy link
Contributor Author

sikuan commented Apr 19, 2020

Commit: 17f55db

For proposing #load_file:

@sikuan
Copy link
Contributor Author

sikuan commented Apr 19, 2020

@ioquatix

X509 *x509, *ca;
STACK_OF(X509) *certs_stack;
unsigned long err;
SSL_CTX *ctx = SSL_CTX_new(TLS_method());
Copy link
Member

Choose a reason for hiding this comment

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

You should be able to avoid the need for this, and just create an array and put the certificates into that array below:

*/
SSL_CTX_clear_chain_certs(ctx);
while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL) {
int r = SSL_CTX_add0_chain_cert(ctx, ca);
Copy link
Member

Choose a reason for hiding this comment

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

Instead of adding to the context, add it to the array here.

Also need to check what is the difference between PEM_read_bio_X509 and PEM_read_bio_x509_AUX.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for your important point!

Now I can follow that[1]:

  • with _AUX will process a trusted X509 certificates
  • without _AUX will process an X509 certificates
    • will process a trusted X509 certificates but any trusted settings are discarded.

[1] https://www.openssl.org/docs/manmaster/man3/PEM_read_bio_X509.html#DESCRIPTION

As I understand, the trusted setting is only for trusted certificate [2], so using the function without _AUX will be fit for the purpose of this function? (Because of "discarded" meaning in [1], I may try both myself and see the differences anyway)

[2] https://www.openssl.org/docs/man1.0.2/man1/x509.html#TRUST-SETTINGS

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi again 😄! Could you please review my new commit d24041a? Thank you~

#361 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm continuing the implementation for also reading the DER format certificate.

For now, both PEM format and DER format could be read, but DER format only can read the top one certificate. I'm trying to fix this.

When you have a time, could you please review the commit ff8572a? Thank you~

#361 (comment)

Copy link
Member

@ioquatix ioquatix left a comment

Choose a reason for hiding this comment

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

Good effort, some changes required.

for (int cert_idx = 0; cert_idx < sk_X509_num(certs_stack); ++cert_idx) {
X509 * cert_c = sk_X509_value(certs_stack, cert_idx);
VALUE cert = ossl_x509_new(cert_c);
rb_ary_push(certs, cert);
Copy link
Member

Choose a reason for hiding this comment

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

(Although it's kind of rare condition,) rb_ary_new(), ossl_x509_new(), and rb_ary_push() involve memory allocation and can potentially raise a Ruby exception on failure, which is basically a longjmp().

To prevent possible memory leak, these function calls have to be wrapped by rb_protect().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for careful review!

I think I found the proper supplement[3] for this potential Ruby exception with memory leak problem. I'll try apply rb_protect() soon.

[3] https://silverhammermba.github.io/emberb/c/#rb_protect

@sikuan
Copy link
Contributor Author

sikuan commented Apr 26, 2020

Force-pushed commit:

Diffs in ossl_x509_load_chained_cert_from_file():

  • Remove the SSL_CTX usages.
  • Fix the missing leaf certificate (first certificate in PEM file) from result array.
  • Use PEM_read_bio_x509 even for all certificates.

@sikuan
Copy link
Contributor Author

sikuan commented May 2, 2020

Force-pushed commit:

Diffs in ossl_x509_load_chained_cert_from_file():

@ioquatix
Copy link
Member

ioquatix commented May 6, 2021

Sorry, I totally dropped the ball on this one, it looks good to me but I'll need to review it. cc @rhenium

@ioquatix
Copy link
Member

Continuing here: #441

Thanks for your work.

@ioquatix ioquatix closed this May 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants