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 }