Changeset 846 for trunk/src/network/kernel/qauthenticator.cpp
- Timestamp:
- May 5, 2011, 5:36:53 AM (14 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk
- Property svn:mergeinfo changed
/branches/vendor/nokia/qt/4.7.2 (added) merged: 845 /branches/vendor/nokia/qt/current merged: 844 /branches/vendor/nokia/qt/4.6.3 removed
- Property svn:mergeinfo changed
-
trunk/src/network/kernel/qauthenticator.cpp
r769 r846 1 1 /**************************************************************************** 2 2 ** 3 ** Copyright (C) 201 0Nokia Corporation and/or its subsidiary(-ies).3 ** Copyright (C) 201 Nokia Corporation and/or its subsidiary(-ies). 4 4 ** All rights reserved. 5 5 ** Contact: Nokia Corporation ([email protected]) … … 51 51 #include <qendian.h> 52 52 #include <qstring.h> 53 54 55 53 56 54 57 QT_BEGIN_NAMESPACE 55 58 59 56 60 #include "../../3rdparty/des/des.cpp" 61 57 62 58 63 static QByteArray qNtlmPhase1(); … … 84 89 Note that, in particular, NTLM version 2 is not supported. 85 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 86 129 \sa QSslSocket 87 130 */ … … 122 165 if (d == other.d) 123 166 return *this; 124 detach(); 125 d->user = other.d->user; 126 d->password = other.d->password; 167 168 if (d && !d->ref.deref()) 169 delete d; 170 171 d = other.d; 172 if (d) 173 d->ref.ref(); 127 174 return *this; 128 175 } … … 139 186 && d->password == other.d->password 140 187 && d->realm == other.d->realm 141 && d->method == other.d->method; 188 && d->method == other.d->method 189 && d->options == other.d->options; 142 190 } 143 191 … … 163 211 { 164 212 detach(); 165 d->user = user; 213 int separatorPosn = 0; 214 215 switch(d->method) { 216 case QAuthenticatorPrivate::Ntlm: 217 if((separatorPosn = user.indexOf(QLatin1String("\\"))) != -1) { 218 //domain name is present 219 d->realm.clear(); 220 d->userDomain = user.left(separatorPosn); 221 d->extractedUser = user.mid(separatorPosn + 1); 222 d->user = user; 223 } else if((separatorPosn = user.indexOf(QLatin1String("@"))) != -1) { 224 //domain name is present 225 d->realm.clear(); 226 d->userDomain = user.left(separatorPosn); 227 d->extractedUser = user.left(separatorPosn); 228 d->user = user; 229 } else { 230 d->extractedUser = user; 231 d->user = user; 232 d->realm.clear(); 233 d->userDomain.clear(); 234 } 235 break; 236 default: 237 d->user = user; 238 d->userDomain.clear(); 239 break; 240 } 166 241 } 167 242 … … 206 281 } 207 282 208 209 /*! 210 returns true if the authenticator is null. 283 /*! 284 \since 4.7 285 Returns the value related to option \a opt if it was set by the server. 286 See \l{QAuthenticator#Options} for more information on incoming options. 287 If option \a opt isn't found, an invalid QVariant will be returned. 288 289 \sa options(), QAuthenticator#Options 290 */ 291 QVariant QAuthenticator::option(const QString &opt) const 292 { 293 return d ? d->options.value(opt) : QVariant(); 294 } 295 296 /*! 297 \since 4.7 298 Returns all incoming options set in this QAuthenticator object by parsing 299 the server reply. See \l{QAuthenticator#Options} for more information 300 on incoming options. 301 302 \sa option(), QAuthenticator#Options 303 */ 304 QVariantHash QAuthenticator::options() const 305 { 306 return d ? d->options : QVariantHash(); 307 } 308 309 /*! 310 \since 4.7 311 312 Sets the outgoing option \a opt to value \a value. 313 See \l{QAuthenticator#Options} for more information on outgoing options. 314 315 \sa options(), option(), QAuthenticator#Options 316 */ 317 void QAuthenticator::setOption(const QString &opt, const QVariant &value) 318 { 319 detach(); 320 d->options.insert(opt, value); 321 } 322 323 324 /*! 325 Returns true if the authenticator is null. 211 326 */ 212 327 bool QAuthenticator::isNull() const … … 229 344 void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy) 230 345 { 231 QList<QPair<QString, QString> > values = header.values(); 346 const QList<QPair<QString, QString> > values = header.values(); 347 QList<QPair<QByteArray, QByteArray> > rawValues; 348 349 QList<QPair<QString, QString> >::const_iterator it, end; 350 for (it = values.constBegin(), end = values.constEnd(); it != end; ++it) 351 rawValues.append(qMakePair(it->first.toLatin1(), it->second.toUtf8())); 352 353 // continue in byte array form 354 parseHttpResponse(rawValues, isProxy); 355 } 356 #endif 357 358 void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy) 359 { 232 360 const char *search = isProxy ? "proxy-authenticate" : "www-authenticate"; 233 361 … … 243 371 */ 244 372 245 Q StringheaderVal;373 Q headerVal; 246 374 for (int i = 0; i < values.size(); ++i) { 247 const QPair<Q String, QString> ¤t = values.at(i);248 if (current.first.toLower() != QLatin1String(search))375 const QPair<Q> ¤t = values.at(i); 376 if (current.first.toLower() != ) 249 377 continue; 250 QString str = current.second; 251 if (method < Basic && str.startsWith(QLatin1String("Basic"), Qt::CaseInsensitive)) { 252 method = Basic; headerVal = str.mid(6); 253 } else if (method < Ntlm && str.startsWith(QLatin1String("NTLM"), Qt::CaseInsensitive)) { 378 QByteArray str = current.second.toLower(); 379 if (method < Basic && str.startsWith("basic")) { 380 method = Basic; 381 headerVal = current.second.mid(6); 382 } else if (method < Ntlm && str.startsWith("ntlm")) { 254 383 method = Ntlm; 255 headerVal = str.mid(5);256 } else if (method < DigestMd5 && str.startsWith( QLatin1String("Digest"), Qt::CaseInsensitive)) {384 headerVal = .mid(5); 385 } else if (method < DigestMd5 && str.startsWith()) { 257 386 method = DigestMd5; 258 headerVal = str.mid(7);387 headerVal = .mid(7); 259 388 } 260 389 } 261 390 262 challenge = headerVal.trimmed() .toLatin1();391 challenge = headerVal.trimmed(); 263 392 QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge); 264 393 265 394 switch(method) { 266 395 case Basic: 267 realm = QString::fromLatin1(options.value("realm")); 396 if(realm.isEmpty()) 397 this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm")); 268 398 if (user.isEmpty()) 269 399 phase = Done; … … 271 401 case Ntlm: 272 402 // #### extract from header 273 realm.clear();274 403 break; 275 404 case DigestMd5: { 276 realm = QString::fromLatin1(options.value("realm")); 405 if(realm.isEmpty()) 406 this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm")); 277 407 if (options.value("stale").toLower() == "true") 278 408 phase = Start; … … 287 417 } 288 418 } 289 #endif290 419 291 420 QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path) … … 662 791 #define NTLMSSP_NEGOTIATE_56 0x80000000 663 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 664 807 665 808 /* usage: … … 804 947 // extracted 805 948 QString targetNameStr, targetInfoStr; 949 806 950 }; 807 951 … … 819 963 QByteArray lmResponseBuf, ntlmResponseBuf; 820 964 QString domainStr, userStr, workstationStr, sessionKeyStr; 965 821 966 }; 822 967 … … 900 1045 } 901 1046 902 1047 #ifdef NTLMV1_CLIENT 903 1048 static QByteArray qEncodeNtlmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch) 904 1049 { … … 942 1087 return rc; 943 1088 } 944 1089 #endif 1090 1091 /********************************************************************* 1092 * Function Name: qEncodeHmacMd5 1093 * Params: 1094 * key: Type - QByteArray 1095 * - It is the Authentication key 1096 * message: Type - QByteArray 1097 * - This is the actual message which will be encoded 1098 * using HMacMd5 hash algorithm 1099 * 1100 * Return Value: 1101 * hmacDigest: Type - QByteArray 1102 * 1103 * Description: 1104 * This function will be used to encode the input message using 1105 * HMacMd5 hash algorithm. 1106 * 1107 * As per the RFC2104 the HMacMd5 algorithm can be specified 1108 * --------------------------------------- 1109 * MD5(K XOR opad, MD5(K XOR ipad, text)) 1110 * --------------------------------------- 1111 * 1112 *********************************************************************/ 1113 QByteArray qEncodeHmacMd5(QByteArray &key, const QByteArray &message) 1114 { 1115 Q_ASSERT_X(!(message.isEmpty()),"qEncodeHmacMd5", "Empty message check"); 1116 Q_ASSERT_X(!(key.isEmpty()),"qEncodeHmacMd5", "Empty key check"); 1117 1118 QCryptographicHash hash(QCryptographicHash::Md5); 1119 QByteArray hMsg; 1120 1121 QByteArray iKeyPad(blockSize, 0x36); 1122 QByteArray oKeyPad(blockSize, 0x5c); 1123 1124 hash.reset(); 1125 // Adjust the key length to blockSize 1126 1127 if(blockSize < key.length()) { 1128 hash.addData(key); 1129 key = hash.result(); //MD5 will always return 16 bytes length output 1130 } 1131 1132 //Key will be <= 16 or 20 bytes as hash function (MD5 or SHA hash algorithms) 1133 //key size can be max of Block size only 1134 key = key.leftJustified(blockSize,0,true); 1135 1136 //iKeyPad, oKeyPad and key are all of same size "blockSize" 1137 1138 //xor of iKeyPad with Key and store the result into iKeyPad 1139 for(int i = 0; i<key.size();i++) { 1140 iKeyPad[i] = key[i]^iKeyPad[i]; 1141 } 1142 1143 //xor of oKeyPad with Key and store the result into oKeyPad 1144 for(int i = 0; i<key.size();i++) { 1145 oKeyPad[i] = key[i]^oKeyPad[i]; 1146 } 1147 1148 iKeyPad.append(message); // (K0 xor ipad) || text 1149 1150 hash.reset(); 1151 hash.addData(iKeyPad); 1152 hMsg = hash.result(); 1153 //Digest gen after pass-1: H((K0 xor ipad)||text) 1154 1155 QByteArray hmacDigest; 1156 oKeyPad.append(hMsg); 1157 hash.reset(); 1158 hash.addData(oKeyPad); 1159 hmacDigest = hash.result(); 1160 // H((K0 xor opad )|| H((K0 xor ipad) || text)) 1161 1162 /*hmacDigest should not be less than half the length of the HMAC output 1163 (to match the birthday attack bound) and not less than 80 bits 1164 (a suitable lower bound on the number of bits that need to be 1165 predicted by an attacker). 1166 Refer RFC 2104 for more details on truncation part */ 1167 1168 /*MD5 hash always returns 16 byte digest only and HMAC-MD5 spec 1169 (RFC 2104) also says digest length should be 16 bytes*/ 1170 return hmacDigest; 1171 } 1172 1173 static QByteArray qCreatev2Hash(const QAuthenticatorPrivate *ctx, 1174 QNtlmPhase3Block *phase3) 1175 { 1176 Q_ASSERT(phase3 != 0); 1177 // since v2 Hash is need for both NTLMv2 and LMv2 it is calculated 1178 // only once and stored and reused 1179 if(phase3->v2Hash.size() == 0) { 1180 QCryptographicHash md4(QCryptographicHash::Md4); 1181 QByteArray passUnicode = qStringAsUcs2Le(ctx->password); 1182 md4.addData(passUnicode.data(), passUnicode.size()); 1183 1184 QByteArray hashKey = md4.result(); 1185 Q_ASSERT(hashKey.size() == 16); 1186 // Assuming the user and domain is always unicode in challenge 1187 QByteArray message = 1188 qStringAsUcs2Le(ctx->extractedUser.toUpper()) + 1189 qStringAsUcs2Le(phase3->domainStr); 1190 1191 phase3->v2Hash = qEncodeHmacMd5(hashKey, message); 1192 } 1193 return phase3->v2Hash; 1194 } 1195 1196 static QByteArray clientChallenge(const QAuthenticatorPrivate *ctx) 1197 { 1198 Q_ASSERT(ctx->cnonce.size() >= 8); 1199 QByteArray clientCh = ctx->cnonce.right(8); 1200 return clientCh; 1201 } 1202 1203 // caller has to ensure a valid targetInfoBuff 1204 static QByteArray qExtractServerTime(const QByteArray& targetInfoBuff) 1205 { 1206 QByteArray timeArray; 1207 QDataStream ds(targetInfoBuff); 1208 ds.setByteOrder(QDataStream::LittleEndian); 1209 1210 quint16 avId; 1211 quint16 avLen; 1212 1213 ds >> avId; 1214 ds >> avLen; 1215 while(avId != 0) { 1216 if(avId == AVTIMESTAMP) { 1217 timeArray.resize(avLen); 1218 //avLen size of QByteArray is allocated 1219 ds.readRawData(timeArray.data(), avLen); 1220 break; 1221 } 1222 ds.skipRawData(avLen); 1223 ds >> avId; 1224 ds >> avLen; 1225 } 1226 return timeArray; 1227 } 1228 1229 static QByteArray qEncodeNtlmv2Response(const QAuthenticatorPrivate *ctx, 1230 const QNtlmPhase2Block& ch, 1231 QNtlmPhase3Block *phase3) 1232 { 1233 Q_ASSERT(phase3 != 0); 1234 // return value stored in phase3 1235 qCreatev2Hash(ctx, phase3); 1236 1237 QByteArray temp; 1238 QDataStream ds(&temp, QIODevice::WriteOnly); 1239 ds.setByteOrder(QDataStream::LittleEndian); 1240 1241 ds << respversion; 1242 ds << hirespversion; 1243 1244 //Reserved 1245 QByteArray reserved1(6, 0); 1246 ds.writeRawData(reserved1.constData(), reserved1.size()); 1247 1248 quint64 time = 0; 1249 QByteArray timeArray; 1250 1251 if(ch.targetInfo.len) 1252 { 1253 timeArray = qExtractServerTime(ch.targetInfoBuff); 1254 } 1255 1256 //if server sends time, use it instead of current time 1257 if(timeArray.size()) { 1258 ds.writeRawData(timeArray.constData(), timeArray.size()); 1259 } else { 1260 QDateTime currentTime(QDate::currentDate(), 1261 QTime::currentTime(), Qt::UTC); 1262 1263 // number of seconds between 1601 and epoc(1970) 1264 // 369 years, 89 leap years 1265 // ((369 * 365) + 89) * 24 * 3600 = 11644473600 1266 1267 time = Q_UINT64_C(currentTime.toTime_t() + 11644473600); 1268 1269 // represented as 100 nano seconds 1270 time = Q_UINT64_C(time * 10000000); 1271 ds << time; 1272 } 1273 1274 //8 byte client challenge 1275 QByteArray clientCh = clientChallenge(ctx); 1276 ds.writeRawData(clientCh.constData(), clientCh.size()); 1277 1278 //Reserved 1279 QByteArray reserved2(4, 0); 1280 ds.writeRawData(reserved2.constData(), reserved2.size()); 1281 1282 if (ch.targetInfo.len > 0) { 1283 ds.writeRawData(ch.targetInfoBuff.constData(), 1284 ch.targetInfoBuff.size()); 1285 } 1286 1287 //Reserved 1288 QByteArray reserved3(4, 0); 1289 ds.writeRawData(reserved3.constData(), reserved3.size()); 1290 1291 QByteArray message((const char*)ch.challenge, sizeof(ch.challenge)); 1292 message.append(temp); 1293 1294 QByteArray ntChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message); 1295 ntChallengeResp.append(temp); 1296 1297 return ntChallengeResp; 1298 } 1299 1300 static QByteArray qEncodeLmv2Response(const QAuthenticatorPrivate *ctx, 1301 const QNtlmPhase2Block& ch, 1302 QNtlmPhase3Block *phase3) 1303 { 1304 Q_ASSERT(phase3 != 0); 1305 // return value stored in phase3 1306 qCreatev2Hash(ctx, phase3); 1307 1308 QByteArray message((const char*)ch.challenge, sizeof(ch.challenge)); 1309 QByteArray clientCh = clientChallenge(ctx); 1310 1311 message.append(clientCh); 1312 1313 QByteArray lmChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message); 1314 lmChallengeResp.append(clientCh); 1315 1316 return lmChallengeResp; 1317 } 945 1318 946 1319 static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch) … … 977 1350 978 1351 if (ch.targetInfo.len > 0) { 979 // UNUSED right now 1352 if (ch.targetInfo.len + ch.targetInfo.offset > (unsigned)data.size()) 1353 return false; 1354 1355 ch.targetInfoBuff = data.mid(ch.targetInfo.offset, ch.targetInfo.len); 980 1356 } 981 1357 … … 996 1372 997 1373 bool unicode = ch.flags & NTLMSSP_NEGOTIATE_UNICODE; 998 999 ctx->realm = ch.targetNameStr;1000 1374 1001 1375 pb.flags = NTLMSSP_NEGOTIATE_NTLM; … … 1009 1383 Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase)); 1010 1384 1011 offset = qEncodeNtlmString(pb.domain, offset, ctx->realm, unicode); 1012 pb.domainStr = ctx->realm; 1013 offset = qEncodeNtlmString(pb.user, offset, ctx->user, unicode); 1014 pb.userStr = ctx->user; 1385 if(ctx->userDomain.isEmpty()) { 1386 offset = qEncodeNtlmString(pb.domain, offset, ch.targetNameStr, unicode); 1387 pb.domainStr = ch.targetNameStr; 1388 } else { 1389 offset = qEncodeNtlmString(pb.domain, offset, ctx->userDomain, unicode); 1390 pb.domainStr = ctx->userDomain; 1391 } 1392 1393 offset = qEncodeNtlmString(pb.user, offset, ctx->extractedUser, unicode); 1394 pb.userStr = ctx->extractedUser; 1015 1395 1016 1396 offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode); … … 1018 1398 1019 1399 // Get LM response 1400 1020 1401 pb.lmResponseBuf = qEncodeLmResponse(ctx, ch); 1402 1403 1404 1405 1406 1407 1408 1021 1409 offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf); 1022 1410 1023 1411 // Get NTLM response 1412 1024 1413 pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch); 1414 1415 1416 1025 1417 offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf); 1026 1418
Note:
See TracChangeset
for help on using the changeset viewer.