]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - crypt_common.c
aft: Avoid invalid read.
[paraslash.git] / crypt_common.c
index 235b8b8d3e398551fc28bfbc746349c5dc8f6f1c..ff24e356ab8d837f8d59d67a3c983d5264376db1 100644 (file)
@@ -85,7 +85,7 @@ static int check_ssh_key_header(const unsigned char *blob, int blen)
  *
  * \sa \ref uudecode().
  */
-int decode_ssh_key(const char *filename, unsigned char **blob,
+int decode_public_key(const char *filename, unsigned char **blob,
                size_t *decoded_size)
 {
        int ret, ret2;
@@ -135,7 +135,7 @@ int check_private_key_file(const char *file)
        return 1;
 }
 
-void hash_to_asc(unsigned char *hash, char *asc)
+void hash_to_asc(const unsigned char *hash, char *asc)
 {
        int i;
        const char hexchar[] = "0123456789abcdef";
@@ -147,7 +147,7 @@ void hash_to_asc(unsigned char *hash, char *asc)
        asc[2 * HASH_SIZE] = '\0';
 }
 
-int hash_compare(unsigned char *h1, unsigned char *h2)
+int hash_compare(const unsigned char *h1, const unsigned char *h2)
 {
        int i;
 
@@ -159,3 +159,161 @@ int hash_compare(unsigned char *h1, unsigned char *h2)
        }
        return 0;
 }
+
+/**
+ * Check header of an openssh private key and compute bignum offset.
+ *
+ * \param data The base64-decoded key.
+ * \param len The size of the decoded key.
+ *
+ * Several assumptions are made about the key. Most notably, we only support
+ * single unencrypted keys without comments.
+ *
+ * \return The offset at which the first bignum of the private key (the public
+ * exponent n) starts. Negative error code on failure.
+ */
+int find_openssh_bignum_offset(const unsigned char *data, int len)
+{
+       /*
+        * Unencrypted keys without comments always start with the below byte
+        * sequence. See PROTOCOL.key of the openssh package.
+        */
+       static const unsigned char valid_openssh_header[] = {
+               /* string "openssh-key-v1" */
+               0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2d, 0x6b, 0x65,
+                       0x79, 0x2d, 0x76, 0x31,
+               /* length of the cipher name */
+               0x00, 0x00, 0x00, 0x00, 0x04,
+               /* cipher name: "none" */
+               0x6e, 0x6f, 0x6e, 0x65,
+               /* length of the kdfname (only used for encrypted keys) */
+               0x00, 0x00, 0x00, 0x04,
+               /* kdfname: "none" */
+               0x6e, 0x6f, 0x6e, 0x65,
+               /* length of kdfoptions */
+               0x00, 0x00, 0x00, 0x00,
+               /* number of keys */
+               0x00, 0x00, 0x00, 0x01,
+       };
+       uint32_t val;
+       const unsigned char *p, *end = data + len;
+
+       if (len <= sizeof(valid_openssh_header) + 4)
+               return -E_OPENSSH_PARSE;
+       if (memcmp(data, valid_openssh_header, sizeof(valid_openssh_header)))
+               return -E_OPENSSH_PARSE;
+       p = data + sizeof(valid_openssh_header);
+       /* length of public key */
+       val = read_u32_be(p);
+       if (val > end - p - 4)
+               return -E_OPENSSH_PARSE;
+       p += val + 4;
+       /* length of private key */
+       val = read_u32_be(p);
+       if (val > end - p - 4)
+               return -E_OPENSSH_PARSE;
+       p += 4;
+       /* two equal random integers ("checkint") */
+       if (p + 8 > end)
+               return -E_OPENSSH_PARSE;
+       if (read_u32_be(p) != read_u32_be(p + 4))
+               return -E_OPENSSH_PARSE;
+       p += 8;
+       /* length of name of key type "ssh-rsa" */
+       if (p + 11 > end)
+               return -E_OPENSSH_PARSE;
+       if (read_u32_be(p) != 7)
+               return -E_OPENSSH_PARSE;
+       if (memcmp(p + 4, "ssh-rsa", 7))
+               return -E_OPENSSH_PARSE;
+       p += 11;
+       return p - data;
+}
+
+/** Private PEM keys (legacy format) start with this header. */
+#define PRIVATE_PEM_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
+/** Private OPENSSH keys (RFC4716) start with this header. */
+#define PRIVATE_OPENSSH_KEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----"
+/** Private PEM keys (legacy format) end with this footer. */
+#define PRIVATE_PEM_KEY_FOOTER "-----END RSA PRIVATE KEY-----"
+/** Private OPENSSH keys (RFC4716) end with this footer. */
+#define PRIVATE_OPENSSH_KEY_FOOTER "-----END OPENSSH PRIVATE KEY-----"
+
+/**
+ * Decode an openssh-v1 (aka RFC4716) or PEM (aka ASN.1) private key.
+ *
+ * \param key_file The private key file (usually id_rsa).
+ * \param result Pointer to base64-decoded blob is returned here.
+ * \param blob_size The size of the decoded blob.
+ *
+ * This only checks header and footer and base64-decodes the part in between.
+ * No attempt to read the decoded part is made.
+ *
+ * \return Negative on errors, PKT_PEM or PKT_OPENSSH on success, indicating
+ * the type of key.
+ */
+int decode_private_key(const char *key_file, unsigned char **result,
+               size_t *blob_size)
+{
+       int ret, ret2, i, j, key_type;
+       void *map;
+       size_t map_size, key_size;
+       unsigned char *blob = NULL;
+       char *begin, *footer, *key;
+
+       ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
+       if (ret < 0)
+               goto out;
+       ret = -E_KEY_MARKER;
+       if (strncmp(map, PRIVATE_PEM_KEY_HEADER,
+                       strlen(PRIVATE_PEM_KEY_HEADER)) == 0) {
+               key_type = PKT_PEM;
+               begin = map + strlen(PRIVATE_PEM_KEY_HEADER);
+               footer = strstr(map, PRIVATE_PEM_KEY_FOOTER);
+               PARA_INFO_LOG("detected legacy PEM key %s\n", key_file);
+       } else if (strncmp(map, PRIVATE_OPENSSH_KEY_HEADER,
+                       strlen(PRIVATE_OPENSSH_KEY_HEADER)) == 0) {
+               key_type = PKT_OPENSSH;
+               begin = map + strlen(PRIVATE_OPENSSH_KEY_HEADER);
+               footer = strstr(map, PRIVATE_OPENSSH_KEY_FOOTER);
+               PARA_INFO_LOG("detected openssh key %s\n", key_file);
+       } else
+               goto unmap;
+       if (!footer)
+               goto unmap;
+       /* skip whitespace at the beginning */
+       for (; begin < footer; begin++) {
+               if (para_isspace(*begin))
+                       continue;
+               break;
+       }
+       ret = -E_KEY_MARKER;
+       if (begin >= footer)
+               goto unmap;
+
+       key_size = footer - begin;
+       key = para_malloc(key_size + 1);
+       for (i = 0, j = 0; begin + i < footer; i++) {
+               if (para_isspace(begin[i]))
+                       continue;
+               key[j++] = begin[i];
+       }
+       key[j] = '\0';
+       ret = base64_decode(key, j, (char **)&blob, blob_size);
+       free(key);
+       if (ret < 0)
+               goto unmap;
+       ret = key_type;
+unmap:
+       ret2 = para_munmap(map, map_size);
+       if (ret >= 0 && ret2 < 0)
+               ret = ret2;
+       if (ret < 0) {
+               free(blob);
+               blob = NULL;
+       }
+out:
+       *result = blob;
+       return ret;
+}
+