Created new data types `Filename' and `FontSpec', intended to be
[u/mdw/putty] / sshpubk.c
index 86c9fc0..8bcbbc7 100644 (file)
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -9,6 +9,7 @@
 #include <stdlib.h>
 #include <assert.h>
 
+#include "putty.h"
 #include "ssh.h"
 #include "misc.h"
 
@@ -32,7 +33,7 @@
                           (x)=='+' ? 62 : \
                           (x)=='/' ? 63 : 0 )
 
-static int loadrsakey_main(FILE * fp, struct RSAKey *key,
+static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
                           char **commentptr, char *passphrase)
 {
     unsigned char buf[16384];
@@ -76,6 +77,11 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key,
     if (len - i < 0)
        goto end;                      /* overran */
 
+    if (pub_only) {
+       ret = 1;
+       goto end;
+    }
+
     /* Next, the comment field. */
     j = GET_32BIT(buf + i);
     i += 4;
@@ -100,7 +106,7 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key,
      */
     if (ciphertype) {
        MD5Init(&md5c);
-       MD5Update(&md5c, passphrase, strlen(passphrase));
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
        MD5Final(keybuf, &md5c);
        des3_decrypt_pubkey(keybuf, buf + i, (len - i + 7) & ~7);
        memset(keybuf, 0, sizeof(keybuf));      /* burn the evidence */
@@ -147,12 +153,12 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key,
     return ret;
 }
 
-int loadrsakey(char *filename, struct RSAKey *key, char *passphrase)
+int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase)
 {
     FILE *fp;
-    unsigned char buf[64];
+    char buf[64];
 
-    fp = fopen(filename, "rb");
+    fp = f_open(*filename, "rb");
     if (!fp)
        return 0;                      /* doesn't even exist */
 
@@ -161,7 +167,7 @@ int loadrsakey(char *filename, struct RSAKey *key, char *passphrase)
      * key file.
      */
     if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
-       return loadrsakey_main(fp, key, NULL, passphrase);
+       return loadrsakey_main(fp, key, FALSE, NULL, passphrase);
     }
 
     /*
@@ -175,12 +181,12 @@ int loadrsakey(char *filename, struct RSAKey *key, char *passphrase)
  * See whether an RSA key is encrypted. Return its comment field as
  * well.
  */
-int rsakey_encrypted(char *filename, char **comment)
+int rsakey_encrypted(const Filename *filename, char **comment)
 {
     FILE *fp;
-    unsigned char buf[64];
+    char buf[64];
 
-    fp = fopen(filename, "rb");
+    fp = f_open(*filename, "rb");
     if (!fp)
        return 0;                      /* doesn't even exist */
 
@@ -189,16 +195,53 @@ int rsakey_encrypted(char *filename, char **comment)
      * key file.
      */
     if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
-       return loadrsakey_main(fp, NULL, comment, NULL);
+       return loadrsakey_main(fp, NULL, FALSE, comment, NULL);
     }
     fclose(fp);
     return 0;                         /* wasn't the right kind of file */
 }
 
 /*
+ * Return a malloc'ed chunk of memory containing the public blob of
+ * an RSA key, as given in the agent protocol (modulus bits,
+ * exponent, modulus).
+ */
+int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen)
+{
+    FILE *fp;
+    char buf[64];
+    struct RSAKey key;
+    int ret;
+
+    /* Default return if we fail. */
+    *blob = NULL;
+    *bloblen = 0;
+    ret = 0;
+
+    fp = f_open(*filename, "rb");
+    if (!fp)
+       return 0;                      /* doesn't even exist */
+
+    /*
+     * 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)) {
+       memset(&key, 0, sizeof(key));
+       if (loadrsakey_main(fp, &key, TRUE, NULL, NULL)) {
+           *blob = rsa_public_blob(&key, bloblen);
+           freersakey(&key);
+           ret = 1;
+       }
+    }
+    fclose(fp);
+    return ret;
+}
+
+/*
  * Save an RSA key file. Return nonzero on success.
  */
-int saversakey(char *filename, struct RSAKey *key, char *passphrase)
+int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase)
 {
     unsigned char buf[16384];
     unsigned char keybuf[16];
@@ -279,7 +322,7 @@ int saversakey(char *filename, struct RSAKey *key, char *passphrase)
      */
     if (passphrase) {
        MD5Init(&md5c);
-       MD5Update(&md5c, passphrase, strlen(passphrase));
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
        MD5Final(keybuf, &md5c);
        des3_encrypt_pubkey(keybuf, estart, p - estart);
        memset(keybuf, 0, sizeof(keybuf));      /* burn the evidence */
@@ -288,7 +331,7 @@ int saversakey(char *filename, struct RSAKey *key, char *passphrase)
     /*
      * Done. Write the result to the file.
      */
-    fp = fopen(filename, "wb");
+    fp = f_open(*filename, "wb");
     if (fp) {
        int ret = (fwrite(buf, 1, p - buf, fp) == (size_t) (p - buf));
        ret = ret && (fclose(fp) == 0);
@@ -487,7 +530,7 @@ int base64_decode_atom(char *atom, unsigned char *out)
     return len;
 }
 
-static char *read_blob(FILE * fp, int nlines, int *bloblen)
+static unsigned char *read_blob(FILE * fp, int nlines, int *bloblen)
 {
     unsigned char *blob;
     char *line;
@@ -531,7 +574,8 @@ struct ssh2_userkey ssh2_wrong_passphrase = {
     NULL, NULL, NULL
 };
 
-struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
+struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
+                                      char *passphrase)
 {
     FILE *fp;
     char header[40], *b, *encryption, *comment, *mac;
@@ -547,7 +591,7 @@ struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
     comment = mac = NULL;
     public_blob = private_blob = NULL;
 
-    fp = fopen(filename, "rb");
+    fp = f_open(*filename, "rb");
     if (!fp)
        goto error;
 
@@ -768,7 +812,7 @@ struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
     return ret;
 }
 
-char *ssh2_userkey_loadpub(char *filename, char **algorithm,
+char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
                           int *pub_blob_len)
 {
     FILE *fp;
@@ -780,7 +824,7 @@ char *ssh2_userkey_loadpub(char *filename, char **algorithm,
 
     public_blob = NULL;
 
-    fp = fopen(filename, "rb");
+    fp = f_open(*filename, "rb");
     if (!fp)
        goto error;
 
@@ -827,9 +871,11 @@ char *ssh2_userkey_loadpub(char *filename, char **algorithm,
        goto error;
 
     fclose(fp);
-    *pub_blob_len = public_blob_len;
-    *algorithm = alg->name;
-    return public_blob;
+    if (pub_blob_len)
+       *pub_blob_len = public_blob_len;
+    if (algorithm)
+       *algorithm = alg->name;
+    return (char *)public_blob;
 
     /*
      * Error processing.
@@ -842,7 +888,7 @@ char *ssh2_userkey_loadpub(char *filename, char **algorithm,
     return NULL;
 }
 
-int ssh2_userkey_encrypted(char *filename, char **commentptr)
+int ssh2_userkey_encrypted(const Filename *filename, char **commentptr)
 {
     FILE *fp;
     char header[40], *b, *comment;
@@ -851,7 +897,7 @@ int ssh2_userkey_encrypted(char *filename, char **commentptr)
     if (commentptr)
        *commentptr = NULL;
 
-    fp = fopen(filename, "rb");
+    fp = f_open(*filename, "rb");
     if (!fp)
        return 0;
     if (!read_header(fp, header)
@@ -905,52 +951,30 @@ int base64_lines(int datalen)
     return (datalen + 47) / 48;
 }
 
-void base64_encode_atom(unsigned char *data, int n, char *out)
-{
-    static const char base64_chars[] =
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-    unsigned word;
-
-    word = data[0] << 16;
-    if (n > 1)
-       word |= data[1] << 8;
-    if (n > 2)
-       word |= data[2];
-    out[0] = base64_chars[(word >> 18) & 0x3F];
-    out[1] = base64_chars[(word >> 12) & 0x3F];
-    if (n > 1)
-       out[2] = base64_chars[(word >> 6) & 0x3F];
-    else
-       out[2] = '=';
-    if (n > 2)
-       out[3] = base64_chars[word & 0x3F];
-    else
-       out[3] = '=';
-}
-
-void base64_encode(FILE * fp, unsigned char *data, int datalen)
+void base64_encode(FILE * fp, unsigned char *data, int datalen, int cpl)
 {
     int linelen = 0;
     char out[4];
-    int n;
+    int n, i;
 
     while (datalen > 0) {
-       if (linelen >= 64) {
-           linelen = 0;
-           fputc('\n', fp);
-       }
        n = (datalen < 3 ? datalen : 3);
        base64_encode_atom(data, n, out);
        data += n;
        datalen -= n;
-       fwrite(out, 1, 4, fp);
-       linelen += 4;
+       for (i = 0; i < 4; i++) {
+           if (linelen >= cpl) {
+               linelen = 0;
+               fputc('\n', fp);
+           }
+           fputc(out[i], fp);
+           linelen++;
+       }
     }
     fputc('\n', fp);
 }
 
-int ssh2_save_userkey(char *filename, struct ssh2_userkey *key,
+int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
                      char *passphrase)
 {
     FILE *fp;
@@ -1034,7 +1058,7 @@ int ssh2_save_userkey(char *filename, struct ssh2_userkey *key,
     }
 
     if (passphrase) {
-       char key[40];
+       unsigned char key[40];
        SHA_State s;
 
        passlen = strlen(passphrase);
@@ -1054,16 +1078,16 @@ int ssh2_save_userkey(char *filename, struct ssh2_userkey *key,
        memset(&s, 0, sizeof(s));
     }
 
-    fp = fopen(filename, "w");
+    fp = f_open(*filename, "w");
     if (!fp)
        return 0;
     fprintf(fp, "PuTTY-User-Key-File-2: %s\n", key->alg->name);
     fprintf(fp, "Encryption: %s\n", cipherstr);
     fprintf(fp, "Comment: %s\n", key->comment);
     fprintf(fp, "Public-Lines: %d\n", base64_lines(pub_blob_len));
-    base64_encode(fp, pub_blob, pub_blob_len);
+    base64_encode(fp, pub_blob, pub_blob_len, 64);
     fprintf(fp, "Private-Lines: %d\n", base64_lines(priv_encrypted_len));
-    base64_encode(fp, priv_blob_encrypted, priv_encrypted_len);
+    base64_encode(fp, priv_blob_encrypted, priv_encrypted_len, 64);
     fprintf(fp, "Private-MAC: ");
     for (i = 0; i < 20; i++)
        fprintf(fp, "%02x", priv_mac[i]);
@@ -1078,22 +1102,51 @@ int ssh2_save_userkey(char *filename, struct ssh2_userkey *key,
 }
 
 /* ----------------------------------------------------------------------
- * A function to determine which version of SSH to try on a private
- * key file. Returns 0 on failure, 1 or 2 on success.
+ * A function to determine the type of a private key file. Returns
+ * 0 on failure, 1 or 2 on success.
  */
-int keyfile_version(char *filename)
+int key_type(const Filename *filename)
 {
     FILE *fp;
+    char buf[32];
+    const char putty2_sig[] = "PuTTY-User-Key-File-";
+    const char sshcom_sig[] = "---- BEGIN SSH2 ENCRYPTED PRIVAT";
+    const char openssh_sig[] = "-----BEGIN ";
     int i;
 
-    fp = fopen(filename, "r");
+    fp = f_open(*filename, "r");
     if (!fp)
-       return 0;
-    i = fgetc(fp);
+       return SSH_KEYTYPE_UNOPENABLE;
+    i = fread(buf, 1, sizeof(buf), fp);
     fclose(fp);
-    if (i == 'S')
-       return 1;                      /* "SSH PRIVATE KEY FORMAT" etc */
-    if (i == 'P')                     /* "PuTTY-User-Key-File" etc */
-       return 2;
-    return 0;                         /* unrecognised or EOF */
+    if (i < 0)
+       return SSH_KEYTYPE_UNOPENABLE;
+    if (i < 32)
+       return SSH_KEYTYPE_UNKNOWN;
+    if (!memcmp(buf, rsa_signature, sizeof(rsa_signature)-1))
+       return SSH_KEYTYPE_SSH1;
+    if (!memcmp(buf, putty2_sig, sizeof(putty2_sig)-1))
+       return SSH_KEYTYPE_SSH2;
+    if (!memcmp(buf, openssh_sig, sizeof(openssh_sig)-1))
+       return SSH_KEYTYPE_OPENSSH;
+    if (!memcmp(buf, sshcom_sig, sizeof(sshcom_sig)-1))
+       return SSH_KEYTYPE_SSHCOM;
+    return SSH_KEYTYPE_UNKNOWN;               /* unrecognised or EOF */
+}
+
+/*
+ * Convert the type word to a string, for `wrong type' error
+ * messages.
+ */
+char *key_type_to_str(int type)
+{
+    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;
+      default: return "INTERNAL ERROR"; break;
+    }
 }