summaryrefslogtreecommitdiff
path: root/kdecore/network/ktcpsocket.h
blob: c281b3e7b82b7cc5789525d534995554a8fa4061 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
/* This file is part of the KDE libraries
    Copyright (C) 2007 Thiago Macieira <thiago@kde.org>
    Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#ifndef KTCPSOCKET_H
#define KTCPSOCKET_H

#include <QtNetwork/QSslSocket>
#include <QtNetwork/QSslConfiguration>

#include "kdecore_export.h"

/*
  Notes on QCA::TLS compatibility
  In order to check for all validation problems as far as possible we need to use:
  Validity QCA::TLS::peerCertificateValidity()
  TLS::IdentityResult QCA::TLS::peerIdentityResult()
  CertificateChain QCA::TLS::peerCertificateChain().validate() - to find the failing cert!
  TLS::Error QCA::TLS::errorCode() - for more generic (but stil SSL) errors
 */


class KSslKeyPrivate;

class KDECORE_EXPORT KSslKey {
public:
    enum Algorithm {
        Rsa = 0,
        Dsa,
        Dh
    };
    enum KeySecrecy {
        PublicKey,
        PrivateKey
    };

    KSslKey();
    KSslKey(const KSslKey &other);
    KSslKey(const QSslKey &sslKey);
    ~KSslKey();
    KSslKey &operator=(const KSslKey &other);

    Algorithm algorithm() const;
    bool isExportable() const;
    KeySecrecy secrecy() const;
    QByteArray toDer() const;
private:
    KSslKeyPrivate *const d;
};


class KSslCipherPrivate;

class KDECORE_EXPORT KSslCipher {
public:
    KSslCipher();
    KSslCipher(const KSslCipher &other);
    KSslCipher(const QSslCipher &);
    ~KSslCipher();
    KSslCipher &operator=(const KSslCipher &other);

    bool isNull() const;
    QString authenticationMethod() const;
    QString encryptionMethod() const;
    QString keyExchangeMethod() const;
    QString digestMethod() const;
    /* mainly for internal use */
    QString name() const;
    int supportedBits() const;
    int usedBits() const;

    static QList<KSslCipher> supportedCiphers();

private:
    KSslCipherPrivate *const d;
};


class KSslErrorPrivate;
class KTcpSocket;

class KDECORE_EXPORT KSslError
{
public:
    enum Error {
        NoError = 0,
        UnknownError,
        InvalidCertificateAuthorityCertificate,
        InvalidCertificate,
        CertificateSignatureFailed,
        SelfSignedCertificate,
        ExpiredCertificate,
        RevokedCertificate,
        InvalidCertificatePurpose,
        RejectedCertificate,
        UntrustedCertificate,
        NoPeerCertificate,
        HostNameMismatch,
        PathLengthExceeded
    };
    KSslError(KSslError::Error error = NoError, const QSslCertificate &cert = QSslCertificate());
    KSslError(const QSslError &error);  //### explicit yes or no?
    KSslError(const KSslError &other);
    ~KSslError();
    KSslError &operator=(const KSslError &other);

    Error error() const;
    QString errorString() const;
    QSslCertificate certificate() const;
private:
    KSslErrorPrivate *const d;
};


//consider killing more convenience functions with huge signatures
//### do we need setSession() / session() ?

//BIG FAT TODO: do we keep openMode() up to date everywhere it can change?

//other TODO: limit possible error strings?, SSL key stuff

//TODO protocol (or maybe even application?) dependent automatic proxy choice

class KTcpSocketPrivate;
class QHostAddress;
class KUrl;

class KDECORE_EXPORT KTcpSocket: public QIODevice
{
    Q_OBJECT
public:
    enum State {
        UnconnectedState = 0,
        HostLookupState,
        ConnectingState,
        ConnectedState,
        BoundState,
        ListeningState,
        ClosingState
        //hmmm, do we need an SslNegotiatingState?
    };
    enum SslVersion {
        UnknownSslVersion = 0x01,
        SslV2 = 0x02,
        SslV3 = 0x04,
        TlsV1 = 0x08,
        SslV3_1 = 0x08,
        TlsV1SslV3 = 0x10,
        SecureProtocols = 0x20,
        AnySslVersion = SslV2 | SslV3 | TlsV1
    };
    Q_DECLARE_FLAGS(SslVersions, SslVersion)
    enum Error {
        UnknownError = 0,
        ConnectionRefusedError,
        RemoteHostClosedError,
        HostNotFoundError,
        SocketAccessError,
        SocketResourceError,
        SocketTimeoutError,
        NetworkError,
        UnsupportedSocketOperationError,
        SslHandshakeFailedError
    };
/*
The following is based on reading the OpenSSL interface code of both QSslSocket
and QCA::TLS. Barring oversights it should be accurate. The two cases with the
question marks apparently will never be emitted by QSslSocket so there is nothing
to compare.

QSslError::NoError                                  KTcpSocket::NoError
QSslError::UnableToGetIssuerCertificate             QCA::ErrorSignatureFailed
QSslError::UnableToDecryptCertificateSignature      QCA::ErrorSignatureFailed
QSslError::UnableToDecodeIssuerPublicKey            QCA::ErrorInvalidCA
QSslError::CertificateSignatureFailed               QCA::ErrorSignatureFailed
QSslError::CertificateNotYetValid                   QCA::ErrorExpired
QSslError::CertificateExpired                       QCA::ErrorExpired
QSslError::InvalidNotBeforeField                    QCA::ErrorExpired
QSslError::InvalidNotAfterField                     QCA::ErrorExpired
QSslError::SelfSignedCertificate                    QCA::ErrorSelfSigned
QSslError::SelfSignedCertificateInChain             QCA::ErrorSelfSigned
QSslError::UnableToGetLocalIssuerCertificate        QCA::ErrorInvalidCA
QSslError::UnableToVerifyFirstCertificate           QCA::ErrorSignatureFailed
QSslError::CertificateRevoked                       QCA::ErrorRevoked
QSslError::InvalidCaCertificate                     QCA::ErrorInvalidCA
QSslError::PathLengthExceeded                       QCA::ErrorPathLengthExceeded
QSslError::InvalidPurpose                           QCA::ErrorInvalidPurpose
QSslError::CertificateUntrusted                     QCA::ErrorUntrusted
QSslError::CertificateRejected                      QCA::ErrorRejected
QSslError::SubjectIssuerMismatch                    QCA::TLS::InvalidCertificate ?
QSslError::AuthorityIssuerSerialNumberMismatch      QCA::TLS::InvalidCertificate ?
QSslError::NoPeerCertificate                        QCA::TLS::NoCertificate
QSslError::HostNameMismatch                         QCA::TLS::HostMismatch
QSslError::UnspecifiedError                         KTcpSocket::UnknownError
QSslError::NoSslSupport                             Never happens :)
 */
    enum EncryptionMode {
        UnencryptedMode = 0,
        SslClientMode,
        SslServerMode //### not implemented
    };
    enum ProxyPolicy {
        /// Use the proxy that KProtocolManager suggests for the connection parameters given.
        AutoProxy = 0,
        /// Use the proxy set by setProxy(), if any; otherwise use no proxy.
        ManualProxy
    };

    KTcpSocket(QObject *parent = 0);
    ~KTcpSocket();

    //from QIODevice
    //reimplemented virtuals - the ones not reimplemented are OK for us
    virtual bool atEnd() const;
    virtual qint64 bytesAvailable() const;
    virtual qint64 bytesToWrite() const;
    virtual bool canReadLine() const;
    virtual void close();
    virtual bool isSequential() const;
    virtual bool open(QIODevice::OpenMode open);
    virtual bool waitForBytesWritten(int msecs);
    //### Document that this actually tries to read *more* data
    virtual bool waitForReadyRead(int msecs = 30000);
protected:
    virtual qint64 readData (char *data, qint64 maxSize);
    virtual qint64 writeData (const char *data, qint64 maxSize);
signals:
    /// @since 4.8.1
    /// Forwarded from QSslSocket
    void encryptedBytesWritten( qint64 written );
public:
    //from QAbstractSocket
    void abort();
    void connectToHost(const QString &hostName, quint16 port, ProxyPolicy policy = AutoProxy);
    void connectToHost(const QHostAddress &hostAddress, quint16 port, ProxyPolicy policy = AutoProxy);

    /**
     * Take the hostname and port from @p url and connect to them. The information from a
     * full URL enables the most accurate choice of proxy in case of proxy rules that
     * depend on high-level information like protocol or username.
     * @see KProtocolManager::proxyForUrl()
     */
    void connectToHost(const KUrl &url, ProxyPolicy policy = AutoProxy);
    void disconnectFromHost();
    Error error() const; //### QAbstractSocket's model is strange. error() should be related to the
                         //current state and *NOT* just report the last error if there was one.
    QList<KSslError> sslErrors() const; //### the errors returned can only have a subset of all
                                //possible QSslError::SslError enum values depending on backend
    bool flush();
    bool isValid() const;
    QHostAddress localAddress() const;
    QHostAddress peerAddress() const;
    QString peerName() const;
    quint16 peerPort() const;
    void setVerificationPeerName(const QString& hostName);

#ifndef QT_NO_NETWORKPROXY
    /**
     * @see: connectToHost()
     */
    QNetworkProxy proxy() const;
#endif
    qint64 readBufferSize() const; //probably hard to implement correctly

#ifndef QT_NO_NETWORKPROXY
    /**
     * @see: connectToHost()
     */
    void setProxy(const QNetworkProxy &proxy); //people actually seem to need it
#endif
    void setReadBufferSize(qint64 size);
    State state() const;
    bool waitForConnected(int msecs = 30000);
    bool waitForDisconnected(int msecs = 30000);

    //from QSslSocket
    void addCaCertificate(const QSslCertificate &certificate);
//    bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
//                           QRegExp::PatternSyntax syntax = QRegExp::FixedString);
    void addCaCertificates(const QList<QSslCertificate> &certificates);
    QList<QSslCertificate> caCertificates() const;
    QList<KSslCipher> ciphers() const;
    void connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite);
    // bool isEncrypted() const { return encryptionMode() != UnencryptedMode }
    QSslCertificate localCertificate() const;
    QList<QSslCertificate> peerCertificateChain() const;
    KSslKey privateKey() const;
    KSslCipher sessionCipher() const;
    void setCaCertificates(const QList<QSslCertificate> &certificates);
    void setCiphers(const QList<KSslCipher> &ciphers);
    //### void setCiphers(const QString &ciphers); //what about i18n?
    void setLocalCertificate(const QSslCertificate &certificate);
    void setLocalCertificate(const QString &fileName, QSsl::EncodingFormat format = QSsl::Pem);
    void setPrivateKey(const KSslKey &key);
    void setPrivateKey(const QString &fileName, KSslKey::Algorithm algorithm = KSslKey::Rsa,
                       QSsl::EncodingFormat format = QSsl::Pem,
                       const QByteArray &passPhrase = QByteArray());
    void setAdvertisedSslVersion(SslVersion version);
    SslVersion advertisedSslVersion() const;    //always equal to last setSslAdvertisedVersion
    SslVersion negotiatedSslVersion() const;     //negotiated version; downgrades are possible.
    QString negotiatedSslVersionName() const;
    bool waitForEncrypted(int msecs = 30000);

    EncryptionMode encryptionMode() const;

    /**
     * Returns the state of the socket @p option.
     *
     * @see QAbstractSocket::socketOption
     *
     * @since 4.5.0
     */
    QVariant socketOption(QAbstractSocket::SocketOption options) const;

    /**
     * Sets the socket @p option to @p value.
     *
     * @see QAbstractSocket::setSocketOption
     *
     * @since 4.5.0
     */
    void setSocketOption(QAbstractSocket::SocketOption options, const QVariant &value);

    /**
     * Returns the socket's SSL configuration.
     *
     * @since 4.8.4
     */
    QSslConfiguration sslConfiguration() const;

    /**
     * Sets the socket's SSL configuration.
     *
     * @since 4.8.4
     */
    void setSslConfiguration(const QSslConfiguration& configuration);

Q_SIGNALS:
    //from QAbstractSocket
    void connected();
    void disconnected();
    void error(KTcpSocket::Error);
    void hostFound();
#ifndef QT_NO_NETWORKPROXY
    void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
#endif
    // only for raw socket state, SSL is separate
    void stateChanged(KTcpSocket::State);

    //from QSslSocket
    void encrypted();
    void encryptionModeChanged(EncryptionMode);
    void sslErrors(const QList<KSslError> &errors);

public Q_SLOTS:
    void ignoreSslErrors();
    void startClientEncryption();
    // void startServerEncryption(); //not implemented
private:
    Q_PRIVATE_SLOT(d, void reemitReadyRead())
    Q_PRIVATE_SLOT(d, void reemitSocketError(QAbstractSocket::SocketError))
    Q_PRIVATE_SLOT(d, void reemitSslErrors(const QList<QSslError> &))
    Q_PRIVATE_SLOT(d, void reemitStateChanged(QAbstractSocket::SocketState))
    Q_PRIVATE_SLOT(d, void reemitModeChanged(QSslSocket::SslMode))

//debugging H4X
    void showSslErrors();

    friend class KTcpSocketPrivate;
    KTcpSocketPrivate *const d;
};


/**
 * This class can hold all the necessary data from a KTcpSocket to ask the user
 * to continue connecting in the face of SSL errors.
 * It can be used to carry the data for the UI over time or over thread boundaries.
 *
 * @see: KSslCertificateManager::askIgnoreSslErrors()
 */
class KDECORE_EXPORT KSslErrorUiData
{
public:
    /**
     * Default construct an instance with no useful data.
     */
    KSslErrorUiData();
    /**
     * Create an instance and initialize it with SSL error data from @p socket.
     */
    KSslErrorUiData(const KTcpSocket *socket);
    /**
     * Create an instance and initialize it with SSL error data from @p socket.
     */
    KSslErrorUiData(const QSslSocket *socket);
    KSslErrorUiData(const KSslErrorUiData &other);
    KSslErrorUiData &operator=(const KSslErrorUiData &);
    /**
     * Destructor
     * @since 4.7
     */
    ~KSslErrorUiData();
    class Private;
private:
    friend class Private;
    Private *const d;
};


#endif // KTCPSOCKET_H