240 or if more than one WWW-Authenticate header field is provided, the |
345 or if more than one WWW-Authenticate header field is provided, the |
241 contents of a challenge itself can contain a comma-separated list of |
346 contents of a challenge itself can contain a comma-separated list of |
242 authentication parameters. |
347 authentication parameters. |
243 */ |
348 */ |
244 |
349 |
245 QString headerVal; |
350 QByteArray headerVal; |
246 for (int i = 0; i < values.size(); ++i) { |
351 for (int i = 0; i < values.size(); ++i) { |
247 const QPair<QString, QString> ¤t = values.at(i); |
352 const QPair<QByteArray, QByteArray> ¤t = values.at(i); |
248 if (current.first.toLower() != QLatin1String(search)) |
353 if (current.first.toLower() != search) |
249 continue; |
354 continue; |
250 QString str = current.second; |
355 QByteArray str = current.second.toLower(); |
251 if (method < Basic && str.startsWith(QLatin1String("Basic"), Qt::CaseInsensitive)) { |
356 if (method < Basic && str.startsWith("basic")) { |
252 method = Basic; headerVal = str.mid(6); |
357 method = Basic; |
253 } else if (method < Ntlm && str.startsWith(QLatin1String("NTLM"), Qt::CaseInsensitive)) { |
358 headerVal = current.second.mid(6); |
|
359 } else if (method < Ntlm && str.startsWith("ntlm")) { |
254 method = Ntlm; |
360 method = Ntlm; |
255 headerVal = str.mid(5); |
361 headerVal = current.second.mid(5); |
256 } else if (method < DigestMd5 && str.startsWith(QLatin1String("Digest"), Qt::CaseInsensitive)) { |
362 } else if (method < DigestMd5 && str.startsWith("digest")) { |
257 method = DigestMd5; |
363 method = DigestMd5; |
258 headerVal = str.mid(7); |
364 headerVal = current.second.mid(7); |
259 } |
365 } |
260 } |
366 } |
261 |
367 |
262 challenge = headerVal.trimmed().toLatin1(); |
368 challenge = headerVal.trimmed(); |
263 QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge); |
369 QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge); |
264 |
370 |
265 switch(method) { |
371 switch(method) { |
266 case Basic: |
372 case Basic: |
267 realm = QString::fromLatin1(options.value("realm")); |
373 if(realm.isEmpty()) |
|
374 this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm")); |
268 if (user.isEmpty()) |
375 if (user.isEmpty()) |
269 phase = Done; |
376 phase = Done; |
270 break; |
377 break; |
271 case Ntlm: |
378 case Ntlm: |
272 // #### extract from header |
379 // #### extract from header |
273 realm.clear(); |
|
274 break; |
380 break; |
275 case DigestMd5: { |
381 case DigestMd5: { |
276 realm = QString::fromLatin1(options.value("realm")); |
382 if(realm.isEmpty()) |
|
383 this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm")); |
277 if (options.value("stale").toLower() == "true") |
384 if (options.value("stale").toLower() == "true") |
278 phase = Start; |
385 phase = Start; |
279 if (user.isEmpty()) |
386 if (user.isEmpty()) |
280 phase = Done; |
387 phase = Done; |
281 break; |
388 break; |
939 deshash((unsigned char *)rc.data() + 16, (unsigned char *)hash.data() + 14, ch.challenge); |
1061 deshash((unsigned char *)rc.data() + 16, (unsigned char *)hash.data() + 14, ch.challenge); |
940 |
1062 |
941 hash.fill(0); |
1063 hash.fill(0); |
942 return rc; |
1064 return rc; |
943 } |
1065 } |
944 |
1066 #endif |
|
1067 |
|
1068 /********************************************************************* |
|
1069 * Function Name: qEncodeHmacMd5 |
|
1070 * Params: |
|
1071 * key: Type - QByteArray |
|
1072 * - It is the Authentication key |
|
1073 * message: Type - QByteArray |
|
1074 * - This is the actual message which will be encoded |
|
1075 * using HMacMd5 hash algorithm |
|
1076 * |
|
1077 * Return Value: |
|
1078 * hmacDigest: Type - QByteArray |
|
1079 * |
|
1080 * Description: |
|
1081 * This function will be used to encode the input message using |
|
1082 * HMacMd5 hash algorithm. |
|
1083 * |
|
1084 * As per the RFC2104 the HMacMd5 algorithm can be specified |
|
1085 * --------------------------------------- |
|
1086 * MD5(K XOR opad, MD5(K XOR ipad, text)) |
|
1087 * --------------------------------------- |
|
1088 * |
|
1089 *********************************************************************/ |
|
1090 QByteArray qEncodeHmacMd5(QByteArray &key, const QByteArray &message) |
|
1091 { |
|
1092 Q_ASSERT_X(!(message.isEmpty()),"qEncodeHmacMd5", "Empty message check"); |
|
1093 Q_ASSERT_X(!(key.isEmpty()),"qEncodeHmacMd5", "Empty key check"); |
|
1094 |
|
1095 QCryptographicHash hash(QCryptographicHash::Md5); |
|
1096 QByteArray hMsg; |
|
1097 |
|
1098 QByteArray iKeyPad(blockSize, 0x36); |
|
1099 QByteArray oKeyPad(blockSize, 0x5c); |
|
1100 |
|
1101 hash.reset(); |
|
1102 // Adjust the key length to blockSize |
|
1103 |
|
1104 if(blockSize < key.length()) { |
|
1105 hash.addData(key); |
|
1106 key = hash.result(); //MD5 will always return 16 bytes length output |
|
1107 } |
|
1108 |
|
1109 //Key will be <= 16 or 20 bytes as hash function (MD5 or SHA hash algorithms) |
|
1110 //key size can be max of Block size only |
|
1111 key = key.leftJustified(blockSize,0,true); |
|
1112 |
|
1113 //iKeyPad, oKeyPad and key are all of same size "blockSize" |
|
1114 |
|
1115 //xor of iKeyPad with Key and store the result into iKeyPad |
|
1116 for(int i = 0; i<key.size();i++) { |
|
1117 iKeyPad[i] = key[i]^iKeyPad[i]; |
|
1118 } |
|
1119 |
|
1120 //xor of oKeyPad with Key and store the result into oKeyPad |
|
1121 for(int i = 0; i<key.size();i++) { |
|
1122 oKeyPad[i] = key[i]^oKeyPad[i]; |
|
1123 } |
|
1124 |
|
1125 iKeyPad.append(message); // (K0 xor ipad) || text |
|
1126 |
|
1127 hash.reset(); |
|
1128 hash.addData(iKeyPad); |
|
1129 hMsg = hash.result(); |
|
1130 //Digest gen after pass-1: H((K0 xor ipad)||text) |
|
1131 |
|
1132 QByteArray hmacDigest; |
|
1133 oKeyPad.append(hMsg); |
|
1134 hash.reset(); |
|
1135 hash.addData(oKeyPad); |
|
1136 hmacDigest = hash.result(); |
|
1137 // H((K0 xor opad )|| H((K0 xor ipad) || text)) |
|
1138 |
|
1139 /*hmacDigest should not be less than half the length of the HMAC output |
|
1140 (to match the birthday attack bound) and not less than 80 bits |
|
1141 (a suitable lower bound on the number of bits that need to be |
|
1142 predicted by an attacker). |
|
1143 Refer RFC 2104 for more details on truncation part */ |
|
1144 |
|
1145 /*MD5 hash always returns 16 byte digest only and HMAC-MD5 spec |
|
1146 (RFC 2104) also says digest length should be 16 bytes*/ |
|
1147 return hmacDigest; |
|
1148 } |
|
1149 |
|
1150 static QByteArray qCreatev2Hash(const QAuthenticatorPrivate *ctx, |
|
1151 QNtlmPhase3Block *phase3) |
|
1152 { |
|
1153 Q_ASSERT(phase3 != 0); |
|
1154 // since v2 Hash is need for both NTLMv2 and LMv2 it is calculated |
|
1155 // only once and stored and reused |
|
1156 if(phase3->v2Hash.size() == 0) { |
|
1157 QCryptographicHash md4(QCryptographicHash::Md4); |
|
1158 QByteArray passUnicode = qStringAsUcs2Le(ctx->password); |
|
1159 md4.addData(passUnicode.data(), passUnicode.size()); |
|
1160 |
|
1161 QByteArray hashKey = md4.result(); |
|
1162 Q_ASSERT(hashKey.size() == 16); |
|
1163 // Assuming the user and domain is always unicode in challenge |
|
1164 QByteArray message = |
|
1165 qStringAsUcs2Le(ctx->user.toUpper()) + |
|
1166 qStringAsUcs2Le(ctx->realm); |
|
1167 |
|
1168 phase3->v2Hash = qEncodeHmacMd5(hashKey, message); |
|
1169 } |
|
1170 return phase3->v2Hash; |
|
1171 } |
|
1172 |
|
1173 static QByteArray clientChallenge(const QAuthenticatorPrivate *ctx) |
|
1174 { |
|
1175 Q_ASSERT(ctx->cnonce.size() >= 8); |
|
1176 QByteArray clientCh = ctx->cnonce.right(8); |
|
1177 return clientCh; |
|
1178 } |
|
1179 |
|
1180 // caller has to ensure a valid targetInfoBuff |
|
1181 static bool qExtractServerTime(const QByteArray& targetInfoBuff, |
|
1182 quint64 *serverTime) |
|
1183 { |
|
1184 Q_ASSERT(serverTime != 0); |
|
1185 bool retValue = false; |
|
1186 QDataStream ds(targetInfoBuff); |
|
1187 ds.setByteOrder(QDataStream::LittleEndian); |
|
1188 |
|
1189 quint16 avId; |
|
1190 quint16 avLen; |
|
1191 |
|
1192 ds >> avId; |
|
1193 ds >> avLen; |
|
1194 while(avId != 0) { |
|
1195 if(avId == AVTIMESTAMP) { |
|
1196 QByteArray timeArray(avLen, 0); |
|
1197 //avLen size of QByteArray is allocated |
|
1198 ds.readRawData(timeArray.data(), avLen); |
|
1199 bool ok; |
|
1200 *serverTime = timeArray.toHex().toLongLong(&ok, 16); |
|
1201 retValue = true; |
|
1202 break; |
|
1203 } |
|
1204 ds.skipRawData(avLen); |
|
1205 ds >> avId; |
|
1206 ds >> avLen; |
|
1207 } |
|
1208 return retValue; |
|
1209 } |
|
1210 |
|
1211 static QByteArray qEncodeNtlmv2Response(const QAuthenticatorPrivate *ctx, |
|
1212 const QNtlmPhase2Block& ch, |
|
1213 QNtlmPhase3Block *phase3) |
|
1214 { |
|
1215 Q_ASSERT(phase3 != 0); |
|
1216 // return value stored in phase3 |
|
1217 qCreatev2Hash(ctx, phase3); |
|
1218 |
|
1219 QByteArray temp; |
|
1220 QDataStream ds(&temp, QIODevice::WriteOnly); |
|
1221 ds.setByteOrder(QDataStream::LittleEndian); |
|
1222 |
|
1223 ds << respversion; |
|
1224 ds << hirespversion; |
|
1225 |
|
1226 //Reserved |
|
1227 QByteArray reserved1(6, 0); |
|
1228 ds.writeRawData(reserved1.constData(), reserved1.size()); |
|
1229 |
|
1230 quint64 time = 0; |
|
1231 |
|
1232 //if server sends time, use it instead of current time |
|
1233 if(!(ch.targetInfo.len && qExtractServerTime(ch.targetInfoBuff, &time))) { |
|
1234 QDateTime currentTime(QDate::currentDate(), |
|
1235 QTime::currentTime(), Qt::UTC); |
|
1236 |
|
1237 // number of seconds between 1601 and epoc(1970) |
|
1238 // 369 years, 89 leap years |
|
1239 // ((369 * 365) + 89) * 24 * 3600 = 11644473600 |
|
1240 |
|
1241 time = Q_UINT64_C(currentTime.toTime_t() + 11644473600); |
|
1242 |
|
1243 // represented as 100 nano seconds |
|
1244 time = Q_UINT64_C(time * 10000000); |
|
1245 } |
|
1246 ds << time; |
|
1247 |
|
1248 //8 byte client challenge |
|
1249 QByteArray clientCh = clientChallenge(ctx); |
|
1250 ds.writeRawData(clientCh.constData(), clientCh.size()); |
|
1251 |
|
1252 //Reserved |
|
1253 QByteArray reserved2(4, 0); |
|
1254 ds.writeRawData(reserved2.constData(), reserved2.size()); |
|
1255 |
|
1256 if (ch.targetInfo.len > 0) { |
|
1257 ds.writeRawData(ch.targetInfoBuff.constData(), |
|
1258 ch.targetInfoBuff.size()); |
|
1259 } |
|
1260 |
|
1261 //Reserved |
|
1262 QByteArray reserved3(4, 0); |
|
1263 ds.writeRawData(reserved3.constData(), reserved3.size()); |
|
1264 |
|
1265 QByteArray message((const char*)ch.challenge, sizeof(ch.challenge)); |
|
1266 message.append(temp); |
|
1267 |
|
1268 QByteArray ntChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message); |
|
1269 ntChallengeResp.append(temp); |
|
1270 |
|
1271 return ntChallengeResp; |
|
1272 } |
|
1273 |
|
1274 static QByteArray qEncodeLmv2Response(const QAuthenticatorPrivate *ctx, |
|
1275 const QNtlmPhase2Block& ch, |
|
1276 QNtlmPhase3Block *phase3) |
|
1277 { |
|
1278 Q_ASSERT(phase3 != 0); |
|
1279 // return value stored in phase3 |
|
1280 qCreatev2Hash(ctx, phase3); |
|
1281 |
|
1282 QByteArray message((const char*)ch.challenge, sizeof(ch.challenge)); |
|
1283 QByteArray clientCh = clientChallenge(ctx); |
|
1284 |
|
1285 message.append(clientCh); |
|
1286 |
|
1287 QByteArray lmChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message); |
|
1288 lmChallengeResp.append(clientCh); |
|
1289 |
|
1290 return lmChallengeResp; |
|
1291 } |
945 |
1292 |
946 static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch) |
1293 static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch) |
947 { |
1294 { |
948 Q_ASSERT(QNtlmPhase2BlockBase::Size == sizeof(QNtlmPhase2BlockBase)); |
1295 Q_ASSERT(QNtlmPhase2BlockBase::Size == sizeof(QNtlmPhase2BlockBase)); |
949 if (data.size() < QNtlmPhase2BlockBase::Size) |
1296 if (data.size() < QNtlmPhase2BlockBase::Size) |
1008 int offset = QNtlmPhase3BlockBase::Size; |
1359 int offset = QNtlmPhase3BlockBase::Size; |
1009 Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase)); |
1360 Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase)); |
1010 |
1361 |
1011 offset = qEncodeNtlmString(pb.domain, offset, ctx->realm, unicode); |
1362 offset = qEncodeNtlmString(pb.domain, offset, ctx->realm, unicode); |
1012 pb.domainStr = ctx->realm; |
1363 pb.domainStr = ctx->realm; |
|
1364 |
1013 offset = qEncodeNtlmString(pb.user, offset, ctx->user, unicode); |
1365 offset = qEncodeNtlmString(pb.user, offset, ctx->user, unicode); |
1014 pb.userStr = ctx->user; |
1366 pb.userStr = ctx->user; |
1015 |
1367 |
1016 offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode); |
1368 offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode); |
1017 pb.workstationStr = ctx->workstation; |
1369 pb.workstationStr = ctx->workstation; |
1018 |
1370 |
1019 // Get LM response |
1371 // Get LM response |
|
1372 #ifdef NTLMV1_CLIENT |
1020 pb.lmResponseBuf = qEncodeLmResponse(ctx, ch); |
1373 pb.lmResponseBuf = qEncodeLmResponse(ctx, ch); |
|
1374 #else |
|
1375 if (ch.targetInfo.len > 0) { |
|
1376 pb.lmResponseBuf = QByteArray(); |
|
1377 } else { |
|
1378 pb.lmResponseBuf = qEncodeLmv2Response(ctx, ch, &pb); |
|
1379 } |
|
1380 #endif |
1021 offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf); |
1381 offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf); |
1022 |
1382 |
1023 // Get NTLM response |
1383 // Get NTLM response |
|
1384 #ifdef NTLMV1_CLIENT |
1024 pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch); |
1385 pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch); |
|
1386 #else |
|
1387 pb.ntlmResponseBuf = qEncodeNtlmv2Response(ctx, ch, &pb); |
|
1388 #endif |
1025 offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf); |
1389 offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf); |
1026 |
1390 |
1027 |
1391 |
1028 // Encode and send |
1392 // Encode and send |
1029 ds << pb; |
1393 ds << pb; |