/* -*- 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 #include #include #include #include #include #include #include #include #include #include #define SECURITY_WIN32 #include #include #include 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 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 pUser; sal::systools::ThrowIfFailed(ADsGetObject(o3tl::toW(sLdapUserDN.getStr()), IID_IADsUser, reinterpret_cast(&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 seqCachedData( reinterpret_cast(sCachedData.getStr()), sCachedData.getLength() * sizeof(sal_Unicode)); OUStringBuffer sOutBuf; comphelper::Base64::encode(sOutBuf, seqCachedData); std::shared_ptr 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 seqCachedData; comphelper::Base64::decode(seqCachedData, sCache); sCache = OUString(reinterpret_cast(seqCachedData.getConstArray()), seqCachedData.getLength() / sizeof(sal_Unicode)); } OUString sUserDN; std::map 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 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(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(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(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(this)); return css::uno::Any(css::beans::Optional( !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 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 const&) { return cppu::acquire(new extensions::config::WinUserInfo::WinUserInfoBe()); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */