diff options
Diffstat (limited to '')
-rw-r--r-- | extensions/source/config/WinUserInfo/WinUserInfoBe.cxx | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx b/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx new file mode 100644 index 000000000..2914cf78d --- /dev/null +++ b/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx @@ -0,0 +1,433 @@ +/* -*- 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/. + */ + +#include "WinUserInfoBe.hxx" + +#include <com/sun/star/beans/Optional.hpp> +#include <comphelper/base64.hxx> +#include <comphelper/configuration.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <map> +#include <o3tl/char16_t2wchar_t.hxx> +#include <tools/diagnose_ex.h> +#include <officecfg/UserProfile.hxx> + +#include <Iads.h> +#include <Adshlp.h> +#include <Lmcons.h> +#define SECURITY_WIN32 +#include <Security.h> + +#include <systools/win32/comtools.hxx> +#include <systools/win32/oleauto.hxx> + +namespace extensions +{ +namespace config +{ +namespace WinUserInfo +{ +class WinUserInfoBe_Impl +{ +public: + virtual ~WinUserInfoBe_Impl(){}; + virtual OUString GetGivenName() = 0; + virtual OUString GetSn() { return ""; } + virtual OUString GetFathersname() { return ""; } + virtual OUString GetInitials() { return ""; } + virtual OUString GetStreet() { return ""; } + virtual OUString GetCity() { return ""; } + virtual OUString GetState() { return ""; } + virtual OUString GetApartment() { return ""; } + virtual OUString GetPostalCode() { return ""; } + virtual OUString GetCountry() { return ""; } + virtual OUString GetOrganization() { return ""; } + virtual OUString GetPosition() { return ""; } + virtual OUString GetTitle() { return ""; } + virtual OUString GetHomePhone() { return ""; } + virtual OUString GetTelephoneNumber() { return ""; } + virtual OUString GetFaxNumber() { return ""; } + virtual OUString GetMail() { return ""; } +}; +} +} +} + +namespace +{ +constexpr OUStringLiteral givenname(u"givenname"); +constexpr OUStringLiteral sn(u"sn"); +constexpr char fathersname[]("fathersname"); +constexpr OUStringLiteral initials(u"initials"); +constexpr OUStringLiteral street(u"street"); +constexpr OUStringLiteral l(u"l"); +constexpr OUStringLiteral st(u"st"); +constexpr char apartment[]("apartment"); +constexpr OUStringLiteral postalcode(u"postalcode"); +constexpr OUStringLiteral c(u"c"); +constexpr OUStringLiteral o(u"o"); +constexpr char position[]("position"); +constexpr OUStringLiteral title(u"title"); +constexpr OUStringLiteral homephone(u"homephone"); +constexpr OUStringLiteral telephonenumber(u"telephonenumber"); +constexpr OUStringLiteral facsimiletelephonenumber(u"facsimiletelephonenumber"); +constexpr OUStringLiteral mail(u"mail"); + +// Backend class implementing access to Active Directory user data. It caches its encoded data +// in a configuration entry, to allow reusing it when user later doesn't have access to AD DC +// (otherwise the user would get different data when connected vs not connected). +class ADsUserAccess : public extensions::config::WinUserInfo::WinUserInfoBe_Impl +{ +public: + ADsUserAccess() + { + try + { + sal::systools::CoInitializeGuard aCoInitializeGuard(COINIT_APARTMENTTHREADED); + + sal::systools::COMReference<IADsADSystemInfo> pADsys(CLSID_ADSystemInfo, nullptr, + CLSCTX_INPROC_SERVER); + + sal::systools::BStr sUserDN; + sal::systools::ThrowIfFailed(pADsys->get_UserName(&sUserDN), "get_UserName failed"); + // If this user is an AD user, then without an active connection to the domain, all the + // above will succeed, and m_sUserDN will be correctly initialized, but the following + // call to ADsGetObject will fail, and we will attempt reading cached values. + m_sUserDN = sUserDN; + OUString sLdapUserDN = "LDAP://" + m_sUserDN; + sal::systools::COMReference<IADsUser> pUser; + sal::systools::ThrowIfFailed(ADsGetObject(o3tl::toW(sLdapUserDN.getStr()), IID_IADsUser, + reinterpret_cast<void**>(&pUser)), + "ADsGetObject failed"); + // Fetch all the required information right now, when we know to have access to AD + // (later the connection may already be lost) + m_aMap[givenname] = Str(pUser, &IADsUser::get_FirstName); + m_aMap[sn] = Str(pUser, &IADsUser::get_LastName); + m_aMap[initials] = Str(pUser, L"initials"); + m_aMap[street] = Str(pUser, L"streetAddress"); + m_aMap[l] = Str(pUser, L"l"); + m_aMap[st] = Str(pUser, L"st"); + m_aMap[postalcode] = Str(pUser, L"postalCode"); + m_aMap[c] = Str(pUser, L"co"); + m_aMap[o] = Str(pUser, L"company"); + m_aMap[title] = Str(pUser, &IADsUser::get_Title); + m_aMap[homephone] = Str(pUser, L"homePhone"); + m_aMap[telephonenumber] = Str(pUser, L"TelephoneNumber"); + m_aMap[facsimiletelephonenumber] = Str(pUser, L"facsimileTelephoneNumber"); + m_aMap[mail] = Str(pUser, &IADsUser::get_EmailAddress); + + CacheData(); + } + catch (sal::systools::ComError&) + { + // Maybe we temporarily lost connection to AD; try to get cached data + GetCachedData(); + } + } + + virtual OUString GetGivenName() override { return m_aMap[givenname]; } + virtual OUString GetSn() override { return m_aMap[sn]; } + virtual OUString GetInitials() override { return m_aMap[initials]; } + virtual OUString GetStreet() override { return m_aMap[street]; } + virtual OUString GetCity() override { return m_aMap[l]; } + virtual OUString GetState() override { return m_aMap[st]; } + virtual OUString GetPostalCode() override { return m_aMap[postalcode]; } + virtual OUString GetCountry() override { return m_aMap[c]; } + virtual OUString GetOrganization() override { return m_aMap[o]; } + virtual OUString GetTitle() override { return m_aMap[title]; } + virtual OUString GetHomePhone() override { return m_aMap[homephone]; } + virtual OUString GetTelephoneNumber() override { return m_aMap[telephonenumber]; } + virtual OUString GetFaxNumber() override { return m_aMap[facsimiletelephonenumber]; } + virtual OUString GetMail() override { return m_aMap[mail]; } + +private: + typedef HRESULT (__stdcall IADsUser::*getstrfunc)(BSTR*); + static OUString Str(IADsUser* pUser, getstrfunc func) + { + sal::systools::BStr sBstr; + if (FAILED((pUser->*func)(&sBstr))) + return ""; + return OUString(sBstr); + } + static OUString Str(IADsUser* pUser, const wchar_t* property) + { + sal::systools::BStr sBstrProp{ o3tl::toU(property) }; + struct AutoVariant : public VARIANT + { + AutoVariant() { VariantInit(this); } + ~AutoVariant() { VariantClear(this); } + } varArr; + if (FAILED(pUser->GetEx(sBstrProp, &varArr))) + return ""; + SAFEARRAY* sa = V_ARRAY(&varArr); + LONG nStart, nEnd; + if (FAILED(SafeArrayGetLBound(sa, 1, &nStart)) || FAILED(SafeArrayGetUBound(sa, 1, &nEnd))) + return ""; + AutoVariant varItem; + for (LONG i = nStart; i <= nEnd; i++) + { + if (FAILED(SafeArrayGetElement(sa, &i, &varItem))) + continue; + if (varItem.vt == VT_BSTR) + return OUString(o3tl::toU(V_BSTR(&varItem))); + VariantClear(&varItem); + } + return ""; + } + + void CacheData() + { + try + { + OUString sCachedData = "user=" + m_sUserDN // user DN + + "\0" + givenname + "=" + GetGivenName() // 1st name + + "\0" + sn + "=" + GetSn() // sn + + "\0" + initials + "=" + GetInitials() // initials + + "\0" + street + "=" + GetStreet() // street + + "\0" + l + "=" + GetCity() // l + + "\0" + st + "=" + GetState() // st + + "\0" + postalcode + "=" + GetPostalCode() // p.code + + "\0" + c + "=" + GetCountry() // c + + "\0" + o + "=" + GetOrganization() // o + + "\0" + title + "=" + GetTitle() // title + + "\0" + homephone + "=" + GetHomePhone() // h.phone + + "\0" + telephonenumber + "=" + GetTelephoneNumber() // tel + + "\0" + facsimiletelephonenumber + "=" + GetFaxNumber() // fax + + "\0" + mail + "=" + GetMail(); // mail + const css::uno::Sequence<sal_Int8> seqCachedData( + reinterpret_cast<const sal_Int8*>(sCachedData.getStr()), + sCachedData.getLength() * sizeof(sal_Unicode)); + OUStringBuffer sOutBuf; + comphelper::Base64::encode(sOutBuf, seqCachedData); + + std::shared_ptr<comphelper::ConfigurationChanges> batch( + comphelper::ConfigurationChanges::create()); + officecfg::UserProfile::WinUserInfo::Cache::set(sOutBuf.makeStringAndClear(), batch); + batch->commit(); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("extensions.config", + "ADsUserAccess: access to configuration data failed:"); + } + } + + void GetCachedData() + { + if (m_sUserDN.isEmpty()) + throw css::uno::RuntimeException(); + + OUString sCache = officecfg::UserProfile::WinUserInfo::Cache::get(); + + if (sCache.isEmpty()) + throw css::uno::RuntimeException(); + + { + css::uno::Sequence<sal_Int8> seqCachedData; + comphelper::Base64::decode(seqCachedData, sCache); + sCache = OUString(reinterpret_cast<const sal_Unicode*>(seqCachedData.getConstArray()), + seqCachedData.getLength() / sizeof(sal_Unicode)); + } + + OUString sUserDN; + std::map<const OUString, OUString> aMap; + sal_Int32 nIndex = 0; + do + { + const OUString sEntry = sCache.getToken(0, '\0', nIndex); + sal_Int32 nEqIndex = 0; + const OUString sEntryName = sEntry.getToken(0, '=', nEqIndex); + OUString sEntryVal; + if (nEqIndex >= 0) + sEntryVal = sEntry.copy(nEqIndex); + if (sEntryName == "user") + sUserDN = sEntryVal; + else + aMap[sEntryName] = sEntryVal; + } while (nIndex >= 0); + + if (sUserDN != m_sUserDN) + throw css::uno::RuntimeException(); + m_aMap = std::move(aMap); + } + + OUString m_sUserDN; // used to check if the cached data is for current user + std::map<const OUString, OUString> m_aMap; +}; + +class SysInfoUserAccess : public extensions::config::WinUserInfo::WinUserInfoBe_Impl +{ +public: + SysInfoUserAccess() + { + try + { + ULONG nSize = 0; + GetUserNameExW(NameDisplay, nullptr, &nSize); + if (GetLastError() != ERROR_MORE_DATA) + throw css::uno::RuntimeException(); + auto pNameBuf(std::make_unique<wchar_t[]>(nSize)); + if (!GetUserNameExW(NameDisplay, pNameBuf.get(), &nSize)) + throw css::uno::RuntimeException(); + m_sName = o3tl::toU(pNameBuf.get()); + } + catch (css::uno::RuntimeException&) + { + // GetUserNameEx may fail in some cases (e.g., for built-in AD domain + // administrator account on non-DC systems), where GetUserName will + // still give a name. + DWORD nSize = UNLEN + 1; + auto pNameBuf(std::make_unique<wchar_t[]>(nSize)); + if (!GetUserNameW(pNameBuf.get(), &nSize)) + throw css::uno::RuntimeException(); + m_sName = o3tl::toU(pNameBuf.get()); + } + } + + virtual OUString GetGivenName() override { return m_sName; } + +private: + OUString m_sName; +}; +} + +namespace extensions +{ +namespace config +{ +namespace WinUserInfo +{ +WinUserInfoBe::WinUserInfoBe() + : WinUserInfoMutexHolder() + , BackendBase(mMutex) +{ + try + { + m_pImpl.reset(new ADsUserAccess()); + } + catch (css::uno::RuntimeException&) + { + m_pImpl.reset(new SysInfoUserAccess); + } +} + +WinUserInfoBe::~WinUserInfoBe() {} + +void WinUserInfoBe::setPropertyValue(OUString const&, css::uno::Any const&) +{ + throw css::lang::IllegalArgumentException("setPropertyValue not supported", + static_cast<cppu::OWeakObject*>(this), -1); +} + +css::uno::Any WinUserInfoBe::getPropertyValue(OUString const& PropertyName) +{ + OUString sValue; + // Only process the first argument of possibly multiple space- or comma-separated arguments + OUString sToken = PropertyName.getToken(0, ' ').getToken(0, ','); + if (sToken == givenname) + { + sValue = m_pImpl->GetGivenName(); + } + else if (sToken == sn) + { + sValue = m_pImpl->GetSn(); + } + else if (sToken == fathersname) + { + sValue = m_pImpl->GetFathersname(); + } + else if (sToken == initials) + { + sValue = m_pImpl->GetInitials(); + } + else if (sToken == street) + { + sValue = m_pImpl->GetStreet(); + } + else if (sToken == l) + { + sValue = m_pImpl->GetCity(); + } + else if (sToken == st) + { + sValue = m_pImpl->GetState(); + } + else if (sToken == apartment) + { + sValue = m_pImpl->GetApartment(); + } + else if (sToken == postalcode) + { + sValue = m_pImpl->GetPostalCode(); + } + else if (sToken == c) + { + sValue = m_pImpl->GetCountry(); + } + else if (sToken == o) + { + sValue = m_pImpl->GetOrganization(); + } + else if (sToken == position) + { + sValue = m_pImpl->GetPosition(); + } + else if (sToken == title) + { + sValue = m_pImpl->GetTitle(); + } + else if (sToken == homephone) + { + sValue = m_pImpl->GetHomePhone(); + } + else if (sToken == telephonenumber) + { + sValue = m_pImpl->GetTelephoneNumber(); + } + else if (sToken == facsimiletelephonenumber) + { + sValue = m_pImpl->GetFaxNumber(); + } + else if (sToken == mail) + { + sValue = m_pImpl->GetMail(); + } + else + throw css::beans::UnknownPropertyException(sToken, static_cast<cppu::OWeakObject*>(this)); + + return css::uno::Any(css::beans::Optional<css::uno::Any>( + !sValue.isEmpty(), sValue.isEmpty() ? css::uno::Any() : css::uno::Any(sValue))); +} + +OUString SAL_CALL WinUserInfoBe::getImplementationName() +{ + return "com.sun.star.comp.configuration.backend.WinUserInfoBe"; +} + +sal_Bool SAL_CALL WinUserInfoBe::supportsService(const OUString& aServiceName) +{ + return cppu::supportsService(this, aServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL WinUserInfoBe::getSupportedServiceNames() +{ + return { "com.sun.star.configuration.backend.WinUserInfoBe" }; +} +} +} +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_WinUserInfoBe_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new extensions::config::WinUserInfo::WinUserInfoBe()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |