> Sent: Monday, February 03, 2020 at 2:28 AM
> From: "Damien Miller" <d...@mindrot.org>
> To: "Aham Brahmasmi" <aham.brahma...@gmx.com>
> Cc: Misc <misc@openbsd.org>
> Subject: Re: ssh: probable bug in ssh -current
>
> On Fri, 31 Jan 2020, Aham Brahmasmi wrote:
>
> > Bug:
> > When the client connects to the server, they use the ed25519-cert to
> > establish the connection. After the ssh session is established, the
> > server sends the "hostkeys...@openssh.com" message with the server's
> > ed25519 host public key.
> >
> > This results in the client looping over the keys in known hosts file,
> > and deciding that the @cert-authority host certificate authority public
> > key is "deprecated", because it was not sent by the server [1]. The
> > client then informs the user:
> > "
> > The server has updated its host keys.
> > These changes were verified by the server's existing trusted key.
> > Deprecating obsolete hostkey: ED25519 
> > SHA256:<host_ca_public_key_fingerprint>
> > Accept updated hostkeys? (yes/no):
> > "
>
> Could you plesse try this patch?
>
>
> Index: clientloop.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/ssh/clientloop.c,v
> retrieving revision 1.338
> diff -u -p -r1.338 clientloop.c
> --- clientloop.c      30 Jan 2020 07:20:57 -0000      1.338
> +++ clientloop.c      3 Feb 2020 02:27:18 -0000
> @@ -1856,13 +1859,25 @@ hostkeys_find(struct hostkey_foreach_lin
>
>       /* Mark off keys we've already seen for this host */
>       for (i = 0; i < ctx->nkeys; i++) {
> -             if (sshkey_equal(l->key, ctx->keys[i])) {
> +             if ((l->marker & MRK_CA) != 0) {
> +                     if (!sshkey_is_cert(ctx->keys[i]))
> +                             continue;
> +                     if (!sshkey_equal(ctx->keys[i]->cert->signature_key,
> +                         l->key))
> +                             continue;
> +                     debug3("%s: found %s CA key at %s:%ld", __func__,
> +                         sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum);
> +                     ctx->keys_seen[i] = 1;
> +                     return 0;
> +             } else if (sshkey_equal(l->key, ctx->keys[i])) {
>                       debug3("%s: found %s key at %s:%ld", __func__,
>                           sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum);
>                       ctx->keys_seen[i] = 1;
>                       return 0;
>               }
>       }
> +     if ((l->marker & MRK_REVOKE) != 0)
> +             return 0;
>       /* This line contained a key that not offered by the server */
>       debug3("%s: deprecated %s key at %s:%ld", __func__,
>           sshkey_ssh_name(l->key), l->path, l->linenum);
> @@ -1961,10 +1976,11 @@ update_known_hosts(struct hostkeys_updat
>               if (stat(options.user_hostfiles[i], &sb) != 0) {
>                       if (errno == ENOENT) {
>                               debug("%s: known hosts file %s does not exist",
> -                                 __func__, strerror(errno));
> +                                 __func__, options.user_hostfiles[i]);
>                       } else {
> -                             error("%s: known hosts file %s inaccessible",
> -                                 __func__, strerror(errno));
> +                             error("%s: known hosts file %s inaccessible: "
> +                                 "%s", __func__, options.user_hostfiles[i],
> +                                 strerror(errno));
>                       }
>                       continue;
>               }
>

Namaste Damien,

Thank you very much for the patch. I apologize for the delay in my
response.

Applying the patch as is on -current (clientloop.c, v 1.340) did not
resolve the problem. In fact, the patched ssh client tried to
simultaneously add as well as delete the ssh-ed25519 key present in line
2.

As a result, I tried to read and execute the code. Here is what I
understood:

1) The changes to the strings in the error and debug functions work
correctly.

2) I think we may benefit from changing the lines in the patch:
                if ((l->marker & MRK_CA) != 0) {
to
                if (l->marker == MRK_CA) {
and
        if ((l->marker & MRK_REVOKE) != 0)
to
        if (l->marker == MRK_REVOKE)

I base this on what I saw in record_hostkey function in hostfile.c [1].
In case we do intend to perform bit-wise and operations, could you
please share the rationale behind it?

3) Now, with the above change, given that the known_hosts contains:
@cert-authority <server> ssh-ed25519 <host_ca_public_key>
<server> ssh-ed25519 <server_ed25519_host_public_key>

the following is what happens during the first iteration of
hostkeys_find function in the patched clientloop.c (1.340 + patch +
above change):

the execution enters the following if block and the continue is
executed:
                        if (!sshkey_is_cert(ctx->keys[i]))
                                continue;
The sshkey_is_cert function is in the sshkey.c file [2].
...
int
sshkey_type_is_cert(int type)
{
        const struct keytype *kt;

        for (kt = keytypes; kt->type != -1; kt++) {
                if (kt->type == type)
                        return kt->cert;
        }
        return 0;
}
...
int
sshkey_is_cert(const struct sshkey *k)
{
        if (k == NULL)
                return 0;
        return sshkey_type_is_cert(k->type);
}
...
During the execution, this sshkey_is_cert function returns 0. This is
because the argument key is of type=3 which implies "KEY_ED25519"
according to the enum sshkey_types in file sshkey.h [3].
...
/* Key types */
enum sshkey_types {
        KEY_RSA,
        KEY_DSA,
        KEY_ECDSA,
        KEY_ED25519,
        KEY_RSA_CERT,
        KEY_DSA_CERT,
        KEY_ECDSA_CERT,
        KEY_ED25519_CERT,
        KEY_XMSS,
        KEY_XMSS_CERT,
        KEY_ECDSA_SK,
        KEY_ECDSA_SK_CERT,
        KEY_ED25519_SK,
        KEY_ED25519_SK_CERT,
        KEY_UNSPEC
};
...
The enum keytypes is defined in file sshkey.c [2] as
...
/* Supported key types */
struct keytype {
        const char *name;
        const char *shortname;
        const char *sigalg;
        int type;
        int nid;
        int cert;
        int sigonly;
};
static const struct keytype keytypes[] = {
        { "ssh-ed25519", "ED25519", NULL, KEY_ED25519, 0, 0, 0 },
        { "ssh-ed25519-cert-...@openssh.com", "ED25519-CERT", NULL,
            KEY_ED25519_CERT, 0, 1, 0 },
...

Since "ssh-ed25519" (KEY_ED25519) has cert=0, the sshkey_is_cert
function returns 0.

Now, the notify_hostkeys function in the file sshd.c [4] contains
...
        for (i = nkeys = 0; i < options.num_host_key_files; i++) {
                key = get_hostkey_public_by_index(i, ssh);
                if (key == NULL || key->type == KEY_UNSPEC ||
                    sshkey_is_cert(key))
                        continue;
...
which implies that the ssh daemon does not seem to send the ssh
host certificate keys as part of the hostkeys-00 message.

Further, the client_input_hostkeys function in the file clientloop.c [5]
contains
...
                /* Skip certs */
                if (sshkey_is_cert(key)) {
                        debug3("%s: %s key is a certificate; skipping",
                            __func__, sshkey_ssh_name(key));
                        continue;
                }
...
which implies that the ssh client seems to be skipping the ssh host
certificate keys.

So, there does not seem to be any way to satisfy the sshkey_is_cert
function check in the patch, because from what I understand, the server
does not seem to send the certificate keys and the client skips the
certificate keys before iterating through the known_hosts file.

I am out of ideas beyond this. I am sorry. If there is a stupid mistake
that I am making here, please let me know.

Dhanyavaad,
ab
[1] - 
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/hostfile.c?rev=1.77&content-type=text/x-cvsweb-markup
[2] - 
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/sshkey.c?rev=1.99&content-type=text/x-cvsweb-markup
[3] - 
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/sshkey.h?rev=1.44&content-type=text/x-cvsweb-markup
[4] - 
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/sshd.c?rev=1.549&content-type=text/x-cvsweb-markup
[5] - 
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/clientloop.c?rev=1.340&content-type=text/x-cvsweb-markup
---------|---------|---------|---------|---------|---------|---------|--

Reply via email to