/* -*- 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 OUString _givenname(u"givenname"_ustr); constexpr OUString _sn(u"sn"_ustr); constexpr char _fathersname[]("fathersname"); constexpr OUString _initials(u"initials"_ustr); constexpr OUString _street(u"street"_ustr); constexpr OUString _l(u"l"_ustr); constexpr OUString _st(u"st"_ustr); constexpr char _apartment[]("apartment"); constexpr OUString _postalcode(u"postalcode"_ustr); constexpr OUString _c(u"c"_ustr); constexpr OUString _o(u"o"_ustr); constexpr char _position[]("position"); constexpr OUString _title(u"title"_ustr); constexpr OUString _homephone(u"homephone"_ustr); constexpr OUString _telephonenumber(u"telephonenumber"_ustr); constexpr OUString _facsimiletelephonenumber(u"facsimiletelephonenumber"_ustr); constexpr OUString _mail(u"mail"_ustr); // 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) { struct AutoVariant : public VARIANT { AutoVariant() { VariantInit(this); } ~AutoVariant() { VariantClear(this); } } varArr; if (FAILED(pUser->GetEx(sal::systools::BStr(o3tl::toU(property)), &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: */