QXmpp Version: 1.15.1
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 Blake2s_256,
281 Blake2b_256,
282 Blake2b_512,
283 _End = Blake2b_512,
284};
285
286QCryptographicHash::Algorithm ianaHashAlgorithmToQt(IanaHashAlgorithm alg);
287
288//
289// SASL mechanisms
290//
291
292struct SaslScramMechanism {
293 static std::optional<SaslScramMechanism> fromString(QStringView str);
294 QString toString() const;
295
296 QCryptographicHash::Algorithm qtAlgorithm() const;
297
298 auto operator<=>(const SaslScramMechanism &) const = default;
299
300 enum Algorithm {
301 Sha1,
302 Sha256,
303 Sha512,
304 Sha3_512,
305 } algorithm;
306};
307
308struct SaslHtMechanism {
309 static std::optional<SaslHtMechanism> fromString(QStringView);
310 QString toString() const;
311
312 auto operator<=>(const SaslHtMechanism &) const = default;
313
314 enum ChannelBindingType {
315 TlsServerEndpoint,
316 TlsUnique,
317 TlsExporter,
318 None,
319 };
320
321 IanaHashAlgorithm hashAlgorithm;
322 ChannelBindingType channelBindingType;
323};
324
325struct SaslDigestMd5Mechanism {
326 auto operator<=>(const SaslDigestMd5Mechanism &) const = default;
327};
328struct SaslPlainMechanism {
329 auto operator<=>(const SaslPlainMechanism &) const = default;
330};
331struct SaslAnonymousMechanism {
332 auto operator<=>(const SaslAnonymousMechanism &) const = default;
333};
334struct SaslXFacebookMechanism {
335 auto operator<=>(const SaslXFacebookMechanism &) const = default;
336};
337struct SaslXWindowsLiveMechanism {
338 auto operator<=>(const SaslXWindowsLiveMechanism &) const = default;
339};
340struct SaslXGoogleMechanism {
341 auto operator<=>(const SaslXGoogleMechanism &) const = default;
342};
343
344// Note that the order of the variant alternatives defines the preference/strength of the mechanisms.
345struct SaslMechanism
346 : std::variant<SaslXGoogleMechanism,
347 SaslXWindowsLiveMechanism,
348 SaslXFacebookMechanism,
349 SaslAnonymousMechanism,
350 SaslPlainMechanism,
351 SaslDigestMd5Mechanism,
352 SaslScramMechanism,
353 SaslHtMechanism> {
354 static std::optional<SaslMechanism> fromString(QStringView str);
355 QString toString() const;
356};
357
358inline QDebug operator<<(QDebug dbg, SaslMechanism mechanism) { return dbg << mechanism.toString(); }
359
360//
361// Credentials
362//
363
364struct HtToken {
365 static std::optional<HtToken> fromXml(QXmlStreamReader &);
366 void toXml(XmlWriter &) const;
367 bool operator==(const HtToken &other) const = default;
368
369 SaslHtMechanism mechanism;
370 QString secret;
371 QDateTime expiry;
372};
373
374struct Credentials {
375 QString password;
376 std::optional<HtToken> htToken;
377
378 // Facebook
379 QString facebookAccessToken;
380 QString facebookAppId;
381 // Google
382 QString googleAccessToken;
383 // Windows Live
384 QString windowsLiveAccessToken;
385};
386
387template<>
388struct Enums::Data<Sasl::ErrorCondition> {
389 using enum Sasl::ErrorCondition;
390 static inline constexpr auto Values = makeValues<Sasl::ErrorCondition>({
391 { Aborted, u"aborted" },
392 { AccountDisabled, u"account-disabled" },
393 { CredentialsExpired, u"credentials-expired" },
394 { EncryptionRequired, u"encryption-required" },
395 { IncorrectEncoding, u"incorrect-encoding" },
396 { InvalidAuthzid, u"invalid-authzid" },
397 { InvalidMechanism, u"invalid-mechanism" },
398 { MalformedRequest, u"malformed-request" },
399 { MechanismTooWeak, u"mechanism-too-weak" },
400 { NotAuthorized, u"not-authorized" },
401 { TemporaryAuthFailure, u"temporary-auth-failure" },
402 });
403};
404
405} // namespace QXmpp::Private
406
407class QXMPP_AUTOTEST_EXPORT QXmppSaslClient : public QXmppLoggable
408{
409 Q_OBJECT
410public:
411 QXmppSaslClient(QObject *parent) : QXmppLoggable(parent) { }
412
413 QString host() const { return m_host; }
414 void setHost(const QString &host) { m_host = host; }
415
416 QString serviceType() const { return m_serviceType; }
417 void setServiceType(const QString &serviceType) { m_serviceType = serviceType; }
418
419 QString username() const { return m_username; }
420 void setUsername(const QString &username) { m_username = username; }
421
422 virtual void setCredentials(const QXmpp::Private::Credentials &) = 0;
423 virtual QXmpp::Private::SaslMechanism mechanism() const = 0;
424 virtual std::optional<QByteArray> respond(const QByteArray &challenge) = 0;
425
426 static bool isMechanismAvailable(QXmpp::Private::SaslMechanism, const QXmpp::Private::Credentials &);
427 static std::unique_ptr<QXmppSaslClient> create(const QString &mechanism, QObject *parent = nullptr);
428 static std::unique_ptr<QXmppSaslClient> create(QXmpp::Private::SaslMechanism mechanism, QObject *parent = nullptr);
429
430private:
431 friend class QXmpp::Private::SaslManager;
432
433 QString m_host;
434 QString m_serviceType;
435 QString m_username;
436 QString m_password;
437};
438
439class QXMPP_AUTOTEST_EXPORT QXmppSaslServer : public QXmppLoggable
440{
441 Q_OBJECT
442public:
443 enum Response {
444 Challenge = 0,
445 Succeeded = 1,
446 Failed = 2,
447 InputNeeded = 3
448 };
449
450 QXmppSaslServer(QObject *parent = nullptr);
451 ~QXmppSaslServer() override;
452
453 QString username() const;
454 void setUsername(const QString &username);
455
456 QString password() const;
457 void setPassword(const QString &password);
458
459 QByteArray passwordDigest() const;
460 void setPasswordDigest(const QByteArray &digest);
461
462 QString realm() const;
463 void setRealm(const QString &realm);
464
465 virtual QString mechanism() const = 0;
466 virtual Response respond(const QByteArray &challenge, QByteArray &response) = 0;
467
468 static std::unique_ptr<QXmppSaslServer> create(const QString &mechanism, QObject *parent = nullptr);
469
470private:
471 const std::unique_ptr<QXmppSaslServerPrivate> d;
472};
473
474class QXMPP_AUTOTEST_EXPORT QXmppSaslDigestMd5
475{
476public:
477 static void setNonce(const QByteArray &nonce);
478
479 // message parsing and serialization
480 static QMap<QByteArray, QByteArray> parseMessage(const QByteArray &ba);
481 static QByteArray serializeMessage(const QMap<QByteArray, QByteArray> &map);
482};
483
484class QXmppSaslClientAnonymous : public QXmppSaslClient
485{
486 Q_OBJECT
487public:
488 QXmppSaslClientAnonymous(QObject *parent = nullptr);
489 void setCredentials(const QXmpp::Private::Credentials &) override { }
490 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslAnonymousMechanism() }; }
491 std::optional<QByteArray> respond(const QByteArray &challenge) override;
492
493private:
494 int m_step;
495};
496
497class QXmppSaslClientDigestMd5 : public QXmppSaslClient
498{
499 Q_OBJECT
500public:
501 QXmppSaslClientDigestMd5(QObject *parent = nullptr);
502 void setCredentials(const QXmpp::Private::Credentials &) override;
503 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslDigestMd5Mechanism() }; }
504 std::optional<QByteArray> respond(const QByteArray &challenge) override;
505
506private:
507 QString m_password;
508 QByteArray m_cnonce;
509 QByteArray m_nc;
510 QByteArray m_nonce;
511 QByteArray m_secret;
512 int m_step;
513};
514
515class QXmppSaslClientFacebook : public QXmppSaslClient
516{
517 Q_OBJECT
518public:
519 QXmppSaslClientFacebook(QObject *parent = nullptr);
520 void setCredentials(const QXmpp::Private::Credentials &) override;
521 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslXFacebookMechanism() }; }
522 std::optional<QByteArray> respond(const QByteArray &challenge) override;
523
524private:
525 int m_step;
526 QString m_accessToken;
527 QString m_appId;
528};
529
530class QXmppSaslClientGoogle : public QXmppSaslClient
531{
532 Q_OBJECT
533public:
534 QXmppSaslClientGoogle(QObject *parent = nullptr);
535 void setCredentials(const QXmpp::Private::Credentials &) override;
536 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslXGoogleMechanism() }; }
537 std::optional<QByteArray> respond(const QByteArray &challenge) override;
538
539private:
540 QString m_accessToken;
541 int m_step;
542};
543
544class QXmppSaslClientPlain : public QXmppSaslClient
545{
546 Q_OBJECT
547public:
548 QXmppSaslClientPlain(QObject *parent = nullptr);
549 void setCredentials(const QXmpp::Private::Credentials &) override;
550 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslPlainMechanism() }; }
551 std::optional<QByteArray> respond(const QByteArray &challenge) override;
552
553private:
554 QString m_password;
555 int m_step;
556};
557
558class QXmppSaslClientScram : public QXmppSaslClient
559{
560 Q_OBJECT
561public:
562 QXmppSaslClientScram(QXmpp::Private::SaslScramMechanism mechanism, QObject *parent = nullptr);
563 void setCredentials(const QXmpp::Private::Credentials &) override;
564 QXmpp::Private::SaslMechanism mechanism() const override { return { m_mechanism }; }
565 std::optional<QByteArray> respond(const QByteArray &challenge) override;
566
567private:
568 QXmpp::Private::SaslScramMechanism m_mechanism;
569 int m_step;
570 QString m_password;
571 uint32_t m_dklen;
572 QByteArray m_gs2Header;
573 QByteArray m_clientFirstMessageBare;
574 QByteArray m_serverSignature;
575 QByteArray m_nonce;
576};
577
578class QXmppSaslClientHt : public QXmppSaslClient
579{
580 Q_OBJECT
581 using HtMechanism = QXmpp::Private::SaslHtMechanism;
582
583public:
584 QXmppSaslClientHt(HtMechanism mechanism, QObject *parent)
585 : QXmppSaslClient(parent), m_mechanism(mechanism)
586 {
587 }
588
589 void setCredentials(const QXmpp::Private::Credentials &credentials) override { m_token = credentials.htToken; }
590 QXmpp::Private::SaslMechanism mechanism() const override { return { m_mechanism }; }
591 std::optional<QByteArray> respond(const QByteArray &challenge) override;
592
593private:
594 std::optional<QXmpp::Private::HtToken> m_token;
595 HtMechanism m_mechanism;
596 bool m_done = false;
597};
598
599class QXmppSaslClientWindowsLive : public QXmppSaslClient
600{
601 Q_OBJECT
602public:
603 QXmppSaslClientWindowsLive(QObject *parent = nullptr);
604 void setCredentials(const QXmpp::Private::Credentials &) override;
605 QXmpp::Private::SaslMechanism mechanism() const override { return { QXmpp::Private::SaslXWindowsLiveMechanism() }; }
606 std::optional<QByteArray> respond(const QByteArray &challenge) override;
607
608private:
609 QString m_accessToken;
610 int m_step;
611};
612
613class QXmppSaslServerAnonymous : public QXmppSaslServer
614{
615 Q_OBJECT
616public:
617 QXmppSaslServerAnonymous(QObject *parent = nullptr);
618 QString mechanism() const override;
619
620 Response respond(const QByteArray &challenge, QByteArray &response) override;
621
622private:
623 int m_step;
624};
625
626class QXmppSaslServerDigestMd5 : public QXmppSaslServer
627{
628 Q_OBJECT
629public:
630 QXmppSaslServerDigestMd5(QObject *parent = nullptr);
631 QString mechanism() const override;
632
633 Response respond(const QByteArray &challenge, QByteArray &response) override;
634
635private:
636 QByteArray m_cnonce;
637 QByteArray m_nc;
638 QByteArray m_nonce;
639 QByteArray m_secret;
640 int m_step;
641};
642
643class QXmppSaslServerPlain : public QXmppSaslServer
644{
645 Q_OBJECT
646public:
647 QXmppSaslServerPlain(QObject *parent = nullptr);
648 QString mechanism() const override;
649
650 Response respond(const QByteArray &challenge, QByteArray &response) override;
651
652private:
653 int m_step;
654};
655
656#endif
The QXmppLoggable class represents a source of logging messages.
Definition QXmppLogger.h:109