mirror of
git://anongit.mindrot.org/openssh.git
synced 2024-12-15 23:33:56 +08:00
da108ece68
[auth-options.c auth1.c auth2.c bufaux.c buffer.h kex.c key.c packet.c] [packet.h ssh-dss.c ssh-rsa.c] Add buffer_get_cstring() and related functions that verify that the string extracted from the buffer contains no embedded \0 characters* This prevents random (possibly malicious) crap from being appended to strings where it would not be noticed if the string is used with a string(3) function. Use the new API in a few sensitive places. * actually, we allow a single one at the end of the string for now because we don't know how many deployed implementations get this wrong, but don't count on this to remain indefinitely.
269 lines
7.2 KiB
C
269 lines
7.2 KiB
C
/* $OpenBSD: ssh-rsa.c,v 1.45 2010/08/31 09:58:37 djm Exp $ */
|
|
/*
|
|
* Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <openssl/evp.h>
|
|
#include <openssl/err.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
#include "xmalloc.h"
|
|
#include "log.h"
|
|
#include "buffer.h"
|
|
#include "key.h"
|
|
#include "compat.h"
|
|
#include "misc.h"
|
|
#include "ssh.h"
|
|
|
|
static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *);
|
|
|
|
/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
|
|
int
|
|
ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp,
|
|
const u_char *data, u_int datalen)
|
|
{
|
|
const EVP_MD *evp_md;
|
|
EVP_MD_CTX md;
|
|
u_char digest[EVP_MAX_MD_SIZE], *sig;
|
|
u_int slen, dlen, len;
|
|
int ok, nid;
|
|
Buffer b;
|
|
|
|
if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA &&
|
|
key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) {
|
|
error("ssh_rsa_sign: no RSA key");
|
|
return -1;
|
|
}
|
|
nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1;
|
|
if ((evp_md = EVP_get_digestbynid(nid)) == NULL) {
|
|
error("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid);
|
|
return -1;
|
|
}
|
|
EVP_DigestInit(&md, evp_md);
|
|
EVP_DigestUpdate(&md, data, datalen);
|
|
EVP_DigestFinal(&md, digest, &dlen);
|
|
|
|
slen = RSA_size(key->rsa);
|
|
sig = xmalloc(slen);
|
|
|
|
ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa);
|
|
memset(digest, 'd', sizeof(digest));
|
|
|
|
if (ok != 1) {
|
|
int ecode = ERR_get_error();
|
|
|
|
error("ssh_rsa_sign: RSA_sign failed: %s",
|
|
ERR_error_string(ecode, NULL));
|
|
xfree(sig);
|
|
return -1;
|
|
}
|
|
if (len < slen) {
|
|
u_int diff = slen - len;
|
|
debug("slen %u > len %u", slen, len);
|
|
memmove(sig + diff, sig, len);
|
|
memset(sig, 0, diff);
|
|
} else if (len > slen) {
|
|
error("ssh_rsa_sign: slen %u slen2 %u", slen, len);
|
|
xfree(sig);
|
|
return -1;
|
|
}
|
|
/* encode signature */
|
|
buffer_init(&b);
|
|
buffer_put_cstring(&b, "ssh-rsa");
|
|
buffer_put_string(&b, sig, slen);
|
|
len = buffer_len(&b);
|
|
if (lenp != NULL)
|
|
*lenp = len;
|
|
if (sigp != NULL) {
|
|
*sigp = xmalloc(len);
|
|
memcpy(*sigp, buffer_ptr(&b), len);
|
|
}
|
|
buffer_free(&b);
|
|
memset(sig, 's', slen);
|
|
xfree(sig);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
|
|
const u_char *data, u_int datalen)
|
|
{
|
|
Buffer b;
|
|
const EVP_MD *evp_md;
|
|
EVP_MD_CTX md;
|
|
char *ktype;
|
|
u_char digest[EVP_MAX_MD_SIZE], *sigblob;
|
|
u_int len, dlen, modlen;
|
|
int rlen, ret, nid;
|
|
|
|
if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA &&
|
|
key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) {
|
|
error("ssh_rsa_verify: no RSA key");
|
|
return -1;
|
|
}
|
|
if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
|
|
error("ssh_rsa_verify: RSA modulus too small: %d < minimum %d bits",
|
|
BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE);
|
|
return -1;
|
|
}
|
|
buffer_init(&b);
|
|
buffer_append(&b, signature, signaturelen);
|
|
ktype = buffer_get_cstring(&b, NULL);
|
|
if (strcmp("ssh-rsa", ktype) != 0) {
|
|
error("ssh_rsa_verify: cannot handle type %s", ktype);
|
|
buffer_free(&b);
|
|
xfree(ktype);
|
|
return -1;
|
|
}
|
|
xfree(ktype);
|
|
sigblob = buffer_get_string(&b, &len);
|
|
rlen = buffer_len(&b);
|
|
buffer_free(&b);
|
|
if (rlen != 0) {
|
|
error("ssh_rsa_verify: remaining bytes in signature %d", rlen);
|
|
xfree(sigblob);
|
|
return -1;
|
|
}
|
|
/* RSA_verify expects a signature of RSA_size */
|
|
modlen = RSA_size(key->rsa);
|
|
if (len > modlen) {
|
|
error("ssh_rsa_verify: len %u > modlen %u", len, modlen);
|
|
xfree(sigblob);
|
|
return -1;
|
|
} else if (len < modlen) {
|
|
u_int diff = modlen - len;
|
|
debug("ssh_rsa_verify: add padding: modlen %u > len %u",
|
|
modlen, len);
|
|
sigblob = xrealloc(sigblob, 1, modlen);
|
|
memmove(sigblob + diff, sigblob, len);
|
|
memset(sigblob, 0, diff);
|
|
len = modlen;
|
|
}
|
|
nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1;
|
|
if ((evp_md = EVP_get_digestbynid(nid)) == NULL) {
|
|
error("ssh_rsa_verify: EVP_get_digestbynid %d failed", nid);
|
|
xfree(sigblob);
|
|
return -1;
|
|
}
|
|
EVP_DigestInit(&md, evp_md);
|
|
EVP_DigestUpdate(&md, data, datalen);
|
|
EVP_DigestFinal(&md, digest, &dlen);
|
|
|
|
ret = openssh_RSA_verify(nid, digest, dlen, sigblob, len, key->rsa);
|
|
memset(digest, 'd', sizeof(digest));
|
|
memset(sigblob, 's', len);
|
|
xfree(sigblob);
|
|
debug("ssh_rsa_verify: signature %scorrect", (ret==0) ? "in" : "");
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* See:
|
|
* http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
|
|
* ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
|
|
*/
|
|
/*
|
|
* id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
|
|
* oiw(14) secsig(3) algorithms(2) 26 }
|
|
*/
|
|
static const u_char id_sha1[] = {
|
|
0x30, 0x21, /* type Sequence, length 0x21 (33) */
|
|
0x30, 0x09, /* type Sequence, length 0x09 */
|
|
0x06, 0x05, /* type OID, length 0x05 */
|
|
0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */
|
|
0x05, 0x00, /* NULL */
|
|
0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */
|
|
};
|
|
/*
|
|
* id-md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
|
|
* rsadsi(113549) digestAlgorithm(2) 5 }
|
|
*/
|
|
static const u_char id_md5[] = {
|
|
0x30, 0x20, /* type Sequence, length 0x20 (32) */
|
|
0x30, 0x0c, /* type Sequence, length 0x09 */
|
|
0x06, 0x08, /* type OID, length 0x05 */
|
|
0x2a, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* id-md5 */
|
|
0x05, 0x00, /* NULL */
|
|
0x04, 0x10 /* Octet string, length 0x10 (16), followed by md5 hash */
|
|
};
|
|
|
|
static int
|
|
openssh_RSA_verify(int type, u_char *hash, u_int hashlen,
|
|
u_char *sigbuf, u_int siglen, RSA *rsa)
|
|
{
|
|
u_int ret, rsasize, oidlen = 0, hlen = 0;
|
|
int len, oidmatch, hashmatch;
|
|
const u_char *oid = NULL;
|
|
u_char *decrypted = NULL;
|
|
|
|
ret = 0;
|
|
switch (type) {
|
|
case NID_sha1:
|
|
oid = id_sha1;
|
|
oidlen = sizeof(id_sha1);
|
|
hlen = 20;
|
|
break;
|
|
case NID_md5:
|
|
oid = id_md5;
|
|
oidlen = sizeof(id_md5);
|
|
hlen = 16;
|
|
break;
|
|
default:
|
|
goto done;
|
|
}
|
|
if (hashlen != hlen) {
|
|
error("bad hashlen");
|
|
goto done;
|
|
}
|
|
rsasize = RSA_size(rsa);
|
|
if (siglen == 0 || siglen > rsasize) {
|
|
error("bad siglen");
|
|
goto done;
|
|
}
|
|
decrypted = xmalloc(rsasize);
|
|
if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
|
|
RSA_PKCS1_PADDING)) < 0) {
|
|
error("RSA_public_decrypt failed: %s",
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
goto done;
|
|
}
|
|
if (len < 0 || (u_int)len != hlen + oidlen) {
|
|
error("bad decrypted len: %d != %d + %d", len, hlen, oidlen);
|
|
goto done;
|
|
}
|
|
oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
|
|
hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
|
|
if (!oidmatch) {
|
|
error("oid mismatch");
|
|
goto done;
|
|
}
|
|
if (!hashmatch) {
|
|
error("hash mismatch");
|
|
goto done;
|
|
}
|
|
ret = 1;
|
|
done:
|
|
if (decrypted)
|
|
xfree(decrypted);
|
|
return ret;
|
|
}
|