add RTSP digest authentication
This commit is contained in:
parent
c48fb0124d
commit
3595777d79
|
@ -51,6 +51,7 @@ zm_SOURCES = \
|
|||
zm_rtp_data.cpp \
|
||||
zm_rtp_source.cpp \
|
||||
zm_rtsp.cpp \
|
||||
zm_rtsp_auth.cpp \
|
||||
zm_sdp.cpp \
|
||||
zm_signal.cpp \
|
||||
zm_stream.cpp \
|
||||
|
|
|
@ -56,21 +56,6 @@
|
|||
#endif // ZM_MEM_MAPPED
|
||||
|
||||
//=============================================================================
|
||||
std::string trimSpaces(std::string str)
|
||||
{
|
||||
// Trim Both leading and trailing spaces
|
||||
size_t startpos = str.find_first_not_of(" \t"); // Find the first character position after excluding leading blank spaces
|
||||
size_t endpos = str.find_last_not_of(" \t"); // Find the first character position from reverse af
|
||||
|
||||
// if all spaces or empty return an empty string
|
||||
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
else
|
||||
return str.substr( startpos, endpos-startpos+1 );
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string &s, char delim) {
|
||||
std::vector<std::string> elems;
|
||||
std::stringstream ss(s);
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "zm_zone.h"
|
||||
#include "zm_event.h"
|
||||
#include "zm_camera.h"
|
||||
#include "zm_utils.h"
|
||||
|
||||
#include "zm_image_analyser.h"
|
||||
|
||||
|
|
159
src/zm_rtsp.cpp
159
src/zm_rtsp.cpp
|
@ -39,8 +39,11 @@ RtspThread::PortSet RtspThread::smAssignedPorts;
|
|||
|
||||
bool RtspThread::sendCommand( std::string message )
|
||||
{
|
||||
if ( !mAuth.empty() )
|
||||
message += stringtf( "Authorization: Basic %s\r\n", mAuth64.c_str() );
|
||||
if ( mNeedAuth ) {
|
||||
StringVector parts = split( message, " " );
|
||||
if (parts.size() > 1)
|
||||
message += mAuthenticator->getAuthHeader(parts[0], parts[1]);
|
||||
}
|
||||
message += stringtf( "User-Agent: ZoneMinder/%s\r\n", ZM_VERSION );
|
||||
message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq );
|
||||
Debug( 2, "Sending RTSP message: %s", message.c_str() );
|
||||
|
@ -65,13 +68,41 @@ bool RtspThread::sendCommand( std::string message )
|
|||
return( true );
|
||||
}
|
||||
|
||||
// find WWW-Authenticate header, send to Authenticator to extract required subfields
|
||||
void RtspThread::checkAuthResponse(std::string &response)
|
||||
{
|
||||
std::string authLine;
|
||||
StringVector lines = split( response, "\r\n" );
|
||||
const char* authenticate_match = "WWW-Authenticate:";
|
||||
size_t authenticate_match_len = strlen(authenticate_match);
|
||||
|
||||
for ( size_t i = 0; i < lines.size(); i++ )
|
||||
{
|
||||
// stop at end of headers
|
||||
if (lines[i].length()==0)
|
||||
break;
|
||||
|
||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0)
|
||||
{
|
||||
authLine = lines[i];
|
||||
Debug( 2, "Found auth line at %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!authLine.empty())
|
||||
{
|
||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
||||
mAuthenticator->authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
||||
}
|
||||
}
|
||||
|
||||
bool RtspThread::recvResponse( std::string &response )
|
||||
{
|
||||
if ( mRtspSocket.recv( response ) < 0 )
|
||||
Error( "Recv failed; %s", strerror(errno) );
|
||||
Debug( 2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size() );
|
||||
float respVer = 0;
|
||||
int respCode = -1;
|
||||
respCode = -1;
|
||||
char respText[ZM_NETWORK_BUFSIZ];
|
||||
if ( sscanf( response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 )
|
||||
{
|
||||
|
@ -87,7 +118,14 @@ bool RtspThread::recvResponse( std::string &response )
|
|||
}
|
||||
return( false );
|
||||
}
|
||||
if ( respCode != 200 )
|
||||
if ( respCode == 401)
|
||||
{
|
||||
Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry");
|
||||
checkAuthResponse(response);
|
||||
mNeedAuth = true;
|
||||
return( false );
|
||||
}
|
||||
else if ( respCode != 200 )
|
||||
{
|
||||
Error( "Unexpected response code %d, text is '%s'", respCode, respText );
|
||||
return( false );
|
||||
|
@ -157,14 +195,13 @@ void RtspThread::releasePorts( int port )
|
|||
smAssignedPorts.erase( port );
|
||||
}
|
||||
|
||||
RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth ) :
|
||||
RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth) :
|
||||
mId( id ),
|
||||
mMethod( method ),
|
||||
mProtocol( protocol ),
|
||||
mHost( host ),
|
||||
mPort( port ),
|
||||
mPath( path ),
|
||||
mAuth( auth ),
|
||||
mFormatContext( 0 ),
|
||||
mSeq( 0 ),
|
||||
mSession( 0 ),
|
||||
|
@ -188,9 +225,13 @@ RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol,
|
|||
|
||||
if ( mMethod == RTP_RTSP_HTTP )
|
||||
mHttpSession = stringtf( "%d", rand() );
|
||||
|
||||
if ( !mAuth.empty() )
|
||||
mAuth64 = base64Encode( mAuth );
|
||||
|
||||
mNeedAuth = false;
|
||||
StringVector parts = split(auth,":");
|
||||
if (parts.size() > 1)
|
||||
mAuthenticator = new Authenticator(parts[0], parts[1]);
|
||||
else
|
||||
mAuthenticator = new Authenticator(parts[0], "");
|
||||
}
|
||||
|
||||
RtspThread::~RtspThread()
|
||||
|
@ -214,6 +255,8 @@ int RtspThread::run()
|
|||
//Debug( 4, "Drained %d bytes from RTSP socket", response.size() );
|
||||
//}
|
||||
|
||||
|
||||
bool authTried = false;
|
||||
if ( mMethod == RTP_RTSP_HTTP )
|
||||
{
|
||||
if ( !mRtspSocket2.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) )
|
||||
|
@ -225,42 +268,55 @@ int RtspThread::run()
|
|||
//mRtspSocket2.recv( response );
|
||||
//Debug( 4, "Drained %d bytes from HTTP socket", response.size() );
|
||||
//}
|
||||
|
||||
message = "GET "+mPath+" HTTP/1.0\r\n";
|
||||
message += "X-SessionCookie: "+mHttpSession+"\r\n";
|
||||
if ( !mAuth.empty() )
|
||||
message += stringtf( "Authorization: Basic %s\r\n", mAuth64.c_str() );
|
||||
message += "\r\n";
|
||||
Debug( 2, "Sending HTTP message: %s", message.c_str() );
|
||||
if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() )
|
||||
{
|
||||
Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
if ( mRtspSocket.recv( response ) < 0 )
|
||||
{
|
||||
Error( "Recv failed; %s", strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() );
|
||||
float respVer = 0;
|
||||
|
||||
//possibly retry sending the message for authentication
|
||||
int respCode = -1;
|
||||
char respText[256];
|
||||
if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 )
|
||||
{
|
||||
if ( isalnum(response[0]) )
|
||||
{
|
||||
Error( "Response parse failure in '%s'", response.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Response parse failure, %zd bytes follow", response.size() );
|
||||
if ( response.size() )
|
||||
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
|
||||
}
|
||||
return( -1 );
|
||||
}
|
||||
do {
|
||||
message = "GET "+mPath+" HTTP/1.0\r\n";
|
||||
message += "X-SessionCookie: "+mHttpSession+"\r\n";
|
||||
if ( mNeedAuth ) {
|
||||
message += mAuthenticator->getAuthHeader("GET", mPath);
|
||||
authTried = true;
|
||||
}
|
||||
message += "\r\n";
|
||||
Debug( 2, "Sending HTTP message: %s", message.c_str() );
|
||||
if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() )
|
||||
{
|
||||
Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
if ( mRtspSocket.recv( response ) < 0 )
|
||||
{
|
||||
Error( "Recv failed; %s", strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() );
|
||||
float respVer = 0;
|
||||
respCode = -1;
|
||||
if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 )
|
||||
{
|
||||
if ( isalnum(response[0]) )
|
||||
{
|
||||
Error( "Response parse failure in '%s'", response.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Response parse failure, %zd bytes follow", response.size() );
|
||||
if ( response.size() )
|
||||
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
|
||||
}
|
||||
return( -1 );
|
||||
}
|
||||
// IF Server requests authentication, check WWW-AUthenticate header and fill required fields
|
||||
// für requested authentication method
|
||||
if (respCode == 401 && !authTried) {
|
||||
mNeedAuth = true;
|
||||
}
|
||||
|
||||
} while (respCode == 401 && !authTried);
|
||||
|
||||
if ( respCode != 200 )
|
||||
{
|
||||
Error( "Unexpected response code %d, text is '%s'", respCode, respText );
|
||||
|
@ -269,8 +325,8 @@ int RtspThread::run()
|
|||
|
||||
message = "POST "+mPath+" HTTP/1.0\r\n";
|
||||
message += "X-SessionCookie: "+mHttpSession+"\r\n";
|
||||
if ( !mAuth.empty() )
|
||||
message += stringtf( "Authorization: Basic %s\r\n", mAuth64.c_str() );
|
||||
if ( mNeedAuth )
|
||||
message += mAuthenticator->getAuthHeader("POST", mPath);
|
||||
message += "Content-Length: 32767\r\n";
|
||||
message += "Content-Type: application/x-rtsp-tunnelled\r\n";
|
||||
message += "\r\n";
|
||||
|
@ -290,9 +346,16 @@ int RtspThread::run()
|
|||
//recvResponse( response );
|
||||
|
||||
message = "DESCRIBE "+mUrl+" RTSP/1.0\r\n";
|
||||
sendCommand( message );
|
||||
sleep( 1 );
|
||||
recvResponse( response );
|
||||
bool res;
|
||||
do {
|
||||
if (mNeedAuth)
|
||||
authTried = true;
|
||||
sendCommand( message );
|
||||
sleep( 1 );
|
||||
res = recvResponse( response );
|
||||
if (!res && respCode==401)
|
||||
mNeedAuth = true;
|
||||
} while (!res && respCode==401 && !authTried);
|
||||
|
||||
const std::string endOfHeaders = "\r\n\r\n";
|
||||
size_t sdpStart = response.find( endOfHeaders );
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "zm_comms.h"
|
||||
#include "zm_thread.h"
|
||||
#include "zm_rtp_source.h"
|
||||
#include "zm_rtsp_auth.h"
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
@ -54,8 +55,16 @@ private:
|
|||
std::string mPort;
|
||||
std::string mPath;
|
||||
std::string mUrl;
|
||||
std::string mAuth;
|
||||
std::string mAuth64;
|
||||
|
||||
// Reworked authentication system
|
||||
// First try without authentication, even if we have a username and password
|
||||
// on receiving a 401 response, select authentication method (basic or digest)
|
||||
// fill required fields and set needAuth
|
||||
// subsequent requests can set the required authentication header.
|
||||
bool mNeedAuth;
|
||||
int respCode;
|
||||
Authenticator* mAuthenticator;
|
||||
|
||||
|
||||
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
||||
|
||||
|
@ -81,9 +90,10 @@ private:
|
|||
private:
|
||||
bool sendCommand( std::string message );
|
||||
bool recvResponse( std::string &response );
|
||||
void checkAuthResponse(std::string &response);
|
||||
|
||||
public:
|
||||
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth );
|
||||
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth);
|
||||
~RtspThread();
|
||||
|
||||
public:
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
//
|
||||
// ZoneMinder RTSP Authentication Class Implementation, $Date$, $Revision$
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_utils.h"
|
||||
#include "zm_rtsp_auth.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
Authenticator::Authenticator(std::string &username, std::string password) {
|
||||
#ifdef HAVE_GCRYPT_H
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
#endif // HAVE_GCRYPT_H
|
||||
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fUsername = username;
|
||||
fPassword = password;
|
||||
}
|
||||
|
||||
Authenticator::~Authenticator() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Authenticator::reset() {
|
||||
fRealm.clear();
|
||||
fNonce.clear();
|
||||
fUsername.clear();
|
||||
fPassword.clear();
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
}
|
||||
|
||||
void Authenticator::setRealmAndNonce(std::string &realm, std::string &nonce) {
|
||||
fRealm = realm;
|
||||
fNonce = nonce;
|
||||
}
|
||||
|
||||
void Authenticator::authHandleHeader(std::string headerData)
|
||||
{
|
||||
const char* basic_match = "Basic ";
|
||||
const char* digest_match = "Digest ";
|
||||
size_t digest_match_len = strlen(digest_match);
|
||||
|
||||
// Check if basic auth
|
||||
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
||||
{
|
||||
fAuthMethod = AUTH_BASIC;
|
||||
Debug( 2, "Set authMethod to Basic");
|
||||
}
|
||||
// Check if digest auth
|
||||
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
|
||||
{
|
||||
fAuthMethod = AUTH_DIGEST;
|
||||
Debug( 2, "Set authMethod to Digest");
|
||||
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
||||
// subparts are key="value"
|
||||
for ( size_t i = 0; i < subparts.size(); i++ )
|
||||
{
|
||||
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
||||
std::string key = trimSpaces( kvPair[0] );
|
||||
if (key == "realm") {
|
||||
fRealm = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
if (key == "nonce") {
|
||||
fNonce = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s", username().c_str(), fRealm.c_str(), fNonce.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string Authenticator::getAuthHeader(std::string method, std::string uri)
|
||||
{
|
||||
std::string result = "Authorization: ";
|
||||
if (fAuthMethod == AUTH_BASIC)
|
||||
{
|
||||
result += "Basic " + base64Encode( username() + ":" + password() );
|
||||
}
|
||||
else if (fAuthMethod == AUTH_DIGEST)
|
||||
{
|
||||
result += std::string("Digest ") +
|
||||
"username=\"" + username() + "\", realm=\"" + realm() + "\", " +
|
||||
"nonce=\"" + nonce() + "\", uri=\"" + uri + "\", " +
|
||||
"response=\"" + computeDigestResponse(method, uri) + "\"";
|
||||
//Authorization: Digest username="zm",
|
||||
// realm="NC-336PW-HD-1080P",
|
||||
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
||||
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
||||
// response="4092120557d3099a163bd51a0d59744d",
|
||||
// algorithm=MD5,
|
||||
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
||||
// qop="auth",
|
||||
// cnonce="c8051140765877dc",
|
||||
// nc=00000001
|
||||
|
||||
}
|
||||
result += "\r\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
|
||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
// The "response" field is computed as:
|
||||
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
||||
size_t md5len = 16;
|
||||
unsigned char md5buf[md5len];
|
||||
char md5HexBuf[md5len*2+1];
|
||||
|
||||
// Step 1: md5(<username>:<realm>:<password>)
|
||||
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha1Hash = md5HexBuf;
|
||||
|
||||
// Step 2: md5(<cmd>:<url>)
|
||||
std::string ha2Data = method + ":" + uri;
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha2Hash = md5HexBuf;
|
||||
|
||||
// Step 3: md5(ha1:<nonce>:ha2)
|
||||
std::string digestData = ha1Hash + ":" + nonce() + ":" + ha2Hash;
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
|
||||
return md5HexBuf;
|
||||
#else // HAVE_DECL_MD5
|
||||
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
||||
#endif // HAVE_DECL_MD5
|
||||
return( 0 );
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// ZoneMinder RTSP Authentication Class Interface, $Date$, $Revision$
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
|
||||
#ifndef ZM_RTSP_AUTH_H
|
||||
#define ZM_RTSP_AUTH_H
|
||||
|
||||
#if HAVE_GNUTLS_OPENSSL_H
|
||||
#include <gnutls/openssl.h>
|
||||
#endif
|
||||
#if HAVE_GNUTLS_GNUTLS_H
|
||||
#include <gnutls/gnutls.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_GCRYPT_H
|
||||
#include <gcrypt.h>
|
||||
#elif HAVE_LIBCRYPTO
|
||||
#include <openssl/md5.h>
|
||||
#endif // HAVE_GCRYPT_H || HAVE_LIBCRYPTO
|
||||
|
||||
class Authenticator
|
||||
{
|
||||
public:
|
||||
typedef enum { AUTH_UNDEFINED, AUTH_BASIC, AUTH_DIGEST } RtspAuthMethod;
|
||||
Authenticator(std::string &username, std::string password);
|
||||
virtual ~Authenticator();
|
||||
void reset();
|
||||
void setRealmAndNonce(std::string &realm, std::string &nonce);
|
||||
|
||||
std::string realm() { return fRealm; }
|
||||
std::string nonce() { return fNonce; }
|
||||
std::string username() { return fUsername; }
|
||||
|
||||
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
||||
void authHandleHeader( std::string headerData );
|
||||
std::string getAuthHeader( std::string method, std::string path );
|
||||
|
||||
private:
|
||||
std::string password() { return fPassword; }
|
||||
RtspAuthMethod fAuthMethod;
|
||||
std::string fRealm;
|
||||
std::string fNonce;
|
||||
std::string fUsername;
|
||||
std::string fPassword;
|
||||
};
|
||||
|
||||
#endif // ZM_RTSP_AUTH_H
|
|
@ -27,6 +27,25 @@
|
|||
|
||||
unsigned int sseversion = 0;
|
||||
|
||||
std::string trimSet(std::string str, std::string trimset) {
|
||||
// Trim Both leading and trailing sets
|
||||
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
||||
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
||||
|
||||
// if all spaces or empty return an empty string
|
||||
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
else
|
||||
return str.substr( startpos, endpos-startpos+1 );
|
||||
}
|
||||
|
||||
std::string trimSpaces(std::string str)
|
||||
{
|
||||
return trimSet(str, " \t");
|
||||
}
|
||||
|
||||
const std::string stringtf( const char *format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
|
||||
typedef std::vector<std::string> StringVector;
|
||||
|
||||
std::string trimSpaces(std::string str);
|
||||
std::string trimSet(std::string str, std::string trimset);
|
||||
|
||||
const std::string stringtf( const char *format, ... );
|
||||
const std::string stringtf( const std::string &format, ... );
|
||||
|
||||
|
|
Loading…
Reference in New Issue