| // 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, |
| const PasswordCallback& /*cb*/ = PasswordCallback()) { |
| 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; |
| Status s = (*cb)(&pw); |
| if (!s.ok()) { |
| LOG(ERROR) << "Failed to obtain password: " << s.ToString(); |
| return -1; |
| } |
| 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 PasswordCallback& cb = PasswordCallback()) { |
| 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())); |
| OPENSSL_RET_IF_NULL(bio, "could not create memory BIO"); |
| RETURN_NOT_OK_PREPEND((FromBIO<Type, Traits>(bio.get(), format, ret, cb)), |
| "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, |
| const PasswordCallback& cb = PasswordCallback()) { |
| CHECK(data); |
| auto bio = ssl_make_unique(BIO_new(BIO_s_mem())); |
| OPENSSL_RET_IF_NULL(bio, "could not create memory BIO"); |
| RETURN_NOT_OK_PREPEND((ToBIO<Type, Traits>(bio.get(), format, obj, cb)), |
| "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_IF_NULL(bio, "could not create file BIO"); |
| 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 |