Avoid potential negative array index access to cached text.
[LibreOffice.git] / extensions / source / config / WinUserInfo / WinUserInfoBe.cxx
blob9726c98695258d7c093f244ef04540a4f195ac67
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include "WinUserInfoBe.hxx"
12 #include <com/sun/star/beans/Optional.hpp>
13 #include <comphelper/base64.hxx>
14 #include <comphelper/configuration.hxx>
15 #include <cppuhelper/supportsservice.hxx>
16 #include <map>
17 #include <o3tl/char16_t2wchar_t.hxx>
18 #include <comphelper/diagnose_ex.hxx>
19 #include <officecfg/UserProfile.hxx>
21 #include <Iads.h>
22 #include <Adshlp.h>
23 #include <Lmcons.h>
24 #define SECURITY_WIN32
25 #include <Security.h>
27 #include <systools/win32/comtools.hxx>
28 #include <systools/win32/oleauto.hxx>
30 namespace extensions
32 namespace config
34 namespace WinUserInfo
36 class WinUserInfoBe_Impl
38 public:
39 virtual ~WinUserInfoBe_Impl(){};
40 virtual OUString GetGivenName() = 0;
41 virtual OUString GetSn() { return ""; }
42 virtual OUString GetFathersname() { return ""; }
43 virtual OUString GetInitials() { return ""; }
44 virtual OUString GetStreet() { return ""; }
45 virtual OUString GetCity() { return ""; }
46 virtual OUString GetState() { return ""; }
47 virtual OUString GetApartment() { return ""; }
48 virtual OUString GetPostalCode() { return ""; }
49 virtual OUString GetCountry() { return ""; }
50 virtual OUString GetOrganization() { return ""; }
51 virtual OUString GetPosition() { return ""; }
52 virtual OUString GetTitle() { return ""; }
53 virtual OUString GetHomePhone() { return ""; }
54 virtual OUString GetTelephoneNumber() { return ""; }
55 virtual OUString GetFaxNumber() { return ""; }
56 virtual OUString GetMail() { return ""; }
62 namespace
64 constexpr OUString givenname(u"givenname"_ustr);
65 constexpr OUString sn(u"sn"_ustr);
66 constexpr char fathersname[]("fathersname");
67 constexpr OUString initials(u"initials"_ustr);
68 constexpr OUString street(u"street"_ustr);
69 constexpr OUString l(u"l"_ustr);
70 constexpr OUString st(u"st"_ustr);
71 constexpr char apartment[]("apartment");
72 constexpr OUString postalcode(u"postalcode"_ustr);
73 constexpr OUString c(u"c"_ustr);
74 constexpr OUString o(u"o"_ustr);
75 constexpr char position[]("position");
76 constexpr OUString title(u"title"_ustr);
77 constexpr OUString homephone(u"homephone"_ustr);
78 constexpr OUString telephonenumber(u"telephonenumber"_ustr);
79 constexpr OUString facsimiletelephonenumber(u"facsimiletelephonenumber"_ustr);
80 constexpr OUString mail(u"mail"_ustr);
82 // Backend class implementing access to Active Directory user data. It caches its encoded data
83 // in a configuration entry, to allow reusing it when user later doesn't have access to AD DC
84 // (otherwise the user would get different data when connected vs not connected).
85 class ADsUserAccess : public extensions::config::WinUserInfo::WinUserInfoBe_Impl
87 public:
88 ADsUserAccess()
90 try
92 sal::systools::CoInitializeGuard aCoInitializeGuard(COINIT_APARTMENTTHREADED);
94 sal::systools::COMReference<IADsADSystemInfo> pADsys(CLSID_ADSystemInfo, nullptr,
95 CLSCTX_INPROC_SERVER);
97 sal::systools::BStr sUserDN;
98 sal::systools::ThrowIfFailed(pADsys->get_UserName(&sUserDN), "get_UserName failed");
99 // If this user is an AD user, then without an active connection to the domain, all the
100 // above will succeed, and m_sUserDN will be correctly initialized, but the following
101 // call to ADsGetObject will fail, and we will attempt reading cached values.
102 m_sUserDN = sUserDN;
103 OUString sLdapUserDN = "LDAP://" + m_sUserDN;
104 sal::systools::COMReference<IADsUser> pUser;
105 sal::systools::ThrowIfFailed(ADsGetObject(o3tl::toW(sLdapUserDN.getStr()), IID_IADsUser,
106 reinterpret_cast<void**>(&pUser)),
107 "ADsGetObject failed");
108 // Fetch all the required information right now, when we know to have access to AD
109 // (later the connection may already be lost)
110 m_aMap[givenname] = Str(pUser, &IADsUser::get_FirstName);
111 m_aMap[sn] = Str(pUser, &IADsUser::get_LastName);
112 m_aMap[initials] = Str(pUser, L"initials");
113 m_aMap[street] = Str(pUser, L"streetAddress");
114 m_aMap[l] = Str(pUser, L"l");
115 m_aMap[st] = Str(pUser, L"st");
116 m_aMap[postalcode] = Str(pUser, L"postalCode");
117 m_aMap[c] = Str(pUser, L"co");
118 m_aMap[o] = Str(pUser, L"company");
119 m_aMap[title] = Str(pUser, &IADsUser::get_Title);
120 m_aMap[homephone] = Str(pUser, L"homePhone");
121 m_aMap[telephonenumber] = Str(pUser, L"TelephoneNumber");
122 m_aMap[facsimiletelephonenumber] = Str(pUser, L"facsimileTelephoneNumber");
123 m_aMap[mail] = Str(pUser, &IADsUser::get_EmailAddress);
125 CacheData();
127 catch (sal::systools::ComError&)
129 // Maybe we temporarily lost connection to AD; try to get cached data
130 GetCachedData();
134 virtual OUString GetGivenName() override { return m_aMap[givenname]; }
135 virtual OUString GetSn() override { return m_aMap[sn]; }
136 virtual OUString GetInitials() override { return m_aMap[initials]; }
137 virtual OUString GetStreet() override { return m_aMap[street]; }
138 virtual OUString GetCity() override { return m_aMap[l]; }
139 virtual OUString GetState() override { return m_aMap[st]; }
140 virtual OUString GetPostalCode() override { return m_aMap[postalcode]; }
141 virtual OUString GetCountry() override { return m_aMap[c]; }
142 virtual OUString GetOrganization() override { return m_aMap[o]; }
143 virtual OUString GetTitle() override { return m_aMap[title]; }
144 virtual OUString GetHomePhone() override { return m_aMap[homephone]; }
145 virtual OUString GetTelephoneNumber() override { return m_aMap[telephonenumber]; }
146 virtual OUString GetFaxNumber() override { return m_aMap[facsimiletelephonenumber]; }
147 virtual OUString GetMail() override { return m_aMap[mail]; }
149 private:
150 typedef HRESULT (__stdcall IADsUser::*getstrfunc)(BSTR*);
151 static OUString Str(IADsUser* pUser, getstrfunc func)
153 sal::systools::BStr sBstr;
154 if (FAILED((pUser->*func)(&sBstr)))
155 return "";
156 return OUString(sBstr);
158 static OUString Str(IADsUser* pUser, const wchar_t* property)
160 sal::systools::BStr sBstrProp{ o3tl::toU(property) };
161 struct AutoVariant : public VARIANT
163 AutoVariant() { VariantInit(this); }
164 ~AutoVariant() { VariantClear(this); }
165 } varArr;
166 if (FAILED(pUser->GetEx(sBstrProp, &varArr)))
167 return "";
168 SAFEARRAY* sa = V_ARRAY(&varArr);
169 LONG nStart, nEnd;
170 if (FAILED(SafeArrayGetLBound(sa, 1, &nStart)) || FAILED(SafeArrayGetUBound(sa, 1, &nEnd)))
171 return "";
172 AutoVariant varItem;
173 for (LONG i = nStart; i <= nEnd; i++)
175 if (FAILED(SafeArrayGetElement(sa, &i, &varItem)))
176 continue;
177 if (varItem.vt == VT_BSTR)
178 return OUString(o3tl::toU(V_BSTR(&varItem)));
179 VariantClear(&varItem);
181 return "";
184 void CacheData()
188 OUString sCachedData = "user=" + m_sUserDN // user DN
189 + "\0" + givenname + "=" + GetGivenName() // 1st name
190 + "\0" + sn + "=" + GetSn() // sn
191 + "\0" + initials + "=" + GetInitials() // initials
192 + "\0" + street + "=" + GetStreet() // street
193 + "\0" + l + "=" + GetCity() // l
194 + "\0" + st + "=" + GetState() // st
195 + "\0" + postalcode + "=" + GetPostalCode() // p.code
196 + "\0" + c + "=" + GetCountry() // c
197 + "\0" + o + "=" + GetOrganization() // o
198 + "\0" + title + "=" + GetTitle() // title
199 + "\0" + homephone + "=" + GetHomePhone() // h.phone
200 + "\0" + telephonenumber + "=" + GetTelephoneNumber() // tel
201 + "\0" + facsimiletelephonenumber + "=" + GetFaxNumber() // fax
202 + "\0" + mail + "=" + GetMail(); // mail
203 const css::uno::Sequence<sal_Int8> seqCachedData(
204 reinterpret_cast<const sal_Int8*>(sCachedData.getStr()),
205 sCachedData.getLength() * sizeof(sal_Unicode));
206 OUStringBuffer sOutBuf;
207 comphelper::Base64::encode(sOutBuf, seqCachedData);
209 std::shared_ptr<comphelper::ConfigurationChanges> batch(
210 comphelper::ConfigurationChanges::create());
211 officecfg::UserProfile::WinUserInfo::Cache::set(sOutBuf.makeStringAndClear(), batch);
212 batch->commit();
214 catch (const css::uno::Exception&)
216 TOOLS_WARN_EXCEPTION("extensions.config",
217 "ADsUserAccess: access to configuration data failed:");
221 void GetCachedData()
223 if (m_sUserDN.isEmpty())
224 throw css::uno::RuntimeException();
226 OUString sCache = officecfg::UserProfile::WinUserInfo::Cache::get();
228 if (sCache.isEmpty())
229 throw css::uno::RuntimeException();
232 css::uno::Sequence<sal_Int8> seqCachedData;
233 comphelper::Base64::decode(seqCachedData, sCache);
234 sCache = OUString(reinterpret_cast<const sal_Unicode*>(seqCachedData.getConstArray()),
235 seqCachedData.getLength() / sizeof(sal_Unicode));
238 OUString sUserDN;
239 std::map<const OUString, OUString> aMap;
240 sal_Int32 nIndex = 0;
243 const OUString sEntry = sCache.getToken(0, '\0', nIndex);
244 sal_Int32 nEqIndex = 0;
245 const OUString sEntryName = sEntry.getToken(0, '=', nEqIndex);
246 OUString sEntryVal;
247 if (nEqIndex >= 0)
248 sEntryVal = sEntry.copy(nEqIndex);
249 if (sEntryName == "user")
250 sUserDN = sEntryVal;
251 else
252 aMap[sEntryName] = sEntryVal;
253 } while (nIndex >= 0);
255 if (sUserDN != m_sUserDN)
256 throw css::uno::RuntimeException();
257 m_aMap = std::move(aMap);
260 OUString m_sUserDN; // used to check if the cached data is for current user
261 std::map<const OUString, OUString> m_aMap;
264 class SysInfoUserAccess : public extensions::config::WinUserInfo::WinUserInfoBe_Impl
266 public:
267 SysInfoUserAccess()
271 ULONG nSize = 0;
272 GetUserNameExW(NameDisplay, nullptr, &nSize);
273 if (GetLastError() != ERROR_MORE_DATA)
274 throw css::uno::RuntimeException();
275 auto pNameBuf(std::make_unique<wchar_t[]>(nSize));
276 if (!GetUserNameExW(NameDisplay, pNameBuf.get(), &nSize))
277 throw css::uno::RuntimeException();
278 m_sName = o3tl::toU(pNameBuf.get());
280 catch (css::uno::RuntimeException&)
282 // GetUserNameEx may fail in some cases (e.g., for built-in AD domain
283 // administrator account on non-DC systems), where GetUserName will
284 // still give a name.
285 DWORD nSize = UNLEN + 1;
286 auto pNameBuf(std::make_unique<wchar_t[]>(nSize));
287 if (!GetUserNameW(pNameBuf.get(), &nSize))
288 throw css::uno::RuntimeException();
289 m_sName = o3tl::toU(pNameBuf.get());
293 virtual OUString GetGivenName() override { return m_sName; }
295 private:
296 OUString m_sName;
300 namespace extensions
302 namespace config
304 namespace WinUserInfo
306 WinUserInfoBe::WinUserInfoBe()
307 : WinUserInfoMutexHolder()
308 , BackendBase(mMutex)
312 m_pImpl.reset(new ADsUserAccess());
314 catch (css::uno::RuntimeException&)
316 m_pImpl.reset(new SysInfoUserAccess);
320 WinUserInfoBe::~WinUserInfoBe() {}
322 void WinUserInfoBe::setPropertyValue(OUString const&, css::uno::Any const&)
324 throw css::lang::IllegalArgumentException("setPropertyValue not supported",
325 static_cast<cppu::OWeakObject*>(this), -1);
328 css::uno::Any WinUserInfoBe::getPropertyValue(OUString const& PropertyName)
330 OUString sValue;
331 // Only process the first argument of possibly multiple space- or comma-separated arguments
332 OUString sToken = PropertyName.getToken(0, ' ').getToken(0, ',');
333 if (sToken == givenname)
335 sValue = m_pImpl->GetGivenName();
337 else if (sToken == sn)
339 sValue = m_pImpl->GetSn();
341 else if (sToken == fathersname)
343 sValue = m_pImpl->GetFathersname();
345 else if (sToken == initials)
347 sValue = m_pImpl->GetInitials();
349 else if (sToken == street)
351 sValue = m_pImpl->GetStreet();
353 else if (sToken == l)
355 sValue = m_pImpl->GetCity();
357 else if (sToken == st)
359 sValue = m_pImpl->GetState();
361 else if (sToken == apartment)
363 sValue = m_pImpl->GetApartment();
365 else if (sToken == postalcode)
367 sValue = m_pImpl->GetPostalCode();
369 else if (sToken == c)
371 sValue = m_pImpl->GetCountry();
373 else if (sToken == o)
375 sValue = m_pImpl->GetOrganization();
377 else if (sToken == position)
379 sValue = m_pImpl->GetPosition();
381 else if (sToken == title)
383 sValue = m_pImpl->GetTitle();
385 else if (sToken == homephone)
387 sValue = m_pImpl->GetHomePhone();
389 else if (sToken == telephonenumber)
391 sValue = m_pImpl->GetTelephoneNumber();
393 else if (sToken == facsimiletelephonenumber)
395 sValue = m_pImpl->GetFaxNumber();
397 else if (sToken == mail)
399 sValue = m_pImpl->GetMail();
401 else
402 throw css::beans::UnknownPropertyException(sToken, static_cast<cppu::OWeakObject*>(this));
404 return css::uno::Any(css::beans::Optional<css::uno::Any>(
405 !sValue.isEmpty(), sValue.isEmpty() ? css::uno::Any() : css::uno::Any(sValue)));
408 OUString SAL_CALL WinUserInfoBe::getImplementationName()
410 return "com.sun.star.comp.configuration.backend.WinUserInfoBe";
413 sal_Bool SAL_CALL WinUserInfoBe::supportsService(const OUString& aServiceName)
415 return cppu::supportsService(this, aServiceName);
418 css::uno::Sequence<OUString> SAL_CALL WinUserInfoBe::getSupportedServiceNames()
420 return { "com.sun.star.configuration.backend.WinUserInfoBe" };
426 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
427 extensions_WinUserInfoBe_get_implementation(css::uno::XComponentContext*,
428 css::uno::Sequence<css::uno::Any> const&)
430 return cppu::acquire(new extensions::config::WinUserInfo::WinUserInfoBe());
433 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */