• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KIOSlave

  • kioslave
  • http
httpauthentication.cpp
Go to the documentation of this file.
1/* This file is part of the KDE libraries
2 Copyright (C) 2008, 2009 Andreas Hartmetz <ahartmetz@gmail.com>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "httpauthentication.h"
21
22#ifdef HAVE_LIBGSSAPI
23#ifdef GSSAPI_MIT
24#include <gssapi/gssapi.h>
25#else
26#include <gssapi.h>
27#endif /* GSSAPI_MIT */
28
29// Catch uncompatible crap (BR86019)
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
33#endif
34
35#endif /* HAVE_LIBGSSAPI */
36
37#include <krandom.h>
38#include <kdebug.h>
39#include <klocale.h>
40#include <kglobal.h>
41#include <kcodecs.h>
42#include <kconfiggroup.h>
43#include <kio/authinfo.h>
44#include <misc/kntlm/kntlm.h>
45
46#include <QtCore/QTextCodec>
47
48
49static bool isWhiteSpace(char ch)
50{
51 return (ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f');
52}
53
54static bool isWhiteSpaceOrComma(char ch)
55{
56 return (ch == ',' || isWhiteSpace(ch));
57}
58
59static bool containsScheme(const char input[], int start, int end)
60{
61 // skip any comma or white space
62 while (start < end && isWhiteSpaceOrComma(input[start])) {
63 start++;
64 }
65
66 while (start < end) {
67 if (isWhiteSpace(input[start])) {
68 return true;
69 }
70 start++;
71 }
72
73 return false;
74}
75
76// keys on even indexes, values on odd indexes. Reduces code expansion for the templated
77// alternatives.
78// If "ba" starts with empty content it will be removed from ba to simplify later calls
79static QList<QByteArray> parseChallenge(QByteArray &ba, QByteArray *scheme, QByteArray* nextAuth = 0)
80{
81 QList<QByteArray> values;
82 const char *b = ba.constData();
83 int len = ba.count();
84 int start = 0, end = 0, pos = 0, pos2 = 0;
85
86 // parse scheme
87 while (start < len && isWhiteSpaceOrComma(b[start])) {
88 start++;
89 }
90 end = start;
91 while (end < len && !isWhiteSpace(b[end])) {
92 end++;
93 }
94
95 // drop empty stuff from the given string, it would have to be skipped over and over again
96 if (start != 0) {
97 ba = ba.mid(start);
98 end -= start;
99 len -= start;
100 start = 0;
101 b = ba.constData();
102 }
103 Q_ASSERT(scheme);
104 *scheme = ba.left(end);
105
106 while (end < len) {
107 start = end;
108 while (end < len && b[end] != '=') {
109 end++;
110 }
111 pos = end; // save the end position
112 while (end - 1 > start && isWhiteSpace(b[end - 1])) { // trim whitespace
113 end--;
114 }
115 pos2 = start;
116 while (pos2 < end && isWhiteSpace(b[pos2])) { // skip whitespace
117 pos2++;
118 }
119 if (containsScheme(b, start, end) || (b[pos2] == ',' && b[pos] != '=' && pos == len)) {
120 if (nextAuth) {
121 *nextAuth = QByteArray (b + start);
122 }
123 break; // break on start of next scheme.
124 }
125 while (start < len && isWhiteSpaceOrComma(b[start])) {
126 start++;
127 }
128 values.append(QByteArray (b + start, end - start));
129 end = pos; // restore the end position
130 if (end == len) {
131 break;
132 }
133
134 // parse value
135 start = end + 1; //skip '='
136 while (start < len && isWhiteSpace(b[start])) {
137 start++;
138 }
139
140 if (b[start] == '"') {
141 //quoted string
142 bool hasBs = false;
143 bool hasErr = false;
144 end = ++start;
145 while (end < len) {
146 if (b[end] == '\\') {
147 end++;
148 if (end + 1 >= len) {
149 hasErr = true;
150 break;
151 } else {
152 hasBs = true;
153 end++;
154 }
155 } else if (b[end] == '"') {
156 break;
157 } else {
158 end++;
159 }
160 }
161 if (hasErr || (end == len)) {
162 // remove the key we already inserted
163 kDebug(7113) << "error in quoted text for key" << values.last();
164 values.removeLast();
165 break;
166 }
167 QByteArray value = QByteArray(b + start, end - start);
168 if (hasBs) {
169 // skip over the next character, it might be an escaped backslash
170 int i = -1;
171 while ( (i = value.indexOf('\\', i + 1)) >= 0 ) {
172 value.remove(i, 1);
173 }
174 }
175 values.append(value);
176 end++;
177 } else {
178 //unquoted string
179 end = start;
180 while (end < len && b[end] != ',' && !isWhiteSpace(b[end])) {
181 end++;
182 }
183 values.append(QByteArray(b + start, end - start));
184 }
185
186 //the quoted string has ended, but only a comma ends a key-value pair
187 while (end < len && isWhiteSpace(b[end])) {
188 end++;
189 }
190
191 // garbage, here should be end or field delimiter (comma)
192 if (end < len && b[end] != ',') {
193 kDebug(7113) << "unexpected character" << b[end] << "found in WWW-authentication header where token boundary (,) was expected";
194 break;
195 }
196 }
197 // ensure every key has a value
198 // WARNING: Do not remove the > 1 check or parsing a Type 1 NTLM
199 // authentication challenge will surely fail.
200 if (values.count() > 1 && values.count() % 2) {
201 values.removeLast();
202 }
203 return values;
204}
205
206
207static QByteArray valueForKey(const QList<QByteArray> &ba, const QByteArray &key)
208{
209 for (int i = 0, count = ba.count(); (i + 1) < count; i += 2) {
210 if (ba[i] == key) {
211 return ba[i + 1];
212 }
213 }
214 return QByteArray();
215}
216
217KAbstractHttpAuthentication::KAbstractHttpAuthentication(KConfigGroup *config)
218 :m_config(config), m_finalAuthStage(false)
219{
220 reset();
221}
222
223KAbstractHttpAuthentication::~KAbstractHttpAuthentication()
224{
225}
226
227QByteArray KAbstractHttpAuthentication::bestOffer(const QList<QByteArray> &offers)
228{
229 // choose the most secure auth scheme offered
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") { // krazy:exclude=strings
238 negotiateOffer = offer;
239 } else
240#endif
241 if (scheme == "digest") { // krazy:exclude=strings
242 digestOffer = offer;
243 } else if (scheme == "ntlm") { // krazy:exclude=strings
244 ntlmOffer = offer;
245 } else if (scheme == "basic") { // krazy:exclude=strings
246 basicOffer = offer;
247 }
248 }
249
250 if (!negotiateOffer.isEmpty()) {
251 return negotiateOffer;
252 }
253
254 if (!digestOffer.isEmpty()) {
255 return digestOffer;
256 }
257
258 if (!ntlmOffer.isEmpty()) {
259 return ntlmOffer;
260 }
261
262 return basicOffer; //empty or not...
263}
264
265
266KAbstractHttpAuthentication *KAbstractHttpAuthentication::newAuth(const QByteArray &offer, KConfigGroup* config)
267{
268 const QByteArray scheme = offer.mid(0, offer.indexOf(' ')).toLower();
269#ifdef HAVE_LIBGSSAPI
270 if (scheme == "negotiate") { // krazy:exclude=strings
271 return new KHttpNegotiateAuthentication(config);
272 } else
273#endif
274 if (scheme == "digest") { // krazy:exclude=strings
275 return new KHttpDigestAuthentication();
276 } else if (scheme == "ntlm") { // krazy:exclude=strings
277 return new KHttpNtlmAuthentication(config);
278 } else if (scheme == "basic") { // krazy:exclude=strings
279 return new KHttpBasicAuthentication();
280 }
281 return 0;
282}
283
284QList< QByteArray > KAbstractHttpAuthentication::splitOffers(const QList< QByteArray >& offers)
285{
286 // first detect if one entry may contain multiple offers
287 QList<QByteArray> alloffers;
288 foreach(QByteArray offer, offers) {
289 QByteArray scheme, cont;
290
291 parseChallenge(offer, &scheme, &cont);
292
293 while (!cont.isEmpty()) {
294 offer.chop(cont.length());
295 alloffers << offer;
296 offer = cont;
297 cont.clear();
298 parseChallenge(offer, &scheme, &cont);
299 }
300 alloffers << offer;
301 }
302 return alloffers;
303}
304
305void KAbstractHttpAuthentication::reset()
306{
307 m_scheme.clear();
308 m_challenge.clear();
309 m_challengeText.clear();
310 m_resource.clear();
311 m_httpMethod.clear();
312 m_isError = false;
313 m_needCredentials = true;
314 m_forceKeepAlive = false;
315 m_forceDisconnect = false;
316 m_keepPassword = false;
317 m_headerFragment.clear();
318 m_username.clear();
319 m_password.clear();
320}
321
322void KAbstractHttpAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
323 const QByteArray &httpMethod)
324{
325 reset();
326 m_challengeText = c.trimmed();
327 m_challenge = parseChallenge(m_challengeText, &m_scheme);
328 Q_ASSERT(m_scheme.toLower() == scheme().toLower());
329 m_resource = resource;
330 m_httpMethod = httpMethod;
331}
332
333
334QString KAbstractHttpAuthentication::realm() const
335{
336 const QByteArray realm = valueForKey(m_challenge, "realm");
337 // TODO: Find out what this is supposed to address. The site mentioned below does not exist.
338 if (KGlobal::locale()->language().contains(QLatin1String("ru"))) {
339 //for sites like lib.homelinux.org
340 return QTextCodec::codecForName("CP1251")->toUnicode(realm);
341 }
342 return QString::fromLatin1(realm.constData(), realm.length());
343}
344
345void KAbstractHttpAuthentication::authInfoBoilerplate(KIO::AuthInfo *a) const
346{
347 a->url = m_resource;
348 a->username = m_username;
349 a->password = m_password;
350 a->verifyPath = supportsPathMatching();
351 a->realmValue = realm();
352 a->digestInfo = QLatin1String(authDataToCache());
353 a->keepPassword = m_keepPassword;
354}
355
356
357void KAbstractHttpAuthentication::generateResponseCommon(const QString &user, const QString &password)
358{
359 if (m_scheme.isEmpty() || m_httpMethod.isEmpty()) {
360 m_isError = true;
361 return;
362 }
363
364 if (m_needCredentials) {
365 m_username = user;
366 m_password = password;
367 }
368
369 m_isError = false;
370 m_forceKeepAlive = false;
371 m_forceDisconnect = false;
372 m_finalAuthStage = true;
373}
374
375
376QByteArray KHttpBasicAuthentication::scheme() const
377{
378 return "Basic";
379}
380
381
382void KHttpBasicAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
383{
384 authInfoBoilerplate(ai);
385}
386
387void KHttpBasicAuthentication::generateResponse(const QString &user, const QString &password)
388{
389 generateResponseCommon(user, password);
390 if (m_isError) {
391 return;
392 }
393
394 m_headerFragment = "Basic ";
395 m_headerFragment += QByteArray(m_username.toLatin1() + ':' + m_password.toLatin1()).toBase64();
396 m_headerFragment += "\r\n";
397}
398
399
400QByteArray KHttpDigestAuthentication::scheme() const
401{
402 return "Digest";
403}
404
405
406void KHttpDigestAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
407 const QByteArray &httpMethod)
408{
409 QString oldUsername;
410 QString oldPassword;
411 if (valueForKey(m_challenge, "stale").toLower() == "true") {
412 // stale nonce: the auth failure that triggered this round of authentication is an artifact
413 // of digest authentication. the credentials are probably still good, so keep them.
414 oldUsername = m_username;
415 oldPassword = m_password;
416 }
417 KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
418 if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
419 // keep credentials *and* don't ask for new ones
420 m_needCredentials = false;
421 m_username = oldUsername;
422 m_password = oldPassword;
423 }
424}
425
426
427void KHttpDigestAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
428{
429 authInfoBoilerplate(ai);
430}
431
432
433struct DigestAuthInfo
434{
435 QByteArray nc;
436 QByteArray qop;
437 QByteArray realm;
438 QByteArray nonce;
439 QByteArray method;
440 QByteArray cnonce;
441 QByteArray username;
442 QByteArray password;
443 KUrl::List digestURIs;
444 QByteArray algorithm;
445 QByteArray entityBody;
446};
447
448
449//calculateResponse() from the original HTTPProtocol
450static QByteArray calculateResponse(const DigestAuthInfo &info, const KUrl &resource)
451{
452 KMD5 md;
453 QByteArray HA1;
454 QByteArray HA2;
455
456 // Calculate H(A1)
457 QByteArray authStr = info.username;
458 authStr += ':';
459 authStr += info.realm;
460 authStr += ':';
461 authStr += info.password;
462 md.update( authStr );
463
464 if ( info.algorithm.toLower() == "md5-sess" )
465 {
466 authStr = md.hexDigest();
467 authStr += ':';
468 authStr += info.nonce;
469 authStr += ':';
470 authStr += info.cnonce;
471 md.reset();
472 md.update( authStr );
473 }
474 HA1 = md.hexDigest();
475
476 kDebug(7113) << "A1 => " << HA1;
477
478 // Calcualte H(A2)
479 authStr = info.method;
480 authStr += ':';
481 authStr += resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
482 if ( info.qop == "auth-int" )
483 {
484 authStr += ':';
485 md.reset();
486 md.update(info.entityBody);
487 authStr += md.hexDigest();
488 }
489 md.reset();
490 md.update( authStr );
491 HA2 = md.hexDigest();
492
493 kDebug(7113) << "A2 => " << HA2;
494
495 // Calcualte the response.
496 authStr = HA1;
497 authStr += ':';
498 authStr += info.nonce;
499 authStr += ':';
500 if ( !info.qop.isEmpty() )
501 {
502 authStr += info.nc;
503 authStr += ':';
504 authStr += info.cnonce;
505 authStr += ':';
506 authStr += info.qop;
507 authStr += ':';
508 }
509 authStr += HA2;
510 md.reset();
511 md.update( authStr );
512
513 const QByteArray response = md.hexDigest();
514 kDebug(7113) << "Response =>" << response;
515 return response;
516}
517
518
519void KHttpDigestAuthentication::generateResponse(const QString &user, const QString &password)
520{
521 generateResponseCommon(user, password);
522 if (m_isError) {
523 return;
524 }
525
526 // magic starts here (this part is slightly modified from the original in HTTPProtocol)
527
528 DigestAuthInfo info;
529
530 info.username = m_username.toLatin1(); //### charset breakage
531 info.password = m_password.toLatin1(); //###
532
533 // info.entityBody = p; // FIXME: send digest of data for POST action ??
534 info.realm = "";
535 info.nonce = "";
536 info.qop = "";
537
538 // cnonce is recommended to contain about 64 bits of entropy
539#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
540 info.cnonce = m_nonce;
541#else
542 info.cnonce = KRandom::randomString(16).toLatin1();
543#endif
544
545 // HACK: Should be fixed according to RFC 2617 section 3.2.2
546 info.nc = "00000001";
547
548 // Set the method used...
549 info.method = m_httpMethod;
550
551 // Parse the Digest response....
552 info.realm = valueForKey(m_challenge, "realm");
553
554 info.algorithm = valueForKey(m_challenge, "algorithm");
555 if (info.algorithm.isEmpty()) {
556 info.algorithm = valueForKey(m_challenge, "algorith");
557 }
558 if (info.algorithm.isEmpty()) {
559 info.algorithm = "MD5";
560 }
561
562 Q_FOREACH (const QByteArray &path, valueForKey(m_challenge, "domain").split(' ')) {
563 KUrl u(m_resource, QString::fromLatin1(path));
564 if (u.isValid()) {
565 info.digestURIs.append(u);
566 }
567 }
568
569 info.nonce = valueForKey(m_challenge, "nonce");
570 QByteArray opaque = valueForKey(m_challenge, "opaque");
571 info.qop = valueForKey(m_challenge, "qop");
572
573 // NOTE: Since we do not have access to the entity body, we cannot support
574 // the "auth-int" qop value ; so if the server returns a comma separated
575 // list of qop values, prefer "auth".See RFC 2617 sec 3.2.2 for the details.
576 // If "auth" is not present or it is set to "auth-int" only, then we simply
577 // print a warning message and disregard the qop option altogether.
578 if (info.qop.contains(',')) {
579 const QList<QByteArray> values = info.qop.split(',');
580 if (info.qop.contains("auth"))
581 info.qop = "auth";
582 else {
583 kWarning(7113) << "Unsupported digest authentication qop parameters:" << values;
584 info.qop.clear();
585 }
586 } else if (info.qop == "auth-int") {
587 kWarning(7113) << "Unsupported digest authentication qop parameter:" << info.qop;
588 info.qop.clear();
589 }
590
591 if (info.realm.isEmpty() || info.nonce.isEmpty()) {
592 // ### proper error return
593 m_isError = true;
594 return;
595 }
596
597 // If the "domain" attribute was not specified and the current response code
598 // is authentication needed, add the current request url to the list over which
599 // this credential can be automatically applied.
600 if (info.digestURIs.isEmpty() /*###&& (m_request.responseCode == 401 || m_request.responseCode == 407)*/)
601 info.digestURIs.append (m_resource);
602 else
603 {
604 // Verify whether or not we should send a cached credential to the
605 // server based on the stored "domain" attribute...
606 bool send = true;
607
608 // Determine the path of the request url...
609 QString requestPath = m_resource.directory(KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
610 if (requestPath.isEmpty())
611 requestPath = QLatin1Char('/');
612
613 Q_FOREACH (const KUrl &u, info.digestURIs)
614 {
615 send &= (m_resource.protocol().toLower() == u.protocol().toLower());
616 send &= (m_resource.host().toLower() == u.host().toLower());
617
618 if (m_resource.port() > 0 && u.port() > 0)
619 send &= (m_resource.port() == u.port());
620
621 QString digestPath = u.directory (KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
622 if (digestPath.isEmpty())
623 digestPath = QLatin1Char('/');
624
625 send &= (requestPath.startsWith(digestPath));
626
627 if (send)
628 break;
629 }
630
631 if (!send) {
632 m_isError = true;
633 return;
634 }
635 }
636
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;
643
644 // Calculate the response...
645 const QByteArray response = calculateResponse(info, m_resource);
646
647 QByteArray auth = "Digest username=\"";
648 auth += info.username;
649
650 auth += "\", realm=\"";
651 auth += info.realm;
652 auth += "\"";
653
654 auth += ", nonce=\"";
655 auth += info.nonce;
656
657 auth += "\", uri=\"";
658 auth += m_resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
659
660 if (!info.algorithm.isEmpty()) {
661 auth += "\", algorithm=";
662 auth += info.algorithm;
663 }
664
665 if ( !info.qop.isEmpty() )
666 {
667 auth += ", qop=";
668 auth += info.qop;
669 auth += ", cnonce=\"";
670 auth += info.cnonce;
671 auth += "\", nc=";
672 auth += info.nc;
673 }
674
675 auth += ", response=\"";
676 auth += response;
677 if ( !opaque.isEmpty() )
678 {
679 auth += "\", opaque=\"";
680 auth += opaque;
681 }
682 auth += "\"\r\n";
683
684 // magic ends here
685 // note that auth already contains \r\n
686 m_headerFragment = auth;
687}
688
689#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
690void KHttpDigestAuthentication::setDigestNonceValue(const QByteArray& nonce)
691{
692 m_nonce = nonce;
693}
694#endif
695
696
697QByteArray KHttpNtlmAuthentication::scheme() const
698{
699 return "NTLM";
700}
701
702
703void KHttpNtlmAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
704 const QByteArray &httpMethod)
705{
706 QString oldUsername, oldPassword;
707 if (!m_finalAuthStage && !m_username.isEmpty() && !m_password.isEmpty()) {
708 oldUsername = m_username;
709 oldPassword = m_password;
710 }
711 KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
712 if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
713 m_username = oldUsername;
714 m_password = oldPassword;
715 }
716 // The type 1 message we're going to send needs no credentials;
717 // they come later in the type 3 message.
718 m_needCredentials = m_challenge.isEmpty();
719}
720
721
722void KHttpNtlmAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
723{
724 authInfoBoilerplate(ai);
725 // Every auth scheme is supposed to supply a realm according to the RFCs. Of course this doesn't
726 // prevent Microsoft from not doing it... Dummy value!
727 // we don't have the username yet which may (may!) contain a domain, so we really have no choice
728 ai->realmValue = QLatin1String("NTLM");
729}
730
731
732void KHttpNtlmAuthentication::generateResponse(const QString &_user, const QString &password)
733{
734 generateResponseCommon(_user, password);
735 if (m_isError) {
736 return;
737 }
738
739 QByteArray buf;
740
741 if (m_challenge.isEmpty()) {
742 m_finalAuthStage = false;
743 // first, send type 1 message (with empty domain, workstation..., but it still works)
744 if (!KNTLM::getNegotiate(buf)) {
745 kWarning(7113) << "Error while constructing Type 1 NTLM authentication request";
746 m_isError = true;
747 return;
748 }
749 } else {
750 m_finalAuthStage = true;
751 // we've (hopefully) received a valid type 2 message: send type 3 message as last step
752 QString user, domain;
753 if (m_username.contains(QLatin1Char('\\'))) {
754 domain = m_username.section(QLatin1Char('\\'), 0, 0);
755 user = m_username.section(QLatin1Char('\\'), 1);
756 } else {
757 user = m_username;
758 }
759
760 m_forceKeepAlive = true;
761 const QByteArray challenge = QByteArray::fromBase64(m_challenge[0]);
762
763 KNTLM::AuthFlags flags = KNTLM::Add_LM;
764 if (!m_config || !m_config->readEntry("EnableNTLMv2Auth", false)) {
765 flags |= KNTLM::Force_V1;
766 }
767
768 if (!KNTLM::getAuth(buf, challenge, user, m_password, domain, QLatin1String("WORKSTATION"), flags)) {
769 kWarning(7113) << "Error while constructing Type 3 NTLM authentication request";
770 m_isError = true;
771 return;
772 }
773 }
774
775 m_headerFragment = "NTLM ";
776 m_headerFragment += buf.toBase64();
777 m_headerFragment += "\r\n";
778
779 return;
780}
781
782
784#ifdef HAVE_LIBGSSAPI
785
786// just an error message formatter
787static QByteArray gssError(int major_status, int minor_status)
788{
789 OM_uint32 new_status;
790 OM_uint32 msg_ctx = 0;
791 gss_buffer_desc major_string;
792 gss_buffer_desc minor_string;
793 OM_uint32 ret;
794 QByteArray errorstr;
795
796 do {
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;
799 errorstr += ' ';
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;
802 errorstr += ' ';
803 } while (!GSS_ERROR(ret) && msg_ctx != 0);
804
805 return errorstr;
806}
807
808
809QByteArray KHttpNegotiateAuthentication::scheme() const
810{
811 return "Negotiate";
812}
813
814
815void KHttpNegotiateAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
816 const QByteArray &httpMethod)
817{
818 KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
819 // GSSAPI knows how to get the credentials on its own
820 m_needCredentials = false;
821}
822
823
824void KHttpNegotiateAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
825{
826 authInfoBoilerplate(ai);
827 //### does GSSAPI supply anything realm-like? dummy value for now.
828 ai->realmValue = QLatin1String("Negotiate");
829}
830
831
832void KHttpNegotiateAuthentication::generateResponse(const QString &user, const QString &password)
833{
834 generateResponseCommon(user, password);
835 if (m_isError) {
836 return;
837 }
838
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;
842 gss_name_t server;
843 gss_ctx_id_t ctx;
844 gss_OID mech_oid;
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;
848 gss_OID tmp_oid;
849
850 ctx = GSS_C_NO_CONTEXT;
851 mech_oid = &krb5_oid_desc;
852
853 // see whether we can use the SPNEGO mechanism
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);
857 } else {
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;
864 break;
865 }
866 }
867 gss_release_oid_set(&minor_status, &mech_set);
868 }
869
870 // the service name is "HTTP/f.q.d.n"
871 QByteArray servicename = "HTTP@";
872 servicename += m_resource.host().toLatin1();
873
874 input_token.value = (void *)servicename.data();
875 input_token.length = servicename.length() + 1;
876
877 major_status = gss_import_name(&minor_status, &input_token,
878 GSS_C_NT_HOSTBASED_SERVICE, &server);
879
880 input_token.value = NULL;
881 input_token.length = 0;
882
883 if (GSS_ERROR(major_status)) {
884 kDebug(7113) << "gss_import_name failed: " << gssError(major_status, minor_status);
885 m_isError = true;
886 return;
887 }
888
889 OM_uint32 req_flags;
890 if (m_config && m_config->readEntry("DelegateCredentialsOn", false))
891 req_flags = GSS_C_DELEG_FLAG;
892 else
893 req_flags = 0;
894
895 // GSSAPI knows how to get the credentials its own way, so don't ask for any
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,
901 NULL, NULL);
902
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;
909 }
910 m_isError = true;
911 return;
912 }
913
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";
918
919 // free everything
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;
924 }
925 gss_release_buffer(&minor_status, &output_token);
926}
927
928#endif // HAVE_LIBGSSAPI
authinfo.h
KAbstractHttpAuthentication
Definition: httpauthentication.h:38
KAbstractHttpAuthentication::scheme
virtual QByteArray scheme() const =0
the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
KAbstractHttpAuthentication::m_username
QString m_username
Definition: httpauthentication.h:177
KAbstractHttpAuthentication::m_scheme
QByteArray m_scheme
this is parsed from the header and not necessarily == scheme().
Definition: httpauthentication.h:163
KAbstractHttpAuthentication::bestOffer
static QByteArray bestOffer(const QList< QByteArray > &offers)
Choose the best authentication mechanism from the offered ones.
Definition: httpauthentication.cpp:227
KAbstractHttpAuthentication::m_resource
KUrl m_resource
Definition: httpauthentication.h:166
KAbstractHttpAuthentication::m_finalAuthStage
bool m_finalAuthStage
Definition: httpauthentication.h:173
KAbstractHttpAuthentication::splitOffers
static QList< QByteArray > splitOffers(const QList< QByteArray > &offers)
Split all headers containing multiple authentication offers.
Definition: httpauthentication.cpp:284
KAbstractHttpAuthentication::~KAbstractHttpAuthentication
virtual ~KAbstractHttpAuthentication()
Definition: httpauthentication.cpp:223
KAbstractHttpAuthentication::m_keepPassword
bool m_keepPassword
Definition: httpauthentication.h:174
KAbstractHttpAuthentication::generateResponseCommon
void generateResponseCommon(const QString &user, const QString &password)
Definition: httpauthentication.cpp:357
KAbstractHttpAuthentication::m_headerFragment
QByteArray m_headerFragment
Definition: httpauthentication.h:175
KAbstractHttpAuthentication::m_forceDisconnect
bool m_forceDisconnect
Definition: httpauthentication.h:172
KAbstractHttpAuthentication::m_challenge
QList< QByteArray > m_challenge
Definition: httpauthentication.h:165
KAbstractHttpAuthentication::authDataToCache
virtual QByteArray authDataToCache() const
Returns any authentication data that should be cached for future use.
Definition: httpauthentication.h:159
KAbstractHttpAuthentication::m_isError
bool m_isError
Definition: httpauthentication.h:169
KAbstractHttpAuthentication::m_challengeText
QByteArray m_challengeText
Definition: httpauthentication.h:164
KAbstractHttpAuthentication::m_password
QString m_password
Definition: httpauthentication.h:178
KAbstractHttpAuthentication::supportsPathMatching
virtual bool supportsPathMatching() const
Returns true if the authentication scheme supports path matching to identify resources that belong to...
Definition: httpauthentication.h:111
KAbstractHttpAuthentication::m_needCredentials
bool m_needCredentials
Definition: httpauthentication.h:170
KAbstractHttpAuthentication::authInfoBoilerplate
void authInfoBoilerplate(KIO::AuthInfo *a) const
Definition: httpauthentication.cpp:345
KAbstractHttpAuthentication::m_httpMethod
QByteArray m_httpMethod
Definition: httpauthentication.h:167
KAbstractHttpAuthentication::KAbstractHttpAuthentication
KAbstractHttpAuthentication(KConfigGroup *config=0)
Definition: httpauthentication.cpp:217
KAbstractHttpAuthentication::realm
QString realm() const
Returns the realm sent by the server.
Definition: httpauthentication.cpp:334
KAbstractHttpAuthentication::setChallenge
virtual void setChallenge(const QByteArray &c, const KUrl &resource, const QByteArray &httpMethod)
initiate authentication with challenge string (from HTTP header)
Definition: httpauthentication.cpp:322
KAbstractHttpAuthentication::m_forceKeepAlive
bool m_forceKeepAlive
Definition: httpauthentication.h:171
KAbstractHttpAuthentication::reset
void reset()
reset to state after default construction.
Definition: httpauthentication.cpp:305
KAbstractHttpAuthentication::m_config
KConfigGroup * m_config
Definition: httpauthentication.h:162
KAbstractHttpAuthentication::newAuth
static KAbstractHttpAuthentication * newAuth(const QByteArray &offer, KConfigGroup *config=0)
Returns authentication object instance appropriate for offer.
Definition: httpauthentication.cpp:266
KConfigGroup
KConfigGroup::readEntry
QString readEntry(const char *key, const char *aDefault=0) const
KHttpBasicAuthentication
Definition: httpauthentication.h:183
KHttpBasicAuthentication::scheme
virtual QByteArray scheme() const
the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
Definition: httpauthentication.cpp:376
KHttpBasicAuthentication::generateResponse
virtual void generateResponse(const QString &user, const QString &password)
what to do in response to challenge
Definition: httpauthentication.cpp:387
KHttpBasicAuthentication::fillKioAuthInfo
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const
KIO compatible data to find cached credentials.
Definition: httpauthentication.cpp:382
KHttpDigestAuthentication
Definition: httpauthentication.h:199
KHttpDigestAuthentication::generateResponse
virtual void generateResponse(const QString &user, const QString &password)
what to do in response to challenge
Definition: httpauthentication.cpp:519
KHttpDigestAuthentication::scheme
virtual QByteArray scheme() const
the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
Definition: httpauthentication.cpp:400
KHttpDigestAuthentication::setChallenge
virtual void setChallenge(const QByteArray &c, const KUrl &resource, const QByteArray &httpMethod)
initiate authentication with challenge string (from HTTP header)
Definition: httpauthentication.cpp:406
KHttpDigestAuthentication::fillKioAuthInfo
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const
KIO compatible data to find cached credentials.
Definition: httpauthentication.cpp:427
KHttpNtlmAuthentication
Definition: httpauthentication.h:223
KHttpNtlmAuthentication::fillKioAuthInfo
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const
KIO compatible data to find cached credentials.
Definition: httpauthentication.cpp:722
KHttpNtlmAuthentication::setChallenge
virtual void setChallenge(const QByteArray &c, const KUrl &resource, const QByteArray &httpMethod)
initiate authentication with challenge string (from HTTP header)
Definition: httpauthentication.cpp:703
KHttpNtlmAuthentication::scheme
virtual QByteArray scheme() const
the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
Definition: httpauthentication.cpp:697
KHttpNtlmAuthentication::generateResponse
virtual void generateResponse(const QString &user, const QString &password)
what to do in response to challenge
Definition: httpauthentication.cpp:732
KIO::AuthInfo
KIO::AuthInfo::digestInfo
QString digestInfo
KIO::AuthInfo::realmValue
QString realmValue
KIO::AuthInfo::verifyPath
bool verifyPath
KIO::AuthInfo::keepPassword
bool keepPassword
KIO::AuthInfo::url
KUrl url
KIO::AuthInfo::username
QString username
KIO::AuthInfo::password
QString password
KMD5
KMD5::update
void update(const char *in, int len=-1)
KMD5::hexDigest
QByteArray hexDigest()
KMD5::reset
void reset()
KUrl::List
KUrl
KUrl::encodedPathAndQuery
QString encodedPathAndQuery(AdjustPathOption trailing=LeaveTrailingSlash, const EncodedPathAndQueryOptions &options=PermitEmptyPath) const
KUrl::ObeyTrailingSlash
ObeyTrailingSlash
KUrl::AppendTrailingSlash
AppendTrailingSlash
KUrl::AvoidEmptyPath
AvoidEmptyPath
KUrl::LeaveTrailingSlash
LeaveTrailingSlash
KUrl::directory
QString directory(const DirectoryOptions &options=IgnoreTrailingSlash) const
KUrl::protocol
QString protocol() const
QList
kDebug
#define kDebug
kWarning
#define kWarning
isWhiteSpace
static bool isWhiteSpace(char ch)
Definition: httpauthentication.cpp:49
valueForKey
static QByteArray valueForKey(const QList< QByteArray > &ba, const QByteArray &key)
Definition: httpauthentication.cpp:207
calculateResponse
static QByteArray calculateResponse(const DigestAuthInfo &info, const KUrl &resource)
Definition: httpauthentication.cpp:450
parseChallenge
static QList< QByteArray > parseChallenge(QByteArray &ba, QByteArray *scheme, QByteArray *nextAuth=0)
Definition: httpauthentication.cpp:79
isWhiteSpaceOrComma
static bool isWhiteSpaceOrComma(char ch)
Definition: httpauthentication.cpp:54
containsScheme
static bool containsScheme(const char input[], int start, int end)
Definition: httpauthentication.cpp:59
httpauthentication.h
kcodecs.h
kconfiggroup.h
kdebug.h
kglobal.h
klocale.h
kntlm.h
krandom.h
KGlobal::locale
KLocale * locale()
config
KSharedConfigPtr config()
KRandom::randomString
QString randomString(int length)
cont
KGuiItem cont()
end
const KShortcut & end()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIOSlave

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

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal