aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core
diff options
context:
space:
mode:
authorGravatar jboeuf <jboeuf@google.com>2014-12-12 15:39:47 -0800
committerGravatar Nicolas Noble <nnoble@google.com>2014-12-12 16:36:06 -0800
commitbefd26501a6dbc0dca9d12444b4d245fa5560db0 (patch)
tree1891340ab73ce69fb66e08ab09b1540526098ce7 /src/core
parent993dfcef3ea9c511a47c8d7caed8b52e9584abb9 (diff)
Adding base utils for JWT service account workflow. OpenSSL base64 decoding is
a disaster and does not support url_safe which we need for the JWT. Change on 2014/12/12 by jboeuf <jboeuf@google.com> ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=82020601
Diffstat (limited to 'src/core')
-rw-r--r--src/core/security/base64.c197
-rw-r--r--src/core/security/base64.h48
-rw-r--r--src/core/security/json_token.c152
-rw-r--r--src/core/security/json_token.h61
4 files changed, 458 insertions, 0 deletions
diff --git a/src/core/security/base64.c b/src/core/security/base64.c
new file mode 100644
index 0000000000..f6c3c52327
--- /dev/null
+++ b/src/core/security/base64.c
@@ -0,0 +1,197 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/security/base64.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+/* --- Constants. --- */
+
+static const char base64_bytes[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 0x3E, -1, -1, -1, 0x3F,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, -1, -1,
+ -1, 0x7F, -1, -1, -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, -1, -1, -1, -1, -1,
+ -1, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+ 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+ 0x31, 0x32, 0x33, -1, -1, -1, -1, -1};
+
+static const char base64_url_unsafe_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char base64_url_safe_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+#define GRPC_BASE64_PAD_CHAR '='
+#define GRPC_BASE64_PAD_BYTE 0x7F
+#define GRPC_BASE64_MULTILINE_LINE_LEN 76
+#define GRPC_BASE64_MULTILINE_NUM_BLOCKS (GRPC_BASE64_MULTILINE_LINE_LEN / 4)
+
+/* --- base64 functions. --- */
+
+char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe,
+ int multiline) {
+ const unsigned char *data = vdata;
+ const char *base64_chars =
+ url_safe ? base64_url_safe_chars : base64_url_unsafe_chars;
+ size_t result_projected_size =
+ 4 * ((data_size + 3) / 3) +
+ 2 * (multiline ? (data_size / (3 * GRPC_BASE64_MULTILINE_NUM_BLOCKS))
+ : 0) +
+ 1;
+ char *result = gpr_malloc(result_projected_size);
+ char *current = result;
+ size_t num_blocks = 0;
+ size_t i = 0;
+
+ /* Encode each block. */
+ while (data_size >= 3) {
+ *current++ = base64_chars[(data[i] >> 2) & 0x3F];
+ *current++ =
+ base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)];
+ *current++ =
+ base64_chars[((data[i + 1] & 0x0F) << 2) | ((data[i + 2] >> 6) & 0x03)];
+ *current++ = base64_chars[data[i + 2] & 0x3F];
+
+ data_size -= 3;
+ i += 3;
+ if (multiline && (++num_blocks == GRPC_BASE64_MULTILINE_NUM_BLOCKS)) {
+ *current++ = '\r';
+ *current++ = '\n';
+ num_blocks = 0;
+ }
+ }
+
+ /* Take care of the tail. */
+ if (data_size == 2) {
+ *current++ = base64_chars[(data[i] >> 2) & 0x3F];
+ *current++ =
+ base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)];
+ *current++ = base64_chars[(data[i + 1] & 0x0F) << 2];
+ *current++ = GRPC_BASE64_PAD_CHAR;
+ } else if (data_size == 1) {
+ *current++ = base64_chars[(data[i] >> 2) & 0x3F];
+ *current++ = base64_chars[(data[i] & 0x03) << 4];
+ *current++ = GRPC_BASE64_PAD_CHAR;
+ *current++ = GRPC_BASE64_PAD_CHAR;
+ }
+
+ GPR_ASSERT((current - result) < result_projected_size);
+ result[current - result] = '\0';
+ return result;
+}
+
+gpr_slice grpc_base64_decode(const char *b64, int url_safe) {
+ size_t b64_len = strlen(b64);
+ gpr_slice result = gpr_slice_malloc(b64_len);
+ unsigned char *current = GPR_SLICE_START_PTR(result);
+ size_t result_size = 0;
+ unsigned char codes[4];
+ size_t num_codes = 0;
+
+ while (b64_len--) {
+ unsigned char c = *b64++;
+ signed char code;
+ if (c >= GPR_ARRAY_SIZE(base64_bytes)) continue;
+ if (url_safe) {
+ if (c == '+' || c == '/') {
+ gpr_log(GPR_ERROR, "Invalid character for url safe base64 %c", c);
+ goto fail;
+ }
+ if (c == '-') {
+ c = '+';
+ } else if (c == '_') {
+ c = '/';
+ }
+ }
+ code = base64_bytes[c];
+ if (code == -1) {
+ if (c != '\r' && c != '\n') {
+ gpr_log(GPR_ERROR, "Invalid character %c", c);
+ goto fail;
+ }
+ } else {
+ codes[num_codes++] = code;
+ if (num_codes == 4) {
+ if (codes[0] == GRPC_BASE64_PAD_BYTE ||
+ codes[1] == GRPC_BASE64_PAD_BYTE) {
+ gpr_log(GPR_ERROR, "Invalid padding detected.");
+ goto fail;
+ }
+ if (codes[2] == GRPC_BASE64_PAD_BYTE) {
+ if (codes[3] == GRPC_BASE64_PAD_BYTE) {
+ /* Double padding. */
+ gpr_uint32 packed = (codes[0] << 2) | (codes[1] >> 4);
+ current[result_size++] = (unsigned char)packed;
+ } else {
+ gpr_log(GPR_ERROR, "Invalid padding detected.");
+ goto fail;
+ }
+ } else if (codes[3] == GRPC_BASE64_PAD_BYTE) {
+ /* Single padding. */
+ gpr_uint32 packed =
+ (codes[0] << 10) | (codes[1] << 4) | (codes[2] >> 2);
+ current[result_size++] = (unsigned char)(packed >> 8);
+ current[result_size++] = (unsigned char)(packed);
+ } else {
+ /* No padding. */
+ gpr_uint32 packed =
+ (codes[0] << 18) | (codes[1] << 12) | (codes[2] << 6) | codes[3];
+ current[result_size++] = (unsigned char)(packed >> 16);
+ current[result_size++] = (unsigned char)(packed >> 8);
+ current[result_size++] = (unsigned char)(packed);
+ }
+ num_codes = 0;
+ }
+ }
+ }
+
+ if (num_codes != 0) {
+ gpr_log(GPR_ERROR, "Invalid base64.");
+ gpr_slice_unref(result);
+ return gpr_empty_slice();
+ }
+ GPR_SLICE_SET_LENGTH(result, result_size);
+ return result;
+
+fail:
+ gpr_slice_unref(result);
+ return gpr_empty_slice();
+}
diff --git a/src/core/security/base64.h b/src/core/security/base64.h
new file mode 100644
index 0000000000..7bfb89b071
--- /dev/null
+++ b/src/core/security/base64.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __GRPC_INTERNAL_SECURITY_BASE64_H_
+#define __GRPC_INTERNAL_SECURITY_BASE64_H_
+
+#include <grpc/support/slice.h>
+
+/* Encodes data using base64. It is the caller's responsability to free
+ the returned char * using gpr_free. Returns NULL on NULL input. */
+char *grpc_base64_encode(const void *data, size_t data_size, int url_safe,
+ int multiline);
+
+/* Decodes data according to the base64 specification. Returns an empty
+ slice in case of failure. */
+gpr_slice grpc_base64_decode(const char *b64, int url_safe);
+
+#endif /* __GRPC_INTERNAL_SECURITY_BASE64_H_ */
diff --git a/src/core/security/json_token.c b/src/core/security/json_token.c
new file mode 100644
index 0000000000..ba5e2cf48c
--- /dev/null
+++ b/src/core/security/json_token.c
@@ -0,0 +1,152 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/security/json_token.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+
+#include "third_party/cJSON/cJSON.h"
+
+/* --- Constants. --- */
+
+/* 1 hour max. */
+const gpr_timespec grpc_max_service_accounts_token_validity = {3600, 0};
+
+#define GRPC_AUTH_JSON_KEY_TYPE_INVALID "invalid"
+#define GRPC_AUTH_JSON_KEY_TYPE_SERVICE_ACCOUNT "service_account"
+
+/* --- grpc_auth_json_key. --- */
+
+static const char *json_get_string_property(cJSON *json,
+ const char *prop_name) {
+ cJSON *child = NULL;
+ child = cJSON_GetObjectItem(json, prop_name);
+ if (child == NULL || child->type != cJSON_String) {
+ gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name);
+ return NULL;
+ }
+ return child->valuestring;
+}
+
+static int set_json_key_string_property(cJSON *json, const char *prop_name,
+ char **json_key_field) {
+ const char *prop_value = json_get_string_property(json, prop_name);
+ if (prop_value == NULL) return 0;
+ *json_key_field = gpr_strdup(prop_value);
+ return 1;
+}
+
+int grpc_auth_json_key_is_valid(grpc_auth_json_key *json_key) {
+ return (json_key != NULL) &&
+ strcmp(json_key->type, GRPC_AUTH_JSON_KEY_TYPE_INVALID);
+}
+
+grpc_auth_json_key grpc_auth_json_key_create_from_string(
+ const char *json_string) {
+ grpc_auth_json_key result;
+ cJSON *json = cJSON_Parse(json_string);
+ BIO *bio = NULL;
+ const char *prop_value;
+ int success = 0;
+
+ memset(&result, 0, sizeof(grpc_auth_json_key));
+ result.type = GRPC_AUTH_JSON_KEY_TYPE_INVALID;
+ if (json == NULL) {
+ gpr_log(GPR_ERROR, "Invalid json string %s", json_string);
+ return result;
+ }
+
+ prop_value = json_get_string_property(json, "type");
+ if (prop_value == NULL ||
+ strcmp(prop_value, GRPC_AUTH_JSON_KEY_TYPE_SERVICE_ACCOUNT)) {
+ goto end;
+ }
+ result.type = GRPC_AUTH_JSON_KEY_TYPE_SERVICE_ACCOUNT;
+
+ if (!set_json_key_string_property(json, "private_key_id",
+ &result.private_key_id) ||
+ !set_json_key_string_property(json, "client_id", &result.client_id) ||
+ !set_json_key_string_property(json, "client_email",
+ &result.client_email)) {
+ goto end;
+ }
+
+ prop_value = json_get_string_property(json, "private_key");
+ if (prop_value == NULL) {
+ goto end;
+ }
+ bio = BIO_new(BIO_s_mem());
+ if (BIO_puts(bio, prop_value) != strlen(prop_value)) {
+ gpr_log(GPR_ERROR, "Could not write into openssl BIO.");
+ goto end;
+ }
+ result.private_key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, "");
+ if (result.private_key == NULL) {
+ gpr_log(GPR_ERROR, "Could not deserialize private key.");
+ goto end;
+ }
+ success = 1;
+
+end:
+ if (bio != NULL) BIO_free(bio);
+ if (json != NULL) cJSON_Delete(json);
+ if (!success) grpc_auth_json_key_destruct(&result);
+ return result;
+}
+
+void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key) {
+ if (json_key == NULL) return;
+ json_key->type = GRPC_AUTH_JSON_KEY_TYPE_INVALID;
+ if (json_key->client_id != NULL) {
+ gpr_free(json_key->client_id);
+ json_key->client_id = NULL;
+ }
+ if (json_key->private_key_id != NULL) {
+ gpr_free(json_key->private_key_id);
+ json_key->private_key_id = NULL;
+ }
+ if (json_key->client_email != NULL) {
+ gpr_free(json_key->client_email);
+ json_key->client_email = NULL;
+ }
+ if (json_key->private_key != NULL) {
+ RSA_free(json_key->private_key);
+ json_key->private_key = NULL;
+ }
+}
diff --git a/src/core/security/json_token.h b/src/core/security/json_token.h
new file mode 100644
index 0000000000..a1456ebc22
--- /dev/null
+++ b/src/core/security/json_token.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __GRPC_INTERNAL_SECURITY_JSON_TOKEN_H_
+#define __GRPC_INTERNAL_SECURITY_JSON_TOKEN_H_
+
+#include <grpc/support/slice.h>
+#include <openssl/rsa.h>
+
+/* --- json_key parsing. Exposed for testing only. --- */
+
+typedef struct {
+ char *type;
+ char *private_key_id;
+ char *client_id;
+ char *client_email;
+ RSA *private_key;
+} grpc_auth_json_key;
+
+/* Returns 1 if the object is valid, 0 otherwise. */
+int grpc_auth_json_key_is_valid(grpc_auth_json_key *json_key);
+
+/* Creates a json_key object from string. Returns an invalid object if a parsing
+ error has been encountered. */
+grpc_auth_json_key grpc_auth_json_key_create_from_string(
+ const char *json_string);
+
+/* Destructs the object. */
+void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key);
+
+#endif /* __GRPC_INTERNAL_SECURITY_JSON_TOKEN_H_ */