/* -*- 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 <rtl/ustrbuf.hxx>
#include <rtl/strbuf.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
namespace extensions { namespace config { namespace 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 sal_Char *aOperation,
LdapErrCode aRetCode)
{
if (aRetCode == LDAP_SUCCESS) { return ; }
OUString message;
if (aOperation != nullptr)
{
message += OUString::createFromAscii(aOperation) + ": ";
}
message += OUString::createFromAscii(ldap_err2string(aRetCode)) + " (" ;
sal_Char *stub = nullptr ;
#ifndef LDAP_OPT_SIZELIMIT // for use with OpenLDAP
ldap_get_lderrno(aConnection, NULL, &stub) ;
#endif
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 { 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())
{
// 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("Cannot initialise connection to LDAP: No server specified.");
}
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(
"LdapConnection::findUserDn -User id is empty",
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
sal_Char * attributes [2] = { const_cast<sal_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
sal_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: */
↑ V547 Expression 'stub != nullptr' is always false.