Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/uui/source/iahndl-ssl.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
21
#include <com/sun/star/security/CertificateValidity.hpp>
22
#include <com/sun/star/security/XCertificateExtension.hpp>
23
#include <com/sun/star/security/XSanExtension.hpp>
24
#include <com/sun/star/security/ExtAltNameType.hpp>
25
#include <com/sun/star/task/XInteractionAbort.hpp>
26
#include <com/sun/star/task/XInteractionApprove.hpp>
27
#include <com/sun/star/task/XInteractionRequest.hpp>
28
#include <com/sun/star/ucb/CertificateValidationRequest.hpp>
29
#include <com/sun/star/uno/Reference.hxx>
30
31
#include <comphelper/lok.hxx>
32
#include <comphelper/sequence.hxx>
33
#include <com/sun/star/uno/Sequence.hxx>
34
#include <o3tl/string_view.hxx>
35
#include <svl/numformat.hxx>
36
#include <svl/zforlist.hxx>
37
#include <unotools/resmgr.hxx>
38
#include <vcl/svapp.hxx>
39
#include <vcl/settings.hxx>
40
41
#include <ids.hrc>
42
#include "getcontinuations.hxx"
43
#include "sslwarndlg.hxx"
44
#include "unknownauthdlg.hxx"
45
46
#include "iahndl.hxx"
47
48
#include <memory>
49
50
0
#define OID_SUBJECT_ALTERNATIVE_NAME "2.5.29.17"
51
52
53
using namespace com::sun::star;
54
55
namespace {
56
57
OUString
58
getContentPart( std::u16string_view _rRawString )
59
0
{
60
    // search over some parts to find a string
61
0
    static constexpr OUString aIDs[] = { u"CN="_ustr, u"OU="_ustr, u"O="_ustr, u"E="_ustr };
62
0
    OUString sPart;
63
0
    for (const OUString & sPartId : aIDs )
64
0
    {
65
0
        size_t nContStart = _rRawString.find( sPartId );
66
0
        if ( nContStart != std::u16string_view::npos )
67
0
        {
68
0
            nContStart += sPartId.getLength();
69
0
            size_t nContEnd = _rRawString.find( ',', nContStart );
70
0
            if ( nContEnd != std::u16string_view::npos )
71
0
                sPart = _rRawString.substr( nContStart, nContEnd - nContStart );
72
0
            else
73
0
                sPart = _rRawString.substr( nContStart );
74
0
            break;
75
0
        }
76
0
    }
77
0
    return sPart;
78
0
}
79
80
bool
81
isDomainMatch(
82
              std::u16string_view hostName, const uno::Sequence< OUString >& certHostNames)
83
0
{
84
0
    for ( const OUString& element : certHostNames){
85
0
       if (element.isEmpty())
86
0
           continue;
87
88
0
       if (o3tl::equalsIgnoreAsciiCase( hostName, element ))
89
0
           return true;
90
91
0
       if (element.startsWith("*") &&
92
0
           sal_Int32(hostName.size()) >= element.getLength()  )
93
0
       {
94
0
           OUString cmpStr = element.copy( 1 );
95
0
           if ( o3tl::matchIgnoreAsciiCase(hostName,
96
0
                    cmpStr, hostName.size() - cmpStr.getLength()) )
97
0
               return true;
98
0
       }
99
0
    }
100
101
0
    return false;
102
0
}
103
104
OUString
105
getLocalizedDatTimeStr(
106
    uno::Reference< uno::XComponentContext> const & xContext,
107
    util::DateTime const & rDateTime )
108
0
{
109
0
    OUString aDateTimeStr;
110
0
    Date  aDate( Date::EMPTY );
111
0
    tools::Time  aTime( tools::Time::EMPTY );
112
113
0
    aDate = Date( rDateTime.Day, rDateTime.Month, rDateTime.Year );
114
0
    aTime = tools::Time( rDateTime.Hours, rDateTime.Minutes, rDateTime.Seconds );
115
116
0
    LanguageType eUILang = Application::GetSettings().GetUILanguageTag().getLanguageType();
117
0
    SvNumberFormatter *pNumberFormatter = new SvNumberFormatter( xContext, eUILang );
118
0
    OUString      aTmpStr;
119
0
    const Color* pColor = nullptr;
120
0
    const Date&  rNullDate = pNumberFormatter->GetNullDate();
121
0
    sal_uInt32  nFormat
122
0
        = pNumberFormatter->GetStandardFormat( SvNumFormatType::DATE, eUILang );
123
124
0
    pNumberFormatter->GetOutputString( aDate - rNullDate, nFormat, aTmpStr, &pColor );
125
0
    aDateTimeStr = aTmpStr + " ";
126
127
0
    nFormat = pNumberFormatter->GetStandardFormat( SvNumFormatType::TIME, eUILang );
128
0
    pNumberFormatter->GetOutputString(
129
0
        aTime.GetTimeInDays(), nFormat, aTmpStr, &pColor );
130
0
    aDateTimeStr += aTmpStr;
131
132
0
    return aDateTimeStr;
133
0
}
134
135
bool
136
executeUnknownAuthDialog(
137
    weld::Window * pParent,
138
    uno::Reference< uno::XComponentContext > const & xContext,
139
    const uno::Reference< security::XCertificate >& rXCert)
140
0
{
141
0
    SolarMutexGuard aGuard;
142
143
0
    UnknownAuthDialog aDialog(pParent, rXCert, xContext);
144
145
    // Get correct resource string
146
0
    OUString aMessage;
147
148
0
    std::vector< OUString > aArguments { getContentPart( rXCert->getSubjectName()) };
149
150
0
    std::locale aResLocale(Translate::Create("uui"));
151
152
0
    aMessage = Translate::get(STR_UUI_UNKNOWNAUTH_UNTRUSTED, aResLocale);
153
0
    aMessage = UUIInteractionHelper::replaceMessageWithArguments(
154
0
            aMessage, aArguments );
155
0
    aDialog.setDescriptionText( aMessage );
156
157
0
    return static_cast<bool>(aDialog.run());
158
0
}
159
160
enum class SslWarnType {
161
    DOMAINMISMATCH, EXPIRED, INVALID
162
};
163
164
bool
165
executeSSLWarnDialog(
166
    weld::Window * pParent,
167
    uno::Reference< uno::XComponentContext > const & xContext,
168
    const uno::Reference< security::XCertificate >& rXCert,
169
    SslWarnType failure,
170
    const OUString & hostName )
171
0
{
172
0
    SolarMutexGuard aGuard;
173
174
0
    SSLWarnDialog aDialog(pParent, rXCert, xContext);
175
176
    // Get correct resource string
177
0
    std::vector< OUString > aArguments_1;
178
0
    TranslateId pMessageKey;
179
0
    TranslateId pTitleKey;
180
181
0
    switch( failure )
182
0
    {
183
0
        case SslWarnType::DOMAINMISMATCH:
184
0
            pMessageKey = STR_UUI_SSLWARN_DOMAINMISMATCH;
185
0
            pTitleKey = STR_UUI_SSLWARN_DOMAINMISMATCH_TITLE;
186
0
            aArguments_1.push_back( hostName );
187
0
            aArguments_1.push_back(
188
0
                getContentPart( rXCert->getSubjectName()) );
189
0
            aArguments_1.push_back( hostName );
190
0
            break;
191
0
        case SslWarnType::EXPIRED:
192
0
            pMessageKey = STR_UUI_SSLWARN_EXPIRED;
193
0
            pTitleKey = STR_UUI_SSLWARN_EXPIRED_TITLE;
194
0
            aArguments_1.push_back(
195
0
                getContentPart( rXCert->getSubjectName()) );
196
0
            aArguments_1.push_back(
197
0
                getLocalizedDatTimeStr( xContext,
198
0
                                        rXCert->getNotValidAfter() ) );
199
0
            aArguments_1.push_back(
200
0
                getLocalizedDatTimeStr( xContext,
201
0
                                        rXCert->getNotValidAfter() ) );
202
0
            break;
203
0
        case SslWarnType::INVALID:
204
0
            pMessageKey = STR_UUI_SSLWARN_INVALID;
205
0
            pTitleKey = STR_UUI_SSLWARN_INVALID_TITLE;
206
0
            break;
207
0
        default: assert(false);
208
0
    }
209
210
0
    std::locale aResLocale(Translate::Create("uui"));
211
212
0
    OUString aMessage_1 = Translate::get(pMessageKey, aResLocale);
213
0
    aMessage_1 = UUIInteractionHelper::replaceMessageWithArguments(
214
0
            aMessage_1, aArguments_1 );
215
0
    aDialog.setDescription1Text( aMessage_1 );
216
217
0
    OUString aTitle = Translate::get(pTitleKey, aResLocale);
218
0
    aDialog.set_title(aTitle);
219
220
0
    return static_cast<bool>(aDialog.run());
221
0
}
222
223
void
224
handleCertificateValidationRequest_(
225
    weld::Window * pParent,
226
    uno::Reference< uno::XComponentContext > const & xContext,
227
    ucb::CertificateValidationRequest const & rRequest,
228
    uno::Sequence< uno::Reference< task::XInteractionContinuation > > const &
229
        rContinuations)
230
0
{
231
0
    uno::Reference< task::XInteractionApprove > xApprove;
232
0
    uno::Reference< task::XInteractionAbort > xAbort;
233
0
    getContinuations(rContinuations, &xApprove, &xAbort);
234
235
0
    if ( comphelper::LibreOfficeKit::isActive() && xApprove.is() )
236
0
    {
237
0
        xApprove->select();
238
0
        return;
239
0
    }
240
241
0
    sal_Int32 failures = rRequest.CertificateValidity;
242
0
    bool trustCert = true;
243
244
0
    if ( ((failures & security::CertificateValidity::UNTRUSTED)
245
0
             == security::CertificateValidity::UNTRUSTED ) ||
246
0
         ((failures & security::CertificateValidity::ISSUER_UNTRUSTED)
247
0
             == security::CertificateValidity::ISSUER_UNTRUSTED) ||
248
0
         ((failures & security::CertificateValidity::ROOT_UNTRUSTED)
249
0
             == security::CertificateValidity::ROOT_UNTRUSTED) )
250
0
    {
251
0
        trustCert = executeUnknownAuthDialog( pParent,
252
0
                                              xContext,
253
0
                                              rRequest.Certificate );
254
0
    }
255
256
0
    const uno::Sequence< uno::Reference< security::XCertificateExtension > > extensions = rRequest.Certificate->getExtensions();
257
0
    uno::Reference< security::XSanExtension > sanExtension;
258
0
    auto pExtension = std::find_if(extensions.begin(), extensions.end(),
259
0
        [](const uno::Reference< security::XCertificateExtension >& element) {
260
0
            std::string_view aId ( reinterpret_cast<const char *>(element->getExtensionId().getConstArray()), element->getExtensionId().getLength());
261
0
            return aId == OID_SUBJECT_ALTERNATIVE_NAME;
262
0
        });
263
0
    if (pExtension != extensions.end())
264
0
    {
265
0
       sanExtension = uno::Reference<security::XSanExtension>(*pExtension, uno::UNO_QUERY);
266
0
    }
267
268
0
    std::vector<security::CertAltNameEntry> altNames;
269
0
    if (sanExtension.is())
270
0
    {
271
0
        altNames = comphelper::sequenceToContainer<std::vector<security::CertAltNameEntry>>(sanExtension->getAlternativeNames());
272
0
    }
273
274
0
    uno::Sequence< OUString > certHostNames(altNames.size() + 1);
275
0
    auto pcertHostNames = certHostNames.getArray();
276
0
    pcertHostNames[0] = getContentPart(rRequest.Certificate->getSubjectName());
277
278
0
    for (size_t n = 0; n < altNames.size(); ++n)
279
0
    {
280
0
        if (altNames[n].Type ==  security::ExtAltNameType_DNS_NAME)
281
0
        {
282
0
           altNames[n].Value >>= pcertHostNames[n+1];
283
0
        }
284
0
    }
285
286
0
    if ( (!isDomainMatch(
287
0
              rRequest.HostName,
288
0
              certHostNames )) &&
289
0
          trustCert )
290
0
    {
291
0
        trustCert = executeSSLWarnDialog( pParent,
292
0
                                          xContext,
293
0
                                          rRequest.Certificate,
294
0
                                          SslWarnType::DOMAINMISMATCH,
295
0
                                          rRequest.HostName );
296
0
    }
297
298
0
    else if ( (((failures & security::CertificateValidity::TIME_INVALID)
299
0
                == security::CertificateValidity::TIME_INVALID) ||
300
0
               ((failures & security::CertificateValidity::NOT_TIME_NESTED)
301
0
                == security::CertificateValidity::NOT_TIME_NESTED)) &&
302
0
              trustCert )
303
0
    {
304
0
        trustCert = executeSSLWarnDialog( pParent,
305
0
                                          xContext,
306
0
                                          rRequest.Certificate,
307
0
                                          SslWarnType::EXPIRED,
308
0
                                          rRequest.HostName );
309
0
    }
310
311
0
    else if ( (((failures & security::CertificateValidity::REVOKED)
312
0
                == security::CertificateValidity::REVOKED) ||
313
0
               ((failures & security::CertificateValidity::SIGNATURE_INVALID)
314
0
                == security::CertificateValidity::SIGNATURE_INVALID) ||
315
0
               ((failures & security::CertificateValidity::EXTENSION_INVALID)
316
0
                == security::CertificateValidity::EXTENSION_INVALID) ||
317
0
               ((failures & security::CertificateValidity::INVALID)
318
0
                == security::CertificateValidity::INVALID)) &&
319
0
              trustCert )
320
0
    {
321
0
        trustCert = executeSSLWarnDialog( pParent,
322
0
                                          xContext,
323
0
                                          rRequest.Certificate,
324
0
                                          SslWarnType::INVALID,
325
0
                                          rRequest.HostName );
326
0
    }
327
328
0
    if ( trustCert )
329
0
    {
330
0
        if (xApprove.is())
331
0
            xApprove->select();
332
0
    }
333
0
    else
334
0
    {
335
0
        if (xAbort.is())
336
0
            xAbort->select();
337
0
    }
338
0
}
339
340
} // namespace
341
342
bool
343
UUIInteractionHelper::handleCertificateValidationRequest(
344
    uno::Reference< task::XInteractionRequest > const & rRequest)
345
0
{
346
0
    uno::Any aAnyRequest(rRequest->getRequest());
347
348
0
    ucb::CertificateValidationRequest aCertificateValidationRequest;
349
0
    if (aAnyRequest >>= aCertificateValidationRequest)
350
0
    {
351
0
        uno::Reference<awt::XWindow> xParent = getParentXWindow();
352
0
        handleCertificateValidationRequest_(Application::GetFrameWeld(xParent),
353
0
                                            m_xContext,
354
0
                                            aCertificateValidationRequest,
355
0
                                            rRequest->getContinuations());
356
0
        return true;
357
0
    }
358
359
0
    return false;
360
0
}
361
362
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */