/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/util/icu.h" #include #include #include #include #include #include #include #include #include #include "mongo/util/assert_util.h" #include "mongo/util/str.h" namespace mongo { namespace { /** * Convenience wrapper for a UChar[] string. * Instantiate with UString::fromUTF8() and reseriealize with ustr.toUTF8() */ class UString { public: UString() = delete; explicit UString(size_t size) { _str.resize(size); } const UChar* uc_str() const { return _str.data(); } UChar* data() { return _str.data(); } size_t capacity() const { return _str.capacity(); } size_t size() const { return _str.size(); } void resize(size_t len) { _str.resize(len); } static UString fromUTF8(StringData str) { UErrorCode error = U_ZERO_ERROR; int32_t len = 0; u_strFromUTF8(nullptr, 0, &len, str.rawData(), str.size(), &error); uassert(ErrorCodes::BadValue, "Non UTF-8 data encountered", error != U_INVALID_CHAR_FOUND); uassert(50687, str::stream() << "Error preflighting UTF-8 conversion: " << u_errorName(error), error == U_BUFFER_OVERFLOW_ERROR); error = U_ZERO_ERROR; UString ret(len); u_strFromUTF8(ret.data(), ret.capacity(), &len, str.rawData(), str.size(), &error); uassert(50688, str::stream() << "Error converting UTF-8 string: " << u_errorName(error), U_SUCCESS(error)); ret.resize(len); return ret; } std::string toUTF8() const { UErrorCode error = U_ZERO_ERROR; int32_t len = 0; u_strToUTF8(nullptr, 0, &len, _str.data(), _str.size(), &error); uassert(50689, str::stream() << "Error preflighting UTF-8 conversion: " << u_errorName(error), error == U_BUFFER_OVERFLOW_ERROR); error = U_ZERO_ERROR; std::string ret; ret.resize(len); u_strToUTF8(&ret[0], ret.capacity(), &len, _str.data(), _str.size(), &error); uassert(50690, str::stream() << "Error converting string to UTF-8: " << u_errorName(error), U_SUCCESS(error)); ret.resize(len); return ret; } private: std::vector _str; }; /** * Convenience wrapper for ICU unicode string prep API. */ class USPrep { public: USPrep() = delete; USPrep(UStringPrepProfileType type) { UErrorCode error = U_ZERO_ERROR; _profile.reset(usprep_openByType(type, &error)); uassert(50691, str::stream() << "Unable to open unicode string prep profile: " << u_errorName(error), U_SUCCESS(error)); } UString prepare(const UString& src, int32_t options = USPREP_DEFAULT) { UErrorCode error = U_ZERO_ERROR; auto len = usprep_prepare( _profile.get(), src.uc_str(), src.size(), nullptr, 0, options, nullptr, &error); uassert(ErrorCodes::BadValue, "Unable to normalize input string", error != U_INVALID_CHAR_FOUND); uassert(50692, str::stream() << "Error preflighting normalization: " << u_errorName(error), error == U_BUFFER_OVERFLOW_ERROR); error = U_ZERO_ERROR; UString ret(len); len = usprep_prepare(_profile.get(), src.uc_str(), src.size(), ret.data(), ret.capacity(), options, nullptr, &error); uassert(50693, str::stream() << "Failed normalizing string: " << u_errorName(error), U_SUCCESS(error)); ret.resize(len); return ret; } private: class USPrepDeleter { public: void operator()(UStringPrepProfile* profile) { usprep_close(profile); } }; std::unique_ptr _profile; }; } // namespace StatusWith icuSaslPrep(StringData str, UStringPrepOptions options) try { const auto opts = (options == kUStringPrepDefault) ? USPREP_DEFAULT : USPREP_ALLOW_UNASSIGNED; return USPrep(USPREP_RFC4013_SASLPREP).prepare(UString::fromUTF8(str), opts).toUTF8(); } catch (const DBException& e) { return e.toStatus(); } StatusWith icuX509DNPrep(StringData str) try { return USPrep(USPREP_RFC4518_LDAP).prepare(UString::fromUTF8(str), USPREP_DEFAULT).toUTF8(); } catch (const DBException& e) { return e.toStatus(); } } // namespace mongo