/*
* Generic SSH public-key handling operations. In particular,
* reading of SSH public-key files, and also the generic `sign'
- * operation for ssh2 (which checks the type of the key and
+ * operation for SSH-2 (which checks the type of the key and
* dispatches to the appropriate key-type specific function).
*/
#include "ssh.h"
#include "misc.h"
-#define PUT_32BIT(cp, value) do { \
- (cp)[3] = (value); \
- (cp)[2] = (value) >> 8; \
- (cp)[1] = (value) >> 16; \
- (cp)[0] = (value) >> 24; } while (0)
-
-#define GET_32BIT(cp) \
- (((unsigned long)(unsigned char)(cp)[0] << 24) | \
- ((unsigned long)(unsigned char)(cp)[1] << 16) | \
- ((unsigned long)(unsigned char)(cp)[2] << 8) | \
- ((unsigned long)(unsigned char)(cp)[3]))
-
#define rsa_signature "SSH PRIVATE KEY FILE FORMAT 1.1\n"
#define BASE64_TOINT(x) ( (x)-'A'<26 ? (x)-'A'+0 :\
(x)=='/' ? 63 : 0 )
static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
- char **commentptr, char *passphrase)
+ char **commentptr, char *passphrase,
+ const char **error)
{
unsigned char buf[16384];
unsigned char keybuf[16];
struct MD5Context md5c;
char *comment;
+ *error = NULL;
+
/* Slurp the whole file (minus the header) into a buffer. */
len = fread(buf, 1, sizeof(buf), fp);
fclose(fp);
- if (len < 0 || len == sizeof(buf))
+ if (len < 0 || len == sizeof(buf)) {
+ *error = "error reading file";
goto end; /* file too big or not read */
+ }
i = 0;
+ *error = "file format error";
/*
* A zero byte. (The signature includes a terminating NUL.)
|| buf[i + 3] != 0) goto end; /* reserved field nonzero, panic! */
i += 4;
- /* Now the serious stuff. An ordinary SSH 1 public key. */
- i += makekey(buf + i, key, NULL, 1);
- if (len - i < 0)
+ /* Now the serious stuff. An ordinary SSH-1 public key. */
+ i += makekey(buf + i, len, key, NULL, 1);
+ if (i < 0)
goto end; /* overran */
- if (pub_only) {
- ret = 1;
- goto end;
- }
-
/* Next, the comment field. */
j = GET_32BIT(buf + i);
i += 4;
}
i += j;
if (commentptr)
- *commentptr = comment;
+ *commentptr = dupstr(comment);
if (key)
key->comment = comment;
+ else
+ sfree(comment);
+
+ if (pub_only) {
+ ret = 1;
+ goto end;
+ }
+
if (!key) {
- return ciphertype != 0;
+ ret = ciphertype != 0;
+ *error = NULL;
+ goto end;
}
/*
if (len - i < 4)
goto end;
if (buf[i] != buf[i + 2] || buf[i + 1] != buf[i + 3]) {
+ *error = "wrong passphrase";
ret = -1;
goto end;
}
* decryption exponent, and then the three auxiliary values
* (iqmp, q, p).
*/
- i += makeprivate(buf + i, key);
- if (len - i < 0)
- goto end;
- i += ssh1_read_bignum(buf + i, &key->iqmp);
- if (len - i < 0)
- goto end;
- i += ssh1_read_bignum(buf + i, &key->q);
- if (len - i < 0)
- goto end;
- i += ssh1_read_bignum(buf + i, &key->p);
- if (len - i < 0)
- goto end;
+ j = makeprivate(buf + i, len - i, key);
+ if (j < 0) goto end;
+ i += j;
+ j = ssh1_read_bignum(buf + i, len - i, &key->iqmp);
+ if (j < 0) goto end;
+ i += j;
+ j = ssh1_read_bignum(buf + i, len - i, &key->q);
+ if (j < 0) goto end;
+ i += j;
+ j = ssh1_read_bignum(buf + i, len - i, &key->p);
+ if (j < 0) goto end;
+ i += j;
if (!rsa_verify(key)) {
+ *error = "rsa_verify failed";
freersakey(key);
ret = 0;
} else
return ret;
}
-int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase)
+int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase,
+ const char **errorstr)
{
FILE *fp;
char buf[64];
+ int ret = 0;
+ const char *error = NULL;
- fp = f_open(*filename, "rb");
- if (!fp)
- return 0; /* doesn't even exist */
+ fp = f_open(filename, "rb", FALSE);
+ if (!fp) {
+ error = "can't open file";
+ goto end;
+ }
/*
* Read the first line of the file and see if it's a v1 private
* key file.
*/
if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
- return loadrsakey_main(fp, key, FALSE, NULL, passphrase);
+ /*
+ * This routine will take care of calling fclose() for us.
+ */
+ ret = loadrsakey_main(fp, key, FALSE, NULL, passphrase, &error);
+ fp = NULL;
+ goto end;
}
/*
* Otherwise, we have nothing. Return empty-handed.
*/
- fclose(fp);
- return 0;
+ error = "not an SSH-1 RSA file";
+
+ end:
+ if (fp)
+ fclose(fp);
+ if ((ret != 1) && errorstr)
+ *errorstr = error;
+ return ret;
}
/*
FILE *fp;
char buf[64];
- fp = f_open(*filename, "rb");
+ fp = f_open(filename, "rb", FALSE);
if (!fp)
return 0; /* doesn't even exist */
* key file.
*/
if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
- return loadrsakey_main(fp, NULL, FALSE, comment, NULL);
+ const char *dummy;
+ /*
+ * This routine will take care of calling fclose() for us.
+ */
+ return loadrsakey_main(fp, NULL, FALSE, comment, NULL, &dummy);
}
fclose(fp);
return 0; /* wasn't the right kind of file */
* an RSA key, as given in the agent protocol (modulus bits,
* exponent, modulus).
*/
-int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen)
+int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,
+ char **commentptr, const char **errorstr)
{
FILE *fp;
char buf[64];
struct RSAKey key;
int ret;
+ const char *error = NULL;
/* Default return if we fail. */
*blob = NULL;
*bloblen = 0;
ret = 0;
- fp = f_open(*filename, "rb");
- if (!fp)
- return 0; /* doesn't even exist */
+ fp = f_open(filename, "rb", FALSE);
+ if (!fp) {
+ error = "can't open file";
+ goto end;
+ }
/*
* Read the first line of the file and see if it's a v1 private
*/
if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
memset(&key, 0, sizeof(key));
- if (loadrsakey_main(fp, &key, TRUE, NULL, NULL)) {
+ if (loadrsakey_main(fp, &key, TRUE, commentptr, NULL, &error)) {
*blob = rsa_public_blob(&key, bloblen);
freersakey(&key);
ret = 1;
+ fp = NULL;
}
+ } else {
+ error = "not an SSH-1 RSA file";
}
- fclose(fp);
+
+ end:
+ if (fp)
+ fclose(fp);
+ if ((ret != 1) && errorstr)
+ *errorstr = error;
return ret;
}
p += 4;
/*
- * An ordinary SSH 1 public key consists of: a uint32
+ * An ordinary SSH-1 public key consists of: a uint32
* containing the bit count, then two bignums containing the
* modulus and exponent respectively.
*/
/*
* Done. Write the result to the file.
*/
- fp = f_open(*filename, "wb");
+ fp = f_open(filename, "wb", TRUE);
if (fp) {
int ret = (fwrite(buf, 1, p - buf, fp) == (size_t) (p - buf));
- ret = ret && (fclose(fp) == 0);
+ if (fclose(fp))
+ ret = 0;
return ret;
} else
return 0;
}
/* ----------------------------------------------------------------------
- * SSH2 private key load/store functions.
+ * SSH-2 private key load/store functions.
*/
/*
- * PuTTY's own format for SSH2 keys is as follows:
+ * PuTTY's own format for SSH-2 keys is as follows:
*
* The file is text. Lines are terminated by CRLF, although CR-only
* and LF-only are tolerated on input.
*
* Next there is a line saying "Public-Lines: " plus a number N.
* The following N lines contain a base64 encoding of the public
- * part of the key. This is encoded as the standard SSH2 public key
+ * part of the key. This is encoded as the standard SSH-2 public key
* blob (with no initial length): so for RSA, for example, it will
* read
*
* data "putty-private-key-file-mac-key"
* data passphrase
*
- * Encrypted keys should have a MAC, whereas unencrypted ones must
- * have a hash.
+ * (An empty passphrase is used for unencrypted keys.)
*
* If the key is encrypted, the encryption key is derived from the
* passphrase by means of a succession of SHA-1 hashes. Each hash
* with "PuTTY-User-Key-File-1" (version number differs). In this
* format the Private-MAC: field only covers the private-plaintext
* field and nothing else (and without the 4-byte string length on
- * the front too). Moreover, for RSA keys the Private-MAC: field
- * can be replaced with a Private-Hash: field which is a plain
- * SHA-1 hash instead of an HMAC. This is not allowable in DSA
- * keys. (Yes, the old format was a mess. Guess why it changed :-)
+ * the front too). Moreover, the Private-MAC: field can be replaced
+ * with a Private-Hash: field which is a plain SHA-1 hash instead of
+ * an HMAC (this was generated for unencrypted keys).
*/
static int read_header(FILE * fp, char *header)
while (1) {
c = fgetc(fp);
- if (c == '\r' || c == '\n') {
- c = fgetc(fp);
- if (c != '\r' && c != '\n' && c != EOF)
- ungetc(c, fp);
+ if (c == '\r' || c == '\n' || c == EOF) {
+ if (c != EOF) {
+ c = fgetc(fp);
+ if (c != '\r' && c != '\n')
+ ungetc(c, fp);
+ }
return text;
}
- if (c == EOF) {
- sfree(text);
- return NULL;
- }
- if (len + 1 > size) {
+ if (len + 1 >= size) {
size += 128;
text = sresize(text, size, char);
}
NULL, NULL, NULL
};
+const struct ssh_signkey *find_pubkey_alg(const char *name)
+{
+ if (!strcmp(name, "ssh-rsa"))
+ return &ssh_rsa;
+ else if (!strcmp(name, "ssh-dss"))
+ return &ssh_dss;
+ else
+ return NULL;
+}
+
struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
- char *passphrase)
+ char *passphrase, const char **errorstr)
{
FILE *fp;
char header[40], *b, *encryption, *comment, *mac;
int public_blob_len, private_blob_len;
int i, is_mac, old_fmt;
int passlen = passphrase ? strlen(passphrase) : 0;
+ const char *error = NULL;
ret = NULL; /* return NULL for most errors */
encryption = comment = mac = NULL;
public_blob = private_blob = NULL;
- fp = f_open(*filename, "rb");
- if (!fp)
+ fp = f_open(filename, "rb", FALSE);
+ if (!fp) {
+ error = "can't open file";
goto error;
+ }
/* Read the first header line which contains the key type. */
if (!read_header(fp, header))
/* this is an old key file; warn and then continue */
old_keyfile_warning();
old_fmt = 1;
- } else
+ } else {
+ error = "not a PuTTY SSH-2 private key";
goto error;
+ }
+ error = "file format error";
if ((b = read_body(fp)) == NULL)
goto error;
/* Select key algorithm structure. */
- if (!strcmp(b, "ssh-rsa"))
- alg = &ssh_rsa;
- else if (!strcmp(b, "ssh-dss"))
- alg = &ssh_dss;
- else {
+ alg = find_pubkey_alg(b);
+ if (!alg) {
sfree(b);
goto error;
}
if ((mac = read_body(fp)) == NULL)
goto error;
is_mac = 1;
- } else if (0 == strcmp(header, "Private-Hash") &&
- alg == &ssh_rsa && old_fmt) {
+ } else if (0 == strcmp(header, "Private-Hash") && old_fmt) {
if ((mac = read_body(fp)) == NULL)
goto error;
is_mac = 0;
SHA_Init(&s);
SHA_Bytes(&s, header, sizeof(header)-1);
- if (passphrase)
+ if (cipher && passphrase)
SHA_Bytes(&s, passphrase, passlen);
SHA_Final(&s, mackey);
if (strcmp(mac, realmac)) {
/* An incorrect MAC is an unconditional Error if the key is
* unencrypted. Otherwise, it means Wrong Passphrase. */
- ret = cipher ? SSH2_WRONG_PASSPHRASE : NULL;
+ if (cipher) {
+ error = "wrong passphrase";
+ ret = SSH2_WRONG_PASSPHRASE;
+ } else {
+ error = "MAC failed";
+ ret = NULL;
+ }
goto error;
}
}
sfree(ret->comment);
sfree(ret);
ret = NULL;
+ error = "createkey failed";
+ goto error;
}
sfree(public_blob);
sfree(private_blob);
sfree(encryption);
+ if (errorstr)
+ *errorstr = NULL;
return ret;
/*
sfree(public_blob);
if (private_blob)
sfree(private_blob);
+ if (errorstr)
+ *errorstr = error;
return ret;
}
-char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
- int *pub_blob_len)
+unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
+ int *pub_blob_len, char **commentptr,
+ const char **errorstr)
{
FILE *fp;
char header[40], *b;
unsigned char *public_blob;
int public_blob_len;
int i;
+ const char *error = NULL;
+ char *comment;
public_blob = NULL;
- fp = f_open(*filename, "rb");
- if (!fp)
+ fp = f_open(filename, "rb", FALSE);
+ if (!fp) {
+ error = "can't open file";
goto error;
+ }
/* Read the first header line which contains the key type. */
if (!read_header(fp, header)
|| (0 != strcmp(header, "PuTTY-User-Key-File-2") &&
- 0 != strcmp(header, "PuTTY-User-Key-File-1")))
+ 0 != strcmp(header, "PuTTY-User-Key-File-1"))) {
+ error = "not a PuTTY SSH-2 private key";
goto error;
+ }
+ error = "file format error";
if ((b = read_body(fp)) == NULL)
goto error;
- /* Select key algorithm structure. Currently only ssh-rsa. */
- if (!strcmp(b, "ssh-rsa"))
- alg = &ssh_rsa;
- else if (!strcmp(b, "ssh-dss"))
- alg = &ssh_dss;
- else {
+ /* Select key algorithm structure. */
+ alg = find_pubkey_alg(b);
+ if (!alg) {
sfree(b);
goto error;
}
/* Read the Comment header line. */
if (!read_header(fp, header) || 0 != strcmp(header, "Comment"))
goto error;
- if ((b = read_body(fp)) == NULL)
+ if ((comment = read_body(fp)) == NULL)
goto error;
- sfree(b); /* we don't care */
+
+ if (commentptr)
+ *commentptr = comment;
+ else
+ sfree(comment);
/* Read the Public-Lines header line and the public blob. */
if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines"))
*pub_blob_len = public_blob_len;
if (algorithm)
*algorithm = alg->name;
- return (char *)public_blob;
+ return public_blob;
/*
* Error processing.
fclose(fp);
if (public_blob)
sfree(public_blob);
+ if (errorstr)
+ *errorstr = error;
return NULL;
}
if (commentptr)
*commentptr = NULL;
- fp = f_open(*filename, "rb");
+ fp = f_open(filename, "rb", FALSE);
if (!fp)
return 0;
if (!read_header(fp, header)
memset(&s, 0, sizeof(s));
}
- fp = f_open(*filename, "w");
+ fp = f_open(filename, "w", TRUE);
if (!fp)
return 0;
fprintf(fp, "PuTTY-User-Key-File-2: %s\n", key->alg->name);
const char openssh_sig[] = "-----BEGIN ";
int i;
- fp = f_open(*filename, "r");
+ fp = f_open(filename, "r", FALSE);
if (!fp)
return SSH_KEYTYPE_UNOPENABLE;
i = fread(buf, 1, sizeof(buf), fp);
switch (type) {
case SSH_KEYTYPE_UNOPENABLE: return "unable to open file"; break;
case SSH_KEYTYPE_UNKNOWN: return "not a private key"; break;
- case SSH_KEYTYPE_SSH1: return "SSH1 private key"; break;
- case SSH_KEYTYPE_SSH2: return "PuTTY SSH2 private key"; break;
- case SSH_KEYTYPE_OPENSSH: return "OpenSSH SSH2 private key"; break;
- case SSH_KEYTYPE_SSHCOM: return "ssh.com SSH2 private key"; break;
+ case SSH_KEYTYPE_SSH1: return "SSH-1 private key"; break;
+ case SSH_KEYTYPE_SSH2: return "PuTTY SSH-2 private key"; break;
+ case SSH_KEYTYPE_OPENSSH: return "OpenSSH SSH-2 private key"; break;
+ case SSH_KEYTYPE_SSHCOM: return "ssh.com SSH-2 private key"; break;
default: return "INTERNAL ERROR"; break;
}
}