blob: 4eff7682e2fb8784e71d4529bb6f4f8f6965df93 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
#pragma once
#include "kudu/util/openssl_util.h"
#include <string>
#include <glog/logging.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include "kudu/gutil/strings/substitute.h"
#include "kudu/util/status.h"
namespace kudu {
namespace security {
template<typename TYPE, typename Traits = SslTypeTraits<TYPE>>
Status ToBIO(BIO* bio, DataFormat format, TYPE* obj) {
CHECK(bio);
CHECK(obj);
switch (format) {
case DataFormat::DER:
OPENSSL_RET_NOT_OK(Traits::kWriteDerFunc(bio, obj),
"error exporting data in DER format");
break;
case DataFormat::PEM:
OPENSSL_RET_NOT_OK(Traits::kWritePemFunc(bio, obj),
"error exporting data in PEM format");
break;
}
OPENSSL_RET_NOT_OK(BIO_flush(bio), "error flushing BIO");
return Status::OK();
}
// The callback which is called by the OpenSSL library when trying to decrypt
// a password protected private key.
inline int TLSPasswordCB(char* buf, int size, int /* rwflag */, void* userdata) {
const auto* cb = reinterpret_cast<const PasswordCallback*>(userdata);
std::string pw = (*cb)();
if (pw.size() >= size) {
LOG(ERROR) << "Provided key password is longer than maximum length "
<< size;
return -1;
}
strncpy(buf, pw.c_str(), size);
return pw.size();
}
template<typename TYPE, typename Traits = SslTypeTraits<TYPE>>
Status FromBIO(BIO* bio, DataFormat format, c_unique_ptr<TYPE>* ret,
const PasswordCallback& cb = PasswordCallback()) {
CHECK(bio);
switch (format) {
case DataFormat::DER:
*ret = ssl_make_unique(Traits::kReadDerFunc(bio, nullptr));
break;
case DataFormat::PEM:
*ret = ssl_make_unique(Traits::kReadPemFunc(bio, nullptr, &TLSPasswordCB,
const_cast<PasswordCallback*>(&cb)));
break;
}
if (PREDICT_FALSE(!*ret)) {
return Status::RuntimeError(GetOpenSSLErrors());
}
return Status::OK();
}
template<typename Type, typename Traits = SslTypeTraits<Type>>
Status FromString(const std::string& data, DataFormat format,
c_unique_ptr<Type>* ret) {
const void* mdata = reinterpret_cast<const void*>(data.data());
auto bio = ssl_make_unique(BIO_new_mem_buf(
#if OPENSSL_VERSION_NUMBER < 0x10002000L
const_cast<void*>(mdata),
#else
mdata,
#endif
data.size()));
RETURN_NOT_OK_PREPEND((FromBIO<Type, Traits>(bio.get(), format, ret)),
"unable to load data from memory");
return Status::OK();
}
template<typename Type, typename Traits = SslTypeTraits<Type>>
Status ToString(std::string* data, DataFormat format, Type* obj) {
CHECK(data);
auto bio = ssl_make_unique(BIO_new(BIO_s_mem()));
RETURN_NOT_OK_PREPEND((ToBIO<Type, Traits>(bio.get(), format, obj)),
"error serializing data");
BUF_MEM* membuf;
OPENSSL_CHECK_OK(BIO_get_mem_ptr(bio.get(), &membuf));
data->assign(membuf->data, membuf->length);
return Status::OK();
}
template<typename Type, typename Traits = SslTypeTraits<Type>>
Status FromFile(const std::string& fpath, DataFormat format,
c_unique_ptr<Type>* ret, const PasswordCallback& cb = PasswordCallback()) {
auto bio = ssl_make_unique(BIO_new(BIO_s_file()));
OPENSSL_RET_NOT_OK(BIO_read_filename(bio.get(), fpath.c_str()),
strings::Substitute("could not read data from file '$0'", fpath));
RETURN_NOT_OK_PREPEND((FromBIO<Type, Traits>(bio.get(), format, ret, cb)),
strings::Substitute("unable to load data from file '$0'", fpath));
return Status::OK();
}
} // namespace security
} // namespace kudu