QXmpp  Version: 1.15.1
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 
27 class QDomElement;
28 class QXmlStreamWriter;
29 class QXmppSaslServerPrivate;
30 
31 namespace QXmpp::Private {
32 class 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 
48 namespace QXmpp::Private {
49 
50 class XmlWriter;
51 
52 namespace Sasl {
53 
54 enum 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 
68 struct 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 
77 struct 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 
85 struct 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 
94 struct 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 
102 struct 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 
110 struct 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 
118 struct 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 
130 struct 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 
140 struct 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 
149 struct 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 
157 struct 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 
166 struct 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 
175 namespace Sasl2 {
176 
177 struct 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 
188 struct 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 
198 struct 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 
212 struct 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 
220 struct 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 
228 struct 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 
242 struct 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 
252 struct 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 
262 struct 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 
272 enum 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 
286 QCryptographicHash::Algorithm ianaHashAlgorithmToQt(IanaHashAlgorithm alg);
287 
288 //
289 // SASL mechanisms
290 //
291 
292 struct 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 
308 struct 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 
325 struct SaslDigestMd5Mechanism {
326  auto operator<=>(const SaslDigestMd5Mechanism &) const = default;
327 };
328 struct SaslPlainMechanism {
329  auto operator<=>(const SaslPlainMechanism &) const = default;
330 };
331 struct SaslAnonymousMechanism {
332  auto operator<=>(const SaslAnonymousMechanism &) const = default;
333 };
334 struct SaslXFacebookMechanism {
335  auto operator<=>(const SaslXFacebookMechanism &) const = default;
336 };
337 struct SaslXWindowsLiveMechanism {
338  auto operator<=>(const SaslXWindowsLiveMechanism &) const = default;
339 };
340 struct 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.
345 struct 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 
358 inline QDebug operator<<(QDebug dbg, SaslMechanism mechanism) { return dbg << mechanism.toString(); }
359 
360 //
361 // Credentials
362 //
363 
364 struct 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 
374 struct 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 
387 template<>
388 struct 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 
407 class QXMPP_AUTOTEST_EXPORT QXmppSaslClient : public QXmppLoggable
408 {
409  Q_OBJECT
410 public:
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 
430 private:
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 
439 class QXMPP_AUTOTEST_EXPORT QXmppSaslServer : public QXmppLoggable
440 {
441  Q_OBJECT
442 public:
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 
470 private:
471  const std::unique_ptr<QXmppSaslServerPrivate> d;
472 };
473 
474 class QXMPP_AUTOTEST_EXPORT QXmppSaslDigestMd5
475 {
476 public:
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 
484 class QXmppSaslClientAnonymous : public QXmppSaslClient
485 {
486  Q_OBJECT
487 public:
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 
493 private:
494  int m_step;
495 };
496 
497 class QXmppSaslClientDigestMd5 : public QXmppSaslClient
498 {
499  Q_OBJECT
500 public:
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 
506 private:
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 
515 class QXmppSaslClientFacebook : public QXmppSaslClient
516 {
517  Q_OBJECT
518 public:
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 
524 private:
525  int m_step;
526  QString m_accessToken;
527  QString m_appId;
528 };
529 
530 class QXmppSaslClientGoogle : public QXmppSaslClient
531 {
532  Q_OBJECT
533 public:
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 
539 private:
540  QString m_accessToken;
541  int m_step;
542 };
543 
544 class QXmppSaslClientPlain : public QXmppSaslClient
545 {
546  Q_OBJECT
547 public:
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 
553 private:
554  QString m_password;
555  int m_step;
556 };
557 
558 class QXmppSaslClientScram : public QXmppSaslClient
559 {
560  Q_OBJECT
561 public:
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 
567 private:
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 
578 class QXmppSaslClientHt : public QXmppSaslClient
579 {
580  Q_OBJECT
581  using HtMechanism = QXmpp::Private::SaslHtMechanism;
582 
583 public:
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 
593 private:
594  std::optional<QXmpp::Private::HtToken> m_token;
595  HtMechanism m_mechanism;
596  bool m_done = false;
597 };
598 
599 class QXmppSaslClientWindowsLive : public QXmppSaslClient
600 {
601  Q_OBJECT
602 public:
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 
608 private:
609  QString m_accessToken;
610  int m_step;
611 };
612 
613 class QXmppSaslServerAnonymous : public QXmppSaslServer
614 {
615  Q_OBJECT
616 public:
617  QXmppSaslServerAnonymous(QObject *parent = nullptr);
618  QString mechanism() const override;
619 
620  Response respond(const QByteArray &challenge, QByteArray &response) override;
621 
622 private:
623  int m_step;
624 };
625 
626 class QXmppSaslServerDigestMd5 : public QXmppSaslServer
627 {
628  Q_OBJECT
629 public:
630  QXmppSaslServerDigestMd5(QObject *parent = nullptr);
631  QString mechanism() const override;
632 
633  Response respond(const QByteArray &challenge, QByteArray &response) override;
634 
635 private:
636  QByteArray m_cnonce;
637  QByteArray m_nc;
638  QByteArray m_nonce;
639  QByteArray m_secret;
640  int m_step;
641 };
642 
643 class QXmppSaslServerPlain : public QXmppSaslServer
644 {
645  Q_OBJECT
646 public:
647  QXmppSaslServerPlain(QObject *parent = nullptr);
648  QString mechanism() const override;
649 
650  Response respond(const QByteArray &challenge, QByteArray &response) override;
651 
652 private:
653  int m_step;
654 };
655 
656 #endif
The QXmppLoggable class represents a source of logging messages.
Definition: QXmppLogger.h:108
Definition: Algorithms.h:14