00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
#include "kdesasl.h"
00021
00022
#include <kmdcodec.h>
00023
#include <kurl.h>
00024
00025
#include <qstrlist.h>
00026
00027
#include <stdlib.h>
00028
#include <string.h>
00029
00030 KDESasl::KDESasl(
const KURL &aUrl)
00031 {
00032 mProtocol = aUrl.
protocol();
00033 mUser = aUrl.
user();
00034 mPass = aUrl.
pass();
00035 mFirst =
true;
00036 }
00037
00038 KDESasl::KDESasl(
const QString &aUser,
const QString &aPass,
00039
const QString &aProtocol)
00040 {
00041 mProtocol = aProtocol;
00042 mUser = aUser;
00043 mPass = aPass;
00044 mFirst =
true;
00045 }
00046
00047 KDESasl::~KDESasl() {
00048 }
00049
00050 QCString KDESasl::chooseMethod(
const QStrIList aMethods)
00051 {
00052
if (aMethods.contains(
"DIGEST-MD5")) mMethod =
"DIGEST-MD5";
00053
else if (aMethods.contains(
"CRAM-MD5")) mMethod =
"CRAM-MD5";
00054
else if (aMethods.contains(
"PLAIN")) mMethod =
"PLAIN";
00055
else if (aMethods.contains(
"LOGIN")) mMethod =
"LOGIN";
00056
else mMethod =
QCString();
00057
return mMethod;
00058 }
00059
00060 void KDESasl::setMethod(
const QCString &aMethod)
00061 {
00062 mMethod = aMethod.
upper();
00063 }
00064
00065 QByteArray KDESasl::getPlainResponse()
00066 {
00067
QCString user = mUser.
utf8();
00068
QCString pass = mPass.
utf8();
00069
int userlen = user.
length();
00070
int passlen = pass.
length();
00071
00072
QByteArray result(2 * userlen + passlen + 2);
00073
if ( userlen ) {
00074 memcpy( result.data(), user.data(), userlen );
00075 memcpy( result.data() + userlen + 1, user.data(), userlen );
00076 }
00077
if ( passlen )
00078 memcpy( result.data() + 2 * userlen + 2, pass.data(), passlen );
00079 result[userlen] = result[2*userlen+1] =
'\0';
00080
return result;
00081 }
00082
00083 QByteArray KDESasl::getLoginResponse()
00084 {
00085
QByteArray result = (mFirst) ? mUser.
utf8() : mPass.
utf8();
00086 mFirst = !mFirst;
00087
if (result.size()) result.resize(result.size() - 1);
00088
return result;
00089 }
00090
00091 QByteArray KDESasl::getCramMd5Response(
const QByteArray &aChallenge)
00092 {
00093 uint i;
00094
QByteArray secret = mPass.
utf8();
00095
int len = mPass.
utf8().length();
00096 secret.resize(len);
00097
if (secret.size() > 64)
00098 {
00099
KMD5 md5(secret);
00100 secret.duplicate((
const char*)(&(md5.
rawDigest()[0])), 16);
00101 len = 16;
00102 }
00103 secret.resize(64);
00104
for (i = len; i < 64; i++) secret[i] = 0;
00105
QByteArray XorOpad(64);
00106
for (i = 0; i < 64; i++) XorOpad[i] = secret[i] ^ 0x5C;
00107
QByteArray XorIpad(64);
00108
for (i = 0; i < 64; i++) XorIpad[i] = secret[i] ^ 0x36;
00109
KMD5 md5;
00110 md5.
update(XorIpad);
00111 md5.
update(aChallenge);
00112
KMD5 md5a;
00113 md5a.
update(XorOpad);
00114 md5a.
update(md5.
rawDigest(), 16);
00115
QByteArray result = mUser.
utf8();
00116 len = mUser.
utf8().length();
00117 result.resize(len + 33);
00118 result[len] =
' ';
00119
QCString ch = md5a.
hexDigest();
00120
for (i = 0; i < 32; i++) result[i+len+1] = *(ch.data() + i);
00121
return result;
00122 }
00123
00124 QByteArray KDESasl::getDigestMd5Response(
const QByteArray &aChallenge)
00125 {
00126 mFirst = !mFirst;
00127
if (mFirst)
return QByteArray();
00128
QCString str, realm, nonce, qop, algorithm, charset;
00129
QCString nc =
"00000001";
00130
unsigned int a, b, c, d;
00131 a = 0;
00132
while (a < aChallenge.size())
00133 {
00134 b = a;
00135
while (b < aChallenge.size() && aChallenge[b] !=
'=') b++;
00136 c = b + 1;
00137
if (aChallenge[c] ==
'"')
00138 {
00139 d = c + 1;
00140
while (d < aChallenge.size() && aChallenge[d] !=
'"') d++;
00141 c++;
00142 }
else {
00143 d = c;
00144
while (d < aChallenge.size() && aChallenge[d] !=
',') d++;
00145 }
00146 str =
QCString(aChallenge.data() + c, d - c + 1);
00147
if (qstrnicmp(aChallenge.data() + a,
"realm=", 6) == 0) realm = str;
00148
else if (qstrnicmp(aChallenge.data() + a,
"nonce=", 6) == 0) nonce = str;
00149
else if (qstrnicmp(aChallenge.data() + a,
"qop=", 4) == 0) qop = str;
00150
else if (qstrnicmp(aChallenge.data() + a,
"algorithm=", 10) == 0)
00151 algorithm = str;
00152
else if (qstrnicmp(aChallenge.data() + a,
"charset=", 8) == 0)
00153 charset = str;
00154 a = (d < aChallenge.size() && aChallenge[d] ==
'"') ? d + 2 : d + 1;
00155 }
00156
if (qop.
isEmpty()) qop =
"auth";
00157 qop =
"auth";
00158
bool utf8 = qstricmp(charset,
"utf-8") == 0;
00159
QCString digestUri =
QCString(mProtocol.
latin1()) +
"/" + realm;
00160
00161
00162
00163
00164
00165
00166
KMD5 md, md2;
00167 QCString HA1, HA2;
00168 QCString cnonce;
00169 cnonce.
setNum((1 + static_cast<int>(100000.0*rand()/(RAND_MAX+1.0))));
00170 cnonce = KCodecs::base64Encode( cnonce );
00171
00172
00173 QCString authStr = (utf8) ? mUser.
utf8() : QCString(mUser.
latin1());
00174 authStr +=
':';
00175 authStr += realm;
00176 authStr +=
':';
00177 authStr += (utf8) ? mPass.
utf8() : QCString(mPass.
latin1());
00178
00179 md.
update( authStr );
00180 authStr =
"";
00181
if ( algorithm ==
"md5-sess" )
00182 {
00183 authStr +=
':';
00184 authStr += nonce;
00185 authStr +=
':';
00186 authStr += cnonce;
00187 }
00188 md2.
reset();
00189
00190
00191 md2.
update(md.
rawDigest(), 16);
00192 md2.
update( authStr );
00193 md2.
hexDigest( HA1 );
00194
00195
00196 authStr =
"AUTHENTICATE:";
00197 authStr += digestUri;
00198
if ( qop ==
"auth-int" || qop ==
"auth-conf" )
00199 {
00200 authStr +=
":00000000000000000000000000000000";
00201 }
00202 md.
reset();
00203 md.
update( authStr );
00204 md.
hexDigest( HA2 );
00205
00206
00207 authStr = HA1;
00208 authStr +=
':';
00209 authStr += nonce;
00210 authStr +=
':';
00211
if ( !qop.
isEmpty() )
00212 {
00213 authStr += nc;
00214 authStr +=
':';
00215 authStr += cnonce;
00216 authStr +=
':';
00217 authStr += qop;
00218 authStr +=
':';
00219 }
00220 authStr += HA2;
00221 md.
reset();
00222 md.
update( authStr );
00223 QCString response = md.
hexDigest();
00224
00225
00226 QCString result;
00227
if (utf8)
00228 {
00229 result =
"charset=utf-8,username=\"" + mUser.
utf8();
00230 }
else {
00231 result =
"charset=iso-8859-1,username=\"" + QCString(mUser.
latin1());
00232 }
00233 result +=
"\",realm=\"" + realm +
"\",nonce=\"" + nonce;
00234 result +=
"\",nc=" + nc +
",cnonce=\"" + cnonce;
00235 result +=
"\",digest-uri=\"" + digestUri;
00236 result +=
"\",response=" + response +
",qop=" + qop;
00237
QByteArray ba;
00238 ba.duplicate(result.data(), result.
length());
00239
return ba;
00240 }
00241
00242 QByteArray KDESasl::getBinaryResponse(
const QByteArray &aChallenge,
bool aBase64)
00243 {
00244
if (aBase64)
00245 {
00246
QByteArray ba;
00247 KCodecs::base64Decode(aChallenge, ba);
00248 KCodecs::base64Encode(
getBinaryResponse(ba,
false), ba);
00249
return ba;
00250 }
00251
if (qstricmp(mMethod,
"PLAIN") == 0)
return getPlainResponse();
00252
if (qstricmp(mMethod,
"LOGIN") == 0)
return getLoginResponse();
00253
if (qstricmp(mMethod,
"CRAM-MD5") == 0)
00254
return getCramMd5Response(aChallenge);
00255
if (qstricmp(mMethod,
"DIGEST-MD5") == 0)
00256
return getDigestMd5Response(aChallenge);
00257
00258
return QByteArray();
00259 }
00260
00261 QCString KDESasl::getResponse(
const QByteArray &aChallenge,
bool aBase64)
00262 {
00263
QByteArray ba =
getBinaryResponse(aChallenge, aBase64);
00264
return QCString(ba.data(), ba.size() + 1);
00265 }
00266
00267 QCString KDESasl::method()
const {
00268
return mMethod;
00269 }
00270
00271 bool KDESasl::clientStarts()
const {
00272
return method() ==
"PLAIN";
00273 }
00274
00275 bool KDESasl::dialogComplete(
int n )
const {
00276
if (
method() ==
"PLAIN" ||
method() ==
"CRAM-MD5" )
00277
return n >= 1;
00278
if (
method() ==
"LOGIN" ||
method() ==
"DIGEST-MD5" )
00279
return n >= 2;
00280
return true;
00281 }
00282
00283 bool KDESasl::isClearTextMethod()
const {
00284
return method() ==
"PLAIN" ||
method() ==
"LOGIN" ;
00285 }