source: trunk/src/network/kernel/qauthenticator.cpp@ 67

Last change on this file since 67 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 28.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <qauthenticator.h>
43#include <qauthenticator_p.h>
44#include <qdebug.h>
45#include <qhash.h>
46#include <qbytearray.h>
47#include <qcryptographichash.h>
48#include <qhttp.h>
49#include <qdatastream.h>
50#include <qendian.h>
51#include <qstring.h>
52
53QT_BEGIN_NAMESPACE
54
55#include "../../3rdparty/des/des.cpp"
56
57static QByteArray qNtlmPhase1();
58static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
59
60/*!
61 \class QAuthenticator
62 \brief The QAuthenticator class provides an authentication object.
63 \since 4.3
64
65 \reentrant
66 \ingroup io
67 \inmodule QtNetwork
68
69 The QAuthenticator class is usually used in the
70 \l{QHttp::}{authenticationRequired()} and
71 \l{QHttp::}{proxyAuthenticationRequired()} signals of QHttp and
72 QAbstractSocket. The class provides a way to pass back the required
73 authentication information to the socket when accessing services that
74 require authentication.
75
76 \sa QSslSocket
77*/
78
79
80/*!
81 Constructs an empty authentication object
82*/
83QAuthenticator::QAuthenticator()
84 : d(0)
85{
86}
87
88/*!
89 Destructs the object
90*/
91QAuthenticator::~QAuthenticator()
92{
93 if (d && !d->ref.deref())
94 delete d;
95}
96
97/*!
98 Constructs a copy of \a other.
99*/
100QAuthenticator::QAuthenticator(const QAuthenticator &other)
101 : d(other.d)
102{
103 if (d)
104 d->ref.ref();
105}
106
107/*!
108 Assigns the contents of \a other to this authenticator.
109*/
110QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other)
111{
112 if (d == other.d)
113 return *this;
114 detach();
115 d->user = other.d->user;
116 d->password = other.d->password;
117 return *this;
118}
119
120/*!
121 Returns true if this authenticator is identical to \a other; otherwise
122 returns false.
123*/
124bool QAuthenticator::operator==(const QAuthenticator &other) const
125{
126 if (d == other.d)
127 return true;
128 return d->user == other.d->user
129 && d->password == other.d->password
130 && d->realm == other.d->realm
131 && d->method == other.d->method;
132}
133
134/*!
135 \fn bool QAuthenticator::operator!=(const QAuthenticator &other) const
136
137 Returns true if this authenticator is different from \a other; otherwise
138 returns false.
139*/
140
141/*!
142 returns the user used for authentication.
143*/
144QString QAuthenticator::user() const
145{
146 return d ? d->user : QString();
147}
148
149/*!
150 Sets the \a user used for authentication.
151*/
152void QAuthenticator::setUser(const QString &user)
153{
154 detach();
155 d->user = user;
156}
157
158/*!
159 returns the password used for authentication.
160*/
161QString QAuthenticator::password() const
162{
163 return d ? d->password : QString();
164}
165
166/*!
167 Sets the \a password used for authentication.
168*/
169void QAuthenticator::setPassword(const QString &password)
170{
171 detach();
172 d->password = password;
173}
174
175/*!
176 \internal
177*/
178void QAuthenticator::detach()
179{
180 if (!d) {
181 d = new QAuthenticatorPrivate;
182 d->ref = 1;
183 return;
184 }
185
186 qAtomicDetach(d);
187 d->phase = QAuthenticatorPrivate::Start;
188}
189
190/*!
191 returns the realm requiring authentication.
192*/
193QString QAuthenticator::realm() const
194{
195 return d ? d->realm : QString();
196}
197
198
199/*!
200 returns true if the authenticator is null.
201*/
202bool QAuthenticator::isNull() const
203{
204 return !d;
205}
206
207QAuthenticatorPrivate::QAuthenticatorPrivate()
208 : ref(0)
209 , method(None)
210 , phase(Start)
211 , nonceCount(0)
212{
213 cnonce = QCryptographicHash::hash(QByteArray::number(qrand(), 16) + QByteArray::number(qrand(), 16),
214 QCryptographicHash::Md5).toHex();
215 nonceCount = 0;
216}
217
218#ifndef QT_NO_HTTP
219void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy)
220{
221 QList<QPair<QString, QString> > values = header.values();
222 const char *search = isProxy ? "proxy-authenticate" : "www-authenticate";
223
224 method = None;
225 /*
226 Fun from the HTTP 1.1 specs, that we currently ignore:
227
228 User agents are advised to take special care in parsing the WWW-
229 Authenticate field value as it might contain more than one challenge,
230 or if more than one WWW-Authenticate header field is provided, the
231 contents of a challenge itself can contain a comma-separated list of
232 authentication parameters.
233 */
234
235 QString headerVal;
236 for (int i = 0; i < values.size(); ++i) {
237 const QPair<QString, QString> &current = values.at(i);
238 if (current.first.toLower() != QLatin1String(search))
239 continue;
240 QString str = current.second;
241 if (method < Basic && str.startsWith(QLatin1String("Basic"), Qt::CaseInsensitive)) {
242 method = Basic; headerVal = str.mid(6);
243 } else if (method < Ntlm && str.startsWith(QLatin1String("NTLM"), Qt::CaseInsensitive)) {
244 method = Ntlm;
245 headerVal = str.mid(5);
246 } else if (method < DigestMd5 && str.startsWith(QLatin1String("Digest"), Qt::CaseInsensitive)) {
247 method = DigestMd5;
248 headerVal = str.mid(7);
249 }
250 }
251
252 challenge = headerVal.trimmed().toLatin1();
253 QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
254
255 switch(method) {
256 case Basic:
257 realm = QString::fromLatin1(options.value("realm"));
258 if (user.isEmpty())
259 phase = Done;
260 break;
261 case Ntlm:
262 // #### extract from header
263 realm = QString();
264 break;
265 case DigestMd5: {
266 realm = QString::fromLatin1(options.value("realm"));
267 if (options.value("stale").toLower() == "true")
268 phase = Start;
269 if (user.isEmpty())
270 phase = Done;
271 break;
272 }
273 default:
274 realm = QString();
275 challenge = QByteArray();
276 phase = Invalid;
277 }
278}
279#endif
280
281QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path)
282{
283 QByteArray response;
284 const char *methodString = 0;
285 switch(method) {
286 case QAuthenticatorPrivate::None:
287 methodString = "";
288 phase = Done;
289 break;
290 case QAuthenticatorPrivate::Plain:
291 response = '\0' + user.toUtf8() + '\0' + password.toUtf8();
292 phase = Done;
293 break;
294 case QAuthenticatorPrivate::Basic:
295 methodString = "Basic ";
296 response = user.toLatin1() + ':' + password.toLatin1();
297 response = response.toBase64();
298 phase = Done;
299 break;
300 case QAuthenticatorPrivate::Login:
301 if (challenge.contains("VXNlciBOYW1lAA==")) {
302 response = user.toUtf8().toBase64();
303 phase = Phase2;
304 } else if (challenge.contains("UGFzc3dvcmQA")) {
305 response = password.toUtf8().toBase64();
306 phase = Done;
307 }
308 break;
309 case QAuthenticatorPrivate::CramMd5:
310 break;
311 case QAuthenticatorPrivate::DigestMd5:
312 methodString = "Digest ";
313 response = digestMd5Response(challenge, requestMethod, path);
314 phase = Done;
315 break;
316 case QAuthenticatorPrivate::Ntlm:
317 methodString = "NTLM ";
318 if (challenge.isEmpty()) {
319 response = qNtlmPhase1().toBase64();
320 if (user.isEmpty())
321 phase = Done;
322 else
323 phase = Phase2;
324 } else {
325 response = qNtlmPhase3(this, QByteArray::fromBase64(challenge)).toBase64();
326 phase = Done;
327 }
328
329 break;
330 }
331 return QByteArray(methodString) + response;
332}
333
334
335// ---------------------------- Digest Md5 code ----------------------------------------
336
337QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationChallenge(const QByteArray &challenge)
338{
339 QHash<QByteArray, QByteArray> options;
340 // parse the challenge
341 const char *d = challenge.constData();
342 const char *end = d + challenge.length();
343 while (d < end) {
344 while (d < end && (*d == ' ' || *d == '\n' || *d == '\r'))
345 ++d;
346 const char *start = d;
347 while (d < end && *d != '=')
348 ++d;
349 QByteArray key = QByteArray(start, d - start);
350 ++d;
351 if (d >= end)
352 break;
353 bool quote = (*d == '"');
354 if (quote)
355 ++d;
356 if (d >= end)
357 break;
358 start = d;
359 QByteArray value;
360 while (d < end) {
361 bool backslash = false;
362 if (*d == '\\' && d < end - 1) {
363 ++d;
364 backslash = true;
365 }
366 if (!backslash) {
367 if (quote) {
368 if (*d == '"')
369 break;
370 } else {
371 if (*d == ',')
372 break;
373 }
374 }
375 value += *d;
376 ++d;
377 }
378 while (d < end && *d != ',')
379 ++d;