24 #include <gssapi/gssapi.h>
30 #if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0)
31 #include <gssapi/gssapi_generic.h>
32 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
46 #include <QtCore/QTextCodec>
51 return (ch ==
' ' || ch ==
'\t' || ch ==
'\v' || ch ==
'\f');
79 static QList<QByteArray>
parseChallenge(QByteArray &ba, QByteArray *scheme, QByteArray* nextAuth = 0)
81 QList<QByteArray> values;
82 const char *b = ba.constData();
84 int start = 0,
end = 0, pos = 0, pos2 = 0;
104 *scheme = ba.left(end);
108 while (end < len && b[end] !=
'=') {
119 if (
containsScheme(b, start, end) || (b[pos2] ==
',' && b[pos] !=
'=' && pos == len)) {
121 *nextAuth = QByteArray (b + start);
128 values.append(QByteArray (b + start, end - start));
140 if (b[start] ==
'"') {
146 if (b[end] ==
'\\') {
148 if (end + 1 >= len) {
155 }
else if (b[end] ==
'"') {
161 if (hasErr || (end == len)) {
163 kDebug(7113) <<
"error in quoted text for key" << values.last();
167 QByteArray value = QByteArray(b + start, end - start);
171 while ( (i = value.indexOf(
'\\', i + 1)) >= 0 ) {
175 values.append(value);
180 while (end < len && b[end] !=
',' && !
isWhiteSpace(b[end])) {
183 values.append(QByteArray(b + start, end - start));
192 if (end < len && b[end] !=
',') {
193 kDebug(7113) <<
"unexpected character" << b[end] <<
"found in WWW-authentication header where token boundary (,) was expected";
200 if (values.count() > 1 && values.count() % 2) {
207 static QByteArray
valueForKey(
const QList<QByteArray> &ba,
const QByteArray &key)
209 for (
int i = 0, count = ba.count(); (i + 1) < count; i += 2) {
218 :m_config(config), m_finalAuthStage(false)
230 QByteArray negotiateOffer;
231 QByteArray digestOffer;
232 QByteArray ntlmOffer;
233 QByteArray basicOffer;
234 Q_FOREACH (
const QByteArray &offer, offers) {
235 const QByteArray
scheme = offer.mid(0, offer.indexOf(
' ')).toLower();
236 #ifdef HAVE_LIBGSSAPI
237 if (scheme ==
"negotiate") {
238 negotiateOffer = offer;
241 if (scheme ==
"digest") {
243 }
else if (scheme ==
"ntlm") {
245 }
else if (scheme ==
"basic") {
250 if (!negotiateOffer.isEmpty()) {
251 return negotiateOffer;
254 if (!digestOffer.isEmpty()) {
258 if (!ntlmOffer.isEmpty()) {
268 const QByteArray
scheme = offer.mid(0, offer.indexOf(
' ')).toLower();
269 #ifdef HAVE_LIBGSSAPI
270 if (scheme ==
"negotiate") {
271 return new KHttpNegotiateAuthentication(config);
274 if (scheme ==
"digest") {
276 }
else if (scheme ==
"ntlm") {
278 }
else if (scheme ==
"basic") {
287 QList<QByteArray> alloffers;
288 foreach(QByteArray offer, offers) {
293 while (!cont.isEmpty()) {
294 offer.chop(cont.length());
323 const QByteArray &httpMethod)
340 return QTextCodec::codecForName(
"CP1251")->toUnicode(realm);
342 return QString::fromLatin1(realm.constData(), realm.length());
407 const QByteArray &httpMethod)
418 if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
433 struct DigestAuthInfo
444 QByteArray algorithm;
445 QByteArray entityBody;
457 QByteArray authStr = info.username;
459 authStr += info.realm;
461 authStr += info.password;
464 if ( info.algorithm.toLower() ==
"md5-sess" )
468 authStr += info.nonce;
470 authStr += info.cnonce;
476 kDebug(7113) <<
"A1 => " << HA1;
479 authStr = info.method;
482 if ( info.qop ==
"auth-int" )
486 md.
update(info.entityBody);
493 kDebug(7113) <<
"A2 => " << HA2;
498 authStr += info.nonce;
500 if ( !info.qop.isEmpty() )
504 authStr += info.cnonce;
513 const QByteArray response = md.
hexDigest();
514 kDebug(7113) <<
"Response =>" << response;
539 #ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
540 info.cnonce = m_nonce;
546 info.nc =
"00000001";
555 if (info.algorithm.isEmpty()) {
558 if (info.algorithm.isEmpty()) {
559 info.algorithm =
"MD5";
565 info.digestURIs.append(u);
578 if (info.qop.contains(
',')) {
579 const QList<QByteArray> values = info.qop.split(
',');
580 if (info.qop.contains(
"auth"))
583 kWarning(7113) <<
"Unsupported digest authentication qop parameters:" << values;
586 }
else if (info.qop ==
"auth-int") {
587 kWarning(7113) <<
"Unsupported digest authentication qop parameter:" << info.qop;
591 if (info.realm.isEmpty() || info.nonce.isEmpty()) {
600 if (info.digestURIs.isEmpty() )
601 info.digestURIs.append (m_resource);
610 if (requestPath.isEmpty())
611 requestPath = QLatin1Char(
'/');
613 Q_FOREACH (
const KUrl &u, info.digestURIs)
615 send &= (m_resource.protocol().toLower() == u.
protocol().toLower());
616 send &= (m_resource.host().toLower() == u.host().toLower());
618 if (m_resource.port() > 0 && u.port() > 0)
619 send &= (m_resource.port() == u.port());
622 if (digestPath.isEmpty())
623 digestPath = QLatin1Char(
'/');
625 send &= (requestPath.startsWith(digestPath));
637 kDebug(7113) <<
"RESULT OF PARSING:";
638 kDebug(7113) <<
" algorithm: " << info.algorithm;
639 kDebug(7113) <<
" realm: " << info.realm;
640 kDebug(7113) <<
" nonce: " << info.nonce;
641 kDebug(7113) <<
" opaque: " << opaque;
642 kDebug(7113) <<
" qop: " << info.qop;
647 QByteArray auth =
"Digest username=\"";
648 auth += info.username;
650 auth +=
"\", realm=\"";
654 auth +=
", nonce=\"";
657 auth +=
"\", uri=\"";
660 if (!info.algorithm.isEmpty()) {
661 auth +=
"\", algorithm=";
662 auth += info.algorithm;
665 if ( !info.qop.isEmpty() )
669 auth +=
", cnonce=\"";
675 auth +=
", response=\"";
677 if ( !opaque.isEmpty() )
679 auth +=
"\", opaque=\"";
686 m_headerFragment = auth;
689 #ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
690 void KHttpDigestAuthentication::setDigestNonceValue(
const QByteArray& nonce)
704 const QByteArray &httpMethod)
706 QString oldUsername, oldPassword;
712 if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
745 kWarning(7113) <<
"Error while constructing Type 1 NTLM authentication request";
754 domain =
m_username.section(QLatin1Char(
'\\'), 0, 0);
755 user =
m_username.section(QLatin1Char(
'\\'), 1);
761 const QByteArray challenge = QByteArray::fromBase64(
m_challenge[0]);
769 kWarning(7113) <<
"Error while constructing Type 3 NTLM authentication request";
784 #ifdef HAVE_LIBGSSAPI
787 static QByteArray gssError(
int major_status,
int minor_status)
789 OM_uint32 new_status;
790 OM_uint32 msg_ctx = 0;
791 gss_buffer_desc major_string;
792 gss_buffer_desc minor_string;
797 ret = gss_display_status(&new_status, major_status, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &major_string);
798 errorstr += (
const char *)major_string.value;
800 ret = gss_display_status(&new_status, minor_status, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &minor_string);
801 errorstr += (
const char *)minor_string.value;
803 }
while (!GSS_ERROR(ret) && msg_ctx != 0);
809 QByteArray KHttpNegotiateAuthentication::scheme()
const
815 void KHttpNegotiateAuthentication::setChallenge(
const QByteArray &c,
const KUrl &resource,
816 const QByteArray &httpMethod)
820 m_needCredentials =
false;
824 void KHttpNegotiateAuthentication::fillKioAuthInfo(
KIO::AuthInfo *ai)
const
826 authInfoBoilerplate(ai);
832 void KHttpNegotiateAuthentication::generateResponse(
const QString &user,
const QString &password)
834 generateResponseCommon(user, password);
839 OM_uint32 major_status, minor_status;
840 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
841 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
845 static gss_OID_desc krb5_oid_desc = {9, (
void *)
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
846 static gss_OID_desc spnego_oid_desc = {6, (
void *)
"\x2b\x06\x01\x05\x05\x02"};
847 gss_OID_set mech_set;
850 ctx = GSS_C_NO_CONTEXT;
851 mech_oid = &krb5_oid_desc;
854 major_status = gss_indicate_mechs(&minor_status, &mech_set);
855 if (GSS_ERROR(major_status)) {
856 kDebug(7113) <<
"gss_indicate_mechs failed: " << gssError(major_status, minor_status);
858 for (uint i = 0; i < mech_set->count; i++) {
859 tmp_oid = &mech_set->elements[i];
860 if (tmp_oid->length == spnego_oid_desc.length &&
861 !memcmp(tmp_oid->elements, spnego_oid_desc.elements, tmp_oid->length)) {
862 kDebug(7113) <<
"found SPNEGO mech";
863 mech_oid = &spnego_oid_desc;
867 gss_release_oid_set(&minor_status, &mech_set);
871 QByteArray servicename =
"HTTP@";
872 servicename += m_resource.host().toLatin1();
874 input_token.value = (
void *)servicename.data();
875 input_token.length = servicename.length() + 1;
877 major_status = gss_import_name(&minor_status, &input_token,
878 GSS_C_NT_HOSTBASED_SERVICE, &server);
880 input_token.value = NULL;
881 input_token.length = 0;
883 if (GSS_ERROR(major_status)) {
884 kDebug(7113) <<
"gss_import_name failed: " << gssError(major_status, minor_status);
890 if (m_config && m_config->readEntry(
"DelegateCredentialsOn",
false))
891 req_flags = GSS_C_DELEG_FLAG;
896 major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
897 &ctx, server, mech_oid,
898 req_flags, GSS_C_INDEFINITE,
899 GSS_C_NO_CHANNEL_BINDINGS,
900 GSS_C_NO_BUFFER, NULL, &output_token,
903 if (GSS_ERROR(major_status) || (output_token.length == 0)) {
904 kDebug(7113) <<
"gss_init_sec_context failed: " << gssError(major_status, minor_status);
905 gss_release_name(&minor_status, &server);
906 if (ctx != GSS_C_NO_CONTEXT) {
907 gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
908 ctx = GSS_C_NO_CONTEXT;
914 m_headerFragment =
"Negotiate ";
915 m_headerFragment += QByteArray::fromRawData(static_cast<const char *>(output_token.value),
916 output_token.length).toBase64();
917 m_headerFragment +=
"\r\n";
920 gss_release_name(&minor_status, &server);
921 if (ctx != GSS_C_NO_CONTEXT) {
922 gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
923 ctx = GSS_C_NO_CONTEXT;
925 gss_release_buffer(&minor_status, &output_token);
928 #endif // HAVE_LIBGSSAPI