Skip to content

Commit

Permalink
[LDAP] Support ldaps scheme (#5934) (#6779)
Browse files Browse the repository at this point in the history
  • Loading branch information
molotkov-and authored Jul 18, 2024
1 parent 696b497 commit ef60ef3
Show file tree
Hide file tree
Showing 32 changed files with 1,036 additions and 630 deletions.
2 changes: 1 addition & 1 deletion ydb/core/driver_lib/run/kikimr_services_initializers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
#include <ydb/core/scheme/scheme_type_registry.h>

#include <ydb/core/security/ticket_parser.h>
#include <ydb/core/security/ldap_auth_provider.h>
#include <ydb/core/security/ldap_auth_provider/ldap_auth_provider.h>
#include <ydb/core/security/ticket_parser_settings.h>

#include <ydb/core/sys_view/processor/processor.h>
Expand Down
1 change: 1 addition & 0 deletions ydb/core/driver_lib/run/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ PEERDIR(
ydb/core/scheme
ydb/core/scheme_types
ydb/core/security
ydb/core/security/ldap_auth_provider
ydb/core/statistics
ydb/core/statistics/aggregator
ydb/core/sys_view/processor
Expand Down
2 changes: 1 addition & 1 deletion ydb/core/grpc_services/rpc_login.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include <ydb/core/tx/schemeshard/schemeshard.h>
#include <ydb/core/tx/scheme_cache/scheme_cache.h>

#include <ydb/core/security/ldap_auth_provider.h>
#include <ydb/core/security/ldap_auth_provider/ldap_auth_provider.h>
#include <ydb/core/security/login_shared_func.h>

namespace NKikimr {
Expand Down
1 change: 1 addition & 0 deletions ydb/core/grpc_services/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ PEERDIR(
ydb/core/util
ydb/core/ydb_convert
ydb/core/security
ydb/core/security/ldap_auth_provider
ydb/library/aclib
ydb/library/binary_json
ydb/library/dynumber
Expand Down
4 changes: 3 additions & 1 deletion ydb/core/protos/auth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ message TLdapAuthentication {
optional TCertRequire CertRequire = 3 [default = DEMAND];
}

optional string Host = 1;
optional string Host = 1; // DEPRECATED: Use Hosts instead it
optional uint32 Port = 2;
required string BaseDn = 3;
required string BindDn = 4;
Expand All @@ -108,4 +108,6 @@ message TLdapAuthentication {
optional string SearchAttribute = 7;
optional TUseTls UseTls = 8;
optional string RequestedGroupAttribute = 9;
repeated string Hosts = 10;
optional string Scheme = 11 [default = "ldap"];
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <ydb/library/actors/core/actor_bootstrapped.h>
#include <ydb/library/actors/core/log.h>
#include <ydb/core/base/ticket_parser.h>
#include "ticket_parser_log.h"
#include <ydb/core/security/ticket_parser_log.h>
#include "ldap_auth_provider.h"
#include "ldap_utils.h"

Expand Down Expand Up @@ -156,7 +156,7 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
}

int result = 0;
if (Settings.GetUseTls().GetEnable()) {
if (Settings.GetScheme() != NKikimrLdap::LDAPS_SCHEME && Settings.GetUseTls().GetEnable()) {
result = NKikimrLdap::StartTLS(*ld);
if (!NKikimrLdap::IsSuccess(result)) {
TEvLdapAuthProvider::TError error {
Expand All @@ -173,7 +173,7 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
result = NKikimrLdap::Bind(*ld, Settings.GetBindDn(), Settings.GetBindPassword());
if (!NKikimrLdap::IsSuccess(result)) {
TEvLdapAuthProvider::TError error {
.Message = "Could not perform initial LDAP bind for dn " + Settings.GetBindDn() + " on server " + Settings.GetHost() + "\n"
.Message = "Could not perform initial LDAP bind for dn " + Settings.GetBindDn() + " on server " + UrisList + "\n"
+ NKikimrLdap::ErrorToString(result),
.Retryable = NKikimrLdap::IsRetryableError(result)
};
Expand All @@ -186,15 +186,12 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
}

TInitializeLdapConnectionResponse InitializeLDAPConnection(LDAP** ld) {
const TString& host = Settings.GetHost();
if (host.empty()) {
return {{TEvLdapAuthProvider::EStatus::UNAVAILABLE, {.Message = "Ldap server host is empty", .Retryable = false}}};
if (TInitializeLdapConnectionResponse response = CheckRequiredSettingsParameters(); response.Status != TEvLdapAuthProvider::EStatus::SUCCESS) {
return response;
}

const ui32 port = Settings.GetPort() != 0 ? Settings.GetPort() : NKikimrLdap::GetPort();

int result = 0;
if (Settings.GetUseTls().GetEnable()) {
if (Settings.GetScheme() == NKikimrLdap::LDAPS_SCHEME || Settings.GetUseTls().GetEnable()) {
const TString& caCertificateFile = Settings.GetUseTls().GetCaCertFile();
result = NKikimrLdap::SetOption(*ld, NKikimrLdap::EOption::TLS_CACERTFILE, caCertificateFile.c_str());
if (!NKikimrLdap::IsSuccess(result)) {
Expand All @@ -205,10 +202,12 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
}
}

*ld = NKikimrLdap::Init(host, port);
if (*ld == nullptr) {
const ui32 port = Settings.GetPort() != 0 ? Settings.GetPort() : NKikimrLdap::GetPort(Settings.GetScheme());
UrisList = GetUris(port);
result = NKikimrLdap::Init(ld, Settings.GetScheme(), UrisList, port);
if (!NKikimrLdap::IsSuccess(result)) {
return {{TEvLdapAuthProvider::EStatus::UNAVAILABLE,
{.Message = "Could not initialize LDAP connection for host: " + host + ", port: " + ToString(port) + ". " + NKikimrLdap::LdapError(*ld),
{.Message = "Could not initialize LDAP connection for uris: " + UrisList + ". " + NKikimrLdap::LdapError(*ld),
.Retryable = false}}};
}

Expand All @@ -220,7 +219,7 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
.Retryable = NKikimrLdap::IsRetryableError(result)}}};
}

if (Settings.GetUseTls().GetEnable()) {
if (Settings.GetScheme() == NKikimrLdap::LDAPS_SCHEME || Settings.GetUseTls().GetEnable()) {
int requireCert = NKikimrLdap::ConvertRequireCert(Settings.GetUseTls().GetCertRequire());
result = NKikimrLdap::SetOption(*ld, NKikimrLdap::EOption::TLS_REQUIRE_CERT, &requireCert);
if (!NKikimrLdap::IsSuccess(result)) {
Expand All @@ -230,21 +229,22 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
.Retryable = NKikimrLdap::IsRetryableError(result)}}};
}
}

return {};
}

TAuthenticateUserResponse AuthenticateUser(const TAuthenticateUserRequest& request) {
char* dn = NKikimrLdap::GetDn(*request.Ld, request.Entry);
if (dn == nullptr) {
return {{TEvLdapAuthProvider::EStatus::UNAUTHORIZED,
{.Message = "Could not get dn for the first entry matching " + FilterCreator.GetFilter(request.Login) + " on server " + Settings.GetHost() + "\n"
{.Message = "Could not get dn for the first entry matching " + FilterCreator.GetFilter(request.Login) + " on server " + UrisList + "\n"
+ NKikimrLdap::LdapError(*request.Ld),
.Retryable = false}}};
}
TEvLdapAuthProvider::TError error;
int result = NKikimrLdap::Bind(*request.Ld, dn, request.Password);
if (!NKikimrLdap::IsSuccess(result)) {
error.Message = "LDAP login failed for user " + TString(dn) + " on server " + Settings.GetHost() + "\n"
error.Message = "LDAP login failed for user " + TString(dn) + " on server " + UrisList + "\n"
+ NKikimrLdap::ErrorToString((result));
error.Retryable = NKikimrLdap::IsRetryableError(result);
}
Expand All @@ -266,7 +266,7 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
TSearchUserResponse response;
if (!NKikimrLdap::IsSuccess(result)) {
response.Status = NKikimrLdap::ErrorToStatus(result);
response.Error = {.Message = "Could not search for filter " + searchFilter + " on server " + Settings.GetHost() + "\n"
response.Error = {.Message = "Could not search for filter " + searchFilter + " on server " + UrisList + "\n"
+ NKikimrLdap::ErrorToString(result),
.Retryable = NKikimrLdap::IsRetryableError(result)};
return response;
Expand All @@ -275,11 +275,11 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
if (countEntries != 1) {
if (countEntries == 0) {
response.Error = {.Message = "LDAP user " + request.User + " does not exist. "
"LDAP search for filter " + searchFilter + " on server " + Settings.GetHost() + " return no entries",
"LDAP search for filter " + searchFilter + " on server " + UrisList + " return no entries",
.Retryable = false};
} else {
response.Error = {.Message = "LDAP user " + request.User + " is not unique. "
"LDAP search for filter " + searchFilter + " on server " + Settings.GetHost() + " return " + countEntries + " entries",
"LDAP search for filter " + searchFilter + " on server " + UrisList + " return " + countEntries + " entries",
.Retryable = false};
}
response.Status = TEvLdapAuthProvider::EStatus::UNAUTHORIZED;
Expand All @@ -290,10 +290,58 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
return response;
}

TInitializeLdapConnectionResponse CheckRequiredSettingsParameters() const {
if (Settings.GetHosts().empty() && Settings.GetHost().empty()) {
return {TEvLdapAuthProvider::EStatus::UNAVAILABLE, {.Message = "List of ldap server hosts is empty", .Retryable = false}};
}
if (Settings.GetBaseDn().empty()) {
return {TEvLdapAuthProvider::EStatus::UNAVAILABLE, {.Message = "Parameter BaseDn is empty", .Retryable = false}};
}
if (Settings.GetBindDn().empty()) {
return {TEvLdapAuthProvider::EStatus::UNAVAILABLE, {.Message = "Parameter BindDn is empty", .Retryable = false}};
}
if (Settings.GetBindPassword().empty()) {
return {TEvLdapAuthProvider::EStatus::UNAVAILABLE, {.Message = "Parameter BindPassword is empty", .Retryable = false}};
}
return {TEvLdapAuthProvider::EStatus::SUCCESS, {}};
}

TString GetUris(ui32 port) const {
TStringBuilder uris;
if (Settings.HostsSize() > 0) {
for (const auto& host : Settings.GetHosts()) {
uris << CreateUri(host, port) << " ";
}
uris.remove(uris.size() - 1);
} else {
uris << CreateUri(Settings.GetHost(), port);
}
return uris;
}

TString CreateUri(const TString& endpoint, ui32 port) const {
TStringBuilder uri;
uri << Settings.GetScheme() << "://" << endpoint;
if (!HasEndpointPort(endpoint)) {
uri << ':' << port;
}
return uri;
}

static bool HasEndpointPort(const TString& endpoint) {
size_t colonPos = endpoint.rfind(':');
if (colonPos == TString::npos) {
return false;
}
++colonPos;
return (endpoint.size() - colonPos) > 0;
}

private:
const NKikimrProto::TLdapAuthentication Settings;
const TSearchFilterCreator FilterCreator;
char* RequestedAttributes[2];
TString UrisList;
};

IActor* CreateLdapAuthProvider(const NKikimrProto::TLdapAuthentication& settings) {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <ydb/library/actors/core/actor_bootstrapped.h>
#include <ydb/library/actors/core/log.h>
#include <ydb/core/base/ticket_parser.h>
#include "ticket_parser_log.h"
#include <ydb/core/security/ticket_parser_log.h>
#include "ldap_auth_provider.h"

#define LDAP_DEPRECATED 1
Expand Down Expand Up @@ -38,6 +38,7 @@ int ConvertOption(const EOption& option) {
}

char* noAttributes[] = {ldapNoAttribute, nullptr};
const TString LDAPS_SCHEME = "ldaps";

int Bind(LDAP* ld, const TString& dn, const TString& password) {
return ldap_simple_bind_s(ld, dn.c_str(), password.c_str());
Expand All @@ -47,8 +48,9 @@ int Unbind(LDAP* ld) {
return ldap_unbind(ld);
}

LDAP* Init(const TString& host, ui32 port) {
return ldap_init(host.c_str(), port);
int Init(LDAP** ld, const TString& scheme, const TString& uris, ui32 port) {
Y_UNUSED(scheme, port);
return ldap_initialize(ld, uris.c_str());
}

int Search(LDAP* ld,
Expand Down Expand Up @@ -109,7 +111,10 @@ std::vector<TString> GetAllValuesOfAttribute(LDAP* ld, LDAPMessage* entry, char*
return response;
}

ui32 GetPort() {
ui32 GetPort(const TString& scheme) {
if (scheme == LDAPS_SCHEME) {
return LDAPS_PORT;
}
return LDAP_PORT;
}

Expand Down
Loading

0 comments on commit ef60ef3

Please sign in to comment.