+
+static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result)
+{
+ const unsigned char *p = buf, *end = buf + len;
+ uint32_t bnsize;
+ BIGNUM *bn;
+
+ if (p + 4 < p)
+ return -E_BIGNUM;
+ if (p + 4 > end)
+ return -E_BIGNUM;
+ bnsize = read_ssh_u32(p);
+ PARA_DEBUG_LOG("bnsize: %u\n", bnsize);
+ p += 4;
+ if (p + bnsize < p)
+ return -E_BIGNUM;
+ if (p + bnsize > end)
+ return -E_BIGNUM;
+ if (bnsize > 8192)
+ return -E_BIGNUM;
+ bn = BN_bin2bn(p, bnsize, NULL);
+ if (!bn)
+ return -E_BIGNUM;
+ *result = bn;
+ return bnsize + 4;
+}
+
+static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
+{
+ int ret;
+ RSA *rsa;
+ const unsigned char *p = blob, *end = blob + blen;
+
+ rsa = RSA_new();
+ if (!rsa)
+ return -E_BIGNUM;
+ ret = read_bignum(p, end - p, &rsa->e);
+ if (ret < 0)
+ goto fail;
+ p += ret;
+ ret = read_bignum(p, end - p, &rsa->n);
+ if (ret < 0)
+ goto fail;
+ *result = rsa;
+ return 1;
+fail:
+ if (rsa)
+ RSA_free(rsa);
+ return ret;
+}
+
+int get_asymmetric_key(const char *key_file, int private,
+ struct asymmetric_key **result)
+{
+ struct asymmetric_key *key = NULL;
+ void *map = NULL;
+ unsigned char *blob = NULL;
+ size_t map_size, blob_size, decoded_size;
+ int ret, ret2;
+ char *cp;
+
+ key = para_malloc(sizeof(*key));
+ if (private) {
+ ret = get_openssl_key(key_file, &key->rsa, LOAD_PRIVATE_KEY);
+ goto out;
+ }
+ ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
+ if (ret < 0)
+ goto out;
+ ret = is_ssh_rsa_key(map, map_size);
+ if (!ret) {
+ ret = para_munmap(map, map_size);
+ map = NULL;
+ if (ret < 0)
+ goto out;
+ ret = get_openssl_key(key_file, &key->rsa, LOAD_PUBLIC_KEY);
+ goto out;
+ }
+ cp = map + ret;
+ PARA_INFO_LOG("decoding public rsa-ssh key %s\n", key_file);
+ ret = -ERRNO_TO_PARA_ERROR(EOVERFLOW);
+ if (map_size > INT_MAX / 4)
+ goto out_unmap;
+ blob_size = 2 * map_size;
+ blob = para_malloc(blob_size);
+ ret = uudecode(cp, blob, blob_size);
+ if (ret < 0)
+ goto out_unmap;
+ decoded_size = ret;
+ ret = check_ssh_key_header(blob, decoded_size);
+ if (ret < 0)
+ goto out_unmap;
+ ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
+ if (ret < 0)
+ goto out_unmap;
+ ret = RSA_size(key->rsa);
+out_unmap:
+ ret2 = para_munmap(map, map_size);
+ if (ret >= 0 && ret2 < 0)
+ ret = ret2;
+out:
+ if (ret < 0) {
+ free(key);
+ *result = NULL;
+ PARA_ERROR_LOG("key %s: %s\n", key_file, para_strerror(-ret));
+ } else
+ *result = key;
+ free(blob);
+ return ret;
+}
+