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

KIOSlave

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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.3
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