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
KDE 4.6 API Reference