Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/comphelper/source/misc/xmlsechelper.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
#include <comphelper/xmlsechelper.hxx>
21
22
#include <rtl/ustrbuf.hxx>
23
#include <osl/diagnose.h>
24
#include <o3tl/string_view.hxx>
25
#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
26
27
#include <utility>
28
#include <vector>
29
30
namespace comphelper::xmlsec
31
{
32
    OUString GetCertificateKind( const css::security::CertificateKind &rKind )
33
0
    {
34
0
        switch (rKind)
35
0
        {
36
0
            case css::security::CertificateKind_X509:
37
0
                return u"X.509"_ustr;
38
0
            case css::security::CertificateKind_OPENPGP:
39
0
                return u"OpenPGP"_ustr;
40
0
            default:
41
0
                return OUString();
42
0
        }
43
0
    }
44
45
    /*
46
        Creates two strings based on the distinguished name which are displayed in the
47
        certificate details view. The first string contains only the values of the attribute
48
        and values pairs, which are separated by commas. All escape characters ('"') are
49
        removed.
50
        The second string is for the details view at the bottom. It shows the attribute/value
51
        pairs on different lines. All escape characters ('"') are removed.
52
    */
53
    std::pair< OUString, OUString> GetDNForCertDetailsView( std::u16string_view rRawString)
54
0
    {
55
0
        std::vector< std::pair< OUString, OUString > > vecAttrValueOfDN = parseDN(rRawString);
56
0
        OUStringBuffer s1, s2;
57
0
        for (auto i = vecAttrValueOfDN.cbegin(); i < vecAttrValueOfDN.cend(); ++i)
58
0
        {
59
0
            if (i != vecAttrValueOfDN.cbegin())
60
0
            {
61
0
                s1.append(',');
62
0
                s2.append('\n');
63
0
            }
64
0
            s1.append(i->second);
65
0
            s2.append(i->first + " = " + i->second);
66
0
        }
67
0
        return std::make_pair(s1.makeStringAndClear(), s2.makeStringAndClear());
68
0
    }
69
70
/*
71
    Whenever the attribute value contains special characters, such as '"' or ',' (without '')
72
    then the value will be enclosed in double quotes by the respective Windows or NSS function
73
    which we use to retrieve, for example, the subject name. If double quotes appear in the value then
74
    they are escaped with a double quote. This function removes the escape characters.
75
*/
76
#ifdef _WIN32
77
std::vector< std::pair< OUString, OUString> > parseDN(std::u16string_view rRawString)
78
{
79
        std::vector< std::pair<OUString, OUString> > retVal;
80
        bool bInEscape = false;
81
        bool bInValue = false;
82
        bool bInType = true;
83
        sal_Int32 nTypeNameStart = 0;
84
        std::u16string_view sType;
85
        OUStringBuffer sbufValue;
86
        size_t length = rRawString.size();
87
88
        for (size_t i = 0; i < length; i++)
89
        {
90
            sal_Unicode c = rRawString[i];
91
92
            if (c == '=')
93
            {
94
                if (! bInValue)
95
                {
96
                    sType = rRawString.substr(nTypeNameStart, i - nTypeNameStart);
97
                    sType = o3tl::trim(sType);
98
                    bInType = false;
99
                }
100
                else
101
                {
102
                    sbufValue.append(c);
103
                }
104
            }
105
            else if (c == '"')
106
            {
107
                if (!bInEscape)
108
                {
109
                    //If this is the quote is the first of the couple which enclose the
110
                    //whole value, because the value contains special characters
111
                    //then we just drop it. That is, this character must be followed by
112
                    //a character which is not '"'.
113
                    if ( i + 1 < length && rRawString[i+1] == '"')
114
                        bInEscape = true;
115
                    else
116
                        bInValue = !bInValue; //value is enclosed in " "
117
                }
118
                else
119
                {
120
                    //This quote is escaped by a preceding quote and therefore is
121
                    //part of the value
122
                    sbufValue.append(c);
123
                    bInEscape = false;
124
                }
125
            }
126
            else if (c == ',' || c == '+')
127
            {
128
                //The comma separate the attribute value pairs.
129
                //If the comma is not part of a value (the value would then be enclosed in '"'),
130
                //then we have reached the end of the value
131
                if (!bInValue)
132
                {
133
                    OSL_ASSERT(!sType.empty());
134
                    retVal.emplace_back(OUString(sType), sbufValue.makeStringAndClear());
135
                    sType = {};
136
                    //The next char is the start of the new type
137
                    nTypeNameStart = i + 1;
138
                    bInType = true;
139
                }
140
                else
141
                {
142
                    //The whole string is enclosed because it contains special characters.
143
                    //The enclosing '"' are not part of certificate but will be added by
144
                    //the function (Windows or NSS) which retrieves DN
145
                    sbufValue.append(c);
146
                }
147
            }
148
            else
149
            {
150
                if (!bInType)
151
                    sbufValue.append(c);
152
            }
153
        }
154
        if (sbufValue.getLength())
155
        {
156
            OSL_ASSERT(!sType.empty());
157
            retVal.emplace_back(OUString(sType), sbufValue.makeStringAndClear());
158
        }
159
        return retVal;
160
    }
161
#else
162
std::vector< std::pair< OUString, OUString> > parseDN(std::u16string_view rRawString)
163
0
    {
164
0
        std::vector< std::pair<OUString, OUString> > retVal;
165
        //bInEscape == true means that the preceding character is an escape character
166
0
        bool bInEscape = false;
167
0
        bool bInValue = false;
168
0
        bool bInType = true;
169
0
        sal_Int32 nTypeNameStart = 0;
170
0
        std::u16string_view sType;
171
0
        OUStringBuffer sbufValue;
172
0
        size_t length = rRawString.size();
173
174
0
        for (size_t i = 0; i < length; i++)
175
0
        {
176
0
            sal_Unicode c = rRawString[i];
177
178
0
            if (c == '=')
179
0
            {
180
0
                if (! bInValue)
181
0
                {
182
0
                    sType = rRawString.substr(nTypeNameStart, i - nTypeNameStart);
183
0
                    sType = o3tl::trim(sType);
184
0
                    bInType = false;
185
0
                }
186
0
                else
187
0
                {
188
0
                    sbufValue.append(c);
189
0
                }
190
0
            }
191
0
            else if (c == '\\')
192
0
            {
193
0
                if (!bInEscape)
194
0
                {
195
0
                    bInEscape = true;
196
0
                }
197
0
                else
198
0
                { // bInEscape is true
199
0
                    sbufValue.append(c);
200
0
                    bInEscape = false;
201
0
                }
202
0
            }
203
0
            else if (c == '"')
204
0
            {
205
                //an unescaped '"' is either at the beginning or end of the value
206
0
                if (!bInEscape)
207
0
                {
208
0
                    if ( !bInValue)
209
0
                        bInValue = true;
210
0
                    else if (bInValue)
211
0
                        bInValue = false;
212
0
                }
213
0
                else
214
0
                {
215
                    //This quote is escaped by a preceding quote and therefore is
216
                    //part of the value
217
0
                    sbufValue.append(c);
218
0
                    bInEscape = false;
219
0
                }
220
0
            }
221
0
            else if (c == ',' || c == '+')
222
0
            {
223
                //The comma separate the attribute value pairs.
224
                //If the comma is not part of a value (the value would then be enclosed in '"'),
225
                //then we have reached the end of the value
226
0
                if (!bInValue)
227
0
                {
228
0
                    OSL_ASSERT(!sType.empty());
229
0
                    retVal.emplace_back(sType, sbufValue.makeStringAndClear());
230
0
                    sType = {};
231
                    //The next char is the start of the new type
232
0
                    nTypeNameStart = i + 1;
233
0
                    bInType = true;
234
0
                }
235
0
                else
236
0
                {
237
                    //The whole string is enclosed because it contains special characters.
238
                    //The enclosing '"' are not part of certificate but will be added by
239
                    //the function (Windows or NSS) which retrieves DN
240
0
                    sbufValue.append(c);
241
0
                }
242
0
            }
243
0
            else
244
0
            {
245
0
                if (!bInType)
246
0
                {
247
0
                    sbufValue.append(c);
248
0
                    bInEscape = false;
249
0
                }
250
0
            }
251
0
        }
252
0
        if (!sbufValue.isEmpty())
253
0
        {
254
0
            OSL_ASSERT(!sType.empty());
255
0
            retVal.emplace_back(sType, sbufValue.makeStringAndClear());
256
0
        }
257
0
        return retVal;
258
0
    }
259
260
#endif
261
262
    OUString GetContentPart( const OUString& _rRawString, const css::security::CertificateKind &rKind )
263
0
    {
264
0
        static constexpr OUString aIDs[] { u"CN"_ustr, u"OU"_ustr, u"O"_ustr, u"E"_ustr };
265
266
        // tdf#131733 Don't process OpenPGP certs, only X509
267
0
        if (rKind == css::security::CertificateKind_OPENPGP )
268
0
            return _rRawString;
269
270
0
        OUString retVal;
271
0
        std::vector< std::pair< OUString, OUString > > vecAttrValueOfDN = parseDN(_rRawString);
272
0
        for ( const auto & sPartId : aIDs )
273
0
        {
274
0
            auto idn = std::find_if(vecAttrValueOfDN.cbegin(), vecAttrValueOfDN.cend(),
275
0
                [&sPartId](const std::pair< OUString, OUString >& dn) { return dn.first == sPartId; });
276
0
            if (idn != vecAttrValueOfDN.cend())
277
0
                retVal = idn->second;
278
0
            if (!retVal.isEmpty())
279
0
                break;
280
0
        }
281
0
        return retVal.isEmpty() ? _rRawString : retVal;
282
0
    }
283
284
    OUString GetHexString( const css::uno::Sequence< sal_Int8 >& _rSeq, const char* _pSep, sal_uInt16 _nLineBreak )
285
0
    {
286
0
        OUStringBuffer          aStr;
287
0
        const char              pHexDigs[ 17 ] = "0123456789ABCDEF";
288
0
        char                    pBuffer[ 3 ] = "  ";
289
0
        sal_uInt16                  nBreakStart = _nLineBreak? _nLineBreak : 1;
290
0
        sal_uInt16                  nBreak = nBreakStart;
291
0
        for (sal_Int8 n : _rSeq)
292
0
        {
293
0
            sal_uInt8 nNum = static_cast<sal_uInt8>(n);
294
            // exchange the buffer[0] and buffer[1], which make it consistent with Mozilla and Windows
295
0
            pBuffer[ 1 ] = pHexDigs[ nNum & 0x0F ];
296
0
            nNum >>= 4;
297
0
            pBuffer[ 0 ] = pHexDigs[ nNum ];
298
0
            aStr.appendAscii( pBuffer );
299
300
0
            --nBreak;
301
0
            if( nBreak )
302
0
                aStr.appendAscii( _pSep );
303
0
            else
304
0
            {
305
0
                nBreak = nBreakStart;
306
0
                aStr.append( '\n' );
307
0
            }
308
0
        }
309
310
0
        return aStr.makeStringAndClear();
311
0
    }
312
313
    css::uno::Reference<css::security::XCertificate> FindCertInContext(
314
        const css::uno::Reference<css::xml::crypto::XXMLSecurityContext>& xSecurityContext,
315
        const OUString& rSHA1Thumbprint)
316
0
    {
317
0
        if (!xSecurityContext.is())
318
0
            return {};
319
320
0
        css::uno::Reference<css::xml::crypto::XSecurityEnvironment> xSE
321
0
            = xSecurityContext->getSecurityEnvironment();
322
0
        css::uno::Sequence<css::uno::Reference<css::security::XCertificate>> xCertificates
323
0
            = xSE->getPersonalCertificates();
324
325
0
        auto aCertsIter = asNonConstRange(xCertificates);
326
327
0
        auto pxCert
328
0
                = std::find_if(aCertsIter.begin(), aCertsIter.end(),
329
0
                               [&rSHA1Thumbprint](auto& xCert)
330
0
                               {
331
0
                                   return rSHA1Thumbprint
332
0
                                           == GetHexString(xCert->getSHA1Thumbprint(), "");
333
0
                               });
334
335
0
        if (pxCert == aCertsIter.end())
336
0
            return {};
337
338
0
        return *pxCert;
339
0
    }
340
}
341
342
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */