289 lines
9.3 KiB
C++
289 lines
9.3 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* 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 .
|
|
*/
|
|
|
|
|
|
#include "ldapaccess.hxx"
|
|
|
|
#include <osl/diagnose.h>
|
|
#include <o3tl/char16_t2wchar_t.hxx>
|
|
|
|
#include <com/sun/star/ldap/LdapConnectionException.hpp>
|
|
|
|
|
|
namespace extensions::config::ldap {
|
|
|
|
|
|
typedef int LdapErrCode;
|
|
|
|
struct LdapMessageHolder
|
|
{
|
|
LdapMessageHolder() : msg(nullptr) {}
|
|
~LdapMessageHolder()
|
|
{
|
|
if (msg)
|
|
ldap_msgfree(msg);
|
|
}
|
|
LdapMessageHolder(const LdapMessageHolder&) = delete;
|
|
LdapMessageHolder& operator=(const LdapMessageHolder&) = delete;
|
|
|
|
LDAPMessage * msg;
|
|
};
|
|
|
|
LdapConnection::~LdapConnection()
|
|
{
|
|
if (isValid()) disconnect();
|
|
}
|
|
|
|
|
|
void LdapConnection::disconnect()
|
|
{
|
|
if (mConnection != nullptr)
|
|
{
|
|
ldap_unbind_s(mConnection) ;
|
|
mConnection = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
static void checkLdapReturnCode(const char *aOperation,
|
|
LdapErrCode aRetCode)
|
|
{
|
|
if (aRetCode == LDAP_SUCCESS) { return ; }
|
|
|
|
OUString message;
|
|
|
|
if (aOperation != nullptr)
|
|
{
|
|
message += OUString::createFromAscii(aOperation) + ": ";
|
|
}
|
|
message += OUString::createFromAscii(ldap_err2string(aRetCode)) + " (" ;
|
|
|
|
#ifndef LDAP_OPT_SIZELIMIT // for use with OpenLDAP
|
|
char* stub = nullptr;
|
|
ldap_get_lderrno(aConnection, NULL, &stub) ;
|
|
if (stub != nullptr)
|
|
{
|
|
message += OUString::createFromAscii(stub) ;
|
|
// It would seem the message returned is actually
|
|
// not a copy of a string but rather some static
|
|
// string itself. At any rate freeing it seems to
|
|
// cause some undue problems at least on Windows.
|
|
// This call is thus disabled for the moment.
|
|
//ldap_memfree(stub) ;
|
|
}
|
|
else
|
|
#endif
|
|
{ message += "No additional information"; }
|
|
|
|
message += ")" ;
|
|
throw ldap::LdapGenericException(message, nullptr, aRetCode) ;
|
|
}
|
|
|
|
void LdapConnection::connectSimple(const LdapDefinition& aDefinition)
|
|
{
|
|
OSL_ENSURE(!isValid(), "Re-connecting to an LDAP connection that is already established");
|
|
if (isValid()) disconnect();
|
|
|
|
mLdapDefinition = aDefinition;
|
|
connectSimple();
|
|
}
|
|
|
|
void LdapConnection::connectSimple()
|
|
{
|
|
if (isValid())
|
|
return;
|
|
|
|
// Connect to the server
|
|
initConnection() ;
|
|
// Set Protocol V3
|
|
int version = LDAP_VERSION3;
|
|
ldap_set_option(mConnection,
|
|
LDAP_OPT_PROTOCOL_VERSION,
|
|
&version);
|
|
|
|
#ifdef LDAP_X_OPT_CONNECT_TIMEOUT // OpenLDAP doesn't support this and the func
|
|
/* timeout is specified in milliseconds -> 4 seconds*/
|
|
int timeout = 4000;
|
|
#ifdef _WIN32
|
|
ldap_set_optionW( mConnection,
|
|
LDAP_X_OPT_CONNECT_TIMEOUT,
|
|
&timeout );
|
|
#else
|
|
ldap_set_option( mConnection,
|
|
LDAP_X_OPT_CONNECT_TIMEOUT,
|
|
&timeout );
|
|
#endif
|
|
#endif
|
|
|
|
// Do the bind
|
|
#ifdef _WIN32
|
|
LdapErrCode retCode = ldap_simple_bind_sW(mConnection,
|
|
const_cast<PWSTR>(o3tl::toW(mLdapDefinition.mAnonUser.getStr())),
|
|
const_cast<PWSTR>(o3tl::toW(mLdapDefinition.mAnonCredentials.getStr())) );
|
|
#else
|
|
LdapErrCode retCode = ldap_simple_bind_s(mConnection,
|
|
OUStringToOString( mLdapDefinition.mAnonUser, RTL_TEXTENCODING_UTF8 ).getStr(),
|
|
OUStringToOString( mLdapDefinition.mAnonCredentials, RTL_TEXTENCODING_UTF8 ).getStr()) ;
|
|
#endif
|
|
|
|
checkLdapReturnCode("SimpleBind", retCode) ;
|
|
}
|
|
|
|
void LdapConnection::initConnection()
|
|
{
|
|
if (mLdapDefinition.mServer.isEmpty())
|
|
{
|
|
throw ldap::LdapConnectionException(u"Cannot initialise connection to LDAP: No server specified."_ustr);
|
|
}
|
|
|
|
if (mLdapDefinition.mPort == 0) mLdapDefinition.mPort = LDAP_PORT;
|
|
|
|
#ifdef _WIN32
|
|
mConnection = ldap_initW(const_cast<PWSTR>(o3tl::toW(mLdapDefinition.mServer.getStr())),
|
|
mLdapDefinition.mPort) ;
|
|
#else
|
|
mConnection = ldap_init(OUStringToOString( mLdapDefinition.mServer, RTL_TEXTENCODING_UTF8 ).getStr(),
|
|
mLdapDefinition.mPort) ;
|
|
#endif
|
|
if (mConnection == nullptr)
|
|
{
|
|
throw ldap::LdapConnectionException(
|
|
"Cannot initialise connection to LDAP server "
|
|
+ mLdapDefinition.mServer + ":" + OUString::number(mLdapDefinition.mPort));
|
|
}
|
|
}
|
|
|
|
void LdapConnection::getUserProfile(
|
|
const OUString& aUser, LdapData * data)
|
|
{
|
|
OSL_ASSERT(data != nullptr);
|
|
if (!isValid()) { connectSimple(); }
|
|
|
|
OUString aUserDn =findUserDn( aUser );
|
|
|
|
LdapMessageHolder result;
|
|
#ifdef _WIN32
|
|
LdapErrCode retCode = ldap_search_sW(mConnection,
|
|
const_cast<PWSTR>(o3tl::toW(aUserDn.getStr())),
|
|
LDAP_SCOPE_BASE,
|
|
const_cast<PWSTR>( L"(objectclass=*)" ),
|
|
nullptr,
|
|
0, // Attributes + values
|
|
&result.msg) ;
|
|
#else
|
|
LdapErrCode retCode = ldap_search_s(mConnection,
|
|
OUStringToOString( aUserDn, RTL_TEXTENCODING_UTF8 ).getStr(),
|
|
LDAP_SCOPE_BASE,
|
|
"(objectclass=*)",
|
|
nullptr,
|
|
0, // Attributes + values
|
|
&result.msg) ;
|
|
#endif
|
|
checkLdapReturnCode("getUserProfile", retCode) ;
|
|
|
|
BerElement * ptr;
|
|
#ifdef _WIN32
|
|
PWCHAR attr = ldap_first_attributeW(mConnection, result.msg, &ptr);
|
|
while (attr) {
|
|
PWCHAR * values = ldap_get_valuesW(mConnection, result.msg, attr);
|
|
if (values) {
|
|
const OUString aAttr( o3tl::toU( attr ) );
|
|
const OUString aValues( o3tl::toU( *values ) );
|
|
data->emplace( aAttr, aValues );
|
|
ldap_value_freeW(values);
|
|
}
|
|
attr = ldap_next_attributeW(mConnection, result.msg, ptr);
|
|
#else
|
|
char * attr = ldap_first_attribute(mConnection, result.msg, &ptr);
|
|
while (attr) {
|
|
char ** values = ldap_get_values(mConnection, result.msg, attr);
|
|
if (values) {
|
|
data->emplace(
|
|
OStringToOUString(attr, RTL_TEXTENCODING_ASCII_US),
|
|
OStringToOUString(*values, RTL_TEXTENCODING_UTF8));
|
|
ldap_value_free(values);
|
|
}
|
|
attr = ldap_next_attribute(mConnection, result.msg, ptr);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
OUString LdapConnection::findUserDn(const OUString& aUser)
|
|
{
|
|
if (!isValid()) { connectSimple(); }
|
|
|
|
if (aUser.isEmpty())
|
|
{
|
|
throw lang::IllegalArgumentException(
|
|
u"LdapConnection::findUserDn -User id is empty"_ustr,
|
|
nullptr, 0) ;
|
|
}
|
|
|
|
OUString filter = "(&(objectclass="
|
|
+ mLdapDefinition.mUserObjectClass
|
|
+ ")("
|
|
+ mLdapDefinition.mUserUniqueAttr
|
|
+ "="
|
|
+ aUser
|
|
+ "))";
|
|
|
|
LdapMessageHolder result;
|
|
#ifdef _WIN32
|
|
PWCHAR attributes [2] = { const_cast<PWCHAR>( L"1.1" ), nullptr };
|
|
LdapErrCode retCode = ldap_search_sW(mConnection,
|
|
const_cast<PWSTR>(o3tl::toW(mLdapDefinition.mBaseDN.getStr())),
|
|
LDAP_SCOPE_SUBTREE,
|
|
const_cast<PWSTR>(o3tl::toW(filter.getStr())), attributes, 0, &result.msg) ;
|
|
#else
|
|
char * attributes [2] = { const_cast<char *>(LDAP_NO_ATTRS), nullptr };
|
|
LdapErrCode retCode = ldap_search_s(mConnection,
|
|
OUStringToOString( mLdapDefinition.mBaseDN, RTL_TEXTENCODING_UTF8 ).getStr(),
|
|
LDAP_SCOPE_SUBTREE,
|
|
OUStringToOString( filter, RTL_TEXTENCODING_UTF8 ).getStr(), attributes, 0, &result.msg) ;
|
|
#endif
|
|
checkLdapReturnCode("FindUserDn", retCode) ;
|
|
OUString userDn ;
|
|
LDAPMessage *entry = ldap_first_entry(mConnection, result.msg) ;
|
|
|
|
if (entry != nullptr)
|
|
{
|
|
#ifdef _WIN32
|
|
PWCHAR charsDn = ldap_get_dnW(mConnection, entry) ;
|
|
|
|
userDn = OUString( o3tl::toU( charsDn ) );
|
|
ldap_memfreeW(charsDn) ;
|
|
#else
|
|
char *charsDn = ldap_get_dn(mConnection, entry) ;
|
|
|
|
userDn = OStringToOUString( charsDn, RTL_TEXTENCODING_UTF8 );
|
|
ldap_memfree(charsDn) ;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
OSL_FAIL( "LdapConnection::findUserDn-could not get DN for User ");
|
|
}
|
|
|
|
return userDn ;
|
|
}
|
|
|
|
|
|
} // extensions::config::ldap
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|