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