OpenSSL verify untrusted issue

147 views
Skip to first unread message

Aleksei “filimonic” Filimonov

unread,
Nov 6, 2024, 11:09:55 AM11/6/24
to openssl-users
I have One-Tier PKI, and I have self-signed CA (ca.crt) and host cert  (host.crt). 

root@radius2:/tmp/certs# c_rehash ./ca-list/

root@radius2:/tmp/certs# tree
.
├── ca.crt
├── ca-list
│   ├── 577f4bb3.0 -> ca.crt
│   ├── ca.crt -> ../ca.crt
│   └── ed3eb11a.0 -> ca.crt
└── host.crt

I'm trying to verify certificate against CA, and it says "untrusted". 

root@radius2:/tmp/certs# openssl verify -verbose -crl_download -crl_check -show_chain -verify_depth 10 -issuer_checks -no_alt_chains -check_ss_sig -CAfile /tmp/certs/ca.crt -CApath /tmp/certs/ca-list /tmp/certs/host.crt
/tmp/certs/host.crt: OK
Chain:
depth=0: O = OD.FREEIPA.XYZ, CN = rpi4b.od.freeipa.xyz (untrusted)
depth=1: O = OD.FREEIPA.XYZ, CN = OD.FREEIPA.XYZ Certificate Authority


Could anyone explain why this shows "untrusted" and how to make it "trusted"?

root@radius2:/tmp/certs# openssl version
OpenSSL 3.0.14 4 Jun 2024 (Library: OpenSSL 3.0.14 4 Jun 2024)

root@radius2:/tmp/certs# ldd /usr/sbin/freeradius | grep libssl
        libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3 (0x00007f6665d2f000)

root@radius2:/tmp/certs# strings /lib/x86_64-linux-gnu/libssl.so.3  | grep OPENSSL_3
OPENSSL_3.0.0
OPENSSL_3.0.3


I have the same issue for FreeRADIUS running this server : 

Tue Nov  5 22:37:37 2024 : Warning: Certificate chain - 1 cert(s) untrusted
Tue Nov  5 22:37:37 2024 : Warning: (TLS) untrusted certificate with depth [1] subject name /O=OD.FREEIPA.XYZ/CN=OD.FREEIPA.XYZ Certificate Authority
Tue Nov  5 22:37:37 2024 : Warning: (TLS) untrusted certificate with depth [0] subject name /O=OD.FREEIPA.XYZ/CN=rpi4b.od.freeipa.xyz   

Michael Wojcik

unread,
Nov 6, 2024, 3:16:17 PM11/6/24
to openssl-users
> From: openss...@openssl.org <openss...@openssl.org> On Behalf Of Aleksei “filimonic” Filimonov

> I have One-Tier PKI, and I have self-signed CA (ca.crt) and host cert (host.crt).
...

> root@radius2:/tmp/certs# openssl verify -verbose -crl_download -crl_check -show_chain
> -verify_depth 10 -issuer_checks -no_alt_chains -check_ss_sig -CAfile
> /tmp/certs/ca.crt -CApath /tmp/certs/ca-list /tmp/certs/host.crt

Typically use -CAfile *or* -CApath, not both. -issuer_checks and -no_alt_chains are deprecated.

Do you actually have a CRL server?

> /tmp/certs/host.crt: OK
> Chain:
> depth=0: O = OD.FREEIPA.XYZ, CN = rpi4b.od.freeipa.xyz (untrusted)
> depth=1: O = OD.FREEIPA.XYZ, CN = OD.FREEIPA.XYZ Certificate Authority

I believe openssl verify always lists certificates that aren't trusted in the path as "untrusted". The trusted certificates are the ones found via -CAfile, -CApath, or -trusted (which can't be specified with -CAfile or -CApath). verify is reporting that the chain starts with an untrusted certificate, but can be built to a trusted one.

> I have the same issue for FreeRADIUS running this server :

> Tue Nov 5 22:37:37 2024 : Warning: Certificate chain - 1 cert(s) untrusted
> Tue Nov 5 22:37:37 2024 : Warning: (TLS) untrusted certificate with depth [1]
> subject name /O=OD.FREEIPA.XYZ/CN=OD.FREEIPA.XYZ Certificate Authority
> Tue Nov 5 22:37:37 2024 : Warning: (TLS) untrusted certificate with depth [0]
> subject name /O=OD.FREEIPA.XYZ/CN=rpi4b.od.freeipa.xyz

No, you don't have the same issue with FreeRADIUS. openssl verify says the root is trusted; FreeRADIUS says it is not. The root is not in the collection of trust anchors for FreeRADIUS. I don't work with FreeRADIUS, so I don't know how to fix that, but it's a FreeRADIUS problem, not an OpenSSL one.

Michael Wojcik
Distinguished Techologist, Rocket Software

================================
Rocket Software, Inc. and subsidiaries ■ 77 Fourth Avenue, Waltham MA 02451 ■ Main Office Toll Free Number: +1 855.577.4323
Contact Customer Support: https://my.rocketsoftware.com/RocketCommunity/RCEmailSupport
Unsubscribe from Marketing Messages/Manage Your Subscription Preferences - http://www.rocketsoftware.com/manage-your-email-preferences
Privacy Policy - http://www.rocketsoftware.com/company/legal/privacy-policy
================================

This communication and any attachments may contain confidential information of Rocket Software, Inc. All unauthorized use, disclosure or distribution is prohibited. If you are not the intended recipient, please notify Rocket Software immediately and destroy all copies of this communication. Thank you.

Aleksei “filimonic” Filimonov

unread,
Nov 9, 2024, 7:51:37 AM11/9/24
to openssl-users, Michael Wojcik
I think issue is somewhere near. 
FreeRADIUS uses this code (below) and it shows in logs there is 1 cert is untrusted according to X509_STORE_CTX_get_num_untrusted, and outputs everything (2 certs) from X509_STORE_CTX_get0_untrusted stack.
For start, It's hard to say if this "one untrusted cert" is the root or the peer because X509_STORE_CTX_get0_untrusted returns the stack. 
Is there a way to get information which cert of stack is untrusted?

#if OPENSSL_VERSION_NUMBER >= 0x10100000L
        /*
         *  See if there are any untrusted certificates.
         *  If so, complain about them.
         */
        untrusted = X509_STORE_CTX_get0_untrusted(ctx);
        if (untrusted) {
            if (conf->disallow_untrusted || RDEBUG_ENABLED2) {
                int  i;

                WARN("Certificate chain - %i cert(s) untrusted",
                     X509_STORE_CTX_get_num_untrusted(ctx));
                for (i = sk_X509_num(untrusted); i > 0 ; i--) {
                    X509 *this_cert = sk_X509_value(untrusted, i - 1);

                    X509_NAME_oneline(X509_get_subject_name(this_cert), subject, sizeof(subject));
                    subject[sizeof(subject) - 1] = '\0';

                    WARN("(TLS) untrusted certificate with depth [%i] subject name %s",
                         i - 1, subject);
                }
            }

            if (conf->disallow_untrusted) {
                AUTH(LOG_PREFIX ": There are untrusted certificates in the certificate chain.  Rejecting.");
                my_ok = 0;
            }
        }
#endif

среда, 6 ноября 2024 г. в 23:16:17 UTC+3, Michael Wojcik:

Michael Wojcik

unread,
Nov 9, 2024, 11:01:18 AM11/9/24
to Aleksei “filimonic” Filimonov, openssl-users
> From: Aleksei “filimonic” Filimonov <ale...@filimonic.net>
> Sent: Saturday, 9 November, 2024 05:52

> FreeRADIUS uses this code (below) and it shows in logs there is 1 cert is
> untrusted according to X509_STORE_CTX_get_num_untrusted,

This number is misleading; it does not count the first certificate in the chain. See the documentation for X509_STORE_CTX_get_num_untrusted. That's presumably because the first certificate is *assumed* to be untrusted; the whole point of building a chain is to get from an untrusted certificate (a claim of identity) to a trust anchor (a certificate that you *do* trust).

> and outputs everything (2 certs) from X509_STORE_CTX_get0_untrusted stack.
> For start, It's hard to say if this "one untrusted cert" is the root or the peer

It's both.

It's normal for the peer entity certificate to be untrusted. (If you already trusted it, you'd essentially not be using PKI at all; all the participating peers would already have copies or other verifiers for one another's certificates, and there would be no higher authority.)

The problem, as I wrote in my previous note, appears to be that FreeRADIUS does not trust the root. I can't tell where it got the root from -- if the peer sent it as part of its chain (that's allowed but often omitted), or if FreeRADIUS was configured with the root but not told to trust it for some reason, or if there's something else at work here which is not obvious. Again, I don't know how FreeRADIUS is configured.

--
Michael Wojcik

Aleksei “filimonic” Filimonov

unread,
Nov 9, 2024, 1:12:52 PM11/9/24
to openssl-users, Michael Wojcik, Aleksei “filimonic” Filimonov
>>   FreeRADIUS was configured with the root but not told to trust it for some reason.
FreeRADIUS is used to check client certificate provided by client against limited set of roots and intermediates provided by config.
This is intentionally limited to just a few certificates, rather than system-wide roots, so that when a client presents a certificate to a server, the server only accepts clients with certificate issued by specific chains of CAs (usually, enterprise self-signed CA)

For FreeRADIUS side, the client certificate is provided by client and the context is created using this code, as I assume. I cut many non-important lines from my point, and I'm not OpenSSL or FreeRADIUS pro.
What is FreeRADIUS missing about marking the certificates in store as trusted? 

ctx = SSL_CTX_new(SSLv23_method());
X509_STORE *store = X509_STORE_new(); X509_STORE_load_locations(store, conf->ca_file, conf->ca_path));
SSL_CTX_set_cert_store(ctx, store);
verify_mode |= SSL_VERIFY_PEER;
verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
verify_mode |= SSL_VERIFY_CLIENT_ONCE;
SSL_CTX_set_verify(ctx, verify_mode, cbtls_verify);


суббота, 9 ноября 2024 г. в 19:01:18 UTC+3, Michael Wojcik:

Viktor Dukhovni

unread,
Nov 9, 2024, 4:20:23 PM11/9/24
to openss...@openssl.org
On Sat, Nov 09, 2024 at 04:51:37AM -0800, Aleksei “filimonic” Filimonov wrote:

> FreeRADIUS uses this code (below)

That code is profoundly wrong. The return value of the function
X509_STORE_CTX_get0_untrusted(3) is NOT a set of chain certificates that
failed to be verified. Rather it is the set of potentially useful
intermediate CA certificates that the verification code can draw on to
build the chain. It has little to do with the success or failure of
chain verification.

If (as is common, but not always the case) the untrusted stack consists
of the ordered chain certificates provided by the peer, and does not
include any of the configured trust anchors, nor any redundant entries,
then the length of that chain will be the number of intermediate (a.k.a.
subsidiary or "cross") CA certificates in the complete chain.

> and it shows in logs there is 1 cert is untrusted according to
> X509_STORE_CTX_get_num_untrusted, and outputs everything (2 certs)
> from X509_STORE_CTX_get0_untrusted stack.

That logic is nonsense. The value of "num_untrusted" will only be zero
if the end-entity certificate presented by the client itself appears
verbatim in the trust store, otherwise it will always be at least 1.

> For start, It's hard to say if this "one untrusted cert" is the root or the
> peer because X509_STORE_CTX_get0_untrusted returns the stack.

The stack is red herring, it will contain precisely the intermediate CA
certificates that the application provided when creating the
X509_STORE_CTX via X509_STORE_CTX_init(3) or soon after updated via
X509_STORE_CTX_set0_untrusted(3).

> Is there a way to get information which cert of stack is untrusted?

The first "num_untrusted" (starting at index 0) in the *constructed*
chain returned by X509_STORE_CTX_get0_chain(3) were untrusted (did not
come from the trust store). That number is typically at least 1, more
often 2 or a small handful if more intermediate CAs are employed.

> #if OPENSSL_VERSION_NUMBER >= 0x10100000L
> /*
> * See if there are any untrusted certificates.
> * If so, complain about them.
> */
> untrusted = X509_STORE_CTX_get0_untrusted(ctx);
> if (untrusted) {
> if (conf->disallow_untrusted || RDEBUG_ENABLED2) {
> int i;
>
> WARN("Certificate chain - %i cert(s) untrusted",
> X509_STORE_CTX_get_num_untrusted(ctx));
> for (i = sk_X509_num(untrusted); i > 0 ; i--) {
> X509 *this_cert = sk_X509_value(untrusted, i - 1);
>
> X509_NAME_oneline(X509_get_subject_name(this_cert),
> subject, sizeof(subject));
> subject[sizeof(subject) - 1] = '\0';
>
> WARN("(TLS) untrusted certificate with depth [%i]
> subject name %s",
> i - 1, subject);
> }
> }
>
> if (conf->disallow_untrusted) {
> AUTH(LOG_PREFIX ": There are untrusted certificates in the
> certificate chain. Rejecting.");
> my_ok = 0;
> }
> }
> #endif

This code is garbage, unless the intent to only allow *direct* trust of
the presented EE certificate, which typically also require the use of
the X509_V_FLAG_PARTIAL_CHAIN flag.

--
VIktor.
Reply all
Reply to author
Forward
0 new messages