Merge pull request #5706 from deepaklukose/dev

Expose pem cert in AuthContext for SSL/TLS channels
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index ef7205d..368d5dd 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -48,6 +48,7 @@
 
 #define GRPC_X509_CN_PROPERTY_NAME "x509_common_name"
 #define GRPC_X509_SAN_PROPERTY_NAME "x509_subject_alternative_name"
+#define GRPC_X509_PEM_CERT_PROPERTY_NAME "x509_pem_cert"
 
 typedef struct grpc_auth_context grpc_auth_context;
 
diff --git a/src/core/security/security_connector.c b/src/core/security/security_connector.c
index 33c62a2..fbec263 100644
--- a/src/core/security/security_connector.c
+++ b/src/core/security/security_connector.c
@@ -492,6 +492,9 @@
       peer_identity_property_name = GRPC_X509_SAN_PROPERTY_NAME;
       grpc_auth_context_add_property(ctx, GRPC_X509_SAN_PROPERTY_NAME,
                                      prop->value.data, prop->value.length);
+    } else if (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0) {
+      grpc_auth_context_add_property(ctx, GRPC_X509_PEM_CERT_PROPERTY_NAME,
+                                     prop->value.data, prop->value.length);
     }
   }
   if (peer_identity_property_name != NULL) {
@@ -554,9 +557,9 @@
   grpc_auth_context_unref(auth_context);
 }
 
-static void add_shalow_auth_property_to_peer(tsi_peer *peer,
-                                             const grpc_auth_property *prop,
-                                             const char *tsi_prop_name) {
+static void add_shallow_auth_property_to_peer(tsi_peer *peer,
+                                              const grpc_auth_property *prop,
+                                              const char *tsi_prop_name) {
   tsi_peer_property *tsi_prop = &peer->properties[peer->property_count++];
   tsi_prop->name = (char *)tsi_prop_name;
   tsi_prop->value.data = prop->value;
@@ -579,11 +582,14 @@
     it = grpc_auth_context_property_iterator(auth_context);
     while ((prop = grpc_auth_property_iterator_next(&it)) != NULL) {
       if (strcmp(prop->name, GRPC_X509_SAN_PROPERTY_NAME) == 0) {
-        add_shalow_auth_property_to_peer(
+        add_shallow_auth_property_to_peer(
             &peer, prop, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY);
       } else if (strcmp(prop->name, GRPC_X509_CN_PROPERTY_NAME) == 0) {
-        add_shalow_auth_property_to_peer(
+        add_shallow_auth_property_to_peer(
             &peer, prop, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY);
+      } else if (strcmp(prop->name, GRPC_X509_PEM_CERT_PROPERTY_NAME) == 0) {
+        add_shallow_auth_property_to_peer(&peer, prop,
+                                          TSI_X509_PEM_CERT_PROPERTY);
       }
     }
   }
diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c
index fcbd910..42d25ca 100644
--- a/src/core/tsi/ssl_transport_security.c
+++ b/src/core/tsi/ssl_transport_security.c
@@ -293,6 +293,26 @@
   return result;
 }
 
+/* Gets the X509 cert in PEM format as a tsi_peer_property. */
+static tsi_result add_pem_certificate(X509 *cert, tsi_peer_property *property) {
+  BIO *bio = BIO_new(BIO_s_mem());
+  if (!PEM_write_bio_X509(bio, cert)) {
+    BIO_free(bio);
+    return TSI_INTERNAL_ERROR;
+  }
+  char *contents;
+  long len = BIO_get_mem_data(bio, &contents);
+  if (len <= 0) {
+    BIO_free(bio);
+    return TSI_INTERNAL_ERROR;
+  }
+  tsi_result result = tsi_construct_string_peer_property(
+      TSI_X509_PEM_CERT_PROPERTY, (const char *)contents, (size_t)len,
+      property);
+  BIO_free(bio);
+  return result;
+}
+
 /* Gets the subject SANs from an X509 cert as a tsi_peer_property. */
 static tsi_result add_subject_alt_names_properties_to_peer(
     tsi_peer *peer, GENERAL_NAMES *subject_alt_names,
@@ -363,7 +383,8 @@
   tsi_result result;
   GPR_ASSERT(subject_alt_name_count >= 0);
   property_count = (include_certificate_type ? (size_t)1 : 0) +
-                   1 /* common name */ + (size_t)subject_alt_name_count;
+                   2 /* common name, certificate */ +
+                   (size_t)subject_alt_name_count;
   result = tsi_construct_peer(property_count, peer);
   if (result != TSI_OK) return result;
   do {
@@ -377,6 +398,10 @@
         cert, &peer->properties[include_certificate_type ? 1 : 0]);
     if (result != TSI_OK) break;
 
+    result = add_pem_certificate(
+        cert, &peer->properties[include_certificate_type ? 2 : 1]);
+    if (result != TSI_OK) break;
+
     if (subject_alt_name_count != 0) {
       result = add_subject_alt_names_properties_to_peer(
           peer, subject_alt_names, (size_t)subject_alt_name_count);
diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h
index 4909af4..32bb067 100644
--- a/src/core/tsi/ssl_transport_security.h
+++ b/src/core/tsi/ssl_transport_security.h
@@ -48,6 +48,8 @@
 #define TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY \
   "x509_subject_alternative_name"
 
+#define TSI_X509_PEM_CERT_PROPERTY "x509_pem_cert"
+
 #define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol"
 
 /* --- tsi_ssl_handshaker_factory object ---
diff --git a/test/core/security/security_connector_test.c b/test/core/security/security_connector_test.c
index 420e3a4..609d874 100644
--- a/test/core/security/security_connector_test.c
+++ b/test/core/security/security_connector_test.c
@@ -80,13 +80,14 @@
 
 static int check_ssl_peer_equivalence(const tsi_peer *original,
                                       const tsi_peer *reconstructed) {
-  /* The reconstructed peer only has CN and SAN properties. */
+  /* The reconstructed peer only has CN, SAN and pem cert properties. */
   size_t i;
   for (i = 0; i < original->property_count; i++) {
     const tsi_peer_property *prop = &original->properties[i];
     if ((strcmp(prop->name, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) ||
         (strcmp(prop->name, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) ==
-         0)) {
+         0) ||
+        (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0)) {
       if (!check_peer_property(reconstructed, prop)) return 0;
     }
   }
@@ -164,24 +165,50 @@
   return 1;
 }
 
+static int check_x509_pem_cert(const grpc_auth_context *ctx,
+                               const char *expected_pem_cert) {
+  grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name(
+      ctx, GRPC_X509_PEM_CERT_PROPERTY_NAME);
+  const grpc_auth_property *prop = grpc_auth_property_iterator_next(&it);
+  if (prop == NULL) {
+    gpr_log(GPR_ERROR, "Pem certificate property not found.");
+    return 0;
+  }
+  if (strncmp(prop->value, expected_pem_cert, prop->value_length) != 0) {
+    gpr_log(GPR_ERROR, "Expected pem cert %s and got %s", expected_pem_cert,
+            prop->value);
+    return 0;
+  }
+  if (grpc_auth_property_iterator_next(&it) != NULL) {
+    gpr_log(GPR_ERROR, "Expected only one property for pem cert.");
+    return 0;
+  }
+  return 1;
+}
+
 static void test_cn_only_ssl_peer_to_auth_context(void) {
   tsi_peer peer;
   tsi_peer rpeer;
   grpc_auth_context *ctx;
   const char *expected_cn = "cn1";
-  GPR_ASSERT(tsi_construct_peer(2, &peer) == TSI_OK);
+  const char *expected_pem_cert = "pem_cert1";
+  GPR_ASSERT(tsi_construct_peer(3, &peer) == TSI_OK);
   GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                  TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE,
                  &peer.properties[0]) == TSI_OK);
   GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                  TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, expected_cn,
                  &peer.properties[1]) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert,
+                 &peer.properties[2]) == TSI_OK);
   ctx = tsi_ssl_peer_to_auth_context(&peer);
   GPR_ASSERT(ctx != NULL);
   GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx));
   GPR_ASSERT(check_identity(ctx, GRPC_X509_CN_PROPERTY_NAME, &expected_cn, 1));
   GPR_ASSERT(check_transport_security_type(ctx));
   GPR_ASSERT(check_x509_cn(ctx, expected_cn));
+  GPR_ASSERT(check_x509_pem_cert(ctx, expected_pem_cert));
 
   rpeer = tsi_shallow_peer_from_ssl_auth_context(ctx);
   GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer));
@@ -197,7 +224,8 @@
   grpc_auth_context *ctx;
   const char *expected_cn = "cn1";
   const char *expected_san = "san1";
-  GPR_ASSERT(tsi_construct_peer(3, &peer) == TSI_OK);
+  const char *expected_pem_cert = "pem_cert1";
+  GPR_ASSERT(tsi_construct_peer(4, &peer) == TSI_OK);
   GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                  TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE,
                  &peer.properties[0]) == TSI_OK);
@@ -207,6 +235,9 @@
   GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                  TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, expected_san,
                  &peer.properties[2]) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert,
+                 &peer.properties[3]) == TSI_OK);
   ctx = tsi_ssl_peer_to_auth_context(&peer);
   GPR_ASSERT(ctx != NULL);
   GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx));
@@ -214,6 +245,7 @@
       check_identity(ctx, GRPC_X509_SAN_PROPERTY_NAME, &expected_san, 1));
   GPR_ASSERT(check_transport_security_type(ctx));
   GPR_ASSERT(check_x509_cn(ctx, expected_cn));
+  GPR_ASSERT(check_x509_pem_cert(ctx, expected_pem_cert));
 
   rpeer = tsi_shallow_peer_from_ssl_auth_context(ctx);
   GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer));
@@ -229,8 +261,9 @@
   grpc_auth_context *ctx;
   const char *expected_cn = "cn1";
   const char *expected_sans[] = {"san1", "san2", "san3"};
+  const char *expected_pem_cert = "pem_cert1";
   size_t i;
-  GPR_ASSERT(tsi_construct_peer(2 + GPR_ARRAY_SIZE(expected_sans), &peer) ==
+  GPR_ASSERT(tsi_construct_peer(3 + GPR_ARRAY_SIZE(expected_sans), &peer) ==
              TSI_OK);
   GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                  TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE,
@@ -238,10 +271,13 @@
   GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                  TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, expected_cn,
                  &peer.properties[1]) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert,
+                 &peer.properties[2]) == TSI_OK);
   for (i = 0; i < GPR_ARRAY_SIZE(expected_sans); i++) {
     GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                    TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY,
-                   expected_sans[i], &peer.properties[2 + i]) == TSI_OK);
+                   expected_sans[i], &peer.properties[3 + i]) == TSI_OK);
   }
   ctx = tsi_ssl_peer_to_auth_context(&peer);
   GPR_ASSERT(ctx != NULL);
@@ -250,6 +286,7 @@
                             GPR_ARRAY_SIZE(expected_sans)));
   GPR_ASSERT(check_transport_security_type(ctx));
   GPR_ASSERT(check_x509_cn(ctx, expected_cn));
+  GPR_ASSERT(check_x509_pem_cert(ctx, expected_pem_cert));
 
   rpeer = tsi_shallow_peer_from_ssl_auth_context(ctx);
   GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer));
@@ -265,9 +302,10 @@
   tsi_peer rpeer;
   grpc_auth_context *ctx;
   const char *expected_cn = "cn1";
+  const char *expected_pem_cert = "pem_cert1";
   const char *expected_sans[] = {"san1", "san2", "san3"};
   size_t i;
-  GPR_ASSERT(tsi_construct_peer(4 + GPR_ARRAY_SIZE(expected_sans), &peer) ==
+  GPR_ASSERT(tsi_construct_peer(5 + GPR_ARRAY_SIZE(expected_sans), &peer) ==
              TSI_OK);
   GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                  TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE,
@@ -279,10 +317,13 @@
                  &peer.properties[2]) == TSI_OK);
   GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                  "chapi", "chapo", &peer.properties[3]) == TSI_OK);
+  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
+                 TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert,
+                 &peer.properties[4]) == TSI_OK);
   for (i = 0; i < GPR_ARRAY_SIZE(expected_sans); i++) {
     GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
                    TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY,
-                   expected_sans[i], &peer.properties[4 + i]) == TSI_OK);
+                   expected_sans[i], &peer.properties[5 + i]) == TSI_OK);
   }
   ctx = tsi_ssl_peer_to_auth_context(&peer);
   GPR_ASSERT(ctx != NULL);
@@ -291,6 +332,7 @@
                             GPR_ARRAY_SIZE(expected_sans)));
   GPR_ASSERT(check_transport_security_type(ctx));
   GPR_ASSERT(check_x509_cn(ctx, expected_cn));
+  GPR_ASSERT(check_x509_pem_cert(ctx, expected_pem_cert));
 
   rpeer = tsi_shallow_peer_from_ssl_auth_context(ctx);
   GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer));