📄 qtransportauth_qws.cpp
字号:
: moreData( false ) , dataSize( 0 ){}RequestAnalyzer::~RequestAnalyzer(){}/*! Analzye the data in the\a msgQueue according to some protocol and produce a request string for policy analysis. If enough data is in the queue for analysis of a complete message, return a non-null string, and set a flag so requireMoreData() will return false; otherwise return a null string and requireMoreData() return true. The amount of bytes analyzed is then available via bytesAnalyzed(). A null string is also returned in the case where the message was corrupt and could not be analyzed. In this case requireMoreData() returns false.Note: this method will modify the msgQueue and pull off the data deemed to be corrupt, in the case of corrupt data. In all other cases the msgQueue is left alone. The calling code should then pull off the analyzed data. Use bytesAnalzyed() to find how much data to pull off the queue.*/QString RequestAnalyzer::analyze( QByteArray *msgQueue ){#ifdef Q_WS_QWS dataSize = 0; moreData = false; QBuffer cmdBuf( msgQueue ); cmdBuf.open( QIODevice::ReadOnly | QIODevice::Unbuffered ); QWSCommand::Type command_type = (QWSCommand::Type)(qws_read_uint( &cmdBuf )); QWSCommand *command = QWSCommand::factory(command_type); // if NULL, factory will have already printed warning for bogus // command_type just purge the bad stuff and attempt to recover if ( command == NULL ) { *msgQueue = msgQueue->mid( sizeof(int) ); return QString(); } QString request( qws_getCommandTypeString( command_type ));#ifndef QT_NO_COP if ( !command->read( &cmdBuf )) { // not all command arrived yet - come back later delete command; moreData = true; return QString(); } if ( command_type == QWSCommand::QCopSend ) { QWSQCopSendCommand *sendCommand = static_cast<QWSQCopSendCommand*>(command); request += QString( "/QCop/%1/%2" ).arg( sendCommand->channel ).arg( sendCommand->message ); } if ( command_type == QWSCommand::QCopRegisterChannel ) { QWSQCopRegisterChannelCommand *registerCommand = static_cast<QWSQCopRegisterChannelCommand*>(command); request += QString( "/QCop/RegisterChannel/%1" ).arg( registerCommand->channel ); }#endif dataSize = QWS_PROTOCOL_ITEM_SIZE( *command ); delete command; return request;#else Q_UNUSED(msgQueue); return QString();#endif}//////////////////////////////////////////////////////////////////////////////// AuthDevice definition/////*! Constructs a new auth device for the transport \a data and I/O device \a parent. Incoming or outgoing data will be authenticated according to the auth direction \a dir. The auth device will take ownership of the transport \a data and delete it when the device is destroyed.*/QAuthDevice::QAuthDevice( QIODevice *parent, QTransportAuth::Data *data, AuthDirection dir ) : QIODevice( parent ) , d( data ) , way( dir ) , m_target( parent ) , m_client( 0 ) , m_bytesAvailable( 0 ) , m_skipWritten( 0 ) , analyzer( 0 ){ if ( dir == Receive ) // server side { connect( m_target, SIGNAL(readyRead()), this, SLOT(recvReadyRead())); } else { connect( m_target, SIGNAL(readyRead()), this, SIGNAL(readyRead())); } connect( m_target, SIGNAL(bytesWritten(qint64)), this, SLOT(targetBytesWritten(qint64)) ); open( QIODevice::ReadWrite | QIODevice::Unbuffered );}QAuthDevice::~QAuthDevice(){ if ( analyzer ) delete analyzer; delete d;}/*! \internal Store a pointer to the related device or instance which this authorizer is proxying for*/void QAuthDevice::setClient( QObject *cli ){ m_client = cli; QTransportAuth::getInstance()->d_func()->buffersByClient[cli] = this; QObject::connect( cli, SIGNAL(destroyed(QObject*)), QTransportAuth::getInstance(), SLOT(bufferDestroyed(QObject*)) ); // qDebug( "@@@@@@@@@@@@ client set %p @@@@@@@@@", cli ); // qDebug( " client count %d", QTransportAuth::getInstance()->d_func()->buffersByClient.count() );}QObject *QAuthDevice::client() const{ return m_client;}/* \fn void QAuthDevice::authViolation(QTransportAuth::Data &) This signal is emitted if an authorization failure is generated, as described in checkAuth(); \sa checkAuth()*//* \fn void QAuthDevice::policyCheck(QTransportAuth::Data &transport, const QString &request ) This signal is emitted when a transport successfully delivers a request and gives the opportunity to either deny or accept the request. This signal must be connected in the same thread, ie it cannot be queued. As soon as all handlers connected to this signal are processed the Allow or Deny state on the \a transport is checked, and the request is allowed or denied accordingly. \sa checkAuth()*//*! \internal Reimplement QIODevice writeData method. For client end, when the device is written to the incoming data is processed and an authentication header calculated. This is pushed into the target device, followed by the actual incoming data (the payload). For server end, it is a fatal error to write to the device.*/qint64 QAuthDevice::writeData(const char *data, qint64 len){ if ( way == Receive ) // server return m_target->write( data, len ); // client#ifdef QTRANSPORTAUTH_DEBUG char displaybuf[1024];#endif char header[QSXE_HEADER_LEN]; ::memset( header, 0, QSXE_HEADER_LEN ); qint64 bytes = 0; if ( QTransportAuth::getInstance()->authToMessage( *d, header, data, len )) { m_target->write( header, QSXE_HEADER_LEN );#ifdef QTRANSPORTAUTH_DEBUG hexstring( displaybuf, (const unsigned char *)header, QSXE_HEADER_LEN ); qDebug( "%d QAuthDevice::writeData - CLIENT: Header written: %s", getpid(), displaybuf );#endif m_skipWritten += QSXE_HEADER_LEN; } m_target->write( data, len ); bytes += len;#ifdef QTRANSPORTAUTH_DEBUG int bytesToDisplay = bytes; const unsigned char *dataptr = (const unsigned char *)data; while ( bytesToDisplay > 0 ) { int amt = bytes < 500 ? bytes : 500; hexstring( displaybuf, dataptr, amt ); qDebug( "%d QAuthDevice::writeData - CLIENT: %s", getpid(), bytes > 0 ? displaybuf : "(null)" ); dataptr += 500; bytesToDisplay -= 500; }#endif if ( m_target->inherits( "QAbstractSocket" )) static_cast<QAbstractSocket*>(m_target)->flush(); return bytes;}/*! Reimplement from QIODevice Read data out of the internal message queue, reduce the queue by the amount read. Note that the amount available is only ever the size of a command (although a command can be very big) since we need to check at command boundaries for new authentication headers.*/qint64 QAuthDevice::readData( char *data, qint64 maxSize ){ if ( way == Send ) // client return m_target->read( data, maxSize ); if ( msgQueue.size() == 0 ) return 0;#ifdef QTRANSPORTAUTH_DEBUG char displaybuf[1024]; hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()), msgQueue.size() > 500 ? 500 : msgQueue.size() ); qDebug() << getpid() << "QAuthDevice::readData() buffered/requested/avail" << msgQueue.size() << maxSize << m_bytesAvailable << displaybuf;#endif Q_ASSERT( m_bytesAvailable <= msgQueue.size() ); qint64 bytes = ( maxSize > m_bytesAvailable ) ? m_bytesAvailable : maxSize; ::memcpy( data, msgQueue.constData(), bytes ); msgQueue = msgQueue.mid( bytes ); m_bytesAvailable -= bytes; return bytes;}/*! \internal Receive readyRead signal from the target recv device. In response authorize the data, and write results out to the recvBuf() device for processing by the application. Trigger the readyRead signal. Authorizing involves first checking the transport is valid, ie the handshake has either already been done and is cached on a trusted transport, or was valid with this message; then second passing the string representation of the service request up to any policyReceivers If either of these fail, the message is denied. In discovery mode denied messages are allowed, but the message is logged.*/void QAuthDevice::recvReadyRead(){ qint64 bytes = m_target->bytesAvailable(); if ( bytes <= 0 ) return; open( QIODevice::ReadWrite | QIODevice::Unbuffered ); QUnixSocket *usock = static_cast<QUnixSocket*>(m_target); QUnixSocketMessage msg = usock->read(); msgQueue.append( msg.bytes() ); d->processId = msg.processId(); // if "fragmented" packet 1/2 way through start of a command, ie // in the QWS msg type, cant do anything, come back later when // there's more of the packet if ( msgQueue.size() < (int)sizeof(int) ) { // qDebug() << "returning: msg size too small" << msgQueue.size(); return; }#ifdef QTRANSPORTAUTH_DEBUG char displaybuf[1024]; hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()), msgQueue.size() > 500 ? 500 : msgQueue.size() ); qDebug( "%d ***** SERVER read %lli bytes - msg %s", getpid(), bytes, displaybuf );#endif bool bufHasMessages = msgQueue.size() >= (int)sizeof(int); while ( bufHasMessages ) { unsigned char saveStatus = d->status; if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoSuchKey ) { QTransportAuth::getInstance()->authorizeRequest( *d, "NoSuchKey" ); break; } if ( !QTransportAuth::getInstance()->authFromMessage( *d, msgQueue, msgQueue.size() )) { // not all arrived yet? come back later if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::TooSmall ) { d->status = saveStatus; return; } } if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoMagic ) { // no msg auth header, don't change the success status for connections if ( d->connection() ) d->status = saveStatus; } else { // msg auth header detected and auth determined, remove hdr msgQueue = msgQueue.mid( QSXE_HEADER_LEN ); } if ( !authorizeMessage() ) break; bufHasMessages = msgQueue.size() >= (int)sizeof(int); }}/** \internal Handle bytesWritten signals from the underlying target device. We adjust the target's value for bytes that are part of auth packets.*/void QAuthDevice::targetBytesWritten( qint64 bytes ){ if ( m_skipWritten >= bytes ) { m_skipWritten -= bytes; bytes = 0; } else if ( m_skipWritten > 0 ) { bytes -= m_skipWritten; m_skipWritten = 0; } if ( bytes > 0 ) { emit bytesWritten( bytes ); }}/** \internal Pre-process the message to determine what QWS command it is. This information is used as the "request" for the purposes of authorization. The request and other data on the connection (id, PID, etc.) are forwarded to all policy listeners by emitting a signal. The signal must be processed synchronously because on return the allow/deny status is used immediately to either drop or continue processing the message.*/bool QAuthDevice::authorizeMessage(){ if ( analyzer == NULL ) analyzer = new RequestAnalyzer(); QString request = (*analyzer)( &msgQueue ); if ( analyzer->requireMoreData() ) return false; bool isAuthorized = true; if ( !request.isEmpty() && request != "Unknown" ) { isAuthorized = QTransportAuth::getInstance()->authorizeRequest( *d, request ); } bool moreToProcess = ( msgQueue.size() - analyzer->bytesAnalyzed() ) > (int)sizeof(int); if ( isAuthorized ) {#ifdef QTRANSPORTAUTH_DEBUG qDebug() << getpid() << "SERVER authorized: releasing" << analyzer->bytesAnalyzed() << "byte command" << request;#endif m_bytesAvailable = analyzer->bytesAnalyzed(); emit QIODevice::readyRead(); return moreToProcess; } else { msgQueue = msgQueue.mid( analyzer->bytesAnalyzed() ); } return true;}void QAuthDevice::setRequestAnalyzer( RequestAnalyzer *ra ){ Q_ASSERT( ra ); if ( analyzer ) delete analyzer; analyzer = ra;}/*! \internal Add authentication header to the beginning of a message Note that the per-process auth cookie is used. This key should be rewritten in the binary image of the executable at install time to make it unique. For this to be secure some mechanism (eg MAC kernel or other permissions) must prevent other processes from reading the key.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -