Skip to content

Conversation

levitte
Copy link
Member

@levitte levitte commented May 25, 2017

This is a new pull request to replace #2011, which has encountered github performance issues.

This STORE module adds the following functionality:

  • A function STORE_open(), STORE_load() and STORE_close() that accesses
    a URI and helps loading the supported objects (PKEYs, CERTs and CRLs
    for the moment) from it.
  • An opaque type STORE_INFO that holds information on each loaded object.
  • A few functions to retrieve desired data from a STORE_INFO reference.
  • Functions to register and unregister loaders for different URI schemes. This enables dynamic addition of loaders from applications or from engines.

Also includes a loader for the "file" scheme. The goal is to have it load PEM files and raw DER files alike, transparently.

Fixes #1958, #1959

Checklist
  • documentation is added or updated
  • tests are added or updated

@levitte
Copy link
Member Author

levitte commented May 25, 2017

Copying remaining big issues from #2011:

  1. STORE_ vs OSSL_STORE_ prefix (not yet concluded as far as I can see)
  2. ENDOFDATA vs ferror like interface.

I'm currently working on 2. 1 is waiting on team decision (I want it to become policy if we decide for OSSL_STORE_)...

@dwmw2
Copy link
Contributor

dwmw2 commented May 25, 2017

We spoke of the desire to keep your test cases lifted from OpenConnect, in sync with the ones I have.

So I thought I should let you know I added a new one that you should incorporate:
http://git.infradead.org/users/dwmw2/openconnect.git/commitdiff/962d242

@levitte levitte force-pushed the store2-continued branch from b23fcc9 to 2baf868 Compare May 26, 2017 16:17
@levitte
Copy link
Member Author

levitte commented May 26, 2017

@dwmw2, hmmm, that will take some work to adapt, and especially since I try to be platform agnostic...

@levitte
Copy link
Member Author

levitte commented May 26, 2017

Of the listed things to fix, I realised I forgot to add one, @t-j-h's concern with opening up internal APIs, so I'll add that here:

  1. STORE_ vs OSSL_STORE_ prefix (not yet concluded as far as I can see)
  2. [DONE] ENDOFDATA vs ferror like interface.
  3. Exposing asn1_d2i_read_bio as internal API vs making ASN1_d2i_bio accept a NULL d2i ( @t-j-h )

I feel unsure about (3)... exposing things internally within libcrypto is already done for other things, I don't quite see why this one is a bad thing. @t-j-h, are you very attached to an unreachable asn1_d2i_read_bio or can we let that item go?

@levitte levitte force-pushed the store2-continued branch 2 times, most recently from 29d5e71 to ff95ce4 Compare May 26, 2017 21:12
@dwmw2
Copy link
Contributor

dwmw2 commented May 27, 2017

@dwmw2, hmmm, that will take some work to adapt, and especially since I try to be platform agnostic...

The test case itself, yeah. I need to find a Windows box (suspect Wine won't cut it here) and work that out. But having an expectation that it should work... that's a different story (qv).

@dwmw2
Copy link
Contributor

dwmw2 commented May 27, 2017

Just attempting to work out what this looks like for PKCS#11...

Given a RFC7512 URI string (which is actually a search term that may produce more than result), we call STORE_open() with that URI and then iterate over the results with STORE_load(), yes?

I think that works sanely, and fits the same usage model as when there is more than one object within a given file. Will we eventually have a SSL_CTX_use_STORE(uri, ui_method, ui_data, post_process, post_process_data) function?

The only thing I think I'm missing is explicit documentation of the lifetime of the ui_method and ui_data. I need them to last as long as the last private key exported from the store. They can't go away when the STORE_CTX itself does. That's to deal with CKA_ALWAYS_AUTHENTICATE keys (and probably hotplug and other nastiness).

@levitte
Copy link
Member Author

levitte commented May 27, 2017

@dwmw2 wrote:

Just attempting to work out what this looks like for PKCS#11...

Given a RFC7512 URI string (which is actually a search term that may produce more than result), we call STORE_open() with that URI and then iterate over the results with STORE_load(), yes?

Yes. Basically, it's roughly modeled from stdio, with the difference that we start with a URI in place of a file name.

I think that works sanely, and fits the same usage model as when there is more than one object within a given file. Will we eventually have a SSL_CTX_use_STORE(uri, ui_method, ui_data, post_process, post_process_data) function?

Obviously, you haven't seen #2696 and #2697... They're pretty rad, especially #2697.

The only thing I think I'm missing is explicit documentation of the lifetime of the ui_method and ui_data. I need them to last as long as the last private key exported from the store. They can't go away when the STORE_CTX itself does. That's to deal with CKA_ALWAYS_AUTHENTICATE keys (and probably hotplug and other nastiness).

I would expect ui_method to live for as long as the object (exe or dll) that implements it. As for ui_data, there's no general guarantee. Either way, the decision is up to the application or engine or whatever that implements the ui_method.

I would expect that any condition specific to an engine will be documented with said engine

@levitte
Copy link
Member Author

levitte commented May 27, 2017

(previous comment removed... I can't believe I wrote that out loud...)

@dwmw2
Copy link
Contributor

dwmw2 commented May 27, 2017

So... If a pkcs11 STORE backend might exist one day and if my users might one day give me a string which happens to reference an object therein, as the cert they want to use... Then I need to have read that documentation before I write my application today?

I would be much happier with lifetime rules for the ui_data warning up front that some back ends may keep references to it for as long as keys exist.

Those other PRs... Neat. Need to think about how we handle that for system trust, especially w.r.t. find_issuer() which will want to specify the subject in the uri and also postprocess.

@levitte
Copy link
Member Author

levitte commented May 27, 2017

Do you mean the find_issuer in crypto/x509/x509_vfy.c? In that case, I've no idea what you're talking about... However, if you're thinking in more general searching terms, you might want to have a look at the add-on in #2688.

@levitte
Copy link
Member Author

levitte commented May 27, 2017

So... If a pkcs11 STORE backend might exist one day and if my users might one day give me a string which happens to reference an object therein, as the cert they want to use... Then I need to have read that documentation before I write my application today?

Well, it would seem appropriate to read docs that come with the backends you're about to use, if there is any, no? I don't understand why that would be questionable... I find it as natural as having to read socket(7) to figure out the socket level option names and arguments for getsockopt(3)...

I would be much happier with lifetime rules for the ui_data warning up front that some back ends may keep references to it for as long as keys exist.

Mm, good point. I think I can write something generic enough. I'll get back to that on Monday.

@dwmw2
Copy link
Contributor

dwmw2 commented May 27, 2017

Yes I'm thinking of that find_issuer(). Linux distributions now tend to have trust roots in a PKCS#11 token and only export it to a flat file for backward compatibility. Iterating over all objects​ in the token isn't the best way to do it. I'll take a closer look...

@dwmw2
Copy link
Contributor

dwmw2 commented May 27, 2017

For the other thing... I write my application to use the STORE interface long before the back end in question even exists. A sysadmin installs the PKCS#11 back end once it's written, and a user chooses a cert with a PKCS#11 URI. It wasn't "me" who used that back end.

@levitte
Copy link
Member Author

levitte commented May 28, 2017

Ah, I get your point.

The normal thing, at least from the usual OpenSSL perspective, would be to have the backend duplicate the UI data... which is, of course, difficult with the current UI API. But then, I could easily add something that would do exactly that. I would think that's a saner way of doing things.

@dwmw2
Copy link
Contributor

dwmw2 commented May 28, 2017

"duplicate the ui_data"?

This is an opaque (to OpenSSL) datum which ends up being passed back to the application in order to request the password. It contains GUI context or something, and may have a chain of other refcounted objects hanging off it that OpenSSL can know nothing about....

@levitte
Copy link
Member Author

levitte commented May 28, 2017

See #3575.

@levitte
Copy link
Member Author

levitte commented May 28, 2017

My thought is that if the ui_data can't be duplicated (regardless of reason), then your backend can't support things like CKA_ALWAYS_AUTHENTICATE. The benefit of doing it this way is expliciteness. The application will have to explicitely support this kind of duplication, and the backend will be able to return an error if that support is missing, which I think is much better than merely hoping that the application is well behaved and keeps the ui_data around for its life time (and getting mysterious crashes if it doesn't).

@dwmw2
Copy link
Contributor

dwmw2 commented May 28, 2017

Ack, that works. Thanks. A nice obvious failure mode if an app forgets to provide a dup method -- even today with just the file backend where it isn't strictly needed -- is extra helpful.

@dwmw2
Copy link
Contributor

dwmw2 commented May 28, 2017

I'm starting to wonder if a callback function to get the password might be better than a UI. People build without UI support (e.g. MacOS) and do we want it to be a dependency? The "user" prompts we offer are never going to be perfectly suited for presenting directly to the user anyway (with translations and referring to the object being unlockedj with the terminology that the app+user use for it.

Using a callback also lets us punt on the charset question and insist we only take UTF-8. Because now there is app code between the STORE and the UI_OpenSSL() if they happen to use that, so we can tell them it's their responsibility to convert.

We also get to convey other flags like "wrong PIN" and "last chance" with our own callback, in machine readable way (i.e. so the app can modify its behaviour and stop using the cached pin, etc.)

levitte added a commit that referenced this pull request Jun 29, 2017
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
This command can be used to view the contents of any supported type of
information fetched from a URI, and output them in PEM format.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Containers are objects that are containers for a bunch of other
objects with types we recognise but aren't readable in a stream.  Such
containers are read and parsed, and their content is cached, to be
served one object at a time.

This extends the FILE_HANDLER type to include a function to destroy
the cache and a function to simulate the EOF check.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Some containers might very simply decode into something new that
deserves to be considered as new (embedded) data.  With the help of a
special OSSL_STORE_INFO type, make that new data available to the
loader functions so they can start over.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
…start

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Add a separate handler for encrypted PKCS#8 data.  This uses the new
restart functionality.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
This has it recognised when the given path is a directory.  In that
case, the file loader will give back a series of names, all as URI
formatted as possible given the incoming URI.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
…dlers

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
These tests were inspired by OpenConnect and incorporated
by permission of David Woodhouse under CLA

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
levitte added a commit that referenced this pull request Jun 29, 2017
Sometimes, 'file_load' couldn't really distinguish if a file handler
matched the data and produced an error or if it didn't match the data
at all.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from #3542)
@levitte
Copy link
Member Author

levitte commented Jun 29, 2017

Merged. c785fd4 to 6fc1d33

Thanks for the review, everyone

@levitte levitte closed this Jun 29, 2017
@levitte
Copy link
Member Author

levitte commented Jun 29, 2017

Next in line, I propose starting with a couple of easy ones, #3483 and #2745. After that, #2746 (which might or might not be controversial)

@levitte
Copy link
Member Author

levitte commented Jun 29, 2017

Another choice might be to have a look at #2688, which is a requirement for #3481 (the CNG engine, that I know has caught some interest).

if (a->scheme != NULL && b->scheme != NULL)
return strcmp(a->scheme, b->scheme);
else if (a->scheme == b->scheme)
return 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we super-double-plus ensured that the scheme field was never NULL, so most of this is redundant.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point.


int OSSL_STORE_error(OSSL_STORE_CTX *ctx)
{
return ctx->loader->error(ctx->loader_ctx);
Copy link
Contributor

Choose a reason for hiding this comment

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

These functions seem like a giant footgun, as we don't enforce in OSSL_STORE_register_loader() that all the method functions are non-NULL.
Maybe I missed some previous discussion on this pull request and this is in fact the behavior we want from our "don't check user input for NULL" policy, but my personal preference would be to reject registering an incomplete loader.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point.

@levitte
Copy link
Member Author

levitte commented Jun 29, 2017

@kaduk, see #3805

EVP_PKEY *pkey = NULL;

if (pem_name != NULL) {
if (strcmp(pem_name, PEM_STRING_PUBLIC) != 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do things break if this is pem_check_suffix() instead of strcmp()?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. PEM_STRING_PUBLIC identifies PKCS#8 public keys, which has the algo OID as part of the contents. As far as I know, that's what we actively support. Is there are reason you'd like us to support public keys that are typed in the PEM name?

Copy link
Contributor

Choose a reason for hiding this comment

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

No; I wasn't quite sure on the difference between the forms, and noted that we have a couple examples of "RSA PUBLIC KEY" in documentation, so wondered what the difference was. Thanks for clarifying.

} else if (strncmp(&uri[5], "///", 3) == 0) {
path = &uri[7];
} else if (strncmp(&uri[5], "//", 2) != 0) {
path = &uri[5];
Copy link
Contributor

Choose a reason for hiding this comment

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

No one complained about magic numbers in pointer arithmetic? ;)

Copy link
Member Author

Choose a reason for hiding this comment

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

Obviously not... but then, considering the comparison with "file:" just above, this isn't much of a mystery, is it?

const char *path = NULL;

if (strncasecmp(uri, "file:", 5) == 0) {
if (strncmp(&uri[5], "//localhost/", 12) == 0) {
Copy link
Member

Choose a reason for hiding this comment

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

by the way, isn't the DNS part of an URI case-insensitive?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point.

Copy link
Member Author

Choose a reason for hiding this comment

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

* There is also a OSSL_STORE_open_file() that can be used for file paths
* that can't be represented as URIs, such as Windows backslashes
*/
OSSL_STORE_CTX *ctx = OSSL_STORE_open("file:/foo/bar/data.pem");
Copy link
Contributor

Choose a reason for hiding this comment

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

I have issue with this example provided. The first time I saw this function it hasn't this prototype: https://github.com/openssl/openssl/blame/4fd397821139723fd4e51a03e92df33e9a9fadcc/include/openssl/store.h#L55-L58 I will use code from storeutl to help me.

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.

STORE core library
7 participants