📄 qauthenticator.cpp
字号:
/* * NTLM message flags. * * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru> * * This software is released under the MIT license. *//* * Indicates that Unicode strings are supported for use in security * buffer data. */#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 /* * Indicates that OEM strings are supported for use in security buffer data. */#define NTLMSSP_NEGOTIATE_OEM 0x00000002 /* * Requests that the server's authentication realm be included in the * Type 2 message. */#define NTLMSSP_REQUEST_TARGET 0x00000004 /* * Specifies that authenticated communication between the client and server * should carry a digital signature (message integrity). */#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* * Specifies that authenticated communication between the client and server * should be encrypted (message confidentiality). */#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* * Indicates that datagram authentication is being used. */#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 /* * Indicates that the LAN Manager session key should be * used for signing and sealing authenticated communications. */#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 /* * Indicates that NTLM authentication is being used. */#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 /* * Sent by the client in the Type 1 message to indicate that the name of the * domain in which the client workstation has membership is included in the * message. This is used by the server to determine whether the client is * eligible for local authentication. */#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 /* * Sent by the client in the Type 1 message to indicate that the client * workstation's name is included in the message. This is used by the server * to determine whether the client is eligible for local authentication. */#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 /* * Sent by the server to indicate that the server and client are on the same * machine. Implies that the client may use the established local credentials * for authentication instead of calculating a response to the challenge. */#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000 /* * Indicates that authenticated communication between the client and server * should be signed with a "dummy" signature. */#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 /* * Sent by the server in the Type 2 message to indicate that the target * authentication realm is a domain. */#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 /* * Sent by the server in the Type 2 message to indicate that the target * authentication realm is a server. */#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 /* * Sent by the server in the Type 2 message to indicate that the target * authentication realm is a share. Presumably, this is for share-level * authentication. Usage is unclear. */#define NTLMSSP_TARGET_TYPE_SHARE 0x00040000 /* * Indicates that the NTLM2 signing and sealing scheme should be used for * protecting authenticated communications. Note that this refers to a * particular session security scheme, and is not related to the use of * NTLMv2 authentication. */ #define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 /* * Sent by the server in the Type 2 message to indicate that it is including * a Target Information block in the message. The Target Information block * is used in the calculation of the NTLMv2 response. */#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 /* * Indicates that 128-bit encryption is supported. */#define NTLMSSP_NEGOTIATE_128 0x20000000 /* * Indicates that the client will provide an encrypted master session key in * the "Session Key" field of the Type 3 message. This is used in signing and * sealing, and is RC4-encrypted using the previous session key as the * encryption key. */#define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000 /* * Indicates that 56-bit encryption is supported. */#define NTLMSSP_NEGOTIATE_56 0x80000000 /* usage: // fill up ctx with what we know. QByteArray response = qNtlmPhase1(ctx); // send response (b64 encoded??) // get response from server (b64 decode?) Phase2Block pb; qNtlmDecodePhase2(response, pb); response = qNtlmPhase3(ctx, pb); // send response (b64 encoded??)*//* TODO: - Fix unicode handling - add v2 handling*/class QNtlmBuffer {public: QNtlmBuffer() : len(0), maxLen(0), offset(0) {} quint16 len; quint16 maxLen; quint32 offset; enum { Size = 8 };};class QNtlmPhase1BlockBase{public: char magic[8]; quint32 type; quint32 flags; QNtlmBuffer domain; QNtlmBuffer workstation; enum { Size = 32 };};// ################# check paddingsclass QNtlmPhase2BlockBase{public: char magic[8]; quint32 type; QNtlmBuffer targetName; quint32 flags; unsigned char challenge[8]; quint32 context[2]; QNtlmBuffer targetInfo; enum { Size = 48 };};class QNtlmPhase3BlockBase {public: char magic[8]; quint32 type; QNtlmBuffer lmResponse; QNtlmBuffer ntlmResponse; QNtlmBuffer domain; QNtlmBuffer user; QNtlmBuffer workstation; QNtlmBuffer sessionKey; quint32 flags; enum { Size = 64 };};static void qStreamNtlmBuffer(QDataStream& ds, const QByteArray& s){ ds.writeRawData(s.constData(), s.size());}static void qStreamNtlmString(QDataStream& ds, const QString& s, bool unicode){ if (!unicode) { qStreamNtlmBuffer(ds, s.toLatin1()); return; } const ushort *d = s.utf16(); for (int i = 0; i < s.length(); ++i) ds << d[i];}static int qEncodeNtlmBuffer(QNtlmBuffer& buf, int offset, const QByteArray& s){ buf.len = s.size(); buf.maxLen = buf.len; buf.offset = (offset + 1) & ~1; return buf.offset + buf.len;}static int qEncodeNtlmString(QNtlmBuffer& buf, int offset, const QString& s, bool unicode){ if (!unicode) return qEncodeNtlmBuffer(buf, offset, s.toLatin1()); buf.len = 2 * s.length(); buf.maxLen = buf.len; buf.offset = (offset + 1) & ~1; return buf.offset + buf.len;}static QDataStream& operator<<(QDataStream& s, const QNtlmBuffer& b){ s << b.len << b.maxLen << b.offset; return s;}static QDataStream& operator>>(QDataStream& s, QNtlmBuffer& b){ s >> b.len >> b.maxLen >> b.offset; return s;}class QNtlmPhase1Block : public QNtlmPhase1BlockBase{ // requestpublic: QNtlmPhase1Block() { qstrncpy(magic, "NTLMSSP", 8); type = 1; flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET; } // extracted QString domainStr, workstationStr;};class QNtlmPhase2Block : public QNtlmPhase2BlockBase{ // challengepublic: QNtlmPhase2Block() { magic[0] = 0; type = 0xffffffff; } // extracted QString targetNameStr, targetInfoStr;};class QNtlmPhase3Block : public QNtlmPhase3BlockBase { // responsepublic: QNtlmPhase3Block() { qstrncpy(magic, "NTLMSSP", 8); type = 3; flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO; } // extracted QByteArray lmResponseBuf, ntlmResponseBuf; QString domainStr, userStr, workstationStr, sessionKeyStr;};static QDataStream& operator<<(QDataStream& s, const QNtlmPhase1Block& b) { bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE); s.writeRawData(b.magic, sizeof(b.magic)); s << b.type; s << b.flags; s << b.domain; s << b.workstation; if (!b.domainStr.isEmpty()) qStreamNtlmString(s, b.domainStr, unicode); if (!b.workstationStr.isEmpty()) qStreamNtlmString(s, b.workstationStr, unicode); return s;}static QDataStream& operator<<(QDataStream& s, const QNtlmPhase3Block& b) { bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE); s.writeRawData(b.magic, sizeof(b.magic)); s << b.type; s << b.lmResponse; s << b.ntlmResponse; s << b.domain; s << b.user; s << b.workstation; s << b.sessionKey; s << b.flags; if (!b.domainStr.isEmpty()) qStreamNtlmString(s, b.domainStr, unicode); qStreamNtlmString(s, b.userStr, unicode); if (!b.workstationStr.isEmpty()) qStreamNtlmString(s, b.workstationStr, unicode); // Send auth info qStreamNtlmBuffer(s, b.lmResponseBuf); qStreamNtlmBuffer(s, b.ntlmResponseBuf); return s;}static QByteArray qNtlmPhase1(){ QByteArray rc; QDataStream ds(&rc, QIODevice::WriteOnly); ds.setByteOrder(QDataStream::LittleEndian); QNtlmPhase1Block pb; ds << pb; return rc;}static QByteArray qStringAsUcs2Le(const QString& src){ QByteArray rc(2*src.length(), 0); const unsigned short *s = src.utf16(); unsigned short *d = (unsigned short*)rc.data(); for (int i = 0; i < src.length(); ++i) { d[i] = qToLittleEndian(s[i]); } return rc;}static QString qStringFromUcs2Le(const QByteArray& src){ Q_ASSERT(src.size() % 2 == 0); unsigned short *d = (unsigned short*)src.data(); for (int i = 0; i < src.length() / 2; ++i) { d[i] = qFromLittleEndian(d[i]); } return QString((const QChar *)src.data(), src.size()/2);}static QByteArray qEncodeNtlmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch){ QCryptographicHash md4(QCryptographicHash::Md4); QByteArray asUcs2Le = qStringAsUcs2Le(ctx->password); md4.addData(asUcs2Le.data(), asUcs2Le.size()); unsigned char md4hash[22]; memset(md4hash, 0, sizeof(md4hash)); QByteArray hash = md4.result(); Q_ASSERT(hash.size() == 16); memcpy(md4hash, hash.constData(), 16); QByteArray rc(24, 0); deshash((unsigned char *)rc.data(), md4hash, (unsigned char *)ch.challenge); deshash((unsigned char *)rc.data() + 8, md4hash + 7, (unsigned char *)ch.challenge); deshash((unsigned char *)rc.data() + 16, md4hash + 14, (unsigned char *)ch.challenge); hash.fill(0); return rc;}static QByteArray qEncodeLmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch){ QByteArray hash(21, 0); QByteArray key(14, 0); qstrncpy(key.data(), ctx->password.toUpper().toLatin1(), 14); const char *block = "KGS!@#$%"; deshash((unsigned char *)hash.data(), (unsigned char *)key.data(), (unsigned char *)block); deshash((unsigned char *)hash.data() + 8, (unsigned char *)key.data() + 7, (unsigned char *)block); key.fill(0); QByteArray rc(24, 0); deshash((unsigned char *)rc.data(), (unsigned char *)hash.data(), ch.challenge); deshash((unsigned char *)rc.data() + 8, (unsigned char *)hash.data() + 7, ch.challenge); deshash((unsigned char *)rc.data() + 16, (unsigned char *)hash.data() + 14, ch.challenge); hash.fill(0); return rc;}static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch){ Q_ASSERT(QNtlmPhase2BlockBase::Size == sizeof(QNtlmPhase2BlockBase)); if (data.size() < QNtlmPhase2BlockBase::Size) return false; QDataStream ds(data); ds.setByteOrder(QDataStream::LittleEndian); if (ds.readRawData(ch.magic, 8) < 8) return false; if (strncmp(ch.magic, "NTLMSSP", 8) != 0) return false; ds >> ch.type; if (ch.type != 2) return false; ds >> ch.targetName; ds >> ch.flags; if (ds.readRawData((char *)ch.challenge, 8) < 8) return false; ds >> ch.context[0] >> ch.context[1]; ds >> ch.targetInfo; if (ch.targetName.len > 0) { if (ch.targetName.len + ch.targetName.offset >= (unsigned)data.size()) return false; ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len)); } if (ch.targetInfo.len > 0) { // UNUSED right now } return true;}static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data){ QNtlmPhase2Block ch; if (!qNtlmDecodePhase2(phase2data, ch)) return QByteArray(); QByteArray rc; QDataStream ds(&rc, QIODevice::WriteOnly); ds.setByteOrder(QDataStream::LittleEndian); QNtlmPhase3Block pb; bool unicode = ch.flags & NTLMSSP_NEGOTIATE_UNICODE; ctx->realm = ch.targetNameStr; pb.flags = NTLMSSP_NEGOTIATE_NTLM; if (unicode) pb.flags |= NTLMSSP_NEGOTIATE_UNICODE; else pb.flags |= NTLMSSP_NEGOTIATE_OEM; int offset = QNtlmPhase3BlockBase::Size; Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase)); offset = qEncodeNtlmString(pb.domain, offset, ctx->realm, unicode); pb.domainStr = ctx->realm; offset = qEncodeNtlmString(pb.user, offset, ctx->user, unicode); pb.userStr = ctx->user; offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode); pb.workstationStr = ctx->workstation; // Get LM response pb.lmResponseBuf = qEncodeLmResponse(ctx, ch); offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf); // Get NTLM response pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch); offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf); // Encode and send ds << pb; return rc;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -