• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.3 API Reference
  • KDE Home
  • Contact Us
 

KIOSlave

httpauthentication.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002     Copyright (C) 2008, 2009 Andreas Hartmetz <ahartmetz@gmail.com>
00003 
00004     This library is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU Library General Public
00006     License as published by the Free Software Foundation; either
00007     version 2 of the License, or (at your option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017     Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "httpauthentication.h"
00021 
00022 #ifdef HAVE_LIBGSSAPI
00023 #ifdef GSSAPI_MIT
00024 #include <gssapi/gssapi.h>
00025 #else
00026 #include <gssapi.h>
00027 #endif /* GSSAPI_MIT */
00028 
00029 // Catch uncompatible crap (BR86019)
00030 #if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0)
00031 #include <gssapi/gssapi_generic.h>
00032 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
00033 #endif
00034 
00035 #endif /* HAVE_LIBGSSAPI */
00036 
00037 #include <krandom.h>
00038 #include <kdebug.h>
00039 #include <klocale.h>
00040 #include <kglobal.h>
00041 #include <kcodecs.h>
00042 #include <kconfiggroup.h>
00043 #include <kio/authinfo.h>
00044 #include <misc/kntlm/kntlm.h>
00045 
00046 #include <QtCore/QTextCodec>
00047 
00048 
00049 static bool isWhiteSpace(char ch)
00050 {
00051     return (ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f');
00052 }
00053 
00054 static bool isWhiteSpaceOrComma(char ch)
00055 {
00056     return (ch == ',' || isWhiteSpace(ch));
00057 }
00058 
00059 static bool containsScheme(const char input[], int start, int end)
00060 {
00061     // skip any comma or white space
00062     while (start < end && isWhiteSpaceOrComma(input[start])) {
00063         start++;
00064     }
00065 
00066     while (start < end) {
00067         if (isWhiteSpace(input[start])) {
00068             return true;
00069         }
00070         start++;
00071     }
00072 
00073     return false;
00074 }
00075 
00076 // keys on even indexes, values on odd indexes. Reduces code expansion for the templated
00077 // alternatives.
00078 // If "ba" starts with empty content it will be removed from ba to simplify later calls
00079 static QList<QByteArray> parseChallenge(QByteArray &ba, QByteArray *scheme, QByteArray* nextAuth = 0)
00080 {
00081     QList<QByteArray> values;
00082     const char *b = ba.constData();
00083     int len = ba.count();
00084     int start = 0, end = 0, pos = 0, pos2 = 0;
00085 
00086     // parse scheme
00087     while (start < len && isWhiteSpaceOrComma(b[start])) {
00088         start++;
00089     }
00090     end = start;
00091     while (end < len && !isWhiteSpace(b[end])) {
00092         end++;
00093     }
00094 
00095     // drop empty stuff from the given string, it would have to be skipped over and over again
00096     if (start != 0) {
00097         ba = ba.mid(start);
00098         end -= start;
00099         len -= start;
00100         start = 0;
00101         b = ba.constData();
00102     }
00103     Q_ASSERT(scheme);
00104     *scheme = ba.left(end);
00105 
00106     while (end < len) {
00107         start = end;
00108         while (end < len && b[end] != '=') {
00109             end++;
00110         }
00111         pos = end; // save the end position
00112         while (end - 1 > start && isWhiteSpace(b[end - 1])) { // trim whitespace
00113             end--;
00114         }
00115         pos2 = start;
00116         while (pos2 < end && isWhiteSpace(b[pos2])) { // skip whitespace
00117             pos2++;
00118         }
00119         if (containsScheme(b, start, end) || (b[pos2] == ',' && b[pos] != '=' && pos == len)) {
00120             if (nextAuth) {
00121                 *nextAuth = QByteArray (b + start);
00122             }
00123             break;  // break on start of next scheme.
00124         }
00125         while (start < len && isWhiteSpaceOrComma(b[start])) {
00126             start++;
00127         }
00128         values.append(QByteArray (b + start, end - start));
00129         end = pos; // restore the end position
00130         if (end == len) {
00131             break;
00132         }
00133 
00134         // parse value
00135         start = end + 1;    //skip '='
00136         while (start < len && isWhiteSpace(b[start])) {
00137             start++;
00138         }
00139 
00140         if (b[start] == '"') {
00141             //quoted string
00142             bool hasBs = false;
00143             bool hasErr = false;
00144             end = ++start;
00145             while (end < len) {
00146                 if (b[end] == '\\') {
00147                     end++;
00148                     if (end + 1 >= len) {
00149                         hasErr = true;
00150                         break;
00151                     } else {
00152                         hasBs = true;
00153                         end++;
00154                     }
00155                 } else if (b[end] == '"') {
00156                     break;
00157                 } else {
00158                     end++;
00159                 }
00160             }
00161             if (hasErr || (end == len)) {
00162                 // remove the key we already inserted
00163                 kDebug(7113) << "error in quoted text for key" << values.last();
00164                 values.removeLast();
00165                 break;
00166              }
00167             QByteArray value = QByteArray(b + start, end - start);
00168             if (hasBs) {
00169                 // skip over the next character, it might be an escaped backslash
00170                 int i = -1;
00171                 while ( (i = value.indexOf('\\', i + 1)) >= 0 ) {
00172                     value.remove(i, 1);
00173                 }
00174             }
00175             values.append(value);
00176             end++;
00177         } else {
00178             //unquoted string
00179             end = start;
00180             while (end < len && b[end] != ',' && !isWhiteSpace(b[end])) {
00181                 end++;
00182             }
00183             values.append(QByteArray(b + start, end - start));
00184         }
00185 
00186         //the quoted string has ended, but only a comma ends a key-value pair
00187         while (end < len && isWhiteSpace(b[end])) {
00188             end++;
00189         }
00190 
00191         // garbage, here should be end or field delimiter (comma)
00192         if (end < len && b[end] != ',') {
00193             kDebug(7113) << "unexpected character" << b[end] << "found in WWW-authentication header where token boundary (,) was expected";
00194             break;
00195         }
00196     }
00197     // ensure every key has a value
00198     // WARNING: Do not remove the > 1 check or parsing a Type 1 NTLM
00199     // authentication challenge will surely fail.
00200     if (values.count() > 1 && values.count() % 2) {
00201         values.removeLast();
00202     }
00203     return values;
00204 }
00205 
00206 
00207 static QByteArray valueForKey(const QList<QByteArray> &ba, const QByteArray &key)
00208 {
00209     for (int i = 0, count = ba.count(); (i + 1) < count; i += 2) {
00210         if (ba[i] == key) {
00211             return ba[i + 1];
00212         }
00213     }
00214     return QByteArray();
00215 }
00216 
00217 KAbstractHttpAuthentication::KAbstractHttpAuthentication(KConfigGroup *config)
00218                             :m_config(config), m_finalAuthStage(false)
00219 {
00220     reset();
00221 }
00222 
00223 KAbstractHttpAuthentication::~KAbstractHttpAuthentication()
00224 {
00225 }
00226 
00227 QByteArray KAbstractHttpAuthentication::bestOffer(const QList<QByteArray> &offers)
00228 {
00229     // choose the most secure auth scheme offered
00230     QByteArray negotiateOffer;
00231     QByteArray digestOffer;
00232     QByteArray ntlmOffer;
00233     QByteArray basicOffer;
00234     Q_FOREACH (const QByteArray &offer, offers) {
00235         const QByteArray scheme = offer.mid(0, offer.indexOf(' ')).toLower();
00236 #ifdef HAVE_LIBGSSAPI
00237         if (scheme == "negotiate") { // krazy:exclude=strings
00238             negotiateOffer = offer;
00239         } else
00240 #endif
00241         if (scheme == "digest") { // krazy:exclude=strings
00242             digestOffer = offer;
00243         } else if (scheme == "ntlm") { // krazy:exclude=strings
00244             ntlmOffer = offer;
00245         } else if (scheme == "basic") { // krazy:exclude=strings
00246             basicOffer = offer;
00247         }
00248     }
00249 
00250     if (!negotiateOffer.isEmpty()) {
00251         return negotiateOffer;
00252     }
00253 
00254     if (!digestOffer.isEmpty()) {
00255         return digestOffer;
00256     }
00257 
00258     if (!ntlmOffer.isEmpty()) {
00259         return ntlmOffer;
00260     }
00261 
00262     return basicOffer;  //empty or not...
00263 }
00264 
00265 
00266 KAbstractHttpAuthentication *KAbstractHttpAuthentication::newAuth(const QByteArray &offer, KConfigGroup* config)
00267 {
00268     const QByteArray scheme = offer.mid(0, offer.indexOf(' ')).toLower();
00269 #ifdef HAVE_LIBGSSAPI
00270     if (scheme == "negotiate") { // krazy:exclude=strings
00271         return new KHttpNegotiateAuthentication(config);
00272     } else
00273 #endif
00274     if (scheme == "digest") { // krazy:exclude=strings
00275         return new KHttpDigestAuthentication();
00276     } else if (scheme == "ntlm") { // krazy:exclude=strings
00277         return new KHttpNtlmAuthentication(config);
00278     } else if (scheme == "basic") { // krazy:exclude=strings
00279         return new KHttpBasicAuthentication();
00280     }
00281     return 0;
00282 }
00283 
00284 QList< QByteArray > KAbstractHttpAuthentication::splitOffers(const QList< QByteArray >& offers)
00285 {
00286     // first detect if one entry may contain multiple offers
00287     QList<QByteArray> alloffers;
00288     foreach(QByteArray offer, offers) {
00289         QByteArray scheme, cont;
00290 
00291         parseChallenge(offer, &scheme, &cont);
00292 
00293         while (!cont.isEmpty()) {
00294             offer.chop(cont.length());
00295             alloffers << offer;
00296             offer = cont;
00297             cont.clear();
00298             parseChallenge(offer, &scheme, &cont);
00299         }
00300         alloffers << offer;
00301     }
00302     return alloffers;
00303 }
00304 
00305 void KAbstractHttpAuthentication::reset()
00306 {
00307     m_scheme.clear();
00308     m_challenge.clear();
00309     m_challengeText.clear();
00310     m_resource.clear();
00311     m_httpMethod.clear();
00312     m_isError = false;
00313     m_needCredentials = true;
00314     m_forceKeepAlive = false;
00315     m_forceDisconnect = false;
00316     m_keepPassword = false;
00317     m_headerFragment.clear();
00318     m_username.clear();
00319     m_password.clear();
00320 }
00321 
00322 void KAbstractHttpAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
00323                                                const QByteArray &httpMethod)
00324 {
00325     reset();
00326     m_challengeText = c.trimmed();
00327     m_challenge = parseChallenge(m_challengeText, &m_scheme);
00328     Q_ASSERT(m_scheme.toLower() == scheme().toLower());
00329     m_resource = resource;
00330     m_httpMethod = httpMethod.trimmed();
00331 }
00332 
00333 
00334 QString KAbstractHttpAuthentication::realm() const
00335 {
00336     const QByteArray realm = valueForKey(m_challenge, "realm");
00337     // TODO: Find out what this is supposed to address. The site mentioned below does not exist.
00338     if (KGlobal::locale()->language().contains(QLatin1String("ru"))) {
00339         //for sites like lib.homelinux.org
00340         return QTextCodec::codecForName("CP1251")->toUnicode(realm);
00341     }
00342     return QString::fromLatin1(realm.constData(), realm.length());
00343 }
00344 
00345 void KAbstractHttpAuthentication::authInfoBoilerplate(KIO::AuthInfo *a) const
00346 {
00347     a->url = m_resource;
00348     a->username = m_username;
00349     a->password = m_password;
00350     a->verifyPath = supportsPathMatching();
00351     a->realmValue = realm();
00352     a->digestInfo = QLatin1String(authDataToCache());
00353     a->keepPassword = m_keepPassword;
00354 }
00355 
00356 
00357 void KAbstractHttpAuthentication::generateResponseCommon(const QString &user, const QString &password)
00358 {
00359     if (m_scheme.isEmpty() || m_httpMethod.isEmpty()) {
00360         m_isError = true;
00361         return;
00362     }
00363 
00364     if (m_needCredentials) {
00365         m_username = user;
00366         m_password = password;
00367     }
00368 
00369     m_isError = false;
00370     m_forceKeepAlive = false;
00371     m_forceDisconnect = false;
00372     m_finalAuthStage = true;
00373 }
00374 
00375 
00376 QByteArray KHttpBasicAuthentication::scheme() const
00377 {
00378     return "Basic";
00379 }
00380 
00381 
00382 void KHttpBasicAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
00383 {
00384     authInfoBoilerplate(ai);
00385 }
00386 
00387 void KHttpBasicAuthentication::generateResponse(const QString &user, const QString &password)
00388 {
00389     generateResponseCommon(user, password);
00390     if (m_isError) {
00391         return;
00392     }
00393 
00394     m_headerFragment = "Basic ";
00395     m_headerFragment += QByteArray(m_username.toLatin1() + ':' + m_password.toLatin1()).toBase64();
00396     m_headerFragment += "\r\n";
00397 }
00398 
00399 
00400 QByteArray KHttpDigestAuthentication::scheme() const
00401 {
00402     return "Digest";
00403 }
00404 
00405 
00406 void KHttpDigestAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
00407                                              const QByteArray &httpMethod)
00408 {
00409     QString oldUsername;
00410     QString oldPassword;
00411     if (valueForKey(m_challenge, "stale").toLower() == "true") {
00412         // stale nonce: the auth failure that triggered this round of authentication is an artifact
00413         // of digest authentication. the credentials are probably still good, so keep them.
00414         oldUsername = m_username;
00415         oldPassword = m_password;
00416     }
00417     KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
00418     if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
00419         // keep credentials *and* don't ask for new ones
00420         m_needCredentials = false;
00421         m_username = oldUsername;
00422         m_password = oldPassword;
00423     }
00424 }
00425 
00426 
00427 void KHttpDigestAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
00428 {
00429     authInfoBoilerplate(ai);
00430 }
00431 
00432 
00433 struct DigestAuthInfo
00434 {
00435     QByteArray nc;
00436     QByteArray qop;
00437     QByteArray realm;
00438     QByteArray nonce;
00439     QByteArray method;
00440     QByteArray cnonce;
00441     QByteArray username;
00442     QByteArray password;
00443     KUrl::List digestURIs;
00444     QByteArray algorithm;
00445     QByteArray entityBody;
00446 };
00447 
00448 
00449 //calculateResponse() from the original HTTPProtocol
00450 static QByteArray calculateResponse(const DigestAuthInfo &info, const KUrl &resource)
00451 {
00452   KMD5 md;
00453   QByteArray HA1;
00454   QByteArray HA2;
00455 
00456   // Calculate H(A1)
00457   QByteArray authStr = info.username;
00458   authStr += ':';
00459   authStr += info.realm;
00460   authStr += ':';
00461   authStr += info.password;
00462   md.update( authStr );
00463 
00464   if ( info.algorithm.toLower() == "md5-sess" )
00465   {
00466     authStr = md.hexDigest();
00467     authStr += ':';
00468     authStr += info.nonce;
00469     authStr += ':';
00470     authStr += info.cnonce;
00471     md.reset();
00472     md.update( authStr );
00473   }
00474   HA1 = md.hexDigest();
00475 
00476   kDebug(7113) << "A1 => " << HA1;
00477 
00478   // Calcualte H(A2)
00479   authStr = info.method;
00480   authStr += ':';
00481   authStr += resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
00482   if ( info.qop == "auth-int" )
00483   {
00484     authStr += ':';
00485     md.reset();
00486     md.update(info.entityBody);
00487     authStr += md.hexDigest();
00488   }
00489   md.reset();
00490   md.update( authStr );
00491   HA2 = md.hexDigest();
00492 
00493   kDebug(7113) << "A2 => " << HA2;
00494 
00495   // Calcualte the response.
00496   authStr = HA1;
00497   authStr += ':';
00498   authStr += info.nonce;
00499   authStr += ':';
00500   if ( !info.qop.isEmpty() )
00501   {
00502     authStr += info.nc;
00503     authStr += ':';
00504     authStr += info.cnonce;
00505     authStr += ':';
00506     authStr += info.qop;
00507     authStr += ':';
00508   }
00509   authStr += HA2;
00510   md.reset();
00511   md.update( authStr );
00512 
00513   const QByteArray response = md.hexDigest();
00514   kDebug(7113) << "Response =>" << response;
00515   return response;
00516 }
00517 
00518 
00519 void KHttpDigestAuthentication::generateResponse(const QString &user, const QString &password)
00520 {
00521     generateResponseCommon(user, password);
00522     if (m_isError) {
00523         return;
00524     }
00525 
00526     // magic starts here (this part is slightly modified from the original in HTTPProtocol)
00527 
00528     DigestAuthInfo info;
00529 
00530     info.username = m_username.toLatin1();  //### charset breakage
00531     info.password = m_password.toLatin1();  //###
00532 
00533     // info.entityBody = p;  // FIXME: send digest of data for POST action ??
00534     info.realm = "";
00535     info.nonce = "";
00536     info.qop = "";
00537 
00538     // cnonce is recommended to contain about 64 bits of entropy
00539 #ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
00540     info.cnonce = m_nonce;
00541 #else
00542     info.cnonce = KRandom::randomString(16).toLatin1();
00543 #endif
00544 
00545     // HACK: Should be fixed according to RFC 2617 section 3.2.2
00546     info.nc = "00000001";
00547 
00548     // Set the method used...
00549     info.method = m_httpMethod;
00550 
00551     // Parse the Digest response....
00552     info.realm = valueForKey(m_challenge, "realm");
00553 
00554     info.algorithm = valueForKey(m_challenge, "algorithm");
00555     if (info.algorithm.isEmpty()) {
00556         info.algorithm = valueForKey(m_challenge, "algorith");
00557     }
00558     if (info.algorithm.isEmpty()) {
00559         info.algorithm = "MD5";
00560     }
00561 
00562     Q_FOREACH (const QByteArray &path, valueForKey(m_challenge, "domain").split(' ')) {
00563         KUrl u(m_resource, QString::fromLatin1(path));
00564         if (u.isValid()) {
00565             info.digestURIs.append(u);
00566         }
00567     }
00568 
00569     info.nonce = valueForKey(m_challenge, "nonce");
00570     QByteArray opaque = valueForKey(m_challenge, "opaque");
00571     info.qop = valueForKey(m_challenge, "qop");
00572 
00573     // NOTE: Since we do not have access to the entity body, we cannot support
00574     // the "auth-int" qop value ; so if the server returns a comma separated
00575     // list of qop values, prefer "auth".See RFC 2617 sec 3.2.2 for the details.
00576     // If "auth" is not present or it is set to "auth-int" only, then we simply
00577     // print a warning message and disregard the qop option altogether.
00578     if (info.qop.contains(',')) {
00579         const QList<QByteArray> values = info.qop.split(',');
00580         if (info.qop.contains("auth"))
00581             info.qop = "auth";
00582         else {
00583             kWarning(7113) << "Unsupported digest authentication qop parameters:" << values;
00584             info.qop.clear();
00585         }
00586     } else if (info.qop == "auth-int") {
00587         kWarning(7113) << "Unsupported digest authentication qop parameter:" << info.qop;
00588         info.qop.clear();
00589     }
00590 
00591     if (info.realm.isEmpty() || info.nonce.isEmpty()) {
00592         // ### proper error return
00593         m_isError = true;
00594         return;
00595     }
00596 
00597     // If the "domain" attribute was not specified and the current response code
00598     // is authentication needed, add the current request url to the list over which
00599     // this credential can be automatically applied.
00600     if (info.digestURIs.isEmpty() /*###&& (m_request.responseCode == 401 || m_request.responseCode == 407)*/)
00601         info.digestURIs.append (m_resource);
00602     else
00603     {
00604         // Verify whether or not we should send a cached credential to the
00605         // server based on the stored "domain" attribute...
00606         bool send = true;
00607 
00608         // Determine the path of the request url...
00609         QString requestPath = m_resource.directory(KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
00610         if (requestPath.isEmpty())
00611           requestPath = QLatin1Char('/');
00612 
00613         Q_FOREACH (const KUrl &u, info.digestURIs)
00614         {
00615           send &= (m_resource.protocol().toLower() == u.protocol().toLower());
00616           send &= (m_resource.host().toLower() == u.host().toLower());
00617 
00618           if (m_resource.port() > 0 && u.port() > 0)
00619             send &= (m_resource.port() == u.port());
00620 
00621           QString digestPath = u.directory (KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
00622           if (digestPath.isEmpty())
00623             digestPath = QLatin1Char('/');
00624 
00625           send &= (requestPath.startsWith(digestPath));
00626 
00627           if (send)
00628             break;
00629         }
00630 
00631         if (!send) {
00632             m_isError = true;
00633             return;
00634         }
00635     }
00636 
00637     kDebug(7113) << "RESULT OF PARSING:";
00638     kDebug(7113) << "  algorithm: " << info.algorithm;
00639     kDebug(7113) << "  realm:     " << info.realm;
00640     kDebug(7113) << "  nonce:     " << info.nonce;
00641     kDebug(7113) << "  opaque:    " << opaque;
00642     kDebug(7113) << "  qop:       " << info.qop;
00643 
00644     // Calculate the response...
00645     const QByteArray response = calculateResponse(info, m_resource);
00646 
00647     QByteArray auth = "Digest username=\"";
00648     auth += info.username;
00649 
00650     auth += "\", realm=\"";
00651     auth += info.realm;
00652     auth += "\"";
00653 
00654     auth += ", nonce=\"";
00655     auth += info.nonce;
00656 
00657     auth += "\", uri=\"";
00658     auth += m_resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
00659 
00660     if (!info.algorithm.isEmpty()) {
00661       auth += "\", algorithm=";
00662       auth += info.algorithm;
00663     }
00664 
00665     if ( !info.qop.isEmpty() )
00666     {
00667       auth += ", qop=";
00668       auth += info.qop;
00669       auth += ", cnonce=\"";
00670       auth += info.cnonce;
00671       auth += "\", nc=";
00672       auth += info.nc;
00673     }
00674 
00675     auth += ", response=\"";
00676     auth += response;
00677     if ( !opaque.isEmpty() )
00678     {
00679       auth += "\", opaque=\"";
00680       auth += opaque;
00681     }
00682     auth += "\"\r\n";
00683 
00684     // magic ends here
00685     // note that auth already contains \r\n
00686     m_headerFragment = auth;
00687 }
00688 
00689 #ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
00690 void KHttpDigestAuthentication::setDigestNonceValue(const QByteArray& nonce)
00691 {
00692     m_nonce = nonce;
00693 }
00694 #endif
00695 
00696 
00697 QByteArray KHttpNtlmAuthentication::scheme() const
00698 {
00699     return "NTLM";
00700 }
00701 
00702 
00703 void KHttpNtlmAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
00704                                            const QByteArray &httpMethod)
00705 {
00706     QString oldUsername, oldPassword;
00707     if (!m_finalAuthStage && !m_username.isEmpty() && !m_password.isEmpty()) {
00708         oldUsername = m_username;
00709         oldPassword = m_password;
00710     }
00711     KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
00712     if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
00713         m_username = oldUsername;
00714         m_password = oldPassword;
00715     }
00716     // The type 1 message we're going to send needs no credentials;
00717     // they come later in the type 3 message.
00718     m_needCredentials = m_challenge.isEmpty();
00719 }
00720 
00721 
00722 void KHttpNtlmAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
00723 {
00724     authInfoBoilerplate(ai);
00725     // Every auth scheme is supposed to supply a realm according to the RFCs. Of course this doesn't
00726     // prevent Microsoft from not doing it... Dummy value!
00727     // we don't have the username yet which may (may!) contain a domain, so we really have no choice
00728     ai->realmValue = QLatin1String("NTLM");
00729 }
00730 
00731 
00732 void KHttpNtlmAuthentication::generateResponse(const QString &_user, const QString &password)
00733 {
00734     generateResponseCommon(_user, password);
00735     if (m_isError) {
00736         return;
00737     }
00738 
00739     QByteArray buf;
00740 
00741     if (m_challenge.isEmpty()) {
00742         m_finalAuthStage = false;
00743         // first, send type 1 message (with empty domain, workstation..., but it still works)
00744         if (!KNTLM::getNegotiate(buf)) {
00745             kWarning(7113) << "Error while constructing Type 1 NTLM authentication request";
00746             m_isError = true;
00747             return;
00748         }
00749     } else {
00750         m_finalAuthStage = true;
00751         // we've (hopefully) received a valid type 2 message: send type 3 message as last step
00752         QString user, domain;
00753         if (m_username.contains(QLatin1Char('\\'))) {
00754             domain = m_username.section(QLatin1Char('\\'), 0, 0);
00755             user = m_username.section(QLatin1Char('\\'), 1);
00756         } else {
00757             user = m_username;
00758         }
00759 
00760         m_forceKeepAlive = true;
00761         const QByteArray challenge = QByteArray::fromBase64(m_challenge[0]);
00762 
00763         KNTLM::AuthFlags flags = KNTLM::Add_LM;
00764         if (!m_config || !m_config->readEntry("EnableNTLMv2Auth", false)) {
00765             flags |= KNTLM::Force_V1;
00766         }
00767 
00768         if (!KNTLM::getAuth(buf, challenge, user, m_password, domain, QLatin1String("WORKSTATION"), flags)) {
00769             kWarning(7113) << "Error while constructing Type 3 NTLM authentication request";
00770             m_isError = true;
00771             return;
00772         }
00773     }
00774 
00775     m_headerFragment = "NTLM ";
00776     m_headerFragment += buf.toBase64();
00777     m_headerFragment += "\r\n";
00778 
00779     return;
00780 }
00781 
00782 
00784 #ifdef HAVE_LIBGSSAPI
00785 
00786 // just an error message formatter
00787 static QByteArray gssError(int major_status, int minor_status)
00788 {
00789     OM_uint32 new_status;
00790     OM_uint32 msg_ctx = 0;
00791     gss_buffer_desc major_string;
00792     gss_buffer_desc minor_string;
00793     OM_uint32 ret;
00794     QByteArray errorstr;
00795 
00796     do {
00797         ret = gss_display_status(&new_status, major_status, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &major_string);
00798         errorstr += (const char *)major_string.value;
00799         errorstr += ' ';
00800         ret = gss_display_status(&new_status, minor_status, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &minor_string);
00801         errorstr += (const char *)minor_string.value;
00802         errorstr += ' ';
00803     } while (!GSS_ERROR(ret) && msg_ctx != 0);
00804 
00805     return errorstr;
00806 }
00807 
00808 
00809 QByteArray KHttpNegotiateAuthentication::scheme() const
00810 {
00811     return "Negotiate";
00812 }
00813 
00814 
00815 void KHttpNegotiateAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
00816                                                 const QByteArray &httpMethod)
00817 {
00818     KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
00819     // GSSAPI knows how to get the credentials on its own
00820     m_needCredentials = false;
00821 }
00822 
00823 
00824 void KHttpNegotiateAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
00825 {
00826     authInfoBoilerplate(ai);
00827     //### does GSSAPI supply anything realm-like? dummy value for now.
00828     ai->realmValue = QLatin1String("Negotiate");
00829 }
00830 
00831 
00832 void KHttpNegotiateAuthentication::generateResponse(const QString &user, const QString &password)
00833 {
00834     generateResponseCommon(user, password);
00835     if (m_isError) {
00836         return;
00837     }
00838 
00839     OM_uint32 major_status, minor_status;
00840     gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
00841     gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
00842     gss_name_t server;
00843     gss_ctx_id_t ctx;
00844     gss_OID mech_oid;
00845     static gss_OID_desc krb5_oid_desc = {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
00846     static gss_OID_desc spnego_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"};
00847     gss_OID_set mech_set;
00848     gss_OID tmp_oid;
00849 
00850     ctx = GSS_C_NO_CONTEXT;
00851     mech_oid = &krb5_oid_desc;
00852 
00853     // see whether we can use the SPNEGO mechanism
00854     major_status = gss_indicate_mechs(&minor_status, &mech_set);
00855     if (GSS_ERROR(major_status)) {
00856         kDebug(7113) << "gss_indicate_mechs failed: " << gssError(major_status, minor_status);
00857     } else {
00858         for (uint i = 0; i < mech_set->count; i++) {
00859             tmp_oid = &mech_set->elements[i];
00860             if (tmp_oid->length == spnego_oid_desc.length &&
00861                 !memcmp(tmp_oid->elements, spnego_oid_desc.elements, tmp_oid->length)) {
00862                 kDebug(7113) << "found SPNEGO mech";
00863                 mech_oid = &spnego_oid_desc;
00864                 break;
00865             }
00866         }
00867         gss_release_oid_set(&minor_status, &mech_set);
00868     }
00869 
00870     // the service name is "HTTP/f.q.d.n"
00871     QByteArray servicename = "HTTP@";
00872     servicename += m_resource.host().toAscii();
00873 
00874     input_token.value = (void *)servicename.data();
00875     input_token.length = servicename.length() + 1;
00876 
00877     major_status = gss_import_name(&minor_status, &input_token,
00878                                    GSS_C_NT_HOSTBASED_SERVICE, &server);
00879 
00880     input_token.value = NULL;
00881     input_token.length = 0;
00882 
00883     if (GSS_ERROR(major_status)) {
00884         kDebug(7113) << "gss_import_name failed: " << gssError(major_status, minor_status);
00885         m_isError = true;
00886         return;
00887     }
00888 
00889     OM_uint32 req_flags;
00890     if (m_config && m_config->readEntry("DelegateCredentialsOn", false))
00891        req_flags = GSS_C_DELEG_FLAG;
00892     else
00893        req_flags = 0;
00894 
00895     // GSSAPI knows how to get the credentials its own way, so don't ask for any
00896     major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
00897                                         &ctx, server, mech_oid,
00898                                         req_flags, GSS_C_INDEFINITE,
00899                                         GSS_C_NO_CHANNEL_BINDINGS,
00900                                         GSS_C_NO_BUFFER, NULL, &output_token,
00901                                         NULL, NULL);
00902 
00903     if (GSS_ERROR(major_status) || (output_token.length == 0)) {
00904         kDebug(7113) << "gss_init_sec_context failed: " << gssError(major_status, minor_status);
00905         gss_release_name(&minor_status, &server);
00906         if (ctx != GSS_C_NO_CONTEXT) {
00907             gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
00908             ctx = GSS_C_NO_CONTEXT;
00909         }
00910         m_isError = true;
00911         return;
00912     }
00913 
00914     m_headerFragment = "Negotiate ";
00915     m_headerFragment += QByteArray::fromRawData(static_cast<const char *>(output_token.value),
00916                                                 output_token.length).toBase64();
00917     m_headerFragment += "\r\n";
00918 
00919     // free everything
00920     gss_release_name(&minor_status, &server);
00921     if (ctx != GSS_C_NO_CONTEXT) {
00922         gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
00923         ctx = GSS_C_NO_CONTEXT;
00924     }
00925     gss_release_buffer(&minor_status, &output_token);
00926 }
00927 
00928 #endif // HAVE_LIBGSSAPI
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:57:54 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIOSlave

Skip menu "KIOSlave"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal