File indexing completed on 2024-05-19 15:15:51

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2008, 2009 Andreas Hartmetz <ahartmetz@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef HTTPAUTHENTICATION_H
0009 #define HTTPAUTHENTICATION_H
0010 
0011 #include <config-gssapi.h>
0012 
0013 #include <QByteArray>
0014 #include <QList>
0015 #include <QString>
0016 #include <QUrl>
0017 
0018 #include <QLoggingCategory>
0019 Q_DECLARE_LOGGING_CATEGORY(KIO_HTTP_AUTH)
0020 
0021 namespace KIO
0022 {
0023 class AuthInfo;
0024 }
0025 
0026 class KConfigGroup;
0027 
0028 class KAbstractHttpAuthentication
0029 {
0030 public:
0031     explicit KAbstractHttpAuthentication(KConfigGroup *config = nullptr);
0032     virtual ~KAbstractHttpAuthentication();
0033 
0034     /**
0035      * Choose the best authentication mechanism from the offered ones
0036      *
0037      * This will return the most secure mechanism from the list of
0038      * mechanisms returned by the server.
0039      */
0040     static QByteArray bestOffer(const QList<QByteArray> &offers);
0041 
0042     /**
0043      * Returns authentication object instance appropriate for @p offer.
0044      *
0045      * @param offer   the header from which an authentication object is created.
0046      * @param config  the config object to read stored authentication information.
0047      */
0048     static KAbstractHttpAuthentication *newAuth(const QByteArray &offer, KConfigGroup *config = nullptr);
0049 
0050     /**
0051      * Split all headers containing multiple authentication offers.
0052      *
0053      * @param offers the offers from multiple HTTP authentication header lines.
0054      * @return a list where each entry contains only a single offer
0055      */
0056     static QList<QByteArray> splitOffers(const QList<QByteArray> &offers);
0057 
0058     /**
0059      * reset to state after default construction.
0060      */
0061     void reset();
0062     /**
0063      * the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
0064      */
0065     virtual QByteArray scheme() const = 0;
0066     /**
0067      * initiate authentication with challenge string (from HTTP header)
0068      */
0069     virtual void setChallenge(const QByteArray &c, const QUrl &resource, const QByteArray &httpMethod);
0070     /**
0071      * return value updated by setChallenge()
0072      *
0073      * if this is false user and password passed to generateResponse
0074      * will be ignored and may be empty.
0075      */
0076     bool needCredentials() const
0077     {
0078         return m_needCredentials;
0079     }
0080     /**
0081      * KIO compatible data to find cached credentials.
0082      *
0083      * Note that username and/or password as well as UI text will NOT be filled in.
0084      */
0085     virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const = 0;
0086     /**
0087      * what to do in response to challenge
0088      */
0089     virtual void generateResponse(const QString &user, const QString &password) = 0;
0090 
0091     /**
0092      * returns true when the final stage of authentication is reached.
0093      *
0094      * Unless the authentication scheme requires multiple stages like NTLM this
0095      * function will always return true.
0096      */
0097     bool wasFinalStage() const
0098     {
0099         return m_finalAuthStage;
0100     }
0101     /**
0102      * Returns true if the authentication scheme supports path matching to identify
0103      * resources that belong to the same protection space (realm).
0104      *
0105      * See RFC 2617.
0106      */
0107     virtual bool supportsPathMatching() const
0108     {
0109         return false;
0110     }
0111 
0112     // the following accessors return useful data after generateResponse() has been called.
0113     // clients process the following fields top to bottom: highest priority is on top
0114 
0115     // malformed challenge and similar problems - it is advisable to reconnect
0116     bool isError() const
0117     {
0118         return m_isError;
0119     }
0120     /**
0121      * force keep-alive connection because the authentication method requires it
0122      */
0123     bool forceKeepAlive() const
0124     {
0125         return m_forceKeepAlive;
0126     }
0127     /**
0128      * force disconnection because the authentication method requires it
0129      */
0130     bool forceDisconnect() const
0131     {
0132         return m_forceDisconnect;
0133     }
0134 
0135     /**
0136      * insert this into the next request header after "Authorization: "
0137      * or "Proxy-Authorization: "
0138      */
0139     QByteArray headerFragment() const
0140     {
0141         return m_headerFragment;
0142     }
0143     /**
0144      * Returns the realm sent by the server.
0145      *
0146      * This is mainly for GUI shown to the user. This is the identification of
0147      * the protected area on the server (e.g. "Konquis home directory" or
0148      * "KDE files").
0149      */
0150     QString realm() const;
0151 
0152     /**
0153      * Sets the cache password flag to @p enable.
0154      */
0155     void setCachePasswordEnabled(bool enable)
0156     {
0157         m_keepPassword = enable;
0158     }
0159 
0160 #ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
0161     // NOTE: FOR USE in unit testing ONLY.
0162     virtual void setDigestNonceValue(const QByteArray &)
0163     {
0164     }
0165 #endif
0166 
0167 protected:
0168     void authInfoBoilerplate(KIO::AuthInfo *a) const;
0169     /**
0170      * Returns any authentication data that should be cached for future use.
0171      *
0172      * NOTE: Do not reimplement this function for connection based authentication
0173      * schemes such as NTLM.
0174      */
0175     virtual QByteArray authDataToCache() const
0176     {
0177         return QByteArray();
0178     }
0179     void generateResponseCommon(const QString &user, const QString &password);
0180 
0181     KConfigGroup *m_config;
0182     QByteArray m_scheme; ///< this is parsed from the header and not necessarily == scheme().
0183     QByteArray m_challengeText;
0184     QList<QByteArray> m_challenge;
0185     QUrl m_resource;
0186     QByteArray m_httpMethod;
0187 
0188     bool m_isError;
0189     bool m_needCredentials;
0190     bool m_forceKeepAlive;
0191     bool m_forceDisconnect;
0192     bool m_finalAuthStage;
0193     bool m_keepPassword;
0194     QByteArray m_headerFragment;
0195 
0196     QString m_username;
0197     QString m_password;
0198 };
0199 
0200 class KHttpBasicAuthentication : public KAbstractHttpAuthentication
0201 {
0202 public:
0203     QByteArray scheme() const override;
0204     void fillKioAuthInfo(KIO::AuthInfo *ai) const override;
0205     void generateResponse(const QString &user, const QString &password) override;
0206     bool supportsPathMatching() const override
0207     {
0208         return true;
0209     }
0210 
0211 protected:
0212     QByteArray authDataToCache() const override
0213     {
0214         return m_challengeText;
0215     }
0216 
0217 private:
0218     friend class KAbstractHttpAuthentication;
0219     KHttpBasicAuthentication(KConfigGroup *config = nullptr)
0220         : KAbstractHttpAuthentication(config)
0221     {
0222     }
0223 };
0224 
0225 class KHttpDigestAuthentication : public KAbstractHttpAuthentication
0226 {
0227 public:
0228     QByteArray scheme() const override;
0229     void setChallenge(const QByteArray &c, const QUrl &resource, const QByteArray &httpMethod) override;
0230     void fillKioAuthInfo(KIO::AuthInfo *ai) const override;
0231     void generateResponse(const QString &user, const QString &password) override;
0232     bool supportsPathMatching() const override
0233     {
0234         return true;
0235     }
0236 #ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
0237     void setDigestNonceValue(const QByteArray &) override;
0238 #endif
0239 
0240 protected:
0241     QByteArray authDataToCache() const override
0242     {
0243         return m_challengeText;
0244     }
0245 
0246 private:
0247     friend class KAbstractHttpAuthentication;
0248     KHttpDigestAuthentication(KConfigGroup *config = nullptr)
0249         : KAbstractHttpAuthentication(config)
0250     {
0251     }
0252 #ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
0253     QByteArray m_nonce;
0254 #endif
0255 };
0256 
0257 class KHttpNtlmAuthentication : public KAbstractHttpAuthentication
0258 {
0259 public:
0260     QByteArray scheme() const override;
0261     void setChallenge(const QByteArray &c, const QUrl &resource, const QByteArray &httpMethod) override;
0262     void fillKioAuthInfo(KIO::AuthInfo *ai) const override;
0263     void generateResponse(const QString &user, const QString &password) override;
0264 
0265 private:
0266     friend class KAbstractHttpAuthentication;
0267     explicit KHttpNtlmAuthentication(KConfigGroup *config = nullptr)
0268         : KAbstractHttpAuthentication(config)
0269         , m_stage1State(Init)
0270     {
0271     }
0272     enum Stage1State {
0273         Init = 0,
0274         SentNTLMv1,
0275         SentNTLMv2,
0276     };
0277     Stage1State m_stage1State;
0278 };
0279 
0280 #if HAVE_LIBGSSAPI
0281 class KHttpNegotiateAuthentication : public KAbstractHttpAuthentication
0282 {
0283 public:
0284     QByteArray scheme() const override;
0285     void setChallenge(const QByteArray &c, const QUrl &resource, const QByteArray &httpMethod) override;
0286     void fillKioAuthInfo(KIO::AuthInfo *ai) const override;
0287     void generateResponse(const QString &user, const QString &password) override;
0288 
0289 private:
0290     friend class KAbstractHttpAuthentication;
0291     explicit KHttpNegotiateAuthentication(KConfigGroup *config = nullptr)
0292         : KAbstractHttpAuthentication(config)
0293     {
0294     }
0295 };
0296 #endif // HAVE_LIBGSSAPI
0297 
0298 #endif // HTTPAUTHENTICATION_H