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

KIOSlave

  • sources
  • kde-4.12
  • kdelibs
  • 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 
49 static bool isWhiteSpace(char ch)
50 {
51  return (ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f');
52 }
53 
54 static bool isWhiteSpaceOrComma(char ch)
55 {
56  return (ch == ',' || isWhiteSpace(ch));
57 }
58 
59 static 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
79 static 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 
207 static 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 
217 KAbstractHttpAuthentication::KAbstractHttpAuthentication(KConfigGroup *config)
218  :m_config(config), m_finalAuthStage(false)
219 {
220  reset();
221 }
222 
223 KAbstractHttpAuthentication::~KAbstractHttpAuthentication()
224 {
225 }
226 
227 QByteArray 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 
266 KAbstractHttpAuthentication *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 
284 QList< 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 
305 void 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 
322 void 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 
334 QString 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 
345 void 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 
357 void 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 
376 QByteArray KHttpBasicAuthentication::scheme() const
377 {
378  return "Basic";
379 }
380 
381 
382 void KHttpBasicAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
383 {
384  authInfoBoilerplate(ai);
385 }
386 
387 void 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 
400 QByteArray KHttpDigestAuthentication::scheme() const
401 {
402  return "Digest";
403 }
404 
405 
406 void 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 
427 void KHttpDigestAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
428 {
429  authInfoBoilerplate(ai);
430 }
431 
432 
433 struct 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
450 static 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 
519 void 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
690 void KHttpDigestAuthentication::setDigestNonceValue(const QByteArray& nonce)
691 {
692  m_nonce = nonce;
693 }
694 #endif
695 
696 
697 QByteArray KHttpNtlmAuthentication::scheme() const
698 {
699  return "NTLM";
700 }
701 
702 
703 void 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 
722 void 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 
732 void 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
787 static 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 
809 QByteArray KHttpNegotiateAuthentication::scheme() const
810 {
811  return "Negotiate";
812 }
813 
814 
815 void 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 
824 void 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 
832 void 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
KAbstractHttpAuthentication::newAuth
static KAbstractHttpAuthentication * newAuth(const QByteArray &offer, KConfigGroup *config=0)
Returns authentication object instance appropriate for offer.
Definition: httpauthentication.cpp:266
KAbstractHttpAuthentication::~KAbstractHttpAuthentication
virtual ~KAbstractHttpAuthentication()
Definition: httpauthentication.cpp:223
KHttpDigestAuthentication::generateResponse
virtual void generateResponse(const QString &user, const QString &password)
what to do in response to challenge
Definition: httpauthentication.cpp:519
krandom.h
KUrl::directory
QString directory(const DirectoryOptions &options=IgnoreTrailingSlash) const
KIO::AuthInfo::url
KUrl url
KHttpDigestAuthentication::scheme
virtual QByteArray scheme() const
the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
Definition: httpauthentication.cpp:400
KHttpNtlmAuthentication::scheme
virtual QByteArray scheme() const
the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
Definition: httpauthentication.cpp:697
kdebug.h
KAbstractHttpAuthentication::KAbstractHttpAuthentication
KAbstractHttpAuthentication(KConfigGroup *config=0)
Definition: httpauthentication.cpp:217
KAbstractHttpAuthentication::m_resource
KUrl m_resource
Definition: httpauthentication.h:166
KIO::AuthInfo::keepPassword
bool keepPassword
KIO::AuthInfo::digestInfo
QString digestInfo
KHttpDigestAuthentication::fillKioAuthInfo
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const
KIO compatible data to find cached credentials.
Definition: httpauthentication.cpp:427
KAbstractHttpAuthentication::m_httpMethod
QByteArray m_httpMethod
Definition: httpauthentication.h:167
KAbstractHttpAuthentication::m_password
QString m_password
Definition: httpauthentication.h:178
KAbstractHttpAuthentication::generateResponseCommon
void generateResponseCommon(const QString &user, const QString &password)
Definition: httpauthentication.cpp:357
KIO::AuthInfo
KNTLM::getAuth
static bool getAuth(QByteArray &auth, const QByteArray &challenge, const QString &user, const QString &password, const QString &domain=QString(), const QString &workstation=QString(), AuthFlags authflags=Add_LM)
KHttpDigestAuthentication
Definition: httpauthentication.h:198
containsScheme
static bool containsScheme(const char input[], int start, int end)
Definition: httpauthentication.cpp:59
QString
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
klocale.h
KAbstractHttpAuthentication::scheme
virtual QByteArray scheme() const =0
the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
KUrl
KHttpBasicAuthentication
Definition: httpauthentication.h:182
KAbstractHttpAuthentication::splitOffers
static QList< QByteArray > splitOffers(const QList< QByteArray > &offers)
Split all headers containing multiple authentication offers.
Definition: httpauthentication.cpp:284
KNTLM::getNegotiate
static bool getNegotiate(QByteArray &negotiate, const QString &domain=QString(), const QString &workstation=QString(), quint32 flags=Negotiate_Unicode|Request_Target|Negotiate_NTLM)
KHttpNtlmAuthentication::generateResponse
virtual void generateResponse(const QString &user, const QString &password)
what to do in response to challenge
Definition: httpauthentication.cpp:732
kglobal.h
valueForKey
static QByteArray valueForKey(const QList< QByteArray > &ba, const QByteArray &key)
Definition: httpauthentication.cpp:207
KIO::AuthInfo::realmValue
QString realmValue
KAbstractHttpAuthentication::m_forceDisconnect
bool m_forceDisconnect
Definition: httpauthentication.h:172
KIO::AuthInfo::verifyPath
bool verifyPath
calculateResponse
static QByteArray calculateResponse(const DigestAuthInfo &info, const KUrl &resource)
Definition: httpauthentication.cpp:450
KHttpBasicAuthentication::scheme
virtual QByteArray scheme() const
the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
Definition: httpauthentication.cpp:376
KUrl::protocol
QString protocol() const
KAbstractHttpAuthentication::m_forceKeepAlive
bool m_forceKeepAlive
Definition: httpauthentication.h:171
KNTLM::Force_V1
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
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
kntlm.h
KAbstractHttpAuthentication::bestOffer
static QByteArray bestOffer(const QList< QByteArray > &offers)
Choose the best authentication mechanism from the offered ones.
Definition: httpauthentication.cpp:227
KAbstractHttpAuthentication::m_isError
bool m_isError
Definition: httpauthentication.h:169
KNTLM::Add_LM
KMD5::update
void update(const char *in, int len=-1)
KAbstractHttpAuthentication::authInfoBoilerplate
void authInfoBoilerplate(KIO::AuthInfo *a) const
Definition: httpauthentication.cpp:345
KHttpBasicAuthentication::generateResponse
virtual void generateResponse(const QString &user, const QString &password)
what to do in response to challenge
Definition: httpauthentication.cpp:387
KMD5::hexDigest
QByteArray hexDigest()
KAbstractHttpAuthentication
Definition: httpauthentication.h:37
isWhiteSpaceOrComma
static bool isWhiteSpaceOrComma(char ch)
Definition: httpauthentication.cpp:54
KAbstractHttpAuthentication::m_headerFragment
QByteArray m_headerFragment
Definition: httpauthentication.h:175
authinfo.h
KAbstractHttpAuthentication::m_finalAuthStage
bool m_finalAuthStage
Definition: httpauthentication.h:173
KAbstractHttpAuthentication::reset
void reset()
reset to state after default construction.
Definition: httpauthentication.cpp:305
KMD5
KAbstractHttpAuthentication::m_username
QString m_username
Definition: httpauthentication.h:177
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
KHttpNtlmAuthentication::fillKioAuthInfo
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const
KIO compatible data to find cached credentials.
Definition: httpauthentication.cpp:722
KAbstractHttpAuthentication::m_scheme
QByteArray m_scheme
this is parsed from the header and not necessarily == scheme().
Definition: httpauthentication.h:163
KGlobal::locale
KLocale * locale()
KConfigGroup
KUrl::List
KAbstractHttpAuthentication::authDataToCache
virtual QByteArray authDataToCache() const
Returns any authentication data that should be cached for future use.
Definition: httpauthentication.h:159
KAbstractHttpAuthentication::m_needCredentials
bool m_needCredentials
Definition: httpauthentication.h:170
KUrl::encodedPathAndQuery
QString encodedPathAndQuery(AdjustPathOption trailing=LeaveTrailingSlash, const EncodedPathAndQueryOptions &options=PermitEmptyPath) const
isWhiteSpace
static bool isWhiteSpace(char ch)
Definition: httpauthentication.cpp:49
KIO::AuthInfo::password
QString password
KHttpBasicAuthentication::fillKioAuthInfo
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const
KIO compatible data to find cached credentials.
Definition: httpauthentication.cpp:382
KAbstractHttpAuthentication::m_config
KConfigGroup * m_config
Definition: httpauthentication.h:162
httpauthentication.h
KUrl::ObeyTrailingSlash
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
KIO::AuthInfo::username
QString username
KUrl::AppendTrailingSlash
KAbstractHttpAuthentication::m_challenge
QList< QByteArray > m_challenge
Definition: httpauthentication.h:165
KMD5::reset
void reset()
kWarning
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KAbstractHttpAuthentication::m_keepPassword
bool m_keepPassword
Definition: httpauthentication.h:174
cont
KGuiItem cont()
kcodecs.h
parseChallenge
static QList< QByteArray > parseChallenge(QByteArray &ba, QByteArray *scheme, QByteArray *nextAuth=0)
Definition: httpauthentication.cpp:79
KUrl::AvoidEmptyPath
end
const KShortcut & end()
KRandom::randomString
QString randomString(int length)
KUrl::LeaveTrailingSlash
KConfigGroup::readEntry
T readEntry(const QString &key, const T &aDefault) const
KHttpNtlmAuthentication
Definition: httpauthentication.h:222
KAbstractHttpAuthentication::realm
QString realm() const
Returns the realm sent by the server.
Definition: httpauthentication.cpp:334
kconfiggroup.h
KAbstractHttpAuthentication::m_challengeText
QByteArray m_challengeText
Definition: httpauthentication.h:164
QList< QByteArray >
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:50:58 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



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