QXmpp  Version: 1.10.4
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 
23 class QDomElement;
24 class QXmlStreamWriter;
25 class QXmppSaslServerPrivate;
26 
27 namespace QXmpp::Private {
28 class 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 
44 namespace QXmpp::Private {
45 
46 namespace Sasl {
47 
48 enum 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 
62 QString errorConditionToString(ErrorCondition);
63 std::optional<ErrorCondition> errorConditionFromString(QStringView);
64 
65 struct Auth {
66  static std::optional<Auth> fromDom(const QDomElement &);
67  void toXml(QXmlStreamWriter *writer) const;
68 
69  QString mechanism;
70  QByteArray value;
71 };
72 
73 struct Challenge {
74  static std::optional<Challenge> fromDom(const QDomElement &);
75  void toXml(QXmlStreamWriter *writer) const;
76 
77  QByteArray value;
78 };
79 
80 struct 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 
88 struct Response {
89  static std::optional<Response> fromDom(const QDomElement &);
90  void toXml(QXmlStreamWriter *writer) const;
91 
92  QByteArray value;
93 };
94 
95 struct Success {
96  static std::optional<Success> fromDom(const QDomElement &);
97  void toXml(QXmlStreamWriter *writer) const;
98 };
99 
100 } // namespace Sasl
101 
102 struct Bind2Feature {
103  static std::optional<Bind2Feature> fromDom(const QDomElement &);
104  void toXml(QXmlStreamWriter *) const;
105 
106  std::vector<QString> features;
107 };
108 
109 struct 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 
120 struct 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 
129 struct 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 
137 struct FastTokenRequest {
138  static std::optional<FastTokenRequest> fromDom(const QDomElement &);
139  void toXml(QXmlStreamWriter *) const;
140 
141  QString mechanism;
142 };
143 
144 struct FastToken {
145  static std::optional<FastToken> fromDom(const QDomElement &);
146  void toXml(QXmlStreamWriter *) const;
147 
148  QDateTime expiry;
149  QString token;
150 };
151 
152 struct 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 
160 namespace Sasl2 {
161 
162 struct 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 
172 struct 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 
181 struct 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 
194 struct Challenge {
195  static std::optional<Challenge> fromDom(const QDomElement &);
196  void toXml(QXmlStreamWriter *) const;
197 
198  QByteArray data;
199 };
200 
201 struct Response {
202  static std::optional<Response> fromDom(const QDomElement &);
203  void toXml(QXmlStreamWriter *) const;
204 
205  QByteArray data;
206 };
207 
208 struct 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 
221 struct 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 
230 struct 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 
239 struct Abort {
240  static std::optional<Abort> fromDom(const QDomElement &);
241  void toXml(QXmlStreamWriter *) const;
242 
243  QString text;
244 };
245 
246 } // namespace Sasl2
247 
248 enum 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 
263 QCryptographicHash::Algorithm ianaHashAlgorithmToQt(IanaHashAlgorithm alg);
264 
265 //
266 // SASL mechanisms
267 //
268 
269 struct 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 
285 struct 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 
302 struct SaslDigestMd5Mechanism {
303  auto operator<=>(const SaslDigestMd5Mechanism &) const = default;
304 };
305 struct SaslPlainMechanism {
306  auto operator<=>(const SaslPlainMechanism &) const = default;
307 };
308 struct SaslAnonymousMechanism {
309  auto operator<=>(const SaslAnonymousMechanism &) const = default;
310 };
311 struct SaslXFacebookMechanism {
312  auto operator<=>(const SaslXFacebookMechanism &) const = default;
313 };
314 struct SaslXWindowsLiveMechanism {
315  auto operator<=>(const SaslXWindowsLiveMechanism &) const = default;
316 };
317 struct 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.
322 struct 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 
335 inline QDebug operator<<(QDebug dbg, SaslMechanism mechanism) { return dbg << mechanism.toString(); }
336 
337 //
338 // Credentials
339 //
340 
341 struct 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 
351 struct 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 
366 class QXMPP_AUTOTEST_EXPORT QXmppSaslClient : public QXmppLoggable
367 {
368  Q_OBJECT
369 public:
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 
389 private:
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 
398 class QXMPP_AUTOTEST_EXPORT QXmppSaslServer : public QXmppLoggable
399 {
400  Q_OBJECT
401 public:
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 
429 private:
430  const std::unique_ptr<QXmppSaslServerPrivate> d;
431 };
432 
433 class QXMPP_AUTOTEST_EXPORT QXmppSaslDigestMd5
434 {
435 public:
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 
443 class QXmppSaslClientAnonymous : public QXmppSaslClient
444 {
445  Q_OBJECT
446 public:
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 
452 private:
453  int m_step;
454 };
455 
456 class QXmppSaslClientDigestMd5 : public QXmppSaslClient
457 {
458  Q_OBJECT
459 public:
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 
465 private:
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 
474 class QXmppSaslClientFacebook : public QXmppSaslClient
475 {
476  Q_OBJECT
477 public:
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 
483 private:
484  int m_step;
485  QString m_accessToken;
486  QString m_appId;
487 };
488 
489 class QXmppSaslClientGoogle : public QXmppSaslClient
490 {
491  Q_OBJECT
492 public:
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 
498 private:
499  QString m_accessToken;
500  int m_step;
501 };
502 
503 class QXmppSaslClientPlain : public QXmppSaslClient
504 {
505  Q_OBJECT
506 public:
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 
512 private:
513  QString m_password;
514  int m_step;
515 };
516 
517 class QXmppSaslClientScram : public QXmppSaslClient
518 {
519  Q_OBJECT
520 public:
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 
526 private:
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 
537 class QXmppSaslClientHt : public QXmppSaslClient
538 {
539  Q_OBJECT
540  using HtMechanism = QXmpp::Private::SaslHtMechanism;
541 
542 public:
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 
552 private:
553  std::optional<QXmpp::Private::HtToken> m_token;
554  HtMechanism m_mechanism;
555  bool m_done = false;
556 };
557 
558 class QXmppSaslClientWindowsLive : public QXmppSaslClient
559 {
560  Q_OBJECT
561 public:
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 
567 private:
568  QString m_accessToken;
569  int m_step;
570 };
571 
572 class QXmppSaslServerAnonymous : public QXmppSaslServer
573 {
574  Q_OBJECT
575 public:
576  QXmppSaslServerAnonymous(QObject *parent = nullptr);
577  QString mechanism() const override;
578 
579  Response respond(const QByteArray &challenge, QByteArray &response) override;
580 
581 private:
582  int m_step;
583 };
584 
585 class QXmppSaslServerDigestMd5 : public QXmppSaslServer
586 {
587  Q_OBJECT
588 public:
589  QXmppSaslServerDigestMd5(QObject *parent = nullptr);
590  QString mechanism() const override;
591 
592  Response respond(const QByteArray &challenge, QByteArray &response) override;
593 
594 private:
595  QByteArray m_cnonce;
596  QByteArray m_nc;
597  QByteArray m_nonce;
598  QByteArray m_secret;
599  int m_step;
600 };
601 
602 class QXmppSaslServerPlain : public QXmppSaslServer
603 {
604  Q_OBJECT
605 public:
606  QXmppSaslServerPlain(QObject *parent = nullptr);
607  QString mechanism() const override;
608 
609  Response respond(const QByteArray &challenge, QByteArray &response) override;
610 
611 private:
612  int m_step;
613 };
614 
615 #endif
The QXmppLoggable class represents a source of logging messages.
Definition: QXmppLogger.h:109
Definition: Algorithms.h:12