Package: alpine
Version: 2.26+dfsg-3
Severity: normal
Tags: patch
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
Hello maintainer!
These days, Let's Encrypt is moving towards handing out server certificates
without a Subject Common Name. Instead, all the names are Subject Alt Names. You
can see the different types of certificates Let's Encrypt can issue here:
https://letsencrypt.org/docs/profiles/
tlsserver and shortlived no longer contain a Common Name. Only classic does
this, and it is described as
> We recommend using this profile for subscribers who are happy to let others
> try new things first.
So, let's try new things!
Unfortunately, Alpine does not support a certificate without a Subject Common
Name. I've been rooting around in the code, and I came up with a patch that I
think is correct. However, I found the control flow in the code involved hard to
get my head around, I do not have much experience working with OpenSSL (some,
but not much), and I am fully aware this code is security critical. If you want
to proceed based on my patch, please thoroughly check. It is purely a
suggestion, I cannot vouch for this code. I /think/ it is correct, but that is a
much lighter assertion.
Incidentally, I could not find a tabstop value where the various lines (which
contain a mixture of tab and space indentation, even in a single line) lined up,
so the indent might be even more off than it probably already was. The incorrect
indentation and the omission of redundant (but visually distinguishing) braces
did absolutely not help with analysing the control flow.
I am using Debian stable myself; I had a look to see if I should submit this
upstream, but the website felt rather inactive. I decided to submit it to Debian
instead, but am happy to send it as an e-mail to <[email protected]> if
you say that's better.
In the current code, there are two code paths that check the Common Name, but
one is behind #ifndef OPENSSL_1_1_0, which I think is only unset on truly
ancient OpenSSL's so doesn't apply. Rather, only what the code calls "Method 2,
use cname" is functional. The patched code should still work fine with the def
unset, though.
The current code only checks Subject Alt Names when the Common Name did exist
but did not match. However, this is no longer enough, since the Common Name
might not exist. So the Subject Alt Name matching is moved into its own block.
The check at the end:
> if (ret == NIL && s == NIL)
> ret = "Unable to locate common name in certificate";
is moved to before the Subject Alt Name match. The problem is that the Common
Name check verifies that all Common Names found match the desired host name,
whereas the Subject Alt Name match verifies that at least one Alt Name matches
the desired hostname. This means that for Common Name we initialized the return
value at "all is well" and turned that into "problem found" when a
counterexample was found, whereas for "Subject Alt Name" we need to initialize
to "nothing matches" and turn that into "positive match found" when a
counterexample is found. Since the Common Name and Subject Alt Name checks are
now independent, we need to move that bit.
Additionally, that same bit
> if (ret == NIL && s == NIL)
> ret = "Unable to locate common name in certificate";
is now two possibilities. That exact message is used when there are no Subject
Alt Names, but if there are Subject Alt Names, the message becomes "Server name
does not match certificate" because there /were/ names in the cert. They might
not be DNS names, btw, so a certificate that only contains e-mail addresses will
still be reported as "name does not match" even though "unable to locate name"
would also have been appropriate. I don't consider this important.
When the execution dives into checking Subject Alt Names, it will do so with the
return value (ret variable) primed to error; when a matching Subject Alt Name is
found, the error value is cleared to NIL (this is just the original code).
I tested this with certs with and without Common Name, both with the correct
name and with an incorrect name (but otherwise fully valid). It worked
correctly. I did not test with valid certificates that had no Subject Alt Names
at all, since I didn't have such a valid cert at hand.
I hope this is of use and that in the future we'll be able to use Alpine with
certificates without Common Name as Common Name is NOT RECOMMENDED by the
Baseline Requirements for the Issuance and Management of Publicly-Trusted TLS
Server Certificates (as can be found on the Let's Encrypt Profiles page).
Thanks!
Peter.
- -- System Information:
Debian Release: 13.2
APT prefers stable-updates
APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386, armhf
Kernel: Linux 6.12.57+deb13-amd64 (SMP w/16 CPU threads; PREEMPT)
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8),
LANGUAGE=en_GB:en
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled
Versions of packages alpine depends on:
ii libc6 2.41-12
ii libcrypt1 1:4.4.38-1
ii libgssapi-krb5-2 1.21.3-5
ii libkrb5-3 1.21.3-5
ii libldap2 2.6.10+dfsg-1
ii libssl3t64 3.5.4-1~deb13u1
ii libtinfo6 6.5+20250216-2
Versions of packages alpine recommends:
ii alpine-doc 2.26+dfsg-3
ii sensible-utils 0.0.25
Versions of packages alpine suggests:
ii aspell 0.60.8.1-4
ii nullmailer [mail-transport-agent] 1:2.2+10~g7ed88a0-6.1
- -- no debconf information
-----BEGIN PGP SIGNATURE-----
iQFMBAEBCgA2FiEEZQCNwiCq4qJXTWzVlp4Bj95s3KEFAmlVT6MYHHBldGVyQGRp
Z2l0YWxicmFpbnMuY29tAAoJEJaeAY/ebNyh1UcH/1HI7JO79B8tT5apTuuKlHQ1
iw0kpz14v89Vb1i//j8Yt6dr/dO9FBc98zHhLWRHaP8wRH3Cvv7+aeMHuUJUgNTq
aCL+WyCuC1U3OvzrB03EIM1ABh4AniDj84uYDUjUTbf5O9Mn64KjtzbRm8UXHHgO
zQ+rag0gzAbhVS2GsgNtqXA/d5OC/nXTNXdCA8qo1GhbcmSdb9LFzxZuQHNRb91G
lAahWXNlhEMoEf4dSkizwWVGioSZLA8UQZrALnTcz+zeV0vlWS8hSxsrDpGjS1Sq
0zmqcjepvOx+uF17UyW5N0VSfOqtwgitvIZmxCicFj+d6ai4tp+92MA0N+cKcPg=
=HHNu
-----END PGP SIGNATURE-----
diff '--color=auto' -ur alpine-2.26+dfsg.orig/imap/src/osdep/unix/ssl_unix.c
alpine-2.26+dfsg/imap/src/osdep/unix/ssl_unix.c
--- alpine-2.26+dfsg.orig/imap/src/osdep/unix/ssl_unix.c 2022-06-03
02:14:00.475274788 +0200
+++ alpine-2.26+dfsg/imap/src/osdep/unix/ssl_unix.c 2025-12-31
16:25:16.656354833 +0100
@@ -554,28 +554,33 @@
/* Method 2, use cname */
if(m == 0 || ret != NIL){
cname = X509_get_subject_name(cert);
- for(j = 0, ret = NIL; j < X509_NAME_entry_count(cname) && ret == NIL;
j++){
- if((e = X509_NAME_get_entry(cname, j)) != NULL){
- X509_NAME_get_text_by_OBJ(cname, X509_NAME_ENTRY_get_object(e),
buf, sizeof(buf));
- s = (char *) buf;
- }
- else s = NIL;
- if (s != NIL) {
- /* host name matches pattern? */
- ret = ssl_compare_hostnames (host,s) ? NIL :
- "Server name does not match certificate";
- ext = NIL;
- /* if mismatch, see if in extensions */
- if (ret && (ext = X509_get_ext_d2i
(cert,NID_subject_alt_name,NIL,NIL)) &&
- (n = sk_GENERAL_NAME_num (ext)))
- /* older versions of OpenSSL use "ia5" instead of dNSName */
- for (i = 0; ret && (i < n); i++)
- if ((name = sk_GENERAL_NAME_value (ext,i)) &&
- (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
- ssl_compare_hostnames (host,s)) ret = NIL;
- if(ext) GENERAL_NAMES_free(ext);
- }
- }
+ for(j = 0, ret = NIL; j < X509_NAME_entry_count(cname) && ret == NIL; j++)
+ if((e = X509_NAME_get_entry(cname, j)) != NULL){
+ X509_NAME_get_text_by_OBJ(cname, X509_NAME_ENTRY_get_object(e),
buf, sizeof(buf));
+ s = (char *) buf;
+ /* host name matches pattern? */
+ ret = ssl_compare_hostnames (host,s) ? NIL :
+ "Server name does not match certificate";
+ }
+ else s = NIL;
+ if (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL))
+ n = sk_GENERAL_NAME_num (ext);
+ else
+ n = 0;
+ if (ret == NIL && s == NIL)
+ if (n)
+ /* This value will be used when none of the alt names match either
*/
+ ret = "Server name does not match certificate";
+ else
+ ret = "Unable to locate common name in certificate";
+ /* if no CN or mismatch, see if in extensions */
+ if (ret && n)
+ /* older versions of OpenSSL use "ia5" instead of dNSName */
+ for (i = 0; ret && (i < n); i++)
+ if ((name = sk_GENERAL_NAME_value (ext,i)) &&
+ (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
+ ssl_compare_hostnames (host,s)) ret = NIL;
+ if(ext) GENERAL_NAMES_free(ext);
}
if (ret == NIL
@@ -585,9 +590,6 @@
&& !X509_get_subject_name(cert))
ret = "No name in certificate";
- if (ret == NIL && s == NIL)
- ret = "Unable to locate common name in certificate";
-
return ret;
}