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

KIOSlave

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

kdelibs

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal