📄 qtransportauth_qws.cpp
字号:
The buffer must have AUTH_SPACE(0) bytes spare at the beginning for the authentication header to be added. Returns true if header successfully added. Will fail if the per-process key has not yet been set with setProcessKey()*/bool QTransportAuth::authToMessage( QTransportAuth::Data &d, char *hdr, const char *msg, int msgLen ){ // qDebug( "authToMessage(): prog id %u", d.progId ); // only authorize connection oriented transports once, unless key has changed if ( d.connection() && ((d.status & QTransportAuth::ErrMask) != QTransportAuth::Pending) && d_func()->authKey.progId == d.progId ) return false; d.progId = d_func()->authKey.progId; // If Unix socket credentials are being used the key wont be set if ( !d_func()->keyInitialised ) return false; unsigned char digest[QSXE_KEY_LEN]; char *msgPtr = hdr; // magic always goes on the beginning for ( int m = 0; m < QSXE_MAGIC_BYTES; ++m ) *msgPtr++ = magic[m]; hdr[ QSXE_LEN_IDX ] = (unsigned char)msgLen; if ( !d.trusted()) { // Use HMAC int rc = hmac_md5( (unsigned char *)msg, msgLen, d_func()->authKey.key, QSXE_KEY_LEN, digest ); if ( rc == -1 ) return false; memcpy( hdr + QSXE_KEY_IDX, digest, QSXE_KEY_LEN ); } else { memcpy( hdr + QSXE_KEY_IDX, d_func()->authKey.key, QSXE_KEY_LEN ); } hdr[ QSXE_PROG_IDX ] = d_func()->authKey.progId;#ifdef QTRANSPORTAUTH_DEBUG char keydisplay[QSXE_KEY_LEN*2+1]; hexstring( keydisplay, d_func()->authKey.key, QSXE_KEY_LEN ); qDebug( "%d CLIENT Auth to message %s against prog id %u and key %s\n", getpid(), msg, d_func()->authKey.progId, keydisplay );#endif // TODO implement sequence to prevent replay attack, not required // for trusted transports hdr[ QSXE_SEQ_IDX ] = 1; // dummy sequence d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success; return true;}/*! Check authorization on the \a msg, which must be of size \a msgLen, for the transport \a d. If able to determine authorization, return the program identity of the message source in the reference \a progId, and return true. Otherwise return false. If data is being received on a socket, it may be that more data is yet needed before authentication can proceed. Also the message may not be an authenticated at all. In these cases the method returns false to indicate authorization could not be determined: \list \i The message is too small to carry the authentication data (status TooSmall is set on the \a d transport ) \i The 4 magic bytes are missing from the message start (status NoMagic is set on the \a d transport ) \i The message is too small to carry the auth + claimed payload (status TooSmall is set on the \a d transport ) \endlist If however the authentication header (preceded by the magic bytes) and any authenticated payload is received the method will determine the authentication status, and return true. In the following cases as well as returning true it will also emit an authViolation(): \list \i If the program id claimed by the message is not found in the key file (status NoSuchKey is set on the \a d transport ) \i The authentication token failed against the claimed program id: \list \i in the case of trusted transports, the secret did not match \i in the case of untrusted transports the HMAC code did not match \endlist (status FailMatch is set on the \a d transport ) \endlist In these cases the authViolation( QTransportAuth::Data d ) signal is emitted and the error string can be obtained from the status like this: \code QTransportAuth::Result r = d.status & QTransportAuth::ErrMask; qWarning( "error: %s", QTransportAuth::errorStrings[r] ); \endcode*/bool QTransportAuth::authFromMessage( QTransportAuth::Data &d, const char *msg, int msgLen ){ if ( msgLen < QSXE_MAGIC_BYTES ) { d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall; return false; } // if no magic bytes, exit straight away int m; const unsigned char *mptr = reinterpret_cast<const unsigned char *>(msg); for ( m = 0; m < QSXE_MAGIC_BYTES; ++m ) { if ( *mptr++ != magic[m] ) { d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoMagic; return false; } } if ( msgLen < AUTH_SPACE(1) ) { d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall; return false; } // At this point we know the header is at least long enough to contain valid auth // data, however the data may be spoofed. If it is not verified then the status will // be set to uncertified so the spoofed data will not be relied on. However we want to // know the program id which is being reported (even if it might be spoofed) for // policy debugging purposes. So set it here, rather than after verification. d.progId = msg[QSXE_PROG_IDX];#ifdef QTRANSPORTAUTH_DEBUG char authhdr[QSXE_HEADER_LEN*2+1]; hexstring( authhdr, reinterpret_cast<const unsigned char *>(msg), QSXE_HEADER_LEN ); qDebug( "%d SERVER authFromMessage(): message header is %s", getpid(), authhdr );#endif unsigned char authLen = (unsigned char)(msg[ QSXE_LEN_IDX ]); if ( msgLen < AUTH_SPACE(authLen) ) { d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall; return false; } bool isCached = d_func()->keyCache.contains( d.progId ); const unsigned char *clientKey = d_func()->getClientKey( d.progId ); if ( clientKey == NULL ) { d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey; return false; }#ifdef QTRANSPORTAUTH_DEBUG char keydisplay[QSXE_KEY_LEN*2+1]; hexstring( keydisplay, clientKey, QSXE_KEY_LEN ); qDebug( "\t\tauthFromMessage(): message %s against prog id %u and key %s\n", AUTH_DATA(msg), ((unsigned int)d.progId), keydisplay );#endif const unsigned char *auth_tok; unsigned char digest[QSXE_KEY_LEN]; bool multi_tok = false; bool need_to_recheck=false; do { if ( !d.trusted()) { hmac_md5( AUTH_DATA(msg), authLen, clientKey, QSXE_KEY_LEN, digest ); auth_tok = digest; } else { auth_tok = clientKey; multi_tok = true; // 1 or more keys are in the clientKey } while( true ) { if ( memcmp( auth_tok, magic, QSXE_MAGIC_BYTES ) == 0 && memcmp( auth_tok + QSXE_MAGIC_BYTES, magic, QSXE_MAGIC_BYTES ) == 0 ) break; if ( memcmp( msg + QSXE_KEY_IDX, auth_tok, QSXE_KEY_LEN ) == 0 ) { d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success; return true; } if ( !multi_tok ) break; auth_tok += QSXE_KEY_LEN; } //the keys cached on d.progId may not contain the binary key because the cache entry was made //before the binary had first started, must search for client key again. if ( isCached ) { d_func()->keyCache.remove(d.progId); isCached = false;#ifdef QTRANSPORTAUTH_DEBUG qDebug() << "QTransportAuth::authFromMessage(): key not found in set of keys cached" << "against prog Id =" << d.progId << ". Re-obtaining client key. ";#endif clientKey = d_func()->getClientKey( d.progId ); if ( clientKey == NULL ) { d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey; return false; } need_to_recheck = true; } else { need_to_recheck = false; } } while( need_to_recheck ); d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::FailMatch; qWarning() << "QTransportAuth::authFromMessage():failed authentication"; FAREnforcer::getInstance()->logAuthAttempt( QDateTime::currentDateTime() ); emit authViolation( d ); return false;}#ifdef QTRANSPORTAUTH_DEBUG/*! sprintf into hex - dest \a buf, src \a key, \a key_len is length of key. The target buf should be [ key_len * 2 + 1 ] in size*/void hexstring( char *buf, const unsigned char* key, size_t key_len ){ unsigned int i, p; for ( i = 0, p = 0; i < key_len; i++, p+=2 ) { unsigned char lo_nibble = key[i] & 0x0f; unsigned char hi_nibble = key[i] >> 4; buf[p] = (int)hi_nibble > 9 ? hi_nibble-10 + 'A' : hi_nibble + '0'; buf[p+1] = (int)lo_nibble > 9 ? lo_nibble-10 + 'A' : lo_nibble + '0'; } buf[p] = '\0';}#endif/* HMAC MD5 as listed in RFC 2104 This code is taken from: http://www.faqs.org/rfcs/rfc2104.html with the allowance for keys other than length 16 removed, but otherwise a straight cut-and-paste. The HMAC_MD5 transform looks like: \code MD5(K XOR opad, MD5(K XOR ipad, text)) \endcode \list \i where K is an n byte key \i ipad is the byte 0x36 repeated 64 times \i opad is the byte 0x5c repeated 64 times \i and text is the data being protected \endlist Hardware is available with accelerated implementations of HMAC-MD5 and HMAC-SHA1. Where this hardware is available, this routine should be replaced with a call into the accelerated version.*/static int hmac_md5( unsigned char* text, /* pointer to data stream */ int text_length, /* length of data stream */ const unsigned char* key, /* pointer to authentication key */ int key_length, /* length of authentication key */ unsigned char * digest /* caller digest to be filled in */ ){ MD5Context context; unsigned char k_ipad[65]; /* inner padding - * key XORd with ipad */ unsigned char k_opad[65]; /* outer padding - * key XORd with opad */ int i; /* in this implementation key_length == 16 */ if ( key_length != 16 ) { fprintf( stderr, "Key length was %d - must be 16 bytes", key_length ); return 0; } /* start out by storing key in pads */ memset( k_ipad, 0, sizeof k_ipad ); memset( k_opad, 0, sizeof k_opad ); memcpy( k_ipad, key, key_length ); memcpy( k_opad, key, key_length ); /* XOR key with ipad and opad values */ for (i=0; i<64; i++) { k_ipad[i] ^= 0x36; k_opad[i] ^= 0x5c; } /* perform inner MD5 */ MD5Init(&context); /* init context for 1st pass */ MD5Update(&context, k_ipad, 64); /* start with inner pad */ MD5Update(&context, text, text_length); /* then text of datagram */ MD5Final(&context, digest); /* finish up 1st pass */ /* perform outer MD5 */ MD5Init(&context); /* init context for 2nd pass */ MD5Update(&context, k_opad, 64); /* start with outer pad */ MD5Update(&context, digest, 16); /* then results of 1st * hash */ MD5Final(&context, digest); /* finish up 2nd pass */ return 1;}const int FAREnforcer::minutelyRate = 4; //allowed number of false authentication attempts per minuteconst QString FAREnforcer::FARMessage = "FAR_Exceeded";const QString FAREnforcer::SxeTag = "<SXE Breach>";const int FAREnforcer::minute = 60;FAREnforcer::FAREnforcer():authAttempts(){ QDateTime nullDateTime = QDateTime(); for (int i = 0; i < minutelyRate; i++ ) authAttempts << nullDateTime;}FAREnforcer *FAREnforcer::getInstance(){ static FAREnforcer theInstance; return &theInstance;}void FAREnforcer::logAuthAttempt( QDateTime time ){ QDateTime dt = authAttempts.takeFirst(); authAttempts.append( time ); if ( dt.secsTo( authAttempts.last() ) <= minute ) {#if defined(SXE_DISCOVERY) if ( QTransportAuth::getInstance()->isDiscoveryMode() ) { static QBasicAtomic reported = {0}; if ( reported.testAndSet(0,1) ) {#ifndef QT_NO_TEXTSTREAM QString logFilePath = QTransportAuth::getInstance()->logFilePath(); if ( !logFilePath.isEmpty() ) { QFile log( logFilePath ); if ( !log.open(QIODevice::WriteOnly | QIODevice::Append) ) { qWarning("Could not write to log in discovery mode: %s", qPrintable(logFilePath) ); } else { QTextStream ts( &log ); ts << "\t\tWarning: False Authentication Rate of " << minutelyRate << "\n" << "\t\tserver connections/authentications per minute has been exceeded,\n" << "\t\tno further warnings will be issued\n"; } } }#endif reset(); return; }#endif syslog( LOG_ERR | LOG_LOCAL6, "%s %s", qPrintable( FAREnforcer::SxeTag ), qPrintable( FAREnforcer::FARMessage ) ); reset(); }}void FAREnforcer::reset(){ QDateTime nullDateTime = QDateTime(); for (int i = 0; i < minutelyRate; i++ ) authAttempts[i] = nullDateTime; }#include "moc_qtransportauth_qws_p.cpp"#endif // QT_NO_SXE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -