Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsauthconfig.cpp
3 : : ---------------------
4 : : begin : October 5, 2014
5 : : copyright : (C) 2014 by Boundless Spatial, Inc. USA
6 : : author : Larry Shaffer
7 : : email : lshaffer at boundlessgeo dot com
8 : : ***************************************************************************
9 : : * *
10 : : * This program is free software; you can redistribute it and/or modify *
11 : : * it under the terms of the GNU General Public License as published by *
12 : : * the Free Software Foundation; either version 2 of the License, or *
13 : : * (at your option) any later version. *
14 : : * *
15 : : ***************************************************************************/
16 : :
17 : : #include "qgsauthconfig.h"
18 : :
19 : : #include <QtCrypto>
20 : :
21 : : #include <QFile>
22 : : #include <QObject>
23 : : #include <QCryptographicHash>
24 : : #include <QUrl>
25 : :
26 : : #include "qgsauthcertutils.h"
27 : :
28 : :
29 : : //////////////////////////////////////////////
30 : : // QgsAuthMethodConfig
31 : : //////////////////////////////////////////////
32 : :
33 : 10 : const QString QgsAuthMethodConfig::CONFIG_SEP = QStringLiteral( "|||" );
34 : 10 : const QString QgsAuthMethodConfig::CONFIG_KEY_SEP = QStringLiteral( ":::" );
35 : 10 : const QString QgsAuthMethodConfig::CONFIG_LIST_SEP = QStringLiteral( "```" );
36 : :
37 : : const int QgsAuthMethodConfig::CONFIG_VERSION = 1;
38 : :
39 : : // get uniqueConfigId only on save
40 : 0 : QgsAuthMethodConfig::QgsAuthMethodConfig( const QString &method, int version )
41 : 0 : : mId( QString() )
42 : 0 : , mName( QString() )
43 : 0 : , mUri( QString() )
44 : 0 : , mMethod( method )
45 : 0 : , mVersion( version )
46 : 0 : , mConfigMap( QgsStringMap() )
47 : : {
48 : 0 : }
49 : :
50 : 0 : bool QgsAuthMethodConfig::operator==( const QgsAuthMethodConfig &other ) const
51 : : {
52 : 0 : return ( other.id() == id()
53 : 0 : && other.name() == name()
54 : 0 : && other.uri() == uri()
55 : 0 : && other.method() == method()
56 : 0 : && other.version() == version()
57 : 0 : && other.configMap() == configMap() );
58 : 0 : }
59 : :
60 : 0 : bool QgsAuthMethodConfig::operator!=( const QgsAuthMethodConfig &other ) const
61 : : {
62 : 0 : return !( *this == other );
63 : : }
64 : :
65 : 0 : bool QgsAuthMethodConfig::isValid( bool validateid ) const
66 : : {
67 : 0 : bool idvalid = validateid ? !mId.isEmpty() : true;
68 : :
69 : 0 : return (
70 : 0 : idvalid
71 : 0 : && !mName.isEmpty()
72 : 0 : && !mMethod.isEmpty()
73 : : );
74 : : }
75 : :
76 : 0 : const QString QgsAuthMethodConfig::configString() const
77 : : {
78 : 0 : QStringList confstrs;
79 : 0 : QgsStringMap::const_iterator i = mConfigMap.constBegin();
80 : 0 : while ( i != mConfigMap.constEnd() )
81 : : {
82 : 0 : confstrs << i.key() + CONFIG_KEY_SEP + i.value();
83 : 0 : ++i;
84 : : }
85 : 0 : return confstrs.join( CONFIG_SEP );
86 : 0 : }
87 : :
88 : 0 : void QgsAuthMethodConfig::loadConfigString( const QString &configstr )
89 : : {
90 : 0 : clearConfigMap();
91 : 0 : if ( configstr.isEmpty() )
92 : : {
93 : 0 : return;
94 : : }
95 : :
96 : 0 : const QStringList confs( configstr.split( CONFIG_SEP ) );
97 : :
98 : 0 : for ( const auto &conf : confs )
99 : : {
100 : 0 : if ( conf.contains( CONFIG_KEY_SEP ) )
101 : : {
102 : 0 : QStringList keyval( conf.split( CONFIG_KEY_SEP ) );
103 : 0 : setConfig( keyval.at( 0 ), keyval.at( 1 ) );
104 : 0 : }
105 : : }
106 : :
107 : 0 : if ( configMap().empty() )
108 : : {
109 : 0 : setConfig( QStringLiteral( "oldconfigstyle" ), configstr );
110 : 0 : }
111 : 0 : }
112 : :
113 : 0 : void QgsAuthMethodConfig::setConfig( const QString &key, const QString &value )
114 : : {
115 : 0 : mConfigMap.insert( key, value );
116 : 0 : }
117 : :
118 : 0 : void QgsAuthMethodConfig::setConfigList( const QString &key, const QStringList &value )
119 : : {
120 : 0 : setConfig( key, value.join( CONFIG_LIST_SEP ) );
121 : 0 : }
122 : :
123 : 0 : int QgsAuthMethodConfig::removeConfig( const QString &key )
124 : : {
125 : 0 : return mConfigMap.remove( key );
126 : : }
127 : :
128 : 0 : QString QgsAuthMethodConfig::config( const QString &key, const QString &defaultvalue ) const
129 : : {
130 : 0 : return mConfigMap.value( key, defaultvalue );
131 : : }
132 : :
133 : 0 : QStringList QgsAuthMethodConfig::configList( const QString &key ) const
134 : : {
135 : 0 : return config( key ).split( CONFIG_LIST_SEP );
136 : 0 : }
137 : :
138 : 0 : bool QgsAuthMethodConfig::hasConfig( const QString &key ) const
139 : : {
140 : 0 : return mConfigMap.contains( key );
141 : : }
142 : :
143 : 0 : bool QgsAuthMethodConfig::uriToResource( const QString &accessurl, QString *resource, bool withpath )
144 : : {
145 : 0 : QString res = QString();
146 : 0 : if ( !accessurl.isEmpty() )
147 : : {
148 : 0 : QUrl url( accessurl );
149 : 0 : if ( url.isValid() )
150 : : {
151 : 0 : res = QStringLiteral( "%1://%2:%3%4" ).arg( url.scheme(), url.host() )
152 : 0 : .arg( url.port() ).arg( withpath ? url.path() : QString() );
153 : 0 : }
154 : 0 : }
155 : 0 : *resource = res;
156 : 0 : return ( !res.isEmpty() );
157 : 0 : }
158 : :
159 : :
160 : : #ifndef QT_NO_SSL
161 : :
162 : : //////////////////////////////////////////////////////
163 : : // QgsPkiBundle
164 : : //////////////////////////////////////////////////////
165 : :
166 : 0 : QgsPkiBundle::QgsPkiBundle( const QSslCertificate &clientCert,
167 : : const QSslKey &clientKey,
168 : : const QList<QSslCertificate> &caChain )
169 : 0 : : mCert( QSslCertificate() )
170 : 0 : , mCertKey( QSslKey() )
171 : 0 : , mCaChain( caChain )
172 : : {
173 : 0 : setClientCert( clientCert );
174 : 0 : setClientKey( clientKey );
175 : 0 : }
176 : :
177 : 0 : const QgsPkiBundle QgsPkiBundle::fromPemPaths( const QString &certPath,
178 : : const QString &keyPath,
179 : : const QString &keyPass,
180 : : const QList<QSslCertificate> &caChain )
181 : : {
182 : 0 : QgsPkiBundle pkibundle;
183 : 0 : if ( !certPath.isEmpty() && !keyPath.isEmpty()
184 : 0 : && ( certPath.endsWith( QLatin1String( ".pem" ), Qt::CaseInsensitive )
185 : 0 : || certPath.endsWith( QLatin1String( ".der" ), Qt::CaseInsensitive ) )
186 : 0 : && QFile::exists( certPath ) && QFile::exists( keyPath )
187 : : )
188 : : {
189 : : // client cert
190 : 0 : bool pem = certPath.endsWith( QLatin1String( ".pem" ), Qt::CaseInsensitive );
191 : 0 : QSslCertificate clientcert( QgsAuthCertUtils::fileData( certPath ), pem ? QSsl::Pem : QSsl::Der );
192 : 0 : pkibundle.setClientCert( clientcert );
193 : :
194 : 0 : QSslKey clientkey;
195 : 0 : clientkey = QgsAuthCertUtils::keyFromFile( keyPath, keyPass );
196 : 0 : pkibundle.setClientKey( clientkey );
197 : 0 : if ( !caChain.isEmpty() )
198 : : {
199 : 0 : pkibundle.setCaChain( caChain );
200 : 0 : }
201 : 0 : }
202 : 0 : return pkibundle;
203 : 0 : }
204 : :
205 : 0 : const QgsPkiBundle QgsPkiBundle::fromPkcs12Paths( const QString &bundlepath,
206 : : const QString &bundlepass )
207 : : {
208 : 0 : QgsPkiBundle pkibundle;
209 : 0 : if ( QCA::isSupported( "pkcs12" )
210 : 0 : && !bundlepath.isEmpty()
211 : 0 : && ( bundlepath.endsWith( QLatin1String( ".p12" ), Qt::CaseInsensitive )
212 : 0 : || bundlepath.endsWith( QLatin1String( ".pfx" ), Qt::CaseInsensitive ) )
213 : 0 : && QFile::exists( bundlepath ) )
214 : : {
215 : 0 : QCA::SecureArray passarray;
216 : 0 : if ( !bundlepass.isNull() )
217 : 0 : passarray = QCA::SecureArray( bundlepass.toUtf8() );
218 : : QCA::ConvertResult res;
219 : 0 : QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( bundlepath, passarray, &res, QStringLiteral( "qca-ossl" ) ) );
220 : 0 : if ( res == QCA::ConvertGood && !bundle.isNull() )
221 : : {
222 : 0 : const QCA::CertificateChain cert_chain( bundle.certificateChain() );
223 : 0 : QSslCertificate cert( cert_chain.primary().toPEM().toLatin1() );
224 : 0 : if ( !cert.isNull() )
225 : : {
226 : 0 : pkibundle.setClientCert( cert );
227 : 0 : }
228 : 0 : QSslKey cert_key( bundle.privateKey().toPEM().toLatin1(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, QByteArray() );
229 : 0 : if ( !cert_key.isNull() )
230 : : {
231 : 0 : pkibundle.setClientKey( cert_key );
232 : 0 : }
233 : :
234 : 0 : if ( cert_chain.size() > 1 )
235 : : {
236 : 0 : QList<QSslCertificate> ca_chain;
237 : 0 : for ( const auto &ca_cert : cert_chain )
238 : : {
239 : 0 : if ( ca_cert != cert_chain.primary() )
240 : : {
241 : 0 : ca_chain << QSslCertificate( ca_cert.toPEM().toLatin1() );
242 : 0 : }
243 : : }
244 : 0 : pkibundle.setCaChain( ca_chain );
245 : 0 : }
246 : :
247 : 0 : }
248 : 0 : }
249 : 0 : return pkibundle;
250 : 0 : }
251 : :
252 : 0 : bool QgsPkiBundle::isNull() const
253 : : {
254 : 0 : return ( mCert.isNull() || mCertKey.isNull() );
255 : : }
256 : :
257 : 0 : bool QgsPkiBundle::isValid() const
258 : : {
259 : 0 : return ( !isNull() && QgsAuthCertUtils::certIsViable( mCert ) );
260 : : }
261 : :
262 : 0 : const QString QgsPkiBundle::certId() const
263 : : {
264 : 0 : if ( mCert.isNull() )
265 : : {
266 : 0 : return QString();
267 : : }
268 : 0 : return QString( mCert.digest( QCryptographicHash::Sha1 ).toHex() );
269 : 0 : }
270 : :
271 : 0 : void QgsPkiBundle::setClientCert( const QSslCertificate &cert )
272 : : {
273 : 0 : mCert.clear();
274 : 0 : if ( !cert.isNull() )
275 : : {
276 : 0 : mCert = cert;
277 : 0 : }
278 : 0 : }
279 : :
280 : 0 : void QgsPkiBundle::setClientKey( const QSslKey &certkey )
281 : : {
282 : 0 : mCertKey.clear();
283 : 0 : if ( !certkey.isNull() && certkey.type() == QSsl::PrivateKey )
284 : : {
285 : 0 : mCertKey = certkey;
286 : 0 : }
287 : 0 : }
288 : :
289 : :
290 : : //////////////////////////////////////////////////////
291 : : // QgsPkiConfigBundle
292 : : //////////////////////////////////////////////////////
293 : :
294 : 0 : QgsPkiConfigBundle::QgsPkiConfigBundle( const QgsAuthMethodConfig &config,
295 : : const QSslCertificate &cert,
296 : : const QSslKey &certkey,
297 : : const QList<QSslCertificate> &cachain )
298 : 0 : : mConfig( config )
299 : 0 : , mCert( cert )
300 : 0 : , mCertKey( certkey )
301 : 0 : , mCaChain( cachain )
302 : : {
303 : 0 : }
304 : :
305 : 0 : bool QgsPkiConfigBundle::isValid()
306 : : {
307 : 0 : return ( !mCert.isNull() && !mCertKey.isNull() );
308 : : }
309 : :
310 : :
311 : : //////////////////////////////////////////////
312 : : // QgsAuthConfigSslServer
313 : : //////////////////////////////////////////////
314 : :
315 : 10 : const QString QgsAuthConfigSslServer::CONF_SEP = QStringLiteral( "|||" );
316 : :
317 : 0 : QgsAuthConfigSslServer::QgsAuthConfigSslServer()
318 : 0 : : mSslHostPort( QString() )
319 : 0 : , mSslCert( QSslCertificate() )
320 : 0 : , mSslIgnoredErrors( QList<QSslError::SslError>() )
321 : : {
322 : : // TODO: figure out if Qt 5 has changed yet again, e.g. TLS-only
323 : 0 : mQtVersion = 480;
324 : : // Qt 4.8 defaults to SecureProtocols, i.e. TlsV1SslV3
325 : : // http://qt-project.org/doc/qt-4.8/qssl.html#SslProtocol-enum
326 : 0 : mSslProtocol = QSsl::SecureProtocols;
327 : 0 : }
328 : :
329 : 0 : const QList<QSslError> QgsAuthConfigSslServer::sslIgnoredErrors() const
330 : : {
331 : 0 : QList<QSslError> errors;
332 : 0 : const QList<QSslError::SslError> ignoredErrors = sslIgnoredErrorEnums();
333 : 0 : for ( QSslError::SslError errenum : ignoredErrors )
334 : : {
335 : 0 : errors << QSslError( errenum );
336 : : }
337 : 0 : return errors;
338 : 0 : }
339 : :
340 : 0 : const QString QgsAuthConfigSslServer::configString() const
341 : : {
342 : 0 : QStringList configlist;
343 : 0 : configlist << QString::number( mVersion ) << QString::number( mQtVersion );
344 : :
345 : 0 : configlist << QString::number( static_cast< int >( mSslProtocol ) );
346 : :
347 : 0 : QStringList errs;
348 : 0 : for ( auto err : mSslIgnoredErrors )
349 : : {
350 : 0 : errs << QString::number( static_cast< int >( err ) );
351 : : }
352 : 0 : configlist << errs.join( QLatin1String( "~~" ) );
353 : :
354 : 0 : configlist << QStringLiteral( "%1~~%2" ).arg( static_cast< int >( mSslPeerVerifyMode ) ).arg( mSslPeerVerifyDepth );
355 : :
356 : 0 : return configlist.join( CONF_SEP );
357 : 0 : }
358 : :
359 : 0 : void QgsAuthConfigSslServer::loadConfigString( const QString &config )
360 : : {
361 : 0 : if ( config.isEmpty() )
362 : : {
363 : 0 : return;
364 : : }
365 : 0 : QStringList configlist( config.split( CONF_SEP ) );
366 : :
367 : 0 : mVersion = configlist.at( 0 ).toInt();
368 : 0 : mQtVersion = configlist.at( 1 ).toInt();
369 : :
370 : : // TODO: Conversion between 4.7 -> 4.8 protocol enum differences (and reverse?).
371 : : // This is necessary for users upgrading from 4.7 to 4.8
372 : 0 : mSslProtocol = static_cast< QSsl::SslProtocol >( configlist.at( 2 ).toInt() );
373 : :
374 : 0 : mSslIgnoredErrors.clear();
375 : 0 : const QStringList errs( configlist.at( 3 ).split( QStringLiteral( "~~" ) ) );
376 : 0 : for ( const auto &err : errs )
377 : : {
378 : 0 : mSslIgnoredErrors.append( static_cast< QSslError::SslError >( err.toInt() ) );
379 : : }
380 : :
381 : 0 : QStringList peerverify( configlist.at( 4 ).split( QStringLiteral( "~~" ) ) );
382 : 0 : mSslPeerVerifyMode = static_cast< QSslSocket::PeerVerifyMode >( peerverify.at( 0 ).toInt() );
383 : 0 : mSslPeerVerifyDepth = peerverify.at( 1 ).toInt();
384 : 0 : }
385 : :
386 : 0 : bool QgsAuthConfigSslServer::isNull() const
387 : : {
388 : 0 : return mSslCert.isNull() && mSslHostPort.isEmpty();
389 : : }
390 : :
391 : : #endif
|