Enable the grpc ra-tls demo

Signed-off-by: yuanwu <yuan.wu@intel.com>
This commit is contained in:
yuanwu 2022-01-17 12:42:25 +08:00 committed by Zongmin.Gu
parent b62b5e23eb
commit 2810b8e4ce
34 changed files with 20627 additions and 0 deletions

@ -835,6 +835,54 @@ jobs:
if: ${{ always() }}
run: docker stop ${{ env.CONTAINER_NAME }}
RA_TLS_test:
if: github.event_name == 'push' || ${{ contains(github.event.pull_request.labels.*.name, 'SGX-hardware-test-required') }}
runs-on: ${{ matrix.self_runner }}
strategy:
matrix:
self_runner: [[self-hosted, SGX2-HW]]
steps:
- name: Clean before running
run: |
sudo chown -R ${{ secrets.CI_ADMIN }} "${{ github.workspace }}"
- name: Checkout code
if: github.event_name == 'push'
uses: actions/checkout@v2
with:
submodules: true
- name: Checkout code from fork
if: ${{ contains(github.event.pull_request.labels.*.name, 'SGX-hardware-test-required') }}
uses: actions/checkout@v2
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
submodules: true
- uses: ./.github/workflows/composite_action/hw
with:
container-name: ${{ github.job }}
build-envs: 'OCCLUM_RELEASE_BUILD=1'
- name: Download and build the pakckages
run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./prepare_and_build_package.sh"
- name: Build occlum instances
run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./build_occlum_instance.sh"
- name: Run gRPC server
run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./run.sh server &"
- name: Run gRPC client
run: |
sleep ${{ env.nap_time }};
docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./run.sh client"
- name: Clean the environment
if: ${{ always() }}
run: docker stop ${{ env.CONTAINER_NAME }}
Stress_test_with_musl:
if: github.event_name == 'schedule'
runs-on: ${{ matrix.self_runner }}

25
demos/ra_tls/README.md Normal file

@ -0,0 +1,25 @@
# gRPC Package With RA-TLS
#### Executing the demo in Occlum
The following command will download the gRPC source code and apply the ra-tls patches, then build gRPC source code and demo.
```
./prepare_and_build_package.sh
```
The following command will generate the client and server occlum images. It automatically parses the mr_enclave and mr_signer of the client, and write the value into dynamic_config.json. If you want to verify the other measurements of client, please modify the dynamic_config.json before run the script.
```
./build_occlum_instance.sh
```
Run the gRPC server & client in occlum.
```
./run.sh server &
./run.sh client
```
***Note:*** 1. The demo runs in the same machine by default. If you want to run server and client in different machines. Please modify the examples/cpp/ratls.
2. If you want to test in your local network with your own PCCS server, you need to modify the /etc/sgx_default_qcnl.conf

@ -0,0 +1,55 @@
#!/bin/bash
occlum_glibc=/opt/occlum/glibc/lib/
set -ex
get_mr() {
sgx_sign dump -enclave ../occlum_instance_$1/build/lib/libocclum-libos.signed.so -dumpfile ../metadata_info_$1.txt
if [ "$2" == "mr_enclave" ]; then
sed -n -e '/enclave_hash.m/,/metadata->enclave_css.body.isv_prod_id/p' ../metadata_info_$1.txt |head -3|tail -2|xargs|sed 's/0x//g'|sed 's/ //g'
elif [ "$2" == "mr_signer" ]; then
tail -2 ../metadata_info_$1.txt |xargs|sed 's/0x//g'|sed 's/ //g'
fi
}
build_instance() {
# 1. Init Occlum Workspace
rm -rf occlum_instance_$postfix
mkdir occlum_instance_$postfix
pushd occlum_instance_$postfix
occlum init
new_json="$(jq '.resource_limits.user_space_size = "320MB" |
.process.default_mmap_size = "256MB"' Occlum.json)" && \
echo "${new_json}" > Occlum.json
# 2. Copy files into Occlum Workspace and Build
#cp ../dynamic_config.json image/etc/dynamic_config.json
cp ../dynamic_config.json image/dynamic_config.json
#cp ../dynamic_config_$postfix.json image/dynamic_config.json
if [ "$postfix" == "server" ]; then
jq ' .verify_mr_enclave = "on" |
.verify_mr_signer = "on" |
.sgx_mrs[0].mr_enclave = ''"'`get_mr client mr_enclave`'" |
.sgx_mrs[0].mr_signer = ''"'`get_mr client mr_signer`'" ' ../dynamic_config.json > image/dynamic_config.json
fi
mkdir -p image/usr/share/grpc
cp -rf /share/grpc/* image/usr/share/grpc/
cp $occlum_glibc/libdl.so.2 image/$occlum_glibc
cp $occlum_glibc/librt.so.1 image/$occlum_glibc
cp $occlum_glibc/libm.so.6 image/$occlum_glibc
cp /lib/x86_64-linux-gnu/libtinfo.so.5 image/$occlum_glibc
cp /lib/x86_64-linux-gnu/libnss*.so.2 image/$occlum_glibc
cp /lib/x86_64-linux-gnu/libresolv.so.2 image/$occlum_glibc
cp -rf /etc/hostname image/etc/
cp -rf /etc/ssl image/etc/
cp -rf /etc/passwd image/etc/
cp -rf /etc/group image/etc/
cp -rf /etc/nsswitch.conf image/etc/
cp -rf /grpc/examples/cpp/ratls/build/* image/bin/
occlum build
popd
}
postfix=client
build_instance
postfix=server
build_instance

@ -0,0 +1,15 @@
{
"verify_mr_enclave" : "off",
"verify_mr_signer" : "off",
"verify_isv_prod_id" : "off",
"verify_isv_svn" : "off",
"sgx_mrs": [
{
"mr_enclave" : "",
"mr_signer" : "",
"isv_prod_id" : "0",
"isv_svn" : "0"
}
],
"other" : []
}

1
demos/ra_tls/grpc/.gitignore vendored Normal file

@ -0,0 +1 @@
grpc-*

@ -0,0 +1,46 @@
#
# Copyright (c) 2022 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -ex
export ABSEIL_PATH=${GRPC_PATH}/third_party/abseil-cpp
if [ ! -d "${BUILD_TYPE}" ]; then
BUILD_TYPE=Release
fi
# build and install abseil library
# https://abseil.io/docs/cpp/quickstart-cmake.html
if [ ! -d "${ABSEIL_PATH}/build" ]; then
mkdir -p ${ABSEIL_PATH}/build
cd ${ABSEIL_PATH}/build
cmake -DCMAKE_CXX_STANDARD=11 -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} ..
make -j `nproc`
make install
cd -
fi
# build and install grpc library
mkdir -p ${GRPC_PATH}/build
cd ${GRPC_PATH}/build
cmake -DgRPC_INSTALL=ON -DgRPC_ABSL_PROVIDER=package -DgRPC_BUILD_TESTS=OFF \
-DgRPC_BUILD_CSHARP_EXT=OFF -DgRPC_BUILD_GRPC_CSHARP_PLUGIN=OFF \
-DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF -DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF \
-DDEFINE_SGX_RA_TLS_OCCLUM_BACKEND=ON \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} ..
make -j `nproc`
make install
cd -

File diff suppressed because it is too large Load Diff

@ -0,0 +1,62 @@
# Copyright (c) 2022 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.5.1)
project(RA-TLS C CXX)
include(../cmake/common.cmake)
# Proto file
get_filename_component(hw_proto "../../protos/ratls.proto" ABSOLUTE)
get_filename_component(hw_proto_path "${hw_proto}" PATH)
# Generated sources
set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/ratls.pb.cc")
set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/ratls.pb.h")
set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/ratls.grpc.pb.cc")
set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/ratls.grpc.pb.h")
add_custom_command(
OUTPUT "${hw_proto_srcs}" "${hw_proto_hdrs}" "${hw_grpc_srcs}" "${hw_grpc_hdrs}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
-I "${hw_proto_path}"
--plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
"${hw_proto}"
DEPENDS "${hw_proto}")
# Include generated *.pb.h files
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
# hw_grpc_proto
add_library(hw_grpc_proto
${hw_grpc_srcs}
${hw_grpc_hdrs}
${hw_proto_srcs}
${hw_proto_hdrs})
target_link_libraries(hw_grpc_proto
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})
# Targets greeter_[async_](client|server)
foreach(_target client server)
add_executable(${_target} "${_target}.cc")
target_link_libraries(${_target}
hw_grpc_proto
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})
endforeach()

@ -0,0 +1,15 @@
# gRPC
This directory contains the Makefile and the template manifest for the most
recent version of gRPC (as of this writing, version 3.18.0). This was tested
on a machine with SGX v1 and Ubuntu 18.04.
The Makefile and the template manifest contain extensive comments and are made
self-explanatory. Please review them to gain understanding of Gramine-SGX
and requirements for applications running under Gramine-SGX.
# Quick Start
```
./build.sh
```

@ -0,0 +1,29 @@
#
# Copyright (c) 2022 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -ex
export BUILD_TYPE=Release
export EXP_PATH=`dirname $0`
${GRPC_PATH}/build_cpp.sh
# build c++ example
cd ${EXP_PATH}
mkdir -p build
cd build
cmake -D CMAKE_PREFIX_PATH=${INSTALL_PREFIX} -D CMAKE_BUILD_TYPE=${BUILD_TYPE} ..
make -j `nproc`
cd -

@ -0,0 +1,86 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/sgx/sgx_ra_tls.h>
#ifdef BAZEL_BUILD
#include "examples/protos/ratls.grpc.pb.h"
#else
#include "ratls.grpc.pb.h"
#endif
#include "../getopt.hpp"
using ratls::Greeter;
using ratls::HelloReply;
using ratls::HelloRequest;
struct argparser {
const char* config;
std::string server_address;
argparser() {
server_address = getarg("localhost:50051", "-host", "--host");
config = getarg("dynamic_config.json", "-cfg", "--config");
};
};
class GreeterClient {
public:
GreeterClient(std::shared_ptr<grpc::Channel> channel) : stub_(Greeter::NewStub(channel)) {}
std::string SayHello(const std::string& user) {
HelloRequest request;
request.set_name(user);
HelloReply reply;
grpc::ClientContext context;
grpc::Status status = stub_->SayHello(&context, request, &reply);
if (status.ok()) {
return reply.message();
} else {
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
void run_client() {
argparser args;
auto cred = grpc::sgx::TlsCredentials(args.config);
auto channel = grpc::CreateChannel(args.server_address, cred);
GreeterClient greeter(channel);
std::string user_a = greeter.SayHello("a");
std::string user_b = greeter.SayHello("b");
std::cout << "Greeter received: " << user_a << ", "<< user_b << std::endl;
};
int main(int argc, char** argv) {
run_client();
return 0;
}

@ -0,0 +1,316 @@
// Simple getopt replacement class (C++11).
// - rlyeh, zlib/libpng licensed.
// https://github.com/r-lyeh-archived/getopt/blob/master/getopt.hpp
// Two APIs provided:
//
// 1) Simple functional api `getarg(...)`.
// - No initialization required: (argc, argv) pair automatically retrieved.
// - First argument is default option value, then all option indentifiers follow.
//
// int main() {
// bool help = getarg( false, "-h", "--help", "-?" );
// int version = getarg( 0, "-v", "--version", "--show-version" );
// int depth = getarg( 1, "-d", "--depth", "--max-depth");
// std::string file = getarg( "", "-f", "--file" );
// [...]
// }
//
// 2) Simple OOP map-based api `getopt class`. Initialization (argc, argv) pair required.
//
// This getopt class is a std::map replacement where key/value are std::string types.
// Given invokation './app.out --user=me --pass=123 -h' this class delivers not only:
// map[0] = "./app.out", map[1] = "--user=me", map[2]="--pass=123", map[3]='-h'
// but also, map["--user"]="me", map["--pass"]="123" and also, map["-h"]=true
//
// Additional API:
// - .cmdline() for a print app invokation string
// - .str() for pretty map printing
// - .size() number of arguments (equivalent to argc), rather than std::map.size()
//
// int main( int argc, const char **argv ) {
// getopt args( argc, argv );
// if( args.has("-h") || args.has("--help") || args.has("-?") || args.size() == 1 ) {
// std::cout << args["0"] << " [-?|-h|--help] [-v|--version] [--depth=number]" << std::endl;
// return 0;
// }
// if( args.has("-v") || args.has("--version") ) {
// std::cout << args["0"] << " sample v1.0.0. Compiled on " << __DATE__ << std::endl;
// }
// if( args.has("--depth") ) {
// int depth = atoi( args["--depth"].c_str() );
// std::cout << "depth set to " << depth << std::endl;
// }
// [...]
// }
#pragma once
#include <map>
#include <string>
#include <sstream>
#include <vector>
#ifdef _WIN32
#include <io.h>
#include <winsock2.h>
#include <shellapi.h>
#pragma comment(lib, "Shell32.lib")
#else
#include <fstream>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#endif
#define GETOPT_VERSION "1.0.0" // (2016/04/18) Initial version
namespace getopt_utils
{
// string conversion
template< typename T >
inline T as( const std::string &self ) {
T t;
return (std::istringstream(self) >> t) ? t :
(T)(self.size() && (self != "0") && (self != "false"));
}
template<>
inline char as( const std::string &self ) {
return self.size() == 1 ? (char)(self[0]) : (char)(as<int>(self));
}
template<>
inline signed char as( const std::string &self ) {
return self.size() == 1 ? (signed char)(self[0]) : (signed char)(as<int>(self));
}
template<>
inline unsigned char as( const std::string &self ) {
return self.size() == 1 ? (unsigned char)(self[0]) : (unsigned char)(as<int>(self));
}
template<>
inline const char *as( const std::string &self ) {
return self.c_str();
}
template<>
inline std::string as( const std::string &self ) {
return self;
}
// token split
inline size_t split( std::vector<std::string> &tokens, const std::string &self, const std::string &delimiters ) {
std::string str;
tokens.clear();
for( auto &ch : self ) {
if( delimiters.find_first_of( ch ) != std::string::npos ) {
if( str.size() ) tokens.push_back( str ), str = "";
tokens.push_back( std::string() + ch );
} else str += ch;
}
return str.empty() ? tokens.size() : ( tokens.push_back( str ), tokens.size() );
};
// portable cmdline
inline std::vector<std::string> cmdline() {
std::vector<std::string> args;
std::string arg;
# ifdef _WIN32
int argv;
auto *list = CommandLineToArgvW( GetCommandLineW(), &argv );
if( list ) {
for( int i = 0; i < argv; ++i ) {
std::wstring ws( list[i] );
args.push_back( std::string( ws.begin(), ws.end() ) );
}
LocalFree(list);
}
# else
pid_t pid = getpid();
char fname[32] = {};
sprintf(fname, "/proc/%d/cmdline", pid);
std::ifstream ifs(fname);
if( ifs.good() ) {
std::stringstream ss;
ifs >> ss.rdbuf();
arg = ss.str();
}
for( auto end = arg.size(), i = end - end; i < end; ++i ) {
auto st = i;
while (i < arg.size() && arg[i] != '\0') ++i;
args.push_back( arg.substr(st, i - st) );
}
# endif
return args;
}
}
// main map class; explicit initialization
struct getopt : public std::map< std::string, std::string >
{
using super = std::map< std::string, std::string >;
getopt( int argc, const char **argv ) : super() {
// reconstruct vector
std::vector<std::string> args( argc, std::string() );
for( int i = 0; i < argc; ++i ) {
args[ i ] = argv[ i ];
}
// create key=value and key= args as well
for( auto &it : args ) {
std::vector<std::string> tokens;
auto size = getopt_utils::split( tokens, it, "=" );
if( size == 3 && tokens[1] == "=" )
(*this)[ tokens[0] ] = tokens[2];
else
if( size == 2 && tokens[1] == "=" )
(*this)[ tokens[0] ] = true;
else
if( size == 1 && tokens[0] != argv[0] )
(*this)[ tokens[0] ] = true;
}
// recreate args
while( argc-- ) {
(*this)[ std::to_string(argc) ] = std::string( argv[argc] );
}
}
getopt( const std::vector<std::string> &args ) : super() {
std::vector<const char *> argv;
for( auto &it : args ) {
argv.push_back( it.c_str() );
}
*this = getopt( argv.size(), argv.data() );
}
size_t size() const {
unsigned i = 0;
while( has(std::to_string(i)) ) ++i;
return i;
}
bool has( const std::string &op ) const {
return this->find(op) != this->end();
}
std::string str() const {
std::stringstream ss;
std::string sep;
for( auto &it : *this ) {
ss << sep << it.first << "=" << it.second;
sep = ',';
}
return ss.str();
}
std::string cmdline() const {
std::stringstream cmd;
std::string sep;
// concatenate args
for( auto end = size(), arg = end - end; arg < end; ++arg ) {
cmd << sep << this->find(std::to_string(arg))->second;
sep = ' ';
}
return cmd.str();
}
};
// variadic syntax sugars {
template< typename T >
inline T getarg( const T &defaults, const char *argv ) {
static struct getopt map( getopt_utils::cmdline() );
return map.has( argv ) ? getopt_utils::as<T>(map[ argv ]) : defaults;
}
template< typename T, typename... Args >
inline T getarg( const T &defaults, const char *arg0, Args... argv ) {
T t = getarg<T>( defaults, arg0 );
return t == defaults ? getarg<T>( defaults, argv... ) : t;
}
inline const char * getarg( const char *defaults, const char *argv ) {
static struct getopt map( getopt_utils::cmdline() );
return map.has( argv ) ? getopt_utils::as<const char *>(map[ argv ]) : defaults;
}
template< typename... Args >
inline const char * getarg( const char *defaults, const char *arg0, Args... argv ) {
const char *t = getarg( defaults, arg0 );
return t == defaults ? getarg( defaults, argv... ) : t;
}
// }
#ifdef GETOPT_BUILD_DEMO
#include <iostream>
#include <stdlib.h>
int main( int argc, const char **argv ) {
auto show_help = [&]() {
std::cout << argv[0] << " [-h|--help|-?] [-f=path|--file=path] [-v|--version] [-d=number|--depth=number|--max-depth=number]" << std::endl;
exit(0);
};
// Simple functional api. No initialization required.
bool help = getarg( false, "-h", "--help", "-?" );
int version = getarg( 0, "-v", "--version", "--show-version" );
int depth = getarg( 0, "-d", "--depth", "--max-depth");
std::string file = getarg( "", "-f", "--file" );
if( help || argc <= 1 ) {
show_help();
}
if( version ) {
std::cout << argv[0] << " demo v1.0.0. Compiled on " << __DATE__ << std::endl;
}
if( depth ) {
std::cout << "provided depth: " << depth << std::endl;
}
if( !file.empty() ) {
std::cout << "provided file: " << file << std::endl;
}
// OOP map-based api. Explicit (argc, argv) initialization required.
struct getopt args( argc, argv );
if( args.has("-h") || args.has("--help") || args.has("-?") || args.size() == 1 ) {
show_help();
}
if( args.has("-v") || args.has("--version") ) {
std::cout << args["0"] << " demo v1.0.0. Compiled on " << __DATE__ << std::endl;
}
if( args.has("-d") || args.has("--depth") || args.has("--max-depth") ) {
std::string arg = args["-d"];
if( arg.empty() ) arg = args["--depth"];
if( arg.empty() ) arg = args["--max-depth"];
int depth = atoi( arg.c_str() );
std::cout << "provided depth: " << depth << std::endl;
}
if( args.has("-f") || args.has("--file") ) {
std::string arg = args["-f"];
if( arg.empty() ) arg = args["--file"];
std::string fname = arg;
std::cout << "provided file: " << fname << std::endl;
}
std::cout << "---" << std::endl;
std::cout << args.cmdline() << std::endl;
//std::cout << args.size() << " provided args: " << args.str() << std::endl;
}
#endif

@ -0,0 +1,80 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/sgx/sgx_ra_tls.h>
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#ifdef BAZEL_BUILD
#include "examples/protos/ratls.grpc.pb.h"
#else
#include "ratls.grpc.pb.h"
#endif
#include "../getopt.hpp"
using ratls::Greeter;
using ratls::HelloReply;
using ratls::HelloRequest;
struct argparser {
const char* config;
std::string server_address;
argparser() {
server_address = getarg("localhost:50051", "-host", "--host");
config = getarg("dynamic_config.json", "-cfg", "--config");
};
};
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
grpc::Status SayHello(
grpc::ServerContext* context, const HelloRequest* request, HelloReply* reply) override {
std::string prefix("Hello ");
reply->set_message(prefix + request->name());
return grpc::Status::OK;
}
};
void RunServer() {
argparser args;
GreeterServiceImpl service;
grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
grpc::ServerBuilder builder;
auto creds = grpc::sgx::TlsServerCredentials(args.config);
GPR_ASSERT(creds.get() != nullptr);
builder.AddListeningPort(args.server_address, creds);
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << args.server_address << std::endl;
server->Wait();
}
int main(int argc, char** argv) {
RunServer();
return 0;
}

@ -0,0 +1,38 @@
// Copyright (c) 2022 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.ratls";
option java_outer_classname = "RATLSProto";
option objc_class_prefix = "HLW";
package ratls;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}

@ -0,0 +1,57 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef SGX_RA_TLS_H
#define SGX_RA_TLS_H
#include <memory>
#include <grpcpp/security/credentials.h>
#include <grpcpp/security/server_credentials.h>
namespace grpc {
namespace sgx {
struct sgx_config;
std::vector<std::string> ra_tls_get_key_cert();
void ra_tls_parse_sgx_config(sgx_config sgx_cfg);
void ra_tls_parse_sgx_config(const char* file);
void ra_tls_verify_init();
int ra_tls_auth_check_schedule(void* /* config_user_data */,
grpc_tls_server_authorization_check_arg* arg);
std::shared_ptr<grpc::ChannelCredentials> TlsCredentials(sgx_config sgx_cfg);
std::shared_ptr<grpc::ChannelCredentials> TlsCredentials(const char* sgx_cfg_json);
std::shared_ptr<grpc::ServerCredentials> TlsServerCredentials(sgx_config sgx_cfg);
std::shared_ptr<grpc::ServerCredentials> TlsServerCredentials(const char* sgx_cfg_json);
std::shared_ptr<grpc::Channel> CreateSecureChannel(
string target_str, std::shared_ptr<grpc::ChannelCredentials> channel_creds);
} // namespace sgx
} // namespace grpc
#endif // SGX_RA_TLS_H

@ -0,0 +1,67 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef SGX_RA_TLS_OPTIONS_H
#define SGX_RA_TLS_OPTIONS_H
#include <grpc/grpc_security_constants.h>
#include <grpc/status.h>
#include <grpc/support/log.h>
#include <grpcpp/security/tls_certificate_provider.h>
#include <grpcpp/security/tls_credentials_options.h>
#include <grpcpp/support/config.h>
#include <memory>
#include <vector>
namespace grpc {
namespace sgx {
// Contains configurable options on the client side.
// Client side doesn't need to always use certificate provider. When the
// certificate provider is not set, we will use the root certificates stored
// in the system default locations, and assume client won't provide any
// identity certificates(single side TLS).
// It is used for experimental purposes for now and it is subject to change.
class CredentialsOptions final : public grpc::experimental::TlsCredentialsOptions {
public:
explicit CredentialsOptions() : TlsCredentialsOptions() {}
// Sets option to request the certificates from the client.
// The default is GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE.
void set_cert_request_type(
grpc_ssl_client_certificate_request_type cert_request_type);
// Sets the option to verify the server.
// The default is GRPC_TLS_SERVER_VERIFICATION.
void set_verification_option(
grpc_tls_server_verification_option server_verification_option);
// Sets the custom authorization config.
void set_authorization_check_config(
std::shared_ptr<grpc::experimental::TlsServerAuthorizationCheckConfig>
authorization_check_config);
private:
};
} // namespace sgx
} // namespace grpc
#endif // SGX_RA_TLS_OPTIONS_H

@ -0,0 +1,133 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <grpc/support/port_platform.h>
#include "src/core/lib/security/credentials/tls/tls_credentials.h"
#include <cstring>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/security/security_connector/tls/tls_security_connector.h"
#define GRPC_CREDENTIALS_TYPE_TLS "Tls"
namespace {
bool CredentialOptionSanityCheck(const grpc_tls_credentials_options* options,
bool is_client) {
if (options == nullptr) {
gpr_log(GPR_ERROR, "TLS credentials options is nullptr.");
return false;
}
// TODO(ZhenLian): remove this when it is also supported on server side.
// if (!is_client && options->server_authorization_check_config() != nullptr) {
// gpr_log(GPR_INFO,
// "Server's credentials options should not contain server "
// "authorization check config.");
// }
if (options->server_verification_option() != GRPC_TLS_SERVER_VERIFICATION &&
options->server_authorization_check_config() == nullptr) {
gpr_log(GPR_ERROR,
"Should provider custom verifications if bypassing default ones.");
return false;
}
return true;
}
} // namespace
TlsCredentials::TlsCredentials(
grpc_core::RefCountedPtr<grpc_tls_credentials_options> options)
: grpc_channel_credentials(GRPC_CREDENTIALS_TYPE_TLS),
options_(std::move(options)) {}
TlsCredentials::~TlsCredentials() {}
grpc_core::RefCountedPtr<grpc_channel_security_connector>
TlsCredentials::create_security_connector(
grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
const char* target_name, const grpc_channel_args* args,
grpc_channel_args** new_args) {
const char* overridden_target_name = nullptr;
tsi_ssl_session_cache* ssl_session_cache = nullptr;
for (size_t i = 0; args != nullptr && i < args->num_args; i++) {
grpc_arg* arg = &args->args[i];
if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 &&
arg->type == GRPC_ARG_STRING) {
overridden_target_name = arg->value.string;
}
if (strcmp(arg->key, GRPC_SSL_SESSION_CACHE_ARG) == 0 &&
arg->type == GRPC_ARG_POINTER) {
ssl_session_cache =
static_cast<tsi_ssl_session_cache*>(arg->value.pointer.p);
}
}
grpc_core::RefCountedPtr<grpc_channel_security_connector> sc =
grpc_core::TlsChannelSecurityConnector::CreateTlsChannelSecurityConnector(
this->Ref(), options_, std::move(call_creds), target_name,
overridden_target_name, ssl_session_cache);
if (sc == nullptr) {
return nullptr;
}
if (args != nullptr) {
grpc_arg new_arg = grpc_channel_arg_string_create(
const_cast<char*>(GRPC_ARG_HTTP2_SCHEME), const_cast<char*>("https"));
*new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1);
}
return sc;
}
TlsServerCredentials::TlsServerCredentials(
grpc_core::RefCountedPtr<grpc_tls_credentials_options> options)
: grpc_server_credentials(GRPC_CREDENTIALS_TYPE_TLS),
options_(std::move(options)) {}
TlsServerCredentials::~TlsServerCredentials() {}
grpc_core::RefCountedPtr<grpc_server_security_connector>
TlsServerCredentials::create_security_connector(
const grpc_channel_args* /* args */) {
return grpc_core::TlsServerSecurityConnector::
CreateTlsServerSecurityConnector(this->Ref(), options_);
}
/** -- Wrapper APIs declared in grpc_security.h -- **/
grpc_channel_credentials* grpc_tls_credentials_create(
grpc_tls_credentials_options* options) {
if (!CredentialOptionSanityCheck(options, true /* is_client */)) {
return nullptr;
}
return new TlsCredentials(
grpc_core::RefCountedPtr<grpc_tls_credentials_options>(options));
}
grpc_server_credentials* grpc_tls_server_credentials_create(
grpc_tls_credentials_options* options) {
if (!CredentialOptionSanityCheck(options, false /* is_client */)) {
return nullptr;
}
return new TlsServerCredentials(
grpc_core::RefCountedPtr<grpc_tls_credentials_options>(options));
}

@ -0,0 +1,863 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <grpc/support/port_platform.h>
#include "src/core/lib/security/security_connector/tls/tls_security_connector.h"
#include <stdbool.h>
#include <string.h>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/lib/gprpp/host_port.h"
#include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
#include "src/core/lib/security/credentials/tls/tls_credentials.h"
#include "src/core/lib/security/security_connector/ssl_utils.h"
#include "src/core/lib/security/transport/security_handshaker.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/tsi/ssl_transport_security.h"
#include "src/core/tsi/transport_security.h"
namespace grpc_core {
namespace {
tsi_ssl_pem_key_cert_pair* ConvertToTsiPemKeyCertPair(
const PemKeyCertPairList& cert_pair_list) {
tsi_ssl_pem_key_cert_pair* tsi_pairs = nullptr;
size_t num_key_cert_pairs = cert_pair_list.size();
if (num_key_cert_pairs > 0) {
GPR_ASSERT(cert_pair_list.data() != nullptr);
tsi_pairs = static_cast<tsi_ssl_pem_key_cert_pair*>(
gpr_zalloc(num_key_cert_pairs * sizeof(tsi_ssl_pem_key_cert_pair)));
}
for (size_t i = 0; i < num_key_cert_pairs; i++) {
GPR_ASSERT(!cert_pair_list[i].private_key().empty());
GPR_ASSERT(!cert_pair_list[i].cert_chain().empty());
tsi_pairs[i].cert_chain =
gpr_strdup(cert_pair_list[i].cert_chain().c_str());
tsi_pairs[i].private_key =
gpr_strdup(cert_pair_list[i].private_key().c_str());
}
return tsi_pairs;
}
} // namespace
void gpr_check_free(void* p) {
if (p) {
gpr_free(p);
p = nullptr;
}
}
// -------------------channel security connector-------------------
RefCountedPtr<grpc_channel_security_connector>
TlsChannelSecurityConnector::CreateTlsChannelSecurityConnector(
RefCountedPtr<grpc_channel_credentials> channel_creds,
RefCountedPtr<grpc_tls_credentials_options> options,
RefCountedPtr<grpc_call_credentials> request_metadata_creds,
const char* target_name, const char* overridden_target_name,
tsi_ssl_session_cache* ssl_session_cache) {
if (channel_creds == nullptr) {
gpr_log(GPR_ERROR,
"channel_creds is nullptr in "
"TlsChannelSecurityConnectorCreate()");
return nullptr;
}
if (options == nullptr) {
gpr_log(GPR_ERROR,
"options is nullptr in "
"TlsChannelSecurityConnectorCreate()");
return nullptr;
}
if (target_name == nullptr) {
gpr_log(GPR_ERROR,
"target_name is nullptr in "
"TlsChannelSecurityConnectorCreate()");
return nullptr;
}
return MakeRefCounted<TlsChannelSecurityConnector>(
std::move(channel_creds), std::move(options),
std::move(request_metadata_creds), target_name, overridden_target_name,
ssl_session_cache);
}
TlsChannelSecurityConnector::TlsChannelSecurityConnector(
RefCountedPtr<grpc_channel_credentials> channel_creds,
RefCountedPtr<grpc_tls_credentials_options> options,
RefCountedPtr<grpc_call_credentials> request_metadata_creds,
const char* target_name, const char* overridden_target_name,
tsi_ssl_session_cache* ssl_session_cache)
: grpc_channel_security_connector(GRPC_SSL_URL_SCHEME,
std::move(channel_creds),
std::move(request_metadata_creds)),
options_(std::move(options)),
overridden_target_name_(
overridden_target_name == nullptr ? "" : overridden_target_name),
ssl_session_cache_(ssl_session_cache) {
if (ssl_session_cache_ != nullptr) {
tsi_ssl_session_cache_ref(ssl_session_cache_);
}
check_arg_ = ServerAuthorizationCheckArgCreate(this);
absl::string_view host;
absl::string_view port;
SplitHostPort(target_name, &host, &port);
target_name_ = std::string(host);
// Create a watcher.
auto watcher_ptr = absl::make_unique<TlsChannelCertificateWatcher>(this);
certificate_watcher_ = watcher_ptr.get();
// Register the watcher with the distributor.
grpc_tls_certificate_distributor* distributor =
options_->certificate_distributor();
absl::optional<std::string> watched_root_cert_name;
if (options_->watch_root_cert()) {
watched_root_cert_name = options_->root_cert_name();
}
absl::optional<std::string> watched_identity_cert_name;
if (options_->watch_identity_pair()) {
watched_identity_cert_name = options_->identity_cert_name();
}
// We will use the root certs stored in system default locations if not
// watching root certs on the client side. We will handle this case
// differently here, because "watching a default roots without the identity
// certs" is a valid case(and hence we will need to call
// OnCertificatesChanged), but it requires nothing from the provider, and
// hence no need to register the watcher.
bool use_default_roots = !options_->watch_root_cert();
if (use_default_roots && !options_->watch_identity_pair()) {
watcher_ptr->OnCertificatesChanged(absl::nullopt, absl::nullopt);
} else {
distributor->WatchTlsCertificates(std::move(watcher_ptr),
watched_root_cert_name,
watched_identity_cert_name);
}
}
TlsChannelSecurityConnector::~TlsChannelSecurityConnector() {
if (ssl_session_cache_ != nullptr) {
tsi_ssl_session_cache_unref(ssl_session_cache_);
}
// Cancel all the watchers.
grpc_tls_certificate_distributor* distributor =
options_->certificate_distributor();
if (distributor != nullptr) {
distributor->CancelTlsCertificatesWatch(certificate_watcher_);
}
if (client_handshaker_factory_ != nullptr) {
tsi_ssl_client_handshaker_factory_unref(client_handshaker_factory_);
}
if (check_arg_ != nullptr) {
ServerAuthorizationCheckArgDestroy(check_arg_);
}
}
void TlsChannelSecurityConnector::add_handshakers(
const grpc_channel_args* args, grpc_pollset_set* /*interested_parties*/,
HandshakeManager* handshake_mgr) {
MutexLock lock(&mu_);
if (client_handshaker_factory_ != nullptr) {
// Instantiate TSI handshaker.
tsi_handshaker* tsi_hs = nullptr;
tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker(
client_handshaker_factory_,
overridden_target_name_.empty() ? target_name_.c_str()
: overridden_target_name_.c_str(),
&tsi_hs);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
tsi_result_to_string(result));
return;
}
// Create handshakers.
handshake_mgr->Add(SecurityHandshakerCreate(tsi_hs, this, args));
return;
}
// TODO(ZhenLian): Implement the logic(delegation to
// BlockOnInitialCredentialHandshaker) when certificates are not ready.
gpr_log(GPR_ERROR, "%s not supported yet.",
"Client BlockOnInitialCredentialHandshaker");
}
void TlsChannelSecurityConnector::check_peer(
tsi_peer peer, grpc_endpoint* /*ep*/,
RefCountedPtr<grpc_auth_context>* auth_context,
grpc_closure* on_peer_checked) {
const char* target_name = overridden_target_name_.empty()
? target_name_.c_str()
: overridden_target_name_.c_str();
grpc_error_handle error = grpc_ssl_check_alpn(&peer);
if (error != GRPC_ERROR_NONE) {
ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error);
tsi_peer_destruct(&peer);
return;
}
*auth_context =
grpc_ssl_peer_to_auth_context(&peer, GRPC_TLS_TRANSPORT_SECURITY_TYPE);
if (options_->server_verification_option() == GRPC_TLS_SERVER_VERIFICATION) {
/* Do the default host name check if specifying the target name. */
error = internal::TlsCheckHostName(target_name, &peer);
if (error != GRPC_ERROR_NONE) {
ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error);
tsi_peer_destruct(&peer);
return;
}
}
/* Do the custom server authorization check, if specified by the user. */
const grpc_tls_server_authorization_check_config* config =
options_->server_authorization_check_config();
/* If server authorization config is not null, use it to perform
* server authorization check. */
if (config != nullptr) {
const tsi_peer_property* p =
tsi_peer_get_property_by_name(&peer, TSI_X509_PEM_CERT_PROPERTY);
if (p == nullptr) {
error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Cannot check peer: missing pem cert property.");
} else {
char* peer_pem = static_cast<char*>(gpr_zalloc(p->value.length + 1));
memcpy(peer_pem, p->value.data, p->value.length);
GPR_ASSERT(check_arg_ != nullptr);
check_arg_->peer_cert = check_arg_->peer_cert == nullptr
? gpr_strdup(peer_pem)
: check_arg_->peer_cert;
check_arg_->target_name = check_arg_->target_name == nullptr
? gpr_strdup(target_name)
: check_arg_->target_name;
on_peer_checked_ = on_peer_checked;
gpr_check_free(peer_pem);
const tsi_peer_property* chain = tsi_peer_get_property_by_name(
&peer, TSI_X509_PEM_CERT_CHAIN_PROPERTY);
if (chain != nullptr) {
char* peer_pem_chain =
static_cast<char*>(gpr_zalloc(chain->value.length + 1));
memcpy(peer_pem_chain, chain->value.data, chain->value.length);
check_arg_->peer_cert_full_chain =
check_arg_->peer_cert_full_chain == nullptr
? gpr_strdup(peer_pem_chain)
: check_arg_->peer_cert_full_chain;
gpr_check_free(peer_pem_chain);
}
// TODO(zhenlian) - This should be cleaned up as part of the custom
// verification changes. Fill in the subject alternative names
std::vector<char*> subject_alternative_names;
for (size_t i = 0; i < peer.property_count; i++) {
const tsi_peer_property* prop = &peer.properties[i];
if (strcmp(prop->name,
TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) {
char* san = new char[prop->value.length + 1];
memcpy(san, prop->value.data, prop->value.length);
san[prop->value.length] = '\0';
subject_alternative_names.emplace_back(san);
}
}
if (check_arg_->subject_alternative_names != nullptr) {
for (size_t i = 0; i < check_arg_->subject_alternative_names_size;
++i) {
delete[] check_arg_->subject_alternative_names[i];
}
delete[] check_arg_->subject_alternative_names;
}
check_arg_->subject_alternative_names_size =
subject_alternative_names.size();
if (subject_alternative_names.empty()) {
check_arg_->subject_alternative_names = nullptr;
} else {
check_arg_->subject_alternative_names =
new char*[check_arg_->subject_alternative_names_size];
for (size_t i = 0; i < check_arg_->subject_alternative_names_size;
++i) {
check_arg_->subject_alternative_names[i] =
subject_alternative_names[i];
}
}
int callback_status = config->Schedule(check_arg_);
/* Server authorization check is handled asynchronously. */
if (callback_status) {
tsi_peer_destruct(&peer);
return;
}
/* Server authorization check is handled synchronously. */
error = ProcessServerAuthorizationCheckResult(check_arg_);
}
}
ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error);
tsi_peer_destruct(&peer);
}
int TlsChannelSecurityConnector::cmp(
const grpc_security_connector* other_sc) const {
auto* other = reinterpret_cast<const TlsChannelSecurityConnector*>(other_sc);
int c = channel_security_connector_cmp(other);
if (c != 0) {
return c;
}
return grpc_ssl_cmp_target_name(
target_name_.c_str(), other->target_name_.c_str(),
overridden_target_name_.c_str(), other->overridden_target_name_.c_str());
}
bool TlsChannelSecurityConnector::check_call_host(
absl::string_view host, grpc_auth_context* auth_context,
grpc_closure* /*on_call_host_checked*/, grpc_error_handle* error) {
if (options_->server_verification_option() ==
GRPC_TLS_SKIP_HOSTNAME_VERIFICATION ||
options_->server_verification_option() ==
GRPC_TLS_SKIP_ALL_SERVER_VERIFICATION) {
return true;
}
return grpc_ssl_check_call_host(host, target_name_.c_str(),
overridden_target_name_.c_str(), auth_context,
error);
}
void TlsChannelSecurityConnector::cancel_check_call_host(
grpc_closure* /*on_call_host_checked*/, grpc_error_handle error) {
GRPC_ERROR_UNREF(error);
}
void TlsChannelSecurityConnector::TlsChannelCertificateWatcher::
OnCertificatesChanged(absl::optional<absl::string_view> root_certs,
absl::optional<PemKeyCertPairList> key_cert_pairs) {
GPR_ASSERT(security_connector_ != nullptr);
MutexLock lock(&security_connector_->mu_);
if (root_certs.has_value()) {
security_connector_->pem_root_certs_ = root_certs;
}
if (key_cert_pairs.has_value()) {
security_connector_->pem_key_cert_pair_list_ = std::move(key_cert_pairs);
}
const bool root_ready = !security_connector_->options_->watch_root_cert() ||
security_connector_->pem_root_certs_.has_value();
const bool identity_ready =
!security_connector_->options_->watch_identity_pair() ||
security_connector_->pem_key_cert_pair_list_.has_value();
if (root_ready && identity_ready) {
if (security_connector_->UpdateHandshakerFactoryLocked() !=
GRPC_SECURITY_OK) {
gpr_log(GPR_ERROR, "Update handshaker factory failed.");
}
}
}
// TODO(ZhenLian): implement the logic to signal waiting handshakers once
// BlockOnInitialCredentialHandshaker is implemented.
void TlsChannelSecurityConnector::TlsChannelCertificateWatcher::OnError(
grpc_error_handle root_cert_error, grpc_error_handle identity_cert_error) {
if (root_cert_error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR,
"TlsChannelCertificateWatcher getting root_cert_error: %s",
grpc_error_std_string(root_cert_error).c_str());
}
if (identity_cert_error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR,
"TlsChannelCertificateWatcher getting identity_cert_error: %s",
grpc_error_std_string(identity_cert_error).c_str());
}
GRPC_ERROR_UNREF(root_cert_error);
GRPC_ERROR_UNREF(identity_cert_error);
}
// TODO(ZhenLian): implement the logic to signal waiting handshakers once
// BlockOnInitialCredentialHandshaker is implemented.
grpc_security_status
TlsChannelSecurityConnector::UpdateHandshakerFactoryLocked() {
bool skip_server_certificate_verification =
options_->server_verification_option() ==
GRPC_TLS_SKIP_ALL_SERVER_VERIFICATION;
/* Free the client handshaker factory if exists. */
if (client_handshaker_factory_ != nullptr) {
tsi_ssl_client_handshaker_factory_unref(client_handshaker_factory_);
}
std::string pem_root_certs;
if (pem_root_certs_.has_value()) {
// TODO(ZhenLian): update the underlying TSI layer to use C++ types like
// std::string and absl::string_view to avoid making another copy here.
pem_root_certs = std::string(*pem_root_certs_);
}
tsi_ssl_pem_key_cert_pair* pem_key_cert_pair = nullptr;
if (pem_key_cert_pair_list_.has_value()) {
pem_key_cert_pair = ConvertToTsiPemKeyCertPair(*pem_key_cert_pair_list_);
}
bool use_default_roots = !options_->watch_root_cert();
grpc_security_status status = grpc_ssl_tsi_client_handshaker_factory_init(
pem_key_cert_pair,
pem_root_certs.empty() || use_default_roots ? nullptr
: pem_root_certs.c_str(),
skip_server_certificate_verification,
grpc_get_tsi_tls_version(options_->min_tls_version()),
grpc_get_tsi_tls_version(options_->max_tls_version()), ssl_session_cache_,
&client_handshaker_factory_);
/* Free memory. */
if (pem_key_cert_pair != nullptr) {
grpc_tsi_ssl_pem_key_cert_pairs_destroy(pem_key_cert_pair, 1);
}
return status;
}
void TlsChannelSecurityConnector::ServerAuthorizationCheckDone(
grpc_tls_server_authorization_check_arg* arg) {
GPR_ASSERT(arg != nullptr);
ExecCtx exec_ctx;
grpc_error_handle error = ProcessServerAuthorizationCheckResult(arg);
TlsChannelSecurityConnector* connector =
static_cast<TlsChannelSecurityConnector*>(arg->cb_user_data);
ExecCtx::Run(DEBUG_LOCATION, connector->on_peer_checked_, error);
}
grpc_error_handle
TlsChannelSecurityConnector::ProcessServerAuthorizationCheckResult(
grpc_tls_server_authorization_check_arg* arg) {
grpc_error_handle error = GRPC_ERROR_NONE;
/* Server authorization check is cancelled by caller. */
if (arg->status == GRPC_STATUS_CANCELLED) {
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("Server authorization check is cancelled by the caller "
"with error: ",
arg->error_details->error_details())
.c_str());
} else if (arg->status == GRPC_STATUS_OK) {
/* Server authorization check completed successfully but returned check
* failure. */
if (!arg->success) {
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("Server authorization check failed with error: ",
arg->error_details->error_details())
.c_str());
}
/* Server authorization check did not complete correctly. */
} else {
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat(
"Server authorization check did not finish correctly with error: ",
arg->error_details->error_details())
.c_str());
}
return error;
}
grpc_tls_server_authorization_check_arg*
TlsChannelSecurityConnector::ServerAuthorizationCheckArgCreate(
void* user_data) {
grpc_tls_server_authorization_check_arg* arg =
new grpc_tls_server_authorization_check_arg();
arg->target_name = nullptr;
arg->peer_cert = nullptr;
arg->peer_cert_full_chain = nullptr;
arg->subject_alternative_names = nullptr;
arg->subject_alternative_names_size = 0;
arg->error_details = new grpc_tls_error_details();
arg->cb = ServerAuthorizationCheckDone;
arg->cb_user_data = user_data;
arg->status = GRPC_STATUS_OK;
return arg;
}
void TlsChannelSecurityConnector::ServerAuthorizationCheckArgDestroy(
grpc_tls_server_authorization_check_arg* arg) {
if (arg == nullptr) {
return;
}
gpr_check_free(const_cast<char*>(arg->target_name));
gpr_check_free(const_cast<char*>(arg->peer_cert));
gpr_check_free(const_cast<char*>(arg->peer_cert_full_chain));
for (size_t i = 0; i < arg->subject_alternative_names_size; ++i) {
delete[] arg->subject_alternative_names[i];
}
delete[] arg->subject_alternative_names;
delete arg->error_details;
if (arg->destroy_context != nullptr) {
arg->destroy_context(arg->context);
}
delete arg;
arg = nullptr;
}
// -------------------server security connector-------------------
RefCountedPtr<grpc_server_security_connector>
TlsServerSecurityConnector::CreateTlsServerSecurityConnector(
RefCountedPtr<grpc_server_credentials> server_creds,
RefCountedPtr<grpc_tls_credentials_options> options) {
if (server_creds == nullptr) {
gpr_log(GPR_ERROR,
"server_creds is nullptr in "
"TlsServerSecurityConnectorCreate()");
return nullptr;
}
if (options == nullptr) {
gpr_log(GPR_ERROR,
"options is nullptr in "
"TlsServerSecurityConnectorCreate()");
return nullptr;
}
return MakeRefCounted<TlsServerSecurityConnector>(std::move(server_creds),
std::move(options));
}
void TlsServerSecurityConnector::ClientAuthorizationCheckDone(
grpc_tls_server_authorization_check_arg* arg) {
GPR_ASSERT(arg != nullptr);
ExecCtx exec_ctx;
grpc_error_handle error = ProcessClientAuthorizationCheckResult(arg);
TlsServerSecurityConnector* connector =
static_cast<TlsServerSecurityConnector*>(arg->cb_user_data);
ExecCtx::Run(DEBUG_LOCATION, connector->on_peer_checked_, error);
}
grpc_error_handle
TlsServerSecurityConnector::ProcessClientAuthorizationCheckResult(
grpc_tls_server_authorization_check_arg* arg) {
grpc_error_handle error = GRPC_ERROR_NONE;
/* Client authorization check is cancelled by caller. */
if (arg->status == GRPC_STATUS_CANCELLED) {
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("Client authorization check is cancelled by the caller "
"with error: ",
arg->error_details->error_details())
.c_str());
} else if (arg->status == GRPC_STATUS_OK) {
/* Client authorization check completed successfully but returned check
* failure. */
if (!arg->success) {
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("Client authorization check failed with error: ",
arg->error_details->error_details())
.c_str());
}
/* Client authorization check did not complete correctly. */
} else {
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat(
"Client authorization check did not finish correctly with error: ",
arg->error_details->error_details())
.c_str());
}
return error;
}
grpc_tls_server_authorization_check_arg*
TlsServerSecurityConnector::ClientAuthorizationCheckArgCreate(
void* user_data) {
grpc_tls_server_authorization_check_arg* arg =
new grpc_tls_server_authorization_check_arg();
arg->target_name = nullptr;
arg->peer_cert = nullptr;
arg->peer_cert_full_chain = nullptr;
arg->subject_alternative_names = nullptr;
arg->subject_alternative_names_size = 0;
arg->error_details = new grpc_tls_error_details();
arg->cb = ClientAuthorizationCheckDone;
arg->cb_user_data = user_data;
arg->status = GRPC_STATUS_OK;
return arg;
}
void TlsServerSecurityConnector::ClientAuthorizationCheckArgDestroy(
grpc_tls_server_authorization_check_arg* arg) {
if (arg == nullptr) {
return;
}
gpr_check_free(const_cast<char*>(arg->target_name));
gpr_check_free(const_cast<char*>(arg->peer_cert));
gpr_check_free(const_cast<char*>(arg->peer_cert_full_chain));
for (size_t i = 0; i < arg->subject_alternative_names_size; ++i) {
delete[] arg->subject_alternative_names[i];
}
delete[] arg->subject_alternative_names;
delete arg->error_details;
if (arg->destroy_context != nullptr) {
arg->destroy_context(arg->context);
}
delete arg;
arg = nullptr;
}
TlsServerSecurityConnector::TlsServerSecurityConnector(
RefCountedPtr<grpc_server_credentials> server_creds,
RefCountedPtr<grpc_tls_credentials_options> options)
: grpc_server_security_connector(GRPC_SSL_URL_SCHEME,
std::move(server_creds)),
options_(std::move(options)) {
check_arg_ = ClientAuthorizationCheckArgCreate(this);
// Create a watcher.
auto watcher_ptr = absl::make_unique<TlsServerCertificateWatcher>(this);
certificate_watcher_ = watcher_ptr.get();
// Register the watcher with the distributor.
grpc_tls_certificate_distributor* distributor =
options_->certificate_distributor();
absl::optional<std::string> watched_root_cert_name;
if (options_->watch_root_cert()) {
watched_root_cert_name = options_->root_cert_name();
}
absl::optional<std::string> watched_identity_cert_name;
if (options_->watch_identity_pair()) {
watched_identity_cert_name = options_->identity_cert_name();
}
// Server side won't use default system roots at any time.
distributor->WatchTlsCertificates(std::move(watcher_ptr),
watched_root_cert_name,
watched_identity_cert_name);
}
TlsServerSecurityConnector::~TlsServerSecurityConnector() {
// Cancel all the watchers.
grpc_tls_certificate_distributor* distributor =
options_->certificate_distributor();
if (distributor != nullptr) {
distributor->CancelTlsCertificatesWatch(certificate_watcher_);
}
if (server_handshaker_factory_ != nullptr) {
tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_);
}
if (check_arg_ != nullptr) {
ClientAuthorizationCheckArgDestroy(check_arg_);
}
}
void TlsServerSecurityConnector::add_handshakers(
const grpc_channel_args* args, grpc_pollset_set* /*interested_parties*/,
HandshakeManager* handshake_mgr) {
MutexLock lock(&mu_);
if (server_handshaker_factory_ != nullptr) {
// Instantiate TSI handshaker.
tsi_handshaker* tsi_hs = nullptr;
tsi_result result = tsi_ssl_server_handshaker_factory_create_handshaker(
server_handshaker_factory_, &tsi_hs);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
tsi_result_to_string(result));
return;
}
// Create handshakers.
handshake_mgr->Add(SecurityHandshakerCreate(tsi_hs, this, args));
return;
}
// TODO(ZhenLian): Implement the logic(delegation to
// BlockOnInitialCredentialHandshaker) when certificates are not ready.
gpr_log(GPR_ERROR, "%s not supported yet.",
"Server BlockOnInitialCredentialHandshaker");
}
void TlsServerSecurityConnector::check_peer(
tsi_peer peer, grpc_endpoint* /*ep*/,
RefCountedPtr<grpc_auth_context>* auth_context,
grpc_closure* on_peer_checked) {
grpc_error_handle error = grpc_ssl_check_alpn(&peer);
if (error != GRPC_ERROR_NONE) {
ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error);
tsi_peer_destruct(&peer);
return;
}
*auth_context =
grpc_ssl_peer_to_auth_context(&peer, GRPC_TLS_TRANSPORT_SECURITY_TYPE);
if (options_->server_verification_option() == GRPC_TLS_SERVER_VERIFICATION) {
/* Do the default host name check if specifying the target name. */
error = internal::TlsCheckHostName("", &peer);
if (error != GRPC_ERROR_NONE) {
ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error);
tsi_peer_destruct(&peer);
return;
}
}
/* Do the custom client authorization check, if specified by the user. */
const grpc_tls_server_authorization_check_config* config =
options_->server_authorization_check_config();
/* If client authorization config is not null, use it to perform
* client authorization check. */
if (config != nullptr) {
const tsi_peer_property* p =
tsi_peer_get_property_by_name(&peer, TSI_X509_PEM_CERT_PROPERTY);
if (p == nullptr) {
error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Cannot check peer: missing pem cert property.");
} else {
char* peer_pem = static_cast<char*>(gpr_zalloc(p->value.length + 1));
memcpy(peer_pem, p->value.data, p->value.length);
GPR_ASSERT(check_arg_ != nullptr);
check_arg_->peer_cert = gpr_strdup(peer_pem);
check_arg_->target_name = gpr_strdup("");
on_peer_checked_ = on_peer_checked;
gpr_check_free(peer_pem);
const tsi_peer_property* chain = tsi_peer_get_property_by_name(
&peer, TSI_X509_PEM_CERT_CHAIN_PROPERTY);
if (chain != nullptr) {
char* peer_pem_chain =
static_cast<char*>(gpr_zalloc(chain->value.length + 1));
memcpy(peer_pem_chain, chain->value.data, chain->value.length);
check_arg_->peer_cert_full_chain =
check_arg_->peer_cert_full_chain == nullptr
? gpr_strdup(peer_pem_chain)
: check_arg_->peer_cert_full_chain;
gpr_check_free(peer_pem_chain);
}
// TODO(zhenlian) - This should be cleaned up as part of the custom
// verification changes. Fill in the subject alternative names
std::vector<char*> subject_alternative_names;
for (size_t i = 0; i < peer.property_count; i++) {
const tsi_peer_property* prop = &peer.properties[i];
if (strcmp(prop->name,
TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) {
char* san = new char[prop->value.length + 1];
memcpy(san, prop->value.data, prop->value.length);
san[prop->value.length] = '\0';
subject_alternative_names.emplace_back(san);
}
}
if (check_arg_->subject_alternative_names != nullptr) {
for (size_t i = 0; i < check_arg_->subject_alternative_names_size;
++i) {
delete[] check_arg_->subject_alternative_names[i];
}
delete[] check_arg_->subject_alternative_names;
}
check_arg_->subject_alternative_names_size =
subject_alternative_names.size();
if (subject_alternative_names.empty()) {
check_arg_->subject_alternative_names = nullptr;
} else {
check_arg_->subject_alternative_names =
new char*[check_arg_->subject_alternative_names_size];
for (size_t i = 0; i < check_arg_->subject_alternative_names_size;
++i) {
check_arg_->subject_alternative_names[i] =
subject_alternative_names[i];
}
}
int callback_status = config->Schedule(check_arg_);
/* Client authorization check is handled asynchronously. */
if (callback_status) {
tsi_peer_destruct(&peer);
return;
}
/* Client authorization check is handled synchronously. */
error = ProcessClientAuthorizationCheckResult(check_arg_);
}
}
ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error);
tsi_peer_destruct(&peer);
}
int TlsServerSecurityConnector::cmp(
const grpc_security_connector* other) const {
return server_security_connector_cmp(
static_cast<const grpc_server_security_connector*>(other));
}
void TlsServerSecurityConnector::TlsServerCertificateWatcher::
OnCertificatesChanged(absl::optional<absl::string_view> root_certs,
absl::optional<PemKeyCertPairList> key_cert_pairs) {
GPR_ASSERT(security_connector_ != nullptr);
MutexLock lock(&security_connector_->mu_);
if (root_certs.has_value()) {
security_connector_->pem_root_certs_ = root_certs;
}
if (key_cert_pairs.has_value()) {
security_connector_->pem_key_cert_pair_list_ = std::move(key_cert_pairs);
}
bool root_being_watched = security_connector_->options_->watch_root_cert();
bool root_has_value = security_connector_->pem_root_certs_.has_value();
bool identity_being_watched =
security_connector_->options_->watch_identity_pair();
bool identity_has_value =
security_connector_->pem_key_cert_pair_list_.has_value();
if ((root_being_watched && root_has_value && identity_being_watched &&
identity_has_value) ||
(root_being_watched && root_has_value && !identity_being_watched) ||
(!root_being_watched && identity_being_watched && identity_has_value)) {
if (security_connector_->UpdateHandshakerFactoryLocked() !=
GRPC_SECURITY_OK) {
gpr_log(GPR_ERROR, "Update handshaker factory failed.");
}
}
}
// TODO(ZhenLian): implement the logic to signal waiting handshakers once
// BlockOnInitialCredentialHandshaker is implemented.
void TlsServerSecurityConnector::TlsServerCertificateWatcher::OnError(
grpc_error_handle root_cert_error, grpc_error_handle identity_cert_error) {
if (root_cert_error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR,
"TlsServerCertificateWatcher getting root_cert_error: %s",
grpc_error_std_string(root_cert_error).c_str());
}
if (identity_cert_error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR,
"TlsServerCertificateWatcher getting identity_cert_error: %s",
grpc_error_std_string(identity_cert_error).c_str());
}
GRPC_ERROR_UNREF(root_cert_error);
GRPC_ERROR_UNREF(identity_cert_error);
}
// TODO(ZhenLian): implement the logic to signal waiting handshakers once
// BlockOnInitialCredentialHandshaker is implemented.
grpc_security_status
TlsServerSecurityConnector::UpdateHandshakerFactoryLocked() {
/* Free the server handshaker factory if exists. */
if (server_handshaker_factory_ != nullptr) {
tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_);
}
// The identity certs on the server side shouldn't be empty.
GPR_ASSERT(pem_key_cert_pair_list_.has_value());
GPR_ASSERT(!(*pem_key_cert_pair_list_).empty());
std::string pem_root_certs;
if (pem_root_certs_.has_value()) {
// TODO(ZhenLian): update the underlying TSI layer to use C++ types like
// std::string and absl::string_view to avoid making another copy here.
pem_root_certs = std::string(*pem_root_certs_);
}
tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs = nullptr;
pem_key_cert_pairs = ConvertToTsiPemKeyCertPair(*pem_key_cert_pair_list_);
size_t num_key_cert_pairs = (*pem_key_cert_pair_list_).size();
grpc_security_status status = grpc_ssl_tsi_server_handshaker_factory_init(
pem_key_cert_pairs, num_key_cert_pairs,
pem_root_certs.empty() ? nullptr : pem_root_certs.c_str(),
options_->cert_request_type(),
grpc_get_tsi_tls_version(options_->min_tls_version()),
grpc_get_tsi_tls_version(options_->max_tls_version()),
&server_handshaker_factory_);
/* Free memory. */
grpc_tsi_ssl_pem_key_cert_pairs_destroy(pem_key_cert_pairs,
num_key_cert_pairs);
return status;
}
namespace internal {
grpc_error_handle TlsCheckHostName(const char* peer_name,
const tsi_peer* peer) {
/* Check the peer name if specified. */
if (peer_name != nullptr && !grpc_ssl_host_matches_name(peer, peer_name)) {
return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("Peer name ", peer_name, " is not in peer certificate")
.c_str());
}
return GRPC_ERROR_NONE;
}
} // namespace internal
} // namespace grpc_core

@ -0,0 +1,267 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_TLS_TLS_SECURITY_CONNECTOR_H
#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_TLS_TLS_SECURITY_CONNECTOR_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/security/context/security_context.h"
#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
#include "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h"
#define GRPC_TLS_TRANSPORT_SECURITY_TYPE "tls"
namespace grpc_core {
// Channel security connector using TLS as transport security protocol.
class TlsChannelSecurityConnector final
: public grpc_channel_security_connector {
public:
// static factory method to create a TLS channel security connector.
static RefCountedPtr<grpc_channel_security_connector>
CreateTlsChannelSecurityConnector(
RefCountedPtr<grpc_channel_credentials> channel_creds,
RefCountedPtr<grpc_tls_credentials_options> options,
RefCountedPtr<grpc_call_credentials> request_metadata_creds,
const char* target_name, const char* overridden_target_name,
tsi_ssl_session_cache* ssl_session_cache);
TlsChannelSecurityConnector(
RefCountedPtr<grpc_channel_credentials> channel_creds,
RefCountedPtr<grpc_tls_credentials_options> options,
RefCountedPtr<grpc_call_credentials> request_metadata_creds,
const char* target_name, const char* overridden_target_name,
tsi_ssl_session_cache* ssl_session_cache);
~TlsChannelSecurityConnector() override;
void add_handshakers(const grpc_channel_args* args,
grpc_pollset_set* interested_parties,
HandshakeManager* handshake_mgr) override;
void check_peer(tsi_peer peer, grpc_endpoint* ep,
RefCountedPtr<grpc_auth_context>* auth_context,
grpc_closure* on_peer_checked) override;
void cancel_check_peer(grpc_closure* /*on_peer_checked*/,
grpc_error_handle error) override {
// TODO(ZhenLian): call verifier->cancel() once the verifier is ready.
GRPC_ERROR_UNREF(error);
}
int cmp(const grpc_security_connector* other_sc) const override;
bool check_call_host(absl::string_view host, grpc_auth_context* auth_context,
grpc_closure* on_call_host_checked,
grpc_error_handle* error) override;
void cancel_check_call_host(grpc_closure* on_call_host_checked,
grpc_error_handle error) override;
tsi_ssl_client_handshaker_factory* ClientHandshakerFactoryForTesting() {
MutexLock lock(&mu_);
return client_handshaker_factory_;
};
absl::optional<absl::string_view> RootCertsForTesting() {
MutexLock lock(&mu_);
return pem_root_certs_;
}
absl::optional<PemKeyCertPairList> KeyCertPairListForTesting() {
MutexLock lock(&mu_);
return pem_key_cert_pair_list_;
}
private:
// A watcher that watches certificate updates from
// grpc_tls_certificate_distributor. It will never outlive
// |security_connector_|.
class TlsChannelCertificateWatcher : public grpc_tls_certificate_distributor::
TlsCertificatesWatcherInterface {
public:
explicit TlsChannelCertificateWatcher(
TlsChannelSecurityConnector* security_connector)
: security_connector_(security_connector) {}
void OnCertificatesChanged(
absl::optional<absl::string_view> root_certs,
absl::optional<PemKeyCertPairList> key_cert_pairs) override;
void OnError(grpc_error_handle root_cert_error,
grpc_error_handle identity_cert_error) override;
private:
TlsChannelSecurityConnector* security_connector_ = nullptr;
};
// Updates |client_handshaker_factory_| when the certificates that
// |certificate_watcher_| is watching get updated.
grpc_security_status UpdateHandshakerFactoryLocked()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
// gRPC-provided callback executed by application, which servers to bring the
// control back to gRPC core.
static void ServerAuthorizationCheckDone(
grpc_tls_server_authorization_check_arg* arg);
// A util function to process server authorization check result.
static grpc_error_handle ProcessServerAuthorizationCheckResult(
grpc_tls_server_authorization_check_arg* arg);
// A util function to create a server authorization check arg instance.
static grpc_tls_server_authorization_check_arg*
ServerAuthorizationCheckArgCreate(void* user_data);
// A util function to destroy a server authorization check arg instance.
static void ServerAuthorizationCheckArgDestroy(
grpc_tls_server_authorization_check_arg* arg);
RefCountedPtr<grpc_tls_credentials_options> options_;
grpc_tls_certificate_distributor::TlsCertificatesWatcherInterface*
certificate_watcher_ = nullptr;
grpc_closure* on_peer_checked_ = nullptr;
std::string target_name_;
std::string overridden_target_name_;
grpc_tls_server_authorization_check_arg* check_arg_ = nullptr;
Mutex mu_;
tsi_ssl_client_handshaker_factory* client_handshaker_factory_
ABSL_GUARDED_BY(mu_) = nullptr;
tsi_ssl_session_cache* ssl_session_cache_ ABSL_GUARDED_BY(mu_) = nullptr;
absl::optional<absl::string_view> pem_root_certs_ ABSL_GUARDED_BY(mu_);
absl::optional<PemKeyCertPairList> pem_key_cert_pair_list_
ABSL_GUARDED_BY(mu_);
};
// Server security connector using TLS as transport security protocol.
class TlsServerSecurityConnector final : public grpc_server_security_connector {
public:
// static factory method to create a TLS server security connector.
static RefCountedPtr<grpc_server_security_connector>
CreateTlsServerSecurityConnector(
RefCountedPtr<grpc_server_credentials> server_creds,
RefCountedPtr<grpc_tls_credentials_options> options);
TlsServerSecurityConnector(
RefCountedPtr<grpc_server_credentials> server_creds,
RefCountedPtr<grpc_tls_credentials_options> options);
~TlsServerSecurityConnector() override;
void add_handshakers(const grpc_channel_args* args,
grpc_pollset_set* interested_parties,
HandshakeManager* handshake_mgr) override;
void check_peer(tsi_peer peer, grpc_endpoint* ep,
RefCountedPtr<grpc_auth_context>* auth_context,
grpc_closure* on_peer_checked) override;
void cancel_check_peer(grpc_closure* /*on_peer_checked*/,
grpc_error_handle error) override {
// TODO(ZhenLian): call verifier->cancel() once the verifier is ready.
GRPC_ERROR_UNREF(error);
}
int cmp(const grpc_security_connector* other) const override;
tsi_ssl_server_handshaker_factory* ServerHandshakerFactoryForTesting() {
MutexLock lock(&mu_);
return server_handshaker_factory_;
};
const absl::optional<absl::string_view>& RootCertsForTesting() {
MutexLock lock(&mu_);
return pem_root_certs_;
}
const absl::optional<PemKeyCertPairList>& KeyCertPairListForTesting() {
MutexLock lock(&mu_);
return pem_key_cert_pair_list_;
}
private:
// A watcher that watches certificate updates from
// grpc_tls_certificate_distributor. It will never outlive
// |security_connector_|.
class TlsServerCertificateWatcher : public grpc_tls_certificate_distributor::
TlsCertificatesWatcherInterface {
public:
explicit TlsServerCertificateWatcher(
TlsServerSecurityConnector* security_connector)
: security_connector_(security_connector) {}
void OnCertificatesChanged(
absl::optional<absl::string_view> root_certs,
absl::optional<PemKeyCertPairList> key_cert_pairs) override;
void OnError(grpc_error_handle root_cert_error,
grpc_error_handle identity_cert_error) override;
private:
TlsServerSecurityConnector* security_connector_ = nullptr;
};
// Updates |server_handshaker_factory_| when the certificates that
// |certificate_watcher_| is watching get updated.
grpc_security_status UpdateHandshakerFactoryLocked()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
// gRPC-provided callback executed by application, which servers to bring the
// control back to gRPC core.
static void ClientAuthorizationCheckDone(
grpc_tls_server_authorization_check_arg* arg);
// A util function to process server authorization check result.
static grpc_error_handle ProcessClientAuthorizationCheckResult(
grpc_tls_server_authorization_check_arg* arg);
// A util function to create a server authorization check arg instance.
static grpc_tls_server_authorization_check_arg*
ClientAuthorizationCheckArgCreate(void* user_data);
// A util function to destroy a server authorization check arg instance.
static void ClientAuthorizationCheckArgDestroy(
grpc_tls_server_authorization_check_arg* arg);
RefCountedPtr<grpc_tls_credentials_options> options_;
grpc_tls_certificate_distributor::TlsCertificatesWatcherInterface*
certificate_watcher_ = nullptr;
grpc_closure* on_peer_checked_ = nullptr;
std::string target_name_ = "localhost";
std::string overridden_target_name_= "localhost";
grpc_tls_server_authorization_check_arg* check_arg_ = nullptr;
Mutex mu_;
tsi_ssl_server_handshaker_factory* server_handshaker_factory_
ABSL_GUARDED_BY(mu_) = nullptr;
absl::optional<absl::string_view> pem_root_certs_ ABSL_GUARDED_BY(mu_);
absl::optional<PemKeyCertPairList> pem_key_cert_pair_list_
ABSL_GUARDED_BY(mu_);
};
// ---- Functions below are exposed for testing only -----------------------
namespace internal {
// TlsCheckHostName checks if |peer_name| matches the identity information
// contained in |peer|. This is AKA hostname check.
grpc_error_handle TlsCheckHostName(const char* peer_name, const tsi_peer* peer);
} // namespace internal
} // namespace grpc_core
#endif // GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_TLS_TLS_SECURITY_CONNECTOR_H

@ -0,0 +1,567 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
static const char *ep;
const char *cJSON_GetErrorPtr(void) {return ep;}
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
len = strlen(str) + 1;
if (!(copy = (char*)cJSON_malloc(len))) return 0;
memcpy(copy,str,len);
return copy;
}
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (!hooks) { /* Reset hooks */
cJSON_malloc = malloc;
cJSON_free = free;
return;
}
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
next=c->next;
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
if (c->string) cJSON_free(c->string);
cJSON_free(c);
c=next;
}
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
/* Could use sscanf for this? */
if (*num=='-') sign=-1,num++; /* Has sign? */
if (*num=='0') num++; /* is zero */
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
if (*num=='e' || *num=='E') /* Exponent? */
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
}
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item)
{
char *str;
double d=item->valuedouble;
if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
{
str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str,"%d",item->valueint);
}
else
{
str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
if (str)
{
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
else sprintf(str,"%f",d);
}
}
return str;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
if (*str!='\"') {ep=str;return 0;} /* not a string! */
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;ptr2=out;
while (*ptr!='\"' && *ptr)
{
if (*ptr!='\\') *ptr2++=*ptr++;
else
{
ptr++;
switch (*ptr)
{
case 'b': *ptr2++='\b'; break;
case 'f': *ptr2++='\f'; break;
case 'n': *ptr2++='\n'; break;
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
sscanf(ptr+3,"%4x",&uc2);ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
switch (len) {
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 1: *--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default: *ptr2++=*ptr; break;
}
ptr++;
}
}
*ptr2=0;
if (*ptr=='\"') ptr++;
item->valuestring=out;
item->type=cJSON_String;
return ptr;
}
/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str)
{
const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
if (!str) return cJSON_strdup("");
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;ptr=str;
*ptr2++='\"';
while (*ptr)
{
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
else
{
*ptr2++='\\';
switch (token=*ptr++)
{
case '\\': *ptr2++='\\'; break;
case '\"': *ptr2++='\"'; break;
case '\b': *ptr2++='b'; break;
case '\f': *ptr2++='f'; break;
case '\n': *ptr2++='n'; break;
case '\r': *ptr2++='r'; break;
case '\t': *ptr2++='t'; break;
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
}
}
}
*ptr2++='\"';*ptr2++=0;
return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt);
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
cJSON *c=cJSON_New_Item();
ep=0;
if (!c) return 0; /* memory fail */
end=parse_value(c,skip(value));
if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
if (return_parse_end) *return_parse_end=end;
return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; /* Fail on null. */
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
if (*value=='\"') { return parse_string(item,value); }
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
if (*value=='[') { return parse_array(item,value); }
if (*value=='{') { return parse_object(item,value); }
ep=value;return 0; /* failure. */
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt)
{
char *out=0;
if (!item) return 0;
switch ((item->type)&255)
{
case cJSON_NULL: out=cJSON_strdup("null"); break;
case cJSON_False: out=cJSON_strdup("false");break;
case cJSON_True: out=cJSON_strdup("true"); break;
case cJSON_Number: out=print_number(item);break;
case cJSON_String: out=print_string(item);break;
case cJSON_Array: out=print_array(item,depth,fmt);break;
case cJSON_Object: out=print_object(item,depth,fmt);break;
}
return out;
}
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='[') {ep=value;return 0;} /* not an array! */
item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0; /* memory fail */
}
if (*value==']') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt)
{
char **entries;
char *out=0,*ptr,*ret;int len=5;
cJSON *child=item->child;
int numentries=0,i=0,fail=0;
/* How many entries in the array? */
while (child) numentries++,child=child->next;
/* Explicitly handle numentries==0 */
if (!numentries)
{
out=(char*)cJSON_malloc(3);
if (out) strcpy(out,"[]");
return out;
}
/* Allocate an array to hold the values for each */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
memset(entries,0,numentries*sizeof(char*));
/* Retrieve all the results: */
child=item->child;
while (child && !fail)
{
ret=print_value(child,depth+1,fmt);
entries[i++]=ret;
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
child=child->next;
}
/* If we didn't fail, try to malloc the output string */
if (!fail) out=(char*)cJSON_malloc(len);
/* If that fails, we fail. */
if (!out) fail=1;
/* Handle failure. */
if (fail)
{
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
cJSON_free(entries);
return 0;
}
/* Compose the output array. */
*out='[';
ptr=out+1;*ptr=0;
for (i=0;i<numentries;i++)
{
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
cJSON_free(entries[i]);
}
cJSON_free(entries);
*ptr++=']';*ptr++=0;
return out;
}
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='{') {ep=value;return 0;} /* not an object! */
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt)
{
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries)
{
out=(char*)cJSON_malloc(fmt?depth+3:3);
if (!out) return 0;
ptr=out;*ptr++='{';
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
*ptr++='}';*ptr++=0;
return out;
}
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {cJSON_free(entries);return 0;}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);
/* Collect all the results into our arrays: */
child=item->child;depth++;if (fmt) len+=depth;
while (child)
{
names[i]=str=print_string_ptr(child->string);
entries[i++]=ret=print_value(child,depth,fmt);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
child=child->next;
}
/* Try to allocate the output string */
if (!fail) out=(char*)cJSON_malloc(len);
if (!out) fail=1;
/* Handle failure */
if (fail)
{
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
cJSON_free(names);cJSON_free(entries);
return 0;
}
/* Compose the output: */
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
for (i=0;i<numentries;i++)
{
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
strcpy(ptr,names[i]);ptr+=strlen(names[i]);
*ptr++=':';if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
cJSON_free(names[i]);cJSON_free(entries[i]);
}
cJSON_free(names);cJSON_free(entries);
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr++=0;
return out;
}
/* Get Array size/item / object item. */
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
/* Replace array/object items with new ones. */
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
cJSON *newitem,*cptr,*nptr=0,*newchild;
/* Bail on bad ptr */
if (!item) return 0;
/* Create new item */
newitem=cJSON_New_Item();
if (!newitem) return 0;
/* Copy over all vars */
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
/* If non-recursive, then we're done! */
if (!recurse) return newitem;
/* Walk the ->next chain for the child. */
cptr=item->child;
while (cptr)
{
newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild) {cJSON_Delete(newitem);return 0;}
if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
cptr=cptr->next;
}
return newitem;
}

@ -0,0 +1,140 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* Update array items. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,567 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
static const char *ep;
const char *cJSON_GetErrorPtr(void) {return ep;}
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
len = strlen(str) + 1;
if (!(copy = (char*)cJSON_malloc(len))) return 0;
memcpy(copy,str,len);
return copy;
}
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (!hooks) { /* Reset hooks */
cJSON_malloc = malloc;
cJSON_free = free;
return;
}
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
next=c->next;
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
if (c->string) cJSON_free(c->string);
cJSON_free(c);
c=next;
}
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
/* Could use sscanf for this? */
if (*num=='-') sign=-1,num++; /* Has sign? */
if (*num=='0') num++; /* is zero */
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
if (*num=='e' || *num=='E') /* Exponent? */
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
}
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item)
{
char *str;
double d=item->valuedouble;
if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
{
str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str,"%d",item->valueint);
}
else
{
str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
if (str)
{
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
else sprintf(str,"%f",d);
}
}
return str;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
if (*str!='\"') {ep=str;return 0;} /* not a string! */
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;ptr2=out;
while (*ptr!='\"' && *ptr)
{
if (*ptr!='\\') *ptr2++=*ptr++;
else
{
ptr++;
switch (*ptr)
{
case 'b': *ptr2++='\b'; break;
case 'f': *ptr2++='\f'; break;
case 'n': *ptr2++='\n'; break;
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
sscanf(ptr+3,"%4x",&uc2);ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
switch (len) {
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 1: *--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default: *ptr2++=*ptr; break;
}
ptr++;
}
}
*ptr2=0;
if (*ptr=='\"') ptr++;
item->valuestring=out;
item->type=cJSON_String;
return ptr;
}
/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str)
{
const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
if (!str) return cJSON_strdup("");
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;ptr=str;
*ptr2++='\"';
while (*ptr)
{
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
else
{
*ptr2++='\\';
switch (token=*ptr++)
{
case '\\': *ptr2++='\\'; break;
case '\"': *ptr2++='\"'; break;
case '\b': *ptr2++='b'; break;
case '\f': *ptr2++='f'; break;
case '\n': *ptr2++='n'; break;
case '\r': *ptr2++='r'; break;
case '\t': *ptr2++='t'; break;
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
}
}
}
*ptr2++='\"';*ptr2++=0;
return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt);
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
cJSON *c=cJSON_New_Item();
ep=0;
if (!c) return 0; /* memory fail */
end=parse_value(c,skip(value));
if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
if (return_parse_end) *return_parse_end=end;
return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; /* Fail on null. */
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
if (*value=='\"') { return parse_string(item,value); }
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
if (*value=='[') { return parse_array(item,value); }
if (*value=='{') { return parse_object(item,value); }
ep=value;return 0; /* failure. */
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt)
{
char *out=0;
if (!item) return 0;
switch ((item->type)&255)
{
case cJSON_NULL: out=cJSON_strdup("null"); break;
case cJSON_False: out=cJSON_strdup("false");break;
case cJSON_True: out=cJSON_strdup("true"); break;
case cJSON_Number: out=print_number(item);break;
case cJSON_String: out=print_string(item);break;
case cJSON_Array: out=print_array(item,depth,fmt);break;
case cJSON_Object: out=print_object(item,depth,fmt);break;
}
return out;
}
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='[') {ep=value;return 0;} /* not an array! */
item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0; /* memory fail */
}
if (*value==']') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt)
{
char **entries;
char *out=0,*ptr,*ret;int len=5;
cJSON *child=item->child;
int numentries=0,i=0,fail=0;
/* How many entries in the array? */
while (child) numentries++,child=child->next;
/* Explicitly handle numentries==0 */
if (!numentries)
{
out=(char*)cJSON_malloc(3);
if (out) strcpy(out,"[]");
return out;
}
/* Allocate an array to hold the values for each */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
memset(entries,0,numentries*sizeof(char*));
/* Retrieve all the results: */
child=item->child;
while (child && !fail)
{
ret=print_value(child,depth+1,fmt);
entries[i++]=ret;
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
child=child->next;
}
/* If we didn't fail, try to malloc the output string */
if (!fail) out=(char*)cJSON_malloc(len);
/* If that fails, we fail. */
if (!out) fail=1;
/* Handle failure. */
if (fail)
{
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
cJSON_free(entries);
return 0;
}
/* Compose the output array. */
*out='[';
ptr=out+1;*ptr=0;
for (i=0;i<numentries;i++)
{
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
cJSON_free(entries[i]);
}
cJSON_free(entries);
*ptr++=']';*ptr++=0;
return out;
}
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='{') {ep=value;return 0;} /* not an object! */
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt)
{
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries)
{
out=(char*)cJSON_malloc(fmt?depth+3:3);
if (!out) return 0;
ptr=out;*ptr++='{';
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
*ptr++='}';*ptr++=0;
return out;
}
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {cJSON_free(entries);return 0;}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);
/* Collect all the results into our arrays: */
child=item->child;depth++;if (fmt) len+=depth;
while (child)
{
names[i]=str=print_string_ptr(child->string);
entries[i++]=ret=print_value(child,depth,fmt);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
child=child->next;
}
/* Try to allocate the output string */
if (!fail) out=(char*)cJSON_malloc(len);
if (!out) fail=1;
/* Handle failure */
if (fail)
{
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
cJSON_free(names);cJSON_free(entries);
return 0;
}
/* Compose the output: */
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
for (i=0;i<numentries;i++)
{
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
strcpy(ptr,names[i]);ptr+=strlen(names[i]);
*ptr++=':';if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
cJSON_free(names[i]);cJSON_free(entries[i]);
}
cJSON_free(names);cJSON_free(entries);
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr++=0;
return out;
}
/* Get Array size/item / object item. */
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
/* Replace array/object items with new ones. */
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
cJSON *newitem,*cptr,*nptr=0,*newchild;
/* Bail on bad ptr */
if (!item) return 0;
/* Create new item */
newitem=cJSON_New_Item();
if (!newitem) return 0;
/* Copy over all vars */
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
/* If non-recursive, then we're done! */
if (!recurse) return newitem;
/* Walk the ->next chain for the child. */
cptr=item->child;
while (cptr)
{
newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild) {cJSON_Delete(newitem);return 0;}
if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
cptr=cptr->next;
}
return newitem;
}

@ -0,0 +1,140 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* Update array items. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,68 @@
#include "sgx_ra_tls_utils.h"
#include "sgx_ra_tls_backend.h"
#include "sgx_quote_3.h"
#include "dcap_quote.h"
namespace grpc {
namespace sgx {
int verify_quote (uint8_t * quote_buffer, size_t quote_size) {
void *handle;
handle = dcap_quote_open();
uint32_t supplemental_size, ret;
uint8_t *p_supplemental_buffer;
sgx_ql_qv_result_t quote_verification_result = SGX_QL_QV_RESULT_UNSPECIFIED;
uint32_t collateral_expiration_status = 1;
supplemental_size = dcap_get_supplemental_data_size(handle);
p_supplemental_buffer = (uint8_t *)malloc(supplemental_size);
if (NULL == p_supplemental_buffer) {
printf("Couldn't allocate supplemental buffer\n");
}
memset(p_supplemental_buffer, 0, supplemental_size);
ret = dcap_verify_quote(
handle,
quote_buffer,
quote_size,
&collateral_expiration_status,
&quote_verification_result,
supplemental_size,
p_supplemental_buffer
);
if (0 != ret) {
printf( "Error in dcap_verify_quote.\n");
}
if (collateral_expiration_status != 0) {
printf("the verification collateral has expired\n");
}
dcap_quote_close(handle);
}
int generate_quote(uint8_t *quote_buffer, unsigned char *hash, size_t hash_len) {
void *handle;
handle = dcap_quote_open();
sgx_report_data_t report_data = { 0 };
memcpy(report_data.d, hash, hash_len);
// Get the Quote
int ret = dcap_generate_quote(handle, quote_buffer, &report_data);
if (0 != ret) {
printf( "Error in dcap_generate_quote.\n");
}
dcap_quote_close(handle);
return ret;
}
uint32_t get_quote_size() {
void *handle = dcap_quote_open();
uint32_t quote_size = dcap_get_quote_size(handle);
dcap_quote_close(handle);
return quote_size;
}
}//namespace grpc
}//namesapce sgx

@ -0,0 +1,41 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef SGX_RA_TLS_BACKEND_H
#define SGX_RA_TLS_BACKEND_H
#include <string>
#include <memory>
#include <sstream>
namespace grpc {
namespace sgx {
int verify_quote (uint8_t * quote_buffer, size_t quote_size);
int generate_quote(uint8_t *quote_buffer, unsigned char *hash, size_t hash_len);
uint32_t get_quote_size();
}
}
#endif // SGX_RA_TLS_BACKEND_H

@ -0,0 +1,233 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "sgx_ra_tls_backends.h"
namespace grpc {
namespace sgx {
struct ra_tls_context _ctx_;
std::vector<std::string> ra_tls_get_key_cert() {
#ifdef SGX_RA_TLS_OCCLUM_BACKEND
return occlum_get_key_cert();
#endif
}
static std::vector<grpc::experimental::IdentityKeyCertPair> get_identity_key_cert_pairs(
std::vector<std::string> key_cert) {
grpc::experimental::IdentityKeyCertPair key_cert_pair;
key_cert_pair.private_key = key_cert[0];
key_cert_pair.certificate_chain = key_cert[1];
std::vector<grpc::experimental::IdentityKeyCertPair> identity_key_cert_pairs;
identity_key_cert_pairs.emplace_back(key_cert_pair);
return identity_key_cert_pairs;
}
void credential_option_set_certificate_provider(grpc::sgx::CredentialsOptions& options) {
std::lock_guard<std::mutex> lock(_ctx_.mtx);
_ctx_.cache.id++;
auto certificate_provider = _ctx_.cache.certificate_provider.insert({
_ctx_.cache.id,
std::make_shared<grpc::experimental::StaticDataCertificateProvider>(
get_identity_key_cert_pairs(ra_tls_get_key_cert()))
}).first;
options.set_certificate_provider(certificate_provider->second);
options.watch_identity_key_cert_pairs();
options.set_cert_request_type(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY);
options.set_root_cert_name("");
options.set_identity_cert_name("");
}
static sgx_config parse_sgx_config_json(const char* file) {
class json_engine sgx_json(file);
struct sgx_config sgx_cfg;
sgx_cfg.verify_mr_enclave = sgx_json.compare_item(sgx_json.get_item(sgx_json.get_handle(), "verify_mr_enclave"), "on");
sgx_cfg.verify_mr_signer = sgx_json.compare_item(sgx_json.get_item(sgx_json.get_handle(), "verify_mr_signer"), "on");
sgx_cfg.verify_isv_prod_id = sgx_json.compare_item(sgx_json.get_item(sgx_json.get_handle(), "verify_isv_prod_id"), "on");
sgx_cfg.verify_isv_svn = sgx_json.compare_item(sgx_json.get_item(sgx_json.get_handle(), "verify_isv_svn"), "on");
auto objs = sgx_json.get_item(sgx_json.get_handle(), "sgx_mrs");
auto obj_num = std::min(cJSON_GetArraySize(objs), SGX_MESUREMENTS_MAX_SIZE);
sgx_cfg.sgx_mrs = std::vector<sgx_measurement>(obj_num, sgx_measurement());
for (auto i = 0; i < obj_num; i++) {
auto obj = cJSON_GetArrayItem(objs, i);
auto mr_enclave = sgx_json.print_item(sgx_json.get_item(obj, "mr_enclave"));
memset(sgx_cfg.sgx_mrs[i].mr_enclave, 0, sizeof(sgx_cfg.sgx_mrs[i].mr_enclave));
hex_to_byte(mr_enclave+1, sgx_cfg.sgx_mrs[i].mr_enclave, sizeof(sgx_cfg.sgx_mrs[i].mr_enclave));
auto mr_signer = sgx_json.print_item(sgx_json.get_item(obj, "mr_signer"));
memset(sgx_cfg.sgx_mrs[i].mr_signer, 0, sizeof(sgx_cfg.sgx_mrs[i].mr_signer));
hex_to_byte(mr_signer+1, sgx_cfg.sgx_mrs[i].mr_signer, sizeof(sgx_cfg.sgx_mrs[i].mr_signer));
auto isv_prod_id = sgx_json.print_item(sgx_json.get_item(obj, "isv_prod_id"));
sgx_cfg.sgx_mrs[i].isv_prod_id = strtoul(isv_prod_id, nullptr, 10);
auto isv_svn = sgx_json.print_item(sgx_json.get_item(obj, "isv_svn"));
sgx_cfg.sgx_mrs[i].isv_svn = strtoul(isv_svn, nullptr, 10);
};
return sgx_cfg;
}
void ra_tls_parse_sgx_config(sgx_config sgx_cfg) {
std::lock_guard<std::mutex> lock(_ctx_.mtx);
_ctx_.sgx_cfg = sgx_cfg;
}
void ra_tls_parse_sgx_config(const char* file) {
ra_tls_parse_sgx_config(parse_sgx_config_json(file));
}
void ra_tls_verify_init() {
std::lock_guard<std::mutex> lock(_ctx_.mtx);
}
static bool verify_measurement_internal(const char* mr_enclave, const char* mr_signer,
const char* isv_prod_id, const char* isv_svn) {
bool status = false;
auto & sgx_cfg = _ctx_.sgx_cfg;
for (auto & obj : sgx_cfg.sgx_mrs) {
status = true;
if (status && sgx_cfg.verify_mr_enclave && \
memcmp(obj.mr_enclave, mr_enclave, 32)) {
status = false;
}
if (status && sgx_cfg.verify_mr_signer && \
memcmp(obj.mr_signer, mr_signer, 32)) {
status = false;
}
if (status && sgx_cfg.verify_isv_prod_id && \
(obj.isv_prod_id != *(uint16_t*)isv_prod_id)) {
status = false;
}
if (status && sgx_cfg.verify_isv_svn && \
(obj.isv_svn != *(uint16_t*)isv_svn)) {
status = false;
}
if (status) {
break;
}
}
return status;
}
int verify_measurement(const char* mr_enclave, const char* mr_signer,
const char* isv_prod_id, const char* isv_svn) {
std::lock_guard<std::mutex> lock(_ctx_.mtx);
bool status = false;
try {
assert(mr_enclave && mr_signer && isv_prod_id && isv_svn);
status = verify_measurement_internal(mr_enclave, mr_signer, isv_prod_id, isv_svn);
grpc_printf("remote sgx measurements\n");
grpc_printf(" |- mr_enclave : %s\n", byte_to_hex(mr_enclave, 32).c_str());
grpc_printf(" |- mr_signer : %s\n", byte_to_hex(mr_signer, 32).c_str());
grpc_printf(" |- isv_prod_id : %hu\n", *((uint16_t*)isv_prod_id));
grpc_printf(" |- isv_svn : %hu\n", *((uint16_t*)isv_svn));
if (status) {
grpc_printf(" |- verify result : success\n");
} else {
grpc_printf(" |- verify result : failed\n");
}
} catch (...) {
grpc_printf("unable to verify measurement!");
}
fflush(stdout);
return status ? 0 : -1;
}
int TlsAuthorizationCheck::Schedule(grpc::experimental::TlsServerAuthorizationCheckArg* arg) {
GPR_ASSERT(arg != nullptr);
char der_crt[16000] = "";
auto peer_cert_buf = arg->peer_cert();
peer_cert_buf.copy(der_crt, peer_cert_buf.length(), 0);
#ifdef SGX_RA_TLS_OCCLUM_BACKEND
int ret = occlum_verify_cert((const unsigned char *)der_crt, 16000);
#endif
if (ret != 0) {
grpc_printf("something went wrong while verifying quote\n");
arg->set_success(0);
arg->set_status(GRPC_STATUS_UNAUTHENTICATED);
} else {
arg->set_success(1);
arg->set_status(GRPC_STATUS_OK);
}
return 0;
};
void TlsAuthorizationCheck::Cancel(grpc::experimental::TlsServerAuthorizationCheckArg* arg) {
GPR_ASSERT(arg != nullptr);
arg->set_status(GRPC_STATUS_PERMISSION_DENIED);
arg->set_error_details("cancelled");
};
int ra_tls_auth_check_schedule(void* /* confiuser_data */,
grpc_tls_server_authorization_check_arg* arg) {
char der_crt[16000] = "";
memcpy(der_crt, arg->peer_cert, strlen(arg->peer_cert));
#ifdef SGX_RA_TLS_OCCLUM_BACKEND
int ret = occlum_verify_cert((const unsigned char *)der_crt, 16000);
#endif
if (ret != 0) {
grpc_printf("something went wrong while verifying quote\n");
arg->success = 0;
arg->status = GRPC_STATUS_UNAUTHENTICATED;
} else {
arg->success = 1;
arg->status = GRPC_STATUS_OK;
}
return 0;
}
void credential_option_set_authorization_check(grpc::sgx::CredentialsOptions& options) {
std::lock_guard<std::mutex> lock(_ctx_.mtx);
_ctx_.cache.id++;
auto authorization_check = _ctx_.cache.authorization_check.insert({
_ctx_.cache.id, std::make_shared<grpc::sgx::TlsAuthorizationCheck>()
}).first;
auto authorization_check_config = _ctx_.cache.authorization_check_config.insert({
_ctx_.cache.id,
std::make_shared<grpc::experimental::TlsServerAuthorizationCheckConfig>(
authorization_check->second)
}).first;
options.set_authorization_check_config(authorization_check_config->second);
options.set_verification_option(GRPC_TLS_SKIP_ALL_SERVER_VERIFICATION);
}
} // namespace sgx
} // namespace grpc

@ -0,0 +1,109 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef SGX_RA_TLS_BACKENDS_H
#define SGX_RA_TLS_BACKENDS_H
#include "sgx_ra_tls_utils.h"
#include <mutex>
#include <unordered_map>
#include <grpcpp/grpcpp.h>
#include <grpc/grpc_security.h>
#include <grpc/grpc_security_constants.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/security/tls_certificate_provider.h>
#include <grpcpp/security/tls_credentials_options.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/security/sgx/sgx_ra_tls_options.h>
// Set 1 for strict safety checks
#define SGX_MESUREMENTS_MAX_SIZE 16
namespace grpc {
namespace sgx {
class TlsAuthorizationCheck
: public grpc::experimental::TlsServerAuthorizationCheckInterface {
int Schedule(grpc::experimental::TlsServerAuthorizationCheckArg* arg) override;
void Cancel(grpc::experimental::TlsServerAuthorizationCheckArg* arg) override;
};
struct sgx_measurement {
char mr_enclave[32];
char mr_signer[32];
uint16_t isv_prod_id;
uint16_t isv_svn;
};
struct sgx_config {
bool verify_mr_enclave = true;
bool verify_mr_signer = true;
bool verify_isv_prod_id = true;
bool verify_isv_svn = true;
std::vector<sgx_measurement> sgx_mrs;
};
struct ra_tls_cache {
int id = 0;
std::unordered_map<
int, std::shared_ptr<grpc::experimental::StaticDataCertificateProvider>
> certificate_provider;
std::unordered_map<
int, std::shared_ptr<grpc::sgx::TlsAuthorizationCheck>
> authorization_check;
std::unordered_map<
int, std::shared_ptr<grpc::experimental::TlsServerAuthorizationCheckConfig>
> authorization_check_config;
};
struct ra_tls_context {
std::mutex mtx;
struct sgx_config sgx_cfg;
struct ra_tls_cache cache;
};
extern struct ra_tls_context _ctx_;
#ifdef SGX_RA_TLS_OCCLUM_BACKEND
std::vector<std::string> occlum_get_key_cert();
int occlum_verify_cert(const unsigned char * der_crt, size_t len);
#endif // SGX_RA_TLS_OCCLUM_BACKEND
void ra_tls_parse_sgx_config(sgx_config sgx_cfg);
void ra_tls_parse_sgx_config(const char* file);
void ra_tls_verify_init();
int verify_measurement(const char* mr_enclave, const char* mr_signer,
const char* isv_prod_id, const char* isv_svn);
void credential_option_set_certificate_provider(grpc::sgx::CredentialsOptions& options);
void credential_option_set_authorization_check(grpc::sgx::CredentialsOptions& options);
} // namespace sgx
} // namespace grpc
#endif // SGX_RA_TLS_BACKENDS_H

@ -0,0 +1,98 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "sgx_ra_tls_backends.h"
namespace grpc {
namespace sgx {
/*
RA-TLS: on client, only need to register ra_tls_verify_callback() for cert verification
1. extract SGX quote from "quote" OID extension from crt
2. compare public key's hash from cert against quote's report_data
3. prepare user-supplied verification parameter "allow outdated TCB"
4. call into libsgx_dcap_quoteverify to verify ECDSA/based SGX quote
5. verify all measurements from the SGX quote
*/
std::shared_ptr<grpc::ChannelCredentials> TlsCredentials(sgx_config sgx_cfg) {
grpc::sgx::CredentialsOptions options;
ra_tls_parse_sgx_config(sgx_cfg);
credential_option_set_certificate_provider(options);
ra_tls_verify_init();
credential_option_set_authorization_check(options);
return grpc::experimental::TlsCredentials(
reinterpret_cast<const grpc::experimental::TlsChannelCredentialsOptions&>(options));
};
std::shared_ptr<grpc::ChannelCredentials> TlsCredentials(const char* sgx_cfg_json) {
grpc::sgx::CredentialsOptions options;
ra_tls_parse_sgx_config(sgx_cfg_json);
credential_option_set_certificate_provider(options);
ra_tls_verify_init();
credential_option_set_authorization_check(options);
return grpc::experimental::TlsCredentials(
reinterpret_cast<const grpc::experimental::TlsChannelCredentialsOptions&>(options));
};
std::shared_ptr<grpc::ServerCredentials> TlsServerCredentials(sgx_config sgx_cfg) {
grpc::sgx::CredentialsOptions options;
ra_tls_parse_sgx_config(sgx_cfg);
credential_option_set_certificate_provider(options);
ra_tls_verify_init();
credential_option_set_authorization_check(options);
return grpc::experimental::TlsServerCredentials(
reinterpret_cast<const grpc::experimental::TlsServerCredentialsOptions&>(options));
};
std::shared_ptr<grpc::ServerCredentials> TlsServerCredentials(const char* sgx_cfg_json) {
grpc::sgx::CredentialsOptions options;
ra_tls_parse_sgx_config(sgx_cfg_json);
credential_option_set_certificate_provider(options);
ra_tls_verify_init();
credential_option_set_authorization_check(options);
return grpc::experimental::TlsServerCredentials(
reinterpret_cast<const grpc::experimental::TlsServerCredentialsOptions&>(options));
};
std::shared_ptr<grpc::Channel> CreateSecureChannel(
string target_str, std::shared_ptr<grpc::ChannelCredentials> channel_creds) {
GPR_ASSERT(channel_creds.get() != nullptr);
auto channel_args = grpc::ChannelArguments();
channel_args.SetSslTargetNameOverride("RATLS");
return grpc::CreateCustomChannel(target_str, std::move(channel_creds), channel_args);
};
} // namespace sgx
} // namespace grpc

@ -0,0 +1,318 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "sgx_ra_tls_backends.h"
namespace grpc {
namespace sgx {
#ifdef SGX_RA_TLS_OCCLUM_BACKEND
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include <openssl/asn1.h>
#include "sgx_quote_3.h"
#include "dcap_quote.h"
const char * RA_TLS_LONG_NAME = "RA-TLS Extension";
const char * RA_TLS_SHORT_NAME = "RA-TLS";
std::vector<std::string> occlum_get_key_cert() {
unsigned char private_key_pem[16000], cert_pem[16000];
BIGNUM * e = BN_new();
BN_set_word(e, RSA_F4);
RSA * rsa = RSA_new();
RSA_generate_key_ex(rsa, 2048, e, nullptr);
EVP_PKEY * pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);
X509 * x509 = X509_new();
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 630720000L);
X509_set_pubkey(x509, pkey);
X509_NAME * name = X509_NAME_new();
// X509_NAME * name = X509_get_subject_name(x509);
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
(unsigned char *)"CN", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
(unsigned char *)"Intel Inc.", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
(unsigned char *)"localhost", -1, -1, 0);
X509_set_subject_name(x509, name);
X509_set_issuer_name(x509, name);
int32_t ret;
size_t key_len = i2d_PUBKEY(pkey, 0);
unsigned char *public_key = NULL;
// size_t pubkey_len = i2d_PUBKEY(pkey, &public_key);
size_t pubkey_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), &public_key);
if (pubkey_len != key_len) {
grpc_printf("get public key failed!\n");
}
BIO *bio = BIO_new(BIO_s_mem());
if (nullptr == bio) {
grpc_printf("create bio failed!\n");
}
ret = PEM_write_bio_RSAPrivateKey(bio, rsa, nullptr, nullptr, 0, nullptr, nullptr);
if (ret == 0) {
grpc_printf("write private key failed!\n");
}
ret = BIO_read(bio, private_key_pem, bio->num_write);
if (ret == 0) {
grpc_printf("read private key failed!\n");
}
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, public_key, key_len);
SHA256_Final(hash, &sha256);
void *handle;
uint32_t quote_size;
uint8_t *p_quote_buffer;
handle = dcap_quote_open();
quote_size = dcap_get_quote_size(handle);
p_quote_buffer = (uint8_t*)malloc(quote_size);
if (nullptr == p_quote_buffer) {
grpc_printf("Couldn't allocate quote_buffer\n");
}
memset(p_quote_buffer, 0, quote_size);
sgx_report_data_t report_data = { 0 };
memcpy(report_data.d, hash, SHA256_DIGEST_LENGTH);
ret = dcap_generate_quote(handle, p_quote_buffer, &report_data);
if (0 != ret) {
grpc_printf( "Error in dcap_generate_quote.\n");
}
int nid = OBJ_create("1.2.840.113741.1", RA_TLS_SHORT_NAME, RA_TLS_LONG_NAME);
ASN1_OBJECT* obj = OBJ_nid2obj(nid);
ASN1_OCTET_STRING* data = ASN1_OCTET_STRING_new();
ASN1_OCTET_STRING_set(data, p_quote_buffer, quote_size);
X509_EXTENSION* ext = X509_EXTENSION_create_by_OBJ(nullptr, obj, 0, data);
X509_add_ext(x509, ext, -1);
X509_sign(x509, pkey, EVP_sha1());
BIO *cert_bio = BIO_new(BIO_s_mem());
if (nullptr == cert_bio) {
grpc_printf("create crt bio failed!\n");
}
if (0 == PEM_write_bio_X509(cert_bio, x509)) {
BIO_free(cert_bio);
grpc_printf("read crt bio failed!\n");
}
ret = BIO_read(cert_bio, cert_pem, cert_bio->num_write);
if (ret == 0) {
grpc_printf("read pem cert failed!\n");
}
BIO_free(bio);
BIO_free(cert_bio);
EVP_PKEY_free(pkey);
check_free(p_quote_buffer);
dcap_quote_close(handle);
std::vector<std::string> key_cert;
key_cert.emplace_back(std::string((char*) private_key_pem));
key_cert.emplace_back(std::string((char*) cert_pem));
return key_cert;
}
static int occlum_get_quote(X509 *x509, uint8_t **quote, size_t *len) {
STACK_OF(X509_EXTENSION) *exts = x509->cert_info->extensions;
int ext_num;
int ret = -1;
if (exts) {
ext_num = sk_X509_EXTENSION_num(exts);
for (int i = 0; i < ext_num; i++) {
X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
ASN1_OBJECT *obj = X509_EXTENSION_get_object(ext);
unsigned nid = OBJ_obj2nid(obj);
if (nid != NID_undef) {
const char *ln = OBJ_nid2ln(nid);
if (memcmp(RA_TLS_LONG_NAME, ln, sizeof(RA_TLS_LONG_NAME)) == 0) {
BIO *ext_bio = BIO_new(BIO_s_mem());
*len = i2d_ASN1_OCTET_STRING(ext->value, quote);
*quote = *quote + 4;
*len = *len - 4;
ret = 0;
BIO_free(ext_bio);
}
}
}
}
return ret;
}
static int occlum_verify_pubkey_hash(X509 *x509, uint8_t *pubkey_hash, size_t len) {
EVP_PKEY *pkey = X509_get_pubkey(x509);
int32_t ret;
size_t key_len = EVP_PKEY_bits(pkey)/8;
unsigned char *public_key = NULL;
key_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), &public_key);
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, public_key, key_len);
SHA256_Final(hash, &sha256);
ret = memcmp(hash, pubkey_hash, len);
return ret;
}
static int occlum_verify_quote(uint8_t * quote_buffer, size_t quote_size) {
void *handle;
handle = dcap_quote_open();
uint32_t supplemental_size, ret;
uint8_t *p_supplemental_buffer;
sgx_ql_qv_result_t quote_verification_result = SGX_QL_QV_RESULT_UNSPECIFIED;
uint32_t collateral_expiration_status = 1;
supplemental_size = dcap_get_supplemental_data_size(handle);
p_supplemental_buffer = (uint8_t *)malloc(supplemental_size);
if (NULL == p_supplemental_buffer) {
grpc_printf("Couldn't allocate supplemental buffer\n");
}
memset(p_supplemental_buffer, 0, supplemental_size);
ret = dcap_verify_quote(
handle,
quote_buffer,
quote_size,
&collateral_expiration_status,
&quote_verification_result,
supplemental_size,
p_supplemental_buffer
);
if (0 != ret) {
grpc_printf( "Error in dcap_verify_quote.\n");
}
if (collateral_expiration_status != 0) {
grpc_printf("the verification collateral has expired\n");
}
switch (quote_verification_result) {
case SGX_QL_QV_RESULT_OK:
grpc_printf("Succeed to verify the quote!\n");
break;
case SGX_QL_QV_RESULT_CONFIG_NEEDED:
case SGX_QL_QV_RESULT_OUT_OF_DATE:
case SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED:
case SGX_QL_QV_RESULT_SW_HARDENING_NEEDED:
case SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED:
grpc_printf("WARN: App: Verification completed with Non-terminal result: %x\n",
quote_verification_result);
break;
case SGX_QL_QV_RESULT_INVALID_SIGNATURE:
case SGX_QL_QV_RESULT_REVOKED:
case SGX_QL_QV_RESULT_UNSPECIFIED:
default:
grpc_printf("\tError: App: Verification completed with Terminal result: %x\n",
quote_verification_result);
}
check_free(p_supplemental_buffer);
dcap_quote_close(handle);
return ret;
}
int occlum_verify_cert(const unsigned char * der_crt, size_t len) {
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, der_crt, strlen((const char *)der_crt));
X509 *x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (x509 == nullptr) {
grpc_printf("parse the crt failed! \n");
return -1;
}
uint8_t * quote_buf = nullptr;
size_t quote_len = 0;
int ret = occlum_get_quote(x509, &quote_buf, &quote_len);
if (ret != 0) {
grpc_printf("parse quote failed!\n");
return -1;
}
ret = occlum_verify_quote(quote_buf, quote_len);
if (ret != 0) {
grpc_printf("verify quote failed!\n");
return -1;
}
sgx_quote3_t *p_quote = (sgx_quote3_t *)quote_buf;
sgx_report_body_t *p_rep_body = (sgx_report_body_t *)(&p_quote->report_body);
sgx_report_data_t *p_rep_data =(sgx_report_data_t *)(&p_rep_body->report_data);
uint8_t *pubkey_hash = p_rep_data->d;
ret = occlum_verify_pubkey_hash(x509, pubkey_hash, SHA256_DIGEST_LENGTH);
if (ret != 0) {
grpc_printf("verify the public key hash failed!\n");
return -1;
}
ret = verify_measurement((const char *)&p_rep_body->mr_enclave,
(const char *)&p_rep_body->mr_signer,
(const char *)&p_rep_body->isv_prod_id,
(const char *)&p_rep_body->isv_svn);
if (ret != 0) {
grpc_printf("verify the measurement failed!\n");
return -1;
}
BIO_free(bio);
return 0;
}
#endif // SGX_RA_TLS_OCCLUM_BACKEND
} // namespace sgx
} // namespace grpc

@ -0,0 +1,55 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
#include <grpcpp/security/sgx/sgx_ra_tls_options.h>
#include "absl/container/inlined_vector.h"
#include "src/cpp/common/tls_credentials_options_util.h"
namespace grpc {
namespace sgx {
void CredentialsOptions::set_verification_option(
grpc_tls_server_verification_option server_verification_option) {
grpc_tls_credentials_options* options = c_credentials_options();
GPR_ASSERT(options != nullptr);
grpc_tls_credentials_options_set_server_verification_option(
options, server_verification_option);
}
void CredentialsOptions::set_authorization_check_config(
std::shared_ptr<grpc::experimental::TlsServerAuthorizationCheckConfig> config) {
grpc_tls_credentials_options* options = c_credentials_options();
GPR_ASSERT(options != nullptr);
if (config != nullptr) {
grpc_tls_credentials_options_set_server_authorization_check_config(
options, config->c_config());
}
}
void CredentialsOptions::set_cert_request_type(
grpc_ssl_client_certificate_request_type cert_request_type) {
grpc_tls_credentials_options* options = c_credentials_options();
GPR_ASSERT(options != nullptr);
grpc_tls_credentials_options_set_cert_request_type(options, cert_request_type);
}
} // namespace sgx
} // namespace grpc

@ -0,0 +1,169 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "sgx_ra_tls_utils.h"
#include <cstring>
#include <iostream>
#include <fstream>
#include <sstream>
namespace grpc {
namespace sgx {
void check_free(void* ptr) {
if (ptr) {
free(ptr);
ptr = nullptr;
};
}
bool hex_to_byte(const char *src, char *dst, size_t dst_size) {
if (std::strlen(src) < dst_size*2) {
return false;
} else {
for (auto i = 0; i < dst_size; i++) {
if (!isxdigit(src[i*2]) || !isxdigit(src[i*2+1])) {
return false;
} else {
sscanf(src+i*2, "%02hhx", dst+i);
}
}
return true;
}
};
void byte_to_hex(const char *src, char *dst, size_t src_size) {
for (auto i = 0; i < src_size; i++) {
sprintf(dst+i*2, "%02hhx", src[i]);
}
};
std::string byte_to_hex(const char *src, size_t src_size) {
char dst[src_size*2];
memset(dst, 0, sizeof(dst));
byte_to_hex(src, dst, src_size);
return std::string(dst);
};
library_engine::library_engine() : handle(nullptr), error(nullptr) {};
library_engine::library_engine(const char* file, int mode) : handle(nullptr), error(nullptr) {
this->open(file, mode);
}
library_engine::~library_engine() {
this->close();
}
void library_engine::open(const char* file, int mode) {
this->close();
handle = dlopen(file, mode);
error = dlerror();
if (error != nullptr || handle == nullptr) {
throw std::runtime_error("dlopen " + std::string(file) + " error, " + std::string(error));
}
}
void library_engine::close() {
if (handle) {
dlclose(handle);
}
handle = nullptr;
error = nullptr;
}
void* library_engine::get_func(const char* name) {
auto func = dlsym(handle, name);
error = dlerror();
if (error != nullptr || func == nullptr) {
throw std::runtime_error("dlsym " + std::string(name) + " error, " + std::string(error));
return nullptr;
} else {
return func;
}
}
void* library_engine::get_handle() {
return handle;
}
json_engine::json_engine() : handle(nullptr) {};
json_engine::json_engine(const char* file) : handle(nullptr){
this->open(file);
}
json_engine::~json_engine() {
this->close();
}
bool json_engine::open(const char* file) {
if (!file) {
grpc_printf("wrong json file path\n");
return false;
}
this->close();
auto file_ptr = fopen(file, "r");
fseek(file_ptr, 0, SEEK_END);
auto length = ftell(file_ptr);
fseek(file_ptr, 0, SEEK_SET);
auto buffer = malloc(length);
fread(buffer, 1, length, file_ptr);
fclose(file_ptr);
this->handle = cJSON_Parse((const char *)buffer);
check_free(buffer);
if (this->handle) {
return true;
} else {
grpc_printf("cjson open %s error: %s", file, cJSON_GetErrorPtr());
return false;
}
}
void json_engine::close() {
if (this->handle) {
cJSON_Delete(this->handle);
this->handle = nullptr;
}
}
cJSON* json_engine::get_handle() {
return this->handle;
}
cJSON* json_engine::get_item(cJSON* obj, const char* item) {
return cJSON_GetObjectItem(obj, item);
};
char* json_engine::print_item(cJSON* obj) {
return cJSON_Print(obj);
};
bool json_engine::compare_item(cJSON* obj, const char* item) {
auto obj_item = this->print_item(obj);
return strncmp(obj_item+1, item, std::min(strlen(item), strlen(obj_item)-2)) == 0;
};
} // namespace sgx
} // namespace grpc

@ -0,0 +1,91 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef SGX_RA_TLS_UTILS_H
#define SGX_RA_TLS_UTILS_H
#include <string>
#include <vector>
#include <memory>
#include <dlfcn.h>
#define grpc_printf printf
#define grpc_fprintf fprintf
namespace grpc {
namespace sgx {
#include "cjson/cJSON.h"
class library_engine {
public:
library_engine();
library_engine(const char*, int);
~library_engine();
void open(const char*, int);
void close();
void* get_func(const char*);
void* get_handle();
private:
void* handle;
char* error;
};
class json_engine {
public:
json_engine();
json_engine(const char*);
~json_engine();
bool open(const char*);
void close();
cJSON* get_handle();
cJSON* get_item(cJSON* obj, const char* item);
char* print_item(cJSON* obj);
bool compare_item(cJSON* obj, const char* item);
private:
cJSON* handle;
};
void check_free(void* ptr);
bool hex_to_byte(const char* src, char* dst, size_t dst_size);
void byte_to_hex(const char* src, char* dst, size_t src_size);
std::string byte_to_hex(const char* src, size_t src_size);
} // namespace sgx
} // namespace grpc
#endif // SGX_RA_TLS_UTILS_H

@ -0,0 +1,59 @@
#!/bin/bash
INSTALL_PREFIX=/usr/local
apt-get update \
&& apt-get install -y --no-install-recommends apt-utils \
&& apt-get install -y \
ca-certificates \
build-essential \
autoconf \
libtool \
python3-pip \
python3-dev \
git \
wget \
unzip
mkdir -p ${INSTALL_PREFIX} \
&& wget -q -O cmake-linux.sh https://github.com/Kitware/CMake/releases/download/v3.19.6/cmake-3.19.6-Linux-x86_64.sh \
&& sh cmake-linux.sh -- --skip-license --prefix=${INSTALL_PREFIX} \
&& rm cmake-linux.sh
# Install cJSON
CJSON_PATH=/cJSON
git clone https://github.com/DaveGamble/cJSON.git ${CJSON_PATH}
pushd ${CJSON_PATH} \
&& make static \
&& cp -r *.a ${INSTALL_PREFIX}/lib \
&& mkdir -p ${INSTALL_PREFIX}/include/cjson \
&& cp -r *.h ${INSTALL_PREFIX}/include/cjson
popd
# GRPC env
GRPC_VERSION=v1.38.x
export GRPC_PATH=/grpc
# GRPC source code
git clone https://github.com/grpc/grpc -b ${GRPC_VERSION} ${GRPC_PATH}
pushd ${GRPC_PATH} \
&& pip3 install --upgrade pip setuptools==44.1.1 \
&& pip3 install -r requirements.txt \
&& git checkout v1.38.1 \
&& git submodule update --init
popd
cp -rf grpc/common/* ${GRPC_PATH}/
cp -rf grpc/v1.38.1/* ${GRPC_PATH}/
git clone https://github.com/occlum/occlum
pushd occlum
make submodule
cd demos/remote_attestation/dcap/dcap_lib
cargo build --all-targets
cp target/debug/libdcap_quote.a /usr/local/lib/
cp ../c_app/dcap_quote.h /usr/local/include/
popd
pushd ${GRPC_PATH}/examples/cpp/ratls
./build.sh
popd

15
demos/ra_tls/run.sh Executable file

@ -0,0 +1,15 @@
#!/bin/bash
set -ex
postfix=$1
if [ "$postfix" != "server" ] && [ "$postfix" != "client" ]; then
echo "input error args, it should be:"
echo "./run.sh server"
echo "./run.sh client"
exit 1
fi
pushd occlum_instance_$postfix
occlum run /bin/$postfix
popd