QXmpp Version: 1.10.0
QXmppSasl_p.h
1// SPDX-FileCopyrightText: 2012 Manjeet Dahiya <manjeetdahiya@gmail.com>
2// SPDX-FileCopyrightText: 2012 Jeremy Lainé <jeremy.laine@m4x.org>
3// SPDX-FileCopyrightText: 2020 Linus Jahn <lnj@kaidan.im>
4// SPDX-FileCopyrightText: 2023 Melvin Keskin <melvo@olomono.de>
5//
6// SPDX-License-Identifier: LGPL-2.1-or-later
7
8#ifndef QXMPPSASL_P_H
9#define QXMPPSASL_P_H
10
11#include "QXmppGlobal.h"
12#include "QXmppLogger.h"
13#include "QXmppNonza.h"
14#include "QXmppStreamManagement_p.h"
15
16#include <optional>
17
18#include <QCryptographicHash>
19#include <QDateTime>
20#include <QMap>
21#include <QUuid>
22
23class QDomElement;
24class QXmlStreamWriter;
25class QXmppSaslServerPrivate;
26
27namespace QXmpp::Private {
28class SaslManager;
29}
30
31//
32// W A R N I N G
33// -------------
34//
35// This file is not part of the QXmpp API. It exists for the convenience
36// of the QXmppIncomingClient and QXmppOutgoingClient classes.
37//
38// This header file may change from version to version without notice,
39// or even be removed.
40//
41// We mean it.
42//
43
44namespace QXmpp::Private {
45
46namespace Sasl {
47
48enum class ErrorCondition {
49 Aborted,
50 AccountDisabled,
51 CredentialsExpired,
52 EncryptionRequired,
53 IncorrectEncoding,
54 InvalidAuthzid,
55 InvalidMechanism,
56 MalformedRequest,
57 MechanismTooWeak,
58 NotAuthorized,
59 TemporaryAuthFailure,
60};
61
62QString errorConditionToString(ErrorCondition);
63std::optional<ErrorCondition> errorConditionFromString(QStringView);
64
65struct Auth {
66 static std::optional<Auth> fromDom(const QDomElement &);
67 void toXml(QXmlStreamWriter *writer) const;
68
69 QString mechanism;
70 QByteArray value;
71};
72
73struct Challenge {
74 static std::optional<Challenge> fromDom(const QDomElement &);
75 void toXml(QXmlStreamWriter *writer) const;
76
77 QByteArray value;
78};
79
80struct Failure {
81 static std::optional<Failure> fromDom(const QDomElement &);
82 void toXml(QXmlStreamWriter *writer) const;
83
84 std::optional<ErrorCondition> condition;
85 QString text;
86};
87
88struct Response {
89 static std::optional<Response> fromDom(const QDomElement &);
90 void toXml(QXmlStreamWriter *writer) const;
91
92 QByteArray value;
93};
94
95struct Success {
96 static std::optional<Success> fromDom(const QDomElement &);
97 void toXml(QXmlStreamWriter *writer) const;
98};
99
100} // namespace Sasl
101
102struct Bind2Feature {
103 static std::optional<Bind2Feature> fromDom(const QDomElement &);
104 void toXml(QXmlStreamWriter *) const;
105
106 std::vector<QString> features;
107};
108
109struct Bind2Request {
110 static std::optional<Bind2Request> fromDom(const QDomElement &);
111 void toXml(QXmlStreamWriter *) const;
112
113 QString tag;
114 // bind2 extensions
115 bool csiInactive = false;
116 bool carbonsEnable = false;
117 std::optional<SmEnable> smEnable;
118};
119
120struct Bind2Bound {
121 static std::optional<Bind2Bound> fromDom(const QDomElement &);
122 void toXml(QXmlStreamWriter *) const;
123
124 // extensions
125 std::optional<SmFailed> smFailed;
126 std::optional<SmEnabled> smEnabled;
127};
128
129struct FastFeature {
130 static std::optional<FastFeature> fromDom(const QDomElement &);
131 void toXml(QXmlStreamWriter *) const;
132
133 std::vector<QString> mechanisms;
134 bool tls0rtt = false;
135};
136
137struct FastTokenRequest {
138 static std::optional<FastTokenRequest> fromDom(const QDomElement &);
139 void toXml(QXmlStreamWriter *) const;
140
141 QString mechanism;
142};
143
144struct FastToken {
145 static std::optional<FastToken> fromDom(const QDomElement &);
146 void toXml(QXmlStreamWriter *) const;
147
148 QDateTime expiry;
149 QString token;
150};
151
152struct FastRequest {
153 static std::optional<FastRequest> fromDom(const QDomElement &);
154 void toXml(QXmlStreamWriter *) const;
155
156 std::optional<uint64_t> count;
157 bool invalidate = false;
158};
159
160namespace Sasl2 {
161
162struct StreamFeature {
163 static std::optional<StreamFeature> fromDom(const QDomElement &);
164 void toXml(QXmlStreamWriter *) const;
165
166 QList<QString> mechanisms;
167 std::optional<Bind2Feature> bind2Feature;
168 std::optional<FastFeature> fast;
169 bool streamResumptionAvailable = false;
170};
171
172struct UserAgent {
173 static std::optional<UserAgent> fromDom(const QDomElement &);
174 void toXml(QXmlStreamWriter *) const;
175
176 QUuid id;
177 QString software;
178 QString device;
179};
180
181struct Authenticate {
182 static std::optional<Authenticate> fromDom(const QDomElement &);
183 void toXml(QXmlStreamWriter *) const;
184
185 QString mechanism;
186 QByteArray initialResponse;
187 std::optional<UserAgent> userAgent;
188 std::optional<Bind2Request> bindRequest;
189 std::optional<SmResume> smResume;
190 std::optional<FastTokenRequest> tokenRequest;
191 std::optional<FastRequest> fast;
192};
193
194struct Challenge {
195 static std::optional<Challenge> fromDom(const QDomElement &);
196 void toXml(QXmlStreamWriter *) const;
197
198 QByteArray data;
199};
200
201struct Response {
202 static std::optional<Response> fromDom(const QDomElement &);
203 void toXml(QXmlStreamWriter *) const;
204
205 QByteArray data;
206};
207
208struct Success {
209 static std::optional<Success> fromDom(const QDomElement &);
210 void toXml(QXmlStreamWriter *) const;
211
212 std::optional<QByteArray> additionalData;
213 QString authorizationIdentifier;
214 // extensions
215 std::optional<Bind2Bound> bound;
216 std::optional<SmResumed> smResumed;
217 std::optional<SmFailed> smFailed;
218 std::optional<FastToken> token;
219};
220
221struct Failure {
222 static std::optional<Failure> fromDom(const QDomElement &);
223 void toXml(QXmlStreamWriter *) const;
224
225 Sasl::ErrorCondition condition;
226 QString text;
227 // extensions
228};
229
230struct Continue {
231 static std::optional<Continue> fromDom(const QDomElement &);
232 void toXml(QXmlStreamWriter *) const;
233
234 QByteArray additionalData;
235 std::vector<QString> tasks;
236 QString text;
237};
238
239struct Abort {
240 static std::optional<Abort> fromDom(const QDomElement &);
241 void toXml(QXmlStreamWriter *) const;
242
243 QString text;
244};
245
246} // namespace Sasl2
247
248enum class IanaHashAlgorithm {
249 Sha256,
250 Sha384,
251 Sha512,
252 Sha3_224,
253 Sha3_256,
254 Sha3_384,
255 Sha3_512,
256#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
257 Blake2s_256,
258 Blake2b_256,
259 Blake2b_512,
260#endif
261};
262
263QCryptographicHash::Algorithm ianaHashAlgorithmToQt(IanaHashAlgorithm alg);
264
265//
266// SASL mechanisms
267//
268
269struct SaslScramMechanism {
270 static std::optional<SaslScramMechanism> fromString(QStringView str);
271 QString toString() const;
272
273 QCryptographicHash::Algorithm qtAlgorithm() const;
274
275 auto operator<=>(const SaslScramMechanism &) const = default;
276
277 enum Algorithm {
278 Sha1,
279 Sha256,
280 Sha512,
281 Sha3_512,
282 } algorithm;
283};
284
285struct SaslHtMechanism {
286 static std::optional<SaslHtMechanism> fromString(QStringView);
287 QString toString() const;
288
289 auto operator<=>(const SaslHtMechanism &) const = default;
290
291 enum ChannelBindingType {
292 TlsServerEndpoint,
293 TlsUnique,
294 TlsExporter,
295 None,
296 };
297
298 IanaHashAlgorithm hashAlgorithm;
299 ChannelBindingType channelBindingType;
300};
301
302struct SaslDigestMd5Mechanism {
303 auto operator<=>(const SaslDigestMd5Mechanism &) const = default;
304};
305struct SaslPlainMechanism {
306 auto operator<=>(const SaslPlainMechanism &) const = default;
307};
308struct SaslAnonymousMechanism {
309 auto operator<=>(const SaslAnonymousMechanism &) const = default;
310};
311struct SaslXFacebookMechanism {
312 auto operator<=>(const SaslXFacebookMechanism &) const = default;
313};
314struct SaslXWindowsLiveMechanism {
315 auto operator<=>(const SaslXWindowsLiveMechanism &) const = default;
316};
317struct SaslXGoogleMechanism {
318 auto operator<=>(const SaslXGoogleMechanism &) const = default;
319};
320
321// Note that the order of the variant alternatives defines the preference/strength of the mechanisms.
322struct SaslMechanism
323 : std::variant<SaslXGoogleMechanism,
324 SaslXWindowsLiveMechanism,
325 SaslXFacebookMechanism,
326 SaslAnonymousMechanism,
327 SaslPlainMechanism,
328 SaslDigestMd5Mechanism,
329 SaslScramMechanism,
330 SaslHtMechanism> {
331 static std::optional<SaslMechanism> fromString(QStringView str);
332 QString toString() const;
333};
334
335inline QDebug operator<<(QDebug dbg, SaslMechanism mechanism) { return dbg << mechanism.toString(); }
336
337//
338// Credentials
339//
340
341struct HtToken {
342 static std::optional<HtToken> fromXml(QXmlStreamReader &);
343 void toXml(QXmlStreamWriter &) const;
344 bool operator==(const HtToken &other) const = default;
345
346 SaslHtMechanism mechanism;
347 QString secret;
348 QDateTime expiry;
349};
350
351struct Credentials {
352 QString password;
353 std::optional<HtToken> htToken;
354
355 // Facebook
356 QString facebookAccessToken;
357 QString facebookAppId;
358 // Google
359 QString googleAccessToken;
360 // Windows Live
361 QString windowsLiveAccessToken;
362};
363
364} // namespace QXmpp::Private
365
366class QXMPP_AUTOTEST_EXPORT QXmppSaslClient : public QXmppLoggable
367{
368 Q_OBJECT
369public:
370 QXmppSaslClient(QObject *parent) : QXmppLoggable(parent) { }
371
372 QString host() const { return m_host; }
373 void setHost(const QString &host) { m_host = host; }
374
375 QString serviceType() const { return m_serviceType; }
376 void setServiceType(const QString &serviceType) { m_serviceType = serviceType; }
377
378 QString username() const { return m_username; }
379 void setUsername(const QString &username) { m_username = username; }
380
381 virtual void setCredentials(const QXmpp::Private::Credentials &) = 0;
382 virtual QXmpp::Private::SaslMechanism mechanism() const = 0;
383 virtual std::optional<QByteArray> respond(const QByteArray &challenge) = 0;
384
385 static bool isMechanismAvailable(QXmpp::Private::SaslMechanism, const QXmpp::Private::Credentials &);
386 static std::unique_ptr<QXmppSaslClient> create(const QString &mechanism, QObject *parent = nullptr);
387 static std::unique_ptr<QXmppSaslClient> create(QXmpp::Private::SaslMechanism mechanism, QObject *parent = nullptr);
388
389private:
390 friend class QXmpp::Private::SaslManager;
391
392 QString m_host;
393 QString m_serviceType;
394 QString m_username;
395 QString m_password;
396};
397
398class QXMPP_AUTOTEST_EXPORT QXmppSaslServer : public QXmppLoggable
399{
400 Q_OBJECT
401public:
402 enum Response {
403 Challenge = 0,
404 Succeeded = 1,
405 Failed = 2,
406 InputNeeded = 3
407 };
408
409 QXmppSaslServer(QObject *parent = nullptr);
410 ~QXmppSaslServer() override;
411
412 QString username() const;
413 void setUsername(const QString &username);
414
415 QString password() const;
416 void setPassword(const QString &password);
417
418 QByteArray passwordDigest() const;
419 void setPasswordDigest(const QByteArray &digest);
420
421 QString realm() const;
422 void setRealm(const QString &realm);
423
424 virtual QString mechanism() const = 0;
425 virtual Response respond(const QByteArray &challenge, QByteArray &response) = 0;
426
427 static std::unique_ptr<QXmppSaslServer> create(const QString &mechanism, QObject *parent = nullptr);
428
429private:
430 const std::unique_ptr<QXmppSaslServerPrivate> d;
431};
432
433class QXMPP_AUTOTEST_EXPORT QXmppSaslDigestMd5
434{
435public:
436 static void setNonce(const QByteArray &nonce);
437
438 // message parsing and serialization
439 static QMap<QByteArray, QByteArray> parseMessage(const QByteArray &ba);
440 static QByteArray serializeMessage(const QMap<QByteArray, QByteArray> &map);
441};
442
443class QXmppSaslClientAnonymous : public QXmppSaslClient
444{
445 Q_OBJECT
446public:
447 QXmppSaslClientAnonymous(QObject *parent = nullptr);
448 void setCredentials(const QXmpp::Private::Credentials &) override { }
449 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslAnonymousMechanism() }; }
450 std::optional<QByteArray> respond(const QByteArray &challenge) override;
451
452private:
453 int m_step;
454};
455
456class QXmppSaslClientDigestMd5 : public QXmppSaslClient
457{
458 Q_OBJECT
459public:
460 QXmppSaslClientDigestMd5(QObject *parent = nullptr);
461 void setCredentials(const QXmpp::Private::Credentials &) override;
462 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslDigestMd5Mechanism() }; }
463 std::optional<QByteArray> respond(const QByteArray &challenge) override;
464
465private:
466 QString m_password;
467 QByteArray m_cnonce;
468 QByteArray m_nc;
469 QByteArray m_nonce;
470 QByteArray m_secret;
471 int m_step;
472};
473
474class QXmppSaslClientFacebook : public QXmppSaslClient
475{
476 Q_OBJECT
477public:
478 QXmppSaslClientFacebook(QObject *parent = nullptr);
479 void setCredentials(const QXmpp::Private::Credentials &) override;
480 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslXFacebookMechanism() }; }
481 std::optional<QByteArray> respond(const QByteArray &challenge) override;
482
483private:
484 int m_step;
485 QString m_accessToken;
486 QString m_appId;
487};
488
489class QXmppSaslClientGoogle : public QXmppSaslClient
490{
491 Q_OBJECT
492public:
493 QXmppSaslClientGoogle(QObject *parent = nullptr);
494 void setCredentials(const QXmpp::Private::Credentials &) override;
495 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslXGoogleMechanism() }; }
496 std::optional<QByteArray> respond(const QByteArray &challenge) override;
497
498private:
499 QString m_accessToken;
500 int m_step;
501};
502
503class QXmppSaslClientPlain : public QXmppSaslClient
504{
505 Q_OBJECT
506public:
507 QXmppSaslClientPlain(QObject *parent = nullptr);
508 void setCredentials(const QXmpp::Private::Credentials &) override;
509 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslPlainMechanism() }; }
510 std::optional<QByteArray> respond(const QByteArray &challenge) override;
511
512private:
513 QString m_password;
514 int m_step;
515};
516
517class QXmppSaslClientScram : public QXmppSaslClient
518{
519 Q_OBJECT
520public:
521 QXmppSaslClientScram(QXmpp::Private::SaslScramMechanism mechanism, QObject *parent = nullptr);
522 void setCredentials(const QXmpp::Private::Credentials &) override;
523 QXmpp::Private::SaslMechanism mechanism() const override { return { m_mechanism }; }
524 std::optional<QByteArray> respond(const QByteArray &challenge) override;
525
526private:
527 QXmpp::Private::SaslScramMechanism m_mechanism;
528 int m_step;
529 QString m_password;
530 uint32_t m_dklen;
531 QByteArray m_gs2Header;
532 QByteArray m_clientFirstMessageBare;
533 QByteArray m_serverSignature;
534 QByteArray m_nonce;
535};
536
537class QXmppSaslClientHt : public QXmppSaslClient
538{
539 Q_OBJECT
540 using HtMechanism = QXmpp::Private::SaslHtMechanism;
541
542public:
543 QXmppSaslClientHt(HtMechanism mechanism, QObject *parent)
544 : QXmppSaslClient(parent), m_mechanism(mechanism)
545 {
546 }
547
548 void setCredentials(const QXmpp::Private::Credentials &credentials) override { m_token = credentials.htToken; }
549 QXmpp::Private::SaslMechanism mechanism() const override { return { m_mechanism }; }
550 std::optional<QByteArray> respond(const QByteArray &challenge) override;
551
552private:
553 std::optional<QXmpp::Private::HtToken> m_token;
554 HtMechanism m_mechanism;
555 bool m_done = false;
556};
557
558class QXmppSaslClientWindowsLive : public QXmppSaslClient
559{
560 Q_OBJECT
561public:
562 QXmppSaslClientWindowsLive(QObject *parent = nullptr);
563 void setCredentials(const QXmpp::Private::Credentials &) override;
564 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslXWindowsLiveMechanism() }; }
565 std::optional<QByteArray> respond(const QByteArray &challenge) override;
566
567private:
568 QString m_accessToken;
569 int m_step;
570};
571
572class QXmppSaslServerAnonymous : public QXmppSaslServer
573{
574 Q_OBJECT
575public:
576 QXmppSaslServerAnonymous(QObject *parent = nullptr);
577 QString mechanism() const override;
578
579 Response respond(const QByteArray &challenge, QByteArray &response) override;
580
581private:
582 int m_step;
583};
584
585class QXmppSaslServerDigestMd5 : public QXmppSaslServer
586{
587 Q_OBJECT
588public:
589 QXmppSaslServerDigestMd5(QObject *parent = nullptr);
590 QString mechanism() const override;
591
592 Response respond(const QByteArray &challenge, QByteArray &response) override;
593
594private:
595 QByteArray m_cnonce;
596 QByteArray m_nc;
597 QByteArray m_nonce;
598 QByteArray m_secret;
599 int m_step;
600};
601
602class QXmppSaslServerPlain : public QXmppSaslServer
603{
604 Q_OBJECT
605public:
606 QXmppSaslServerPlain(QObject *parent = nullptr);
607 QString mechanism() const override;
608
609 Response respond(const QByteArray &challenge, QByteArray &response) override;
610
611private:
612 int m_step;
613};
614
615#endif
The QXmppLoggable class represents a source of logging messages.
Definition: QXmppLogger.h:110