kdelibs/kioslave/http/httpauthentication.cpp

928 lines
27 KiB
C++
Raw Normal View History

2014-11-13 01:04:59 +02:00
/* This file is part of the KDE libraries
Copyright (C) 2008, 2009 Andreas Hartmetz <ahartmetz@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "httpauthentication.h"
#ifdef HAVE_LIBGSSAPI
#ifdef GSSAPI_MIT
#include <gssapi/gssapi.h>
#else
#include <gssapi.h>
#endif /* GSSAPI_MIT */
// Catch uncompatible crap (BR86019)
#if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0)
#include <gssapi/gssapi_generic.h>
#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
#endif
#endif /* HAVE_LIBGSSAPI */
#include <krandom.h>
#include <kdebug.h>
#include <klocale.h>
#include <kglobal.h>
#include <kconfiggroup.h>
#include <kio/authinfo.h>
#include <misc/kntlm/kntlm.h>
#include <QtCore/QTextCodec>
#include <QtCore/QCryptographicHash>
2014-11-13 01:04:59 +02:00
static bool isWhiteSpace(char ch)
{
return (ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f');
}
static bool isWhiteSpaceOrComma(char ch)
{
return (ch == ',' || isWhiteSpace(ch));
}
static bool containsScheme(const char input[], int start, int end)
{
// skip any comma or white space
while (start < end && isWhiteSpaceOrComma(input[start])) {
start++;
}
while (start < end) {
if (isWhiteSpace(input[start])) {
return true;
}
start++;
}
return false;
}
// keys on even indexes, values on odd indexes. Reduces code expansion for the templated
// alternatives.
// If "ba" starts with empty content it will be removed from ba to simplify later calls
static QList<QByteArray> parseChallenge(QByteArray &ba, QByteArray *scheme, QByteArray* nextAuth = 0)
{
QList<QByteArray> values;
const char *b = ba.constData();
int len = ba.count();
int start = 0, end = 0, pos = 0, pos2 = 0;
// parse scheme
while (start < len && isWhiteSpaceOrComma(b[start])) {
start++;
}
end = start;
while (end < len && !isWhiteSpace(b[end])) {
end++;
}
// drop empty stuff from the given string, it would have to be skipped over and over again
if (start != 0) {
ba = ba.mid(start);
end -= start;
len -= start;
start = 0;
b = ba.constData();
}
Q_ASSERT(scheme);
*scheme = ba.left(end);
while (end < len) {
start = end;
while (end < len && b[end] != '=') {
end++;
}
pos = end; // save the end position
while (end - 1 > start && isWhiteSpace(b[end - 1])) { // trim whitespace
end--;
}
pos2 = start;
while (pos2 < end && isWhiteSpace(b[pos2])) { // skip whitespace
pos2++;
}
if (containsScheme(b, start, end) || (b[pos2] == ',' && b[pos] != '=' && pos == len)) {
if (nextAuth) {
*nextAuth = QByteArray (b + start);
}
break; // break on start of next scheme.
}
while (start < len && isWhiteSpaceOrComma(b[start])) {
start++;
}
values.append(QByteArray (b + start, end - start));
end = pos; // restore the end position
if (end == len) {
break;
}
// parse value
start = end + 1; //skip '='
while (start < len && isWhiteSpace(b[start])) {
start++;
}
if (b[start] == '"') {
//quoted string
bool hasBs = false;
bool hasErr = false;
end = ++start;
while (end < len) {
if (b[end] == '\\') {
end++;
if (end + 1 >= len) {
hasErr = true;
break;
} else {
hasBs = true;
end++;
}
} else if (b[end] == '"') {
break;
} else {
end++;
}
}
if (hasErr || (end == len)) {
// remove the key we already inserted
kDebug(7113) << "error in quoted text for key" << values.last();
values.removeLast();
break;
}
QByteArray value = QByteArray(b + start, end - start);
if (hasBs) {
// skip over the next character, it might be an escaped backslash
int i = -1;
while ( (i = value.indexOf('\\', i + 1)) >= 0 ) {
value.remove(i, 1);
}
}
values.append(value);
end++;
} else {
//unquoted string
end = start;
while (end < len && b[end] != ',' && !isWhiteSpace(b[end])) {
end++;
}
values.append(QByteArray(b + start, end - start));
}
//the quoted string has ended, but only a comma ends a key-value pair
while (end < len && isWhiteSpace(b[end])) {
end++;
}
// garbage, here should be end or field delimiter (comma)
if (end < len && b[end] != ',') {
kDebug(7113) << "unexpected character" << b[end] << "found in WWW-authentication header where token boundary (,) was expected";
break;
}
}
// ensure every key has a value
// WARNING: Do not remove the > 1 check or parsing a Type 1 NTLM
// authentication challenge will surely fail.
if (values.count() > 1 && values.count() % 2) {
values.removeLast();
}
return values;
}
static QByteArray valueForKey(const QList<QByteArray> &ba, const QByteArray &key)
{
for (int i = 0, count = ba.count(); (i + 1) < count; i += 2) {
if (ba[i] == key) {
return ba[i + 1];
}
}
return QByteArray();
}
KAbstractHttpAuthentication::KAbstractHttpAuthentication(KConfigGroup *config)
:m_config(config), m_finalAuthStage(false)
{
reset();
}
KAbstractHttpAuthentication::~KAbstractHttpAuthentication()
{
}
QByteArray KAbstractHttpAuthentication::bestOffer(const QList<QByteArray> &offers)
{
// choose the most secure auth scheme offered
QByteArray negotiateOffer;
QByteArray digestOffer;
QByteArray ntlmOffer;
QByteArray basicOffer;
Q_FOREACH (const QByteArray &offer, offers) {
const QByteArray scheme = offer.mid(0, offer.indexOf(' ')).toLower();
#ifdef HAVE_LIBGSSAPI
if (scheme == "negotiate") { // krazy:exclude=strings
negotiateOffer = offer;
} else
#endif
if (scheme == "digest") { // krazy:exclude=strings
digestOffer = offer;
} else if (scheme == "ntlm") { // krazy:exclude=strings
ntlmOffer = offer;
} else if (scheme == "basic") { // krazy:exclude=strings
basicOffer = offer;
}
}
if (!negotiateOffer.isEmpty()) {
return negotiateOffer;
}
if (!digestOffer.isEmpty()) {
return digestOffer;
}
if (!ntlmOffer.isEmpty()) {
return ntlmOffer;
}
return basicOffer; //empty or not...
}
KAbstractHttpAuthentication *KAbstractHttpAuthentication::newAuth(const QByteArray &offer, KConfigGroup* config)
{
const QByteArray scheme = offer.mid(0, offer.indexOf(' ')).toLower();
#ifdef HAVE_LIBGSSAPI
if (scheme == "negotiate") { // krazy:exclude=strings
return new KHttpNegotiateAuthentication(config);
} else
#endif
if (scheme == "digest") { // krazy:exclude=strings
return new KHttpDigestAuthentication();
} else if (scheme == "ntlm") { // krazy:exclude=strings
return new KHttpNtlmAuthentication(config);
} else if (scheme == "basic") { // krazy:exclude=strings
return new KHttpBasicAuthentication();
}
return 0;
}
QList< QByteArray > KAbstractHttpAuthentication::splitOffers(const QList< QByteArray >& offers)
{
// first detect if one entry may contain multiple offers
QList<QByteArray> alloffers;
foreach(QByteArray offer, offers) {
QByteArray scheme, cont;
parseChallenge(offer, &scheme, &cont);
while (!cont.isEmpty()) {
offer.chop(cont.length());
alloffers << offer;
offer = cont;
cont.clear();
parseChallenge(offer, &scheme, &cont);
}
alloffers << offer;
}
return alloffers;
}
void KAbstractHttpAuthentication::reset()
{
m_scheme.clear();
m_challenge.clear();
m_challengeText.clear();
m_resource.clear();
m_httpMethod.clear();
m_isError = false;
m_needCredentials = true;
m_forceKeepAlive = false;
m_forceDisconnect = false;
m_keepPassword = false;
m_headerFragment.clear();
m_username.clear();
m_password.clear();
}
void KAbstractHttpAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
const QByteArray &httpMethod)
{
reset();
m_challengeText = c.trimmed();
m_challenge = parseChallenge(m_challengeText, &m_scheme);
Q_ASSERT(m_scheme.toLower() == scheme().toLower());
m_resource = resource;
m_httpMethod = httpMethod;
}
QString KAbstractHttpAuthentication::realm() const
{
const QByteArray realm = valueForKey(m_challenge, "realm");
// TODO: Find out what this is supposed to address. The site mentioned below does not exist.
if (KGlobal::locale()->language().contains(QLatin1String("ru"))) {
//for sites like lib.homelinux.org
return QTextCodec::codecForName("CP1251")->toUnicode(realm);
}
return QString::fromLatin1(realm.constData(), realm.length());
}
void KAbstractHttpAuthentication::authInfoBoilerplate(KIO::AuthInfo *a) const
{
a->url = m_resource;
a->username = m_username;
a->password = m_password;
a->verifyPath = supportsPathMatching();
a->realmValue = realm();
a->digestInfo = QLatin1String(authDataToCache());
a->keepPassword = m_keepPassword;
}
void KAbstractHttpAuthentication::generateResponseCommon(const QString &user, const QString &password)
{
if (m_scheme.isEmpty() || m_httpMethod.isEmpty()) {
m_isError = true;
return;
}
if (m_needCredentials) {
m_username = user;
m_password = password;
}
m_isError = false;
m_forceKeepAlive = false;
m_forceDisconnect = false;
m_finalAuthStage = true;
}
QByteArray KHttpBasicAuthentication::scheme() const
{
return "Basic";
}
void KHttpBasicAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
}
void KHttpBasicAuthentication::generateResponse(const QString &user, const QString &password)
{
generateResponseCommon(user, password);
if (m_isError) {
return;
}
m_headerFragment = "Basic ";
m_headerFragment += QByteArray(m_username.toLatin1() + ':' + m_password.toLatin1()).toBase64();
m_headerFragment += "\r\n";
}
QByteArray KHttpDigestAuthentication::scheme() const
{
return "Digest";
}
void KHttpDigestAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
const QByteArray &httpMethod)
{
QString oldUsername;
QString oldPassword;
if (valueForKey(m_challenge, "stale").toLower() == "true") {
// stale nonce: the auth failure that triggered this round of authentication is an artifact
// of digest authentication. the credentials are probably still good, so keep them.
oldUsername = m_username;
oldPassword = m_password;
}
KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
// keep credentials *and* don't ask for new ones
m_needCredentials = false;
m_username = oldUsername;
m_password = oldPassword;
}
}
void KHttpDigestAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
}
struct DigestAuthInfo
{
QByteArray nc;
QByteArray qop;
QByteArray realm;
QByteArray nonce;
QByteArray method;
QByteArray cnonce;
QByteArray username;
QByteArray password;
KUrl::List digestURIs;
QByteArray algorithm;
QByteArray entityBody;
};
//calculateResponse() from the original HTTPProtocol
static QByteArray calculateResponse(const DigestAuthInfo &info, const KUrl &resource)
{
QCryptographicHash md(QCryptographicHash::Md5);
2014-11-13 01:04:59 +02:00
QByteArray HA1;
QByteArray HA2;
// Calculate H(A1)
QByteArray authStr = info.username;
authStr += ':';
authStr += info.realm;
authStr += ':';
authStr += info.password;
md.addData( authStr );
2014-11-13 01:04:59 +02:00
if ( info.algorithm.toLower() == "md5-sess" )
{
authStr = md.result().toHex();
2014-11-13 01:04:59 +02:00
authStr += ':';
authStr += info.nonce;
authStr += ':';
authStr += info.cnonce;
md.reset();
md.addData( authStr );
2014-11-13 01:04:59 +02:00
}
HA1 = md.result().toHex();
2014-11-13 01:04:59 +02:00
kDebug(7113) << "A1 => " << HA1;
// Calcualte H(A2)
authStr = info.method;
authStr += ':';
authStr += resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
if ( info.qop == "auth-int" )
{
authStr += ':';
md.reset();
md.addData(info.entityBody);
authStr += md.result().toHex();
2014-11-13 01:04:59 +02:00
}
md.reset();
md.addData( authStr );
HA2 = md.result().toHex();
2014-11-13 01:04:59 +02:00
kDebug(7113) << "A2 => " << HA2;
// Calcualte the response.
authStr = HA1;
authStr += ':';
authStr += info.nonce;
authStr += ':';
if ( !info.qop.isEmpty() )
{
authStr += info.nc;
authStr += ':';
authStr += info.cnonce;
authStr += ':';
authStr += info.qop;
authStr += ':';
}
authStr += HA2;
md.reset();
md.addData( authStr );
2014-11-13 01:04:59 +02:00
const QByteArray response = md.result().toHex();
2014-11-13 01:04:59 +02:00
kDebug(7113) << "Response =>" << response;
return response;
}
void KHttpDigestAuthentication::generateResponse(const QString &user, const QString &password)
{
generateResponseCommon(user, password);
if (m_isError) {
return;
}
// magic starts here (this part is slightly modified from the original in HTTPProtocol)
DigestAuthInfo info;
info.username = m_username.toLatin1(); //### charset breakage
info.password = m_password.toLatin1(); //###
// info.entityBody = p; // FIXME: send digest of data for POST action ??
info.realm = "";
info.nonce = "";
info.qop = "";
// cnonce is recommended to contain about 64 bits of entropy
#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
info.cnonce = m_nonce;
#else
info.cnonce = KRandom::randomString(16).toLatin1();
#endif
// HACK: Should be fixed according to RFC 2617 section 3.2.2
info.nc = "00000001";
// Set the method used...
info.method = m_httpMethod;
// Parse the Digest response....
info.realm = valueForKey(m_challenge, "realm");
info.algorithm = valueForKey(m_challenge, "algorithm");
if (info.algorithm.isEmpty()) {
info.algorithm = valueForKey(m_challenge, "algorith");
}
if (info.algorithm.isEmpty()) {
info.algorithm = "MD5";
}
Q_FOREACH (const QByteArray &path, valueForKey(m_challenge, "domain").split(' ')) {
KUrl u(m_resource, QString::fromLatin1(path));
if (u.isValid()) {
info.digestURIs.append(u);
}
}
info.nonce = valueForKey(m_challenge, "nonce");
QByteArray opaque = valueForKey(m_challenge, "opaque");
info.qop = valueForKey(m_challenge, "qop");
// NOTE: Since we do not have access to the entity body, we cannot support
// the "auth-int" qop value ; so if the server returns a comma separated
// list of qop values, prefer "auth".See RFC 2617 sec 3.2.2 for the details.
// If "auth" is not present or it is set to "auth-int" only, then we simply
// print a warning message and disregard the qop option altogether.
if (info.qop.contains(',')) {
const QList<QByteArray> values = info.qop.split(',');
if (info.qop.contains("auth"))
info.qop = "auth";
else {
kWarning(7113) << "Unsupported digest authentication qop parameters:" << values;
info.qop.clear();
}
} else if (info.qop == "auth-int") {
kWarning(7113) << "Unsupported digest authentication qop parameter:" << info.qop;
info.qop.clear();
}
if (info.realm.isEmpty() || info.nonce.isEmpty()) {
// ### proper error return
m_isError = true;
return;
}
// If the "domain" attribute was not specified and the current response code
// is authentication needed, add the current request url to the list over which
// this credential can be automatically applied.
if (info.digestURIs.isEmpty() /*###&& (m_request.responseCode == 401 || m_request.responseCode == 407)*/)
info.digestURIs.append (m_resource);
else
{
// Verify whether or not we should send a cached credential to the
// server based on the stored "domain" attribute...
bool send = true;
// Determine the path of the request url...
QString requestPath = m_resource.directory(KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
if (requestPath.isEmpty())
requestPath = QLatin1Char('/');
Q_FOREACH (const KUrl &u, info.digestURIs)
{
send &= (m_resource.protocol().toLower() == u.protocol().toLower());
send &= (m_resource.host().toLower() == u.host().toLower());
if (m_resource.port() > 0 && u.port() > 0)
send &= (m_resource.port() == u.port());
QString digestPath = u.directory (KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
if (digestPath.isEmpty())
digestPath = QLatin1Char('/');
send &= (requestPath.startsWith(digestPath));
if (send)
break;
}
if (!send) {
m_isError = true;
return;
}
}
kDebug(7113) << "RESULT OF PARSING:";
kDebug(7113) << " algorithm: " << info.algorithm;
kDebug(7113) << " realm: " << info.realm;
kDebug(7113) << " nonce: " << info.nonce;
kDebug(7113) << " opaque: " << opaque;
kDebug(7113) << " qop: " << info.qop;
// Calculate the response...
const QByteArray response = calculateResponse(info, m_resource);
QByteArray auth = "Digest username=\"";
auth += info.username;
auth += "\", realm=\"";
auth += info.realm;
auth += "\"";
auth += ", nonce=\"";
auth += info.nonce;
auth += "\", uri=\"";
auth += m_resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
if (!info.algorithm.isEmpty()) {
auth += "\", algorithm=";
auth += info.algorithm;
}
if ( !info.qop.isEmpty() )
{
auth += ", qop=";
auth += info.qop;
auth += ", cnonce=\"";
auth += info.cnonce;
auth += "\", nc=";
auth += info.nc;
}
auth += ", response=\"";
auth += response;
if ( !opaque.isEmpty() )
{
auth += "\", opaque=\"";
auth += opaque;
}
auth += "\"\r\n";
// magic ends here
// note that auth already contains \r\n
m_headerFragment = auth;
}
#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
void KHttpDigestAuthentication::setDigestNonceValue(const QByteArray& nonce)
{
m_nonce = nonce;
}
#endif
QByteArray KHttpNtlmAuthentication::scheme() const
{
return "NTLM";
}
void KHttpNtlmAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
const QByteArray &httpMethod)
{
QString oldUsername, oldPassword;
if (!m_finalAuthStage && !m_username.isEmpty() && !m_password.isEmpty()) {
oldUsername = m_username;
oldPassword = m_password;
}
KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
m_username = oldUsername;
m_password = oldPassword;
}
// The type 1 message we're going to send needs no credentials;
// they come later in the type 3 message.
m_needCredentials = m_challenge.isEmpty();
}
void KHttpNtlmAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
// Every auth scheme is supposed to supply a realm according to the RFCs. Of course this doesn't
// prevent Microsoft from not doing it... Dummy value!
// we don't have the username yet which may (may!) contain a domain, so we really have no choice
ai->realmValue = QLatin1String("NTLM");
}
void KHttpNtlmAuthentication::generateResponse(const QString &_user, const QString &password)
{
generateResponseCommon(_user, password);
if (m_isError) {
return;
}
QByteArray buf;
if (m_challenge.isEmpty()) {
m_finalAuthStage = false;
// first, send type 1 message (with empty domain, workstation..., but it still works)
if (!KNTLM::getNegotiate(buf)) {
kWarning(7113) << "Error while constructing Type 1 NTLM authentication request";
m_isError = true;
return;
}
} else {
m_finalAuthStage = true;
// we've (hopefully) received a valid type 2 message: send type 3 message as last step
QString user, domain;
if (m_username.contains(QLatin1Char('\\'))) {
domain = m_username.section(QLatin1Char('\\'), 0, 0);
user = m_username.section(QLatin1Char('\\'), 1);
} else {
user = m_username;
}
m_forceKeepAlive = true;
const QByteArray challenge = QByteArray::fromBase64(m_challenge[0]);
KNTLM::AuthFlags flags = KNTLM::Add_LM;
if (!m_config || !m_config->readEntry("EnableNTLMv2Auth", false)) {
flags |= KNTLM::Force_V1;
}
if (!KNTLM::getAuth(buf, challenge, user, m_password, domain, QLatin1String("WORKSTATION"), flags)) {
kWarning(7113) << "Error while constructing Type 3 NTLM authentication request";
m_isError = true;
return;
}
}
m_headerFragment = "NTLM ";
m_headerFragment += buf.toBase64();
m_headerFragment += "\r\n";
return;
}
//////////////////////////
#ifdef HAVE_LIBGSSAPI
// just an error message formatter
static QByteArray gssError(int major_status, int minor_status)
{
OM_uint32 new_status;
OM_uint32 msg_ctx = 0;
gss_buffer_desc major_string;
gss_buffer_desc minor_string;
OM_uint32 ret;
QByteArray errorstr;
do {
ret = gss_display_status(&new_status, major_status, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &major_string);
errorstr += (const char *)major_string.value;
errorstr += ' ';
ret = gss_display_status(&new_status, minor_status, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &minor_string);
errorstr += (const char *)minor_string.value;
errorstr += ' ';
} while (!GSS_ERROR(ret) && msg_ctx != 0);
return errorstr;
}
QByteArray KHttpNegotiateAuthentication::scheme() const
{
return "Negotiate";
}
void KHttpNegotiateAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
const QByteArray &httpMethod)
{
KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
// GSSAPI knows how to get the credentials on its own
m_needCredentials = false;
}
void KHttpNegotiateAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
//### does GSSAPI supply anything realm-like? dummy value for now.
ai->realmValue = QLatin1String("Negotiate");
}
void KHttpNegotiateAuthentication::generateResponse(const QString &user, const QString &password)
{
generateResponseCommon(user, password);
if (m_isError) {
return;
}
OM_uint32 major_status, minor_status;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
gss_name_t server;
gss_ctx_id_t ctx;
gss_OID mech_oid;
static gss_OID_desc krb5_oid_desc = {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
static gss_OID_desc spnego_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"};
gss_OID_set mech_set;
gss_OID tmp_oid;
ctx = GSS_C_NO_CONTEXT;
mech_oid = &krb5_oid_desc;
// see whether we can use the SPNEGO mechanism
major_status = gss_indicate_mechs(&minor_status, &mech_set);
if (GSS_ERROR(major_status)) {
kDebug(7113) << "gss_indicate_mechs failed: " << gssError(major_status, minor_status);
} else {
for (uint i = 0; i < mech_set->count; i++) {
tmp_oid = &mech_set->elements[i];
if (tmp_oid->length == spnego_oid_desc.length &&
!memcmp(tmp_oid->elements, spnego_oid_desc.elements, tmp_oid->length)) {
kDebug(7113) << "found SPNEGO mech";
mech_oid = &spnego_oid_desc;
break;
}
}
gss_release_oid_set(&minor_status, &mech_set);
}
// the service name is "HTTP/f.q.d.n"
QByteArray servicename = "HTTP@";
servicename += m_resource.host().toLatin1();
input_token.value = (void *)servicename.data();
input_token.length = servicename.length() + 1;
major_status = gss_import_name(&minor_status, &input_token,
GSS_C_NT_HOSTBASED_SERVICE, &server);
input_token.value = NULL;
input_token.length = 0;
if (GSS_ERROR(major_status)) {
kDebug(7113) << "gss_import_name failed: " << gssError(major_status, minor_status);
m_isError = true;
return;
}
OM_uint32 req_flags;
if (m_config && m_config->readEntry("DelegateCredentialsOn", false))
req_flags = GSS_C_DELEG_FLAG;
else
req_flags = 0;
// GSSAPI knows how to get the credentials its own way, so don't ask for any
major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
&ctx, server, mech_oid,
req_flags, GSS_C_INDEFINITE,
GSS_C_NO_CHANNEL_BINDINGS,
GSS_C_NO_BUFFER, NULL, &output_token,
NULL, NULL);
if (GSS_ERROR(major_status) || (output_token.length == 0)) {
kDebug(7113) << "gss_init_sec_context failed: " << gssError(major_status, minor_status);
gss_release_name(&minor_status, &server);
if (ctx != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
ctx = GSS_C_NO_CONTEXT;
}
m_isError = true;
return;
}
m_headerFragment = "Negotiate ";
m_headerFragment += QByteArray::fromRawData(static_cast<const char *>(output_token.value),
output_token.length).toBase64();
m_headerFragment += "\r\n";
// free everything
gss_release_name(&minor_status, &server);
if (ctx != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
ctx = GSS_C_NO_CONTEXT;
}
gss_release_buffer(&minor_status, &output_token);
}
#endif // HAVE_LIBGSSAPI