Fix confict when pulling master

This commit is contained in:
Emmanuel Papin 2014-11-29 19:11:22 +01:00
commit 9e39924095
12 changed files with 195 additions and 60 deletions

View File

@ -1,3 +1,9 @@
zoneminder (1.28.0+1-utopic-SNAPSHOT2014112001) utopic; urgency=medium
* Various fixes and developments since 1.28.0. Includes Digest-Auth for HTTP and better for RTSP
-- Isaac Connor <iconnor@connortechnology.com> Thu, 20 Nov 2014 10:57:57 -0500
zoneminder (1.28.0-trusty) trusty; urgency=medium
* Release

View File

@ -29,7 +29,6 @@ void zmLoadConfig()
{
FILE *cfg;
char line[512];
char *val;
if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL )
{
Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) );
@ -82,19 +81,16 @@ void zmLoadConfig()
white_len = strspn( val_ptr, " \t" );
val_ptr += white_len;
val = (char *)malloc( strlen(val_ptr)+1 );
strncpy( val, val_ptr, strlen(val_ptr)+1 );
if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )
staticConfig.DB_HOST = val;
staticConfig.DB_HOST = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 )
staticConfig.DB_NAME = val;
staticConfig.DB_NAME = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 )
staticConfig.DB_USER = val;
staticConfig.DB_USER = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
staticConfig.DB_PASS = val;
staticConfig.DB_PASS = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
staticConfig.PATH_WEB = val;
staticConfig.PATH_WEB = std::string(val_ptr);
else
{
// We ignore this now as there may be more parameters than the

View File

@ -140,6 +140,22 @@ Image::Image( const Image &p_image )
Image::~Image()
{
DumpImgBuffer();
if ( initialised )
{
delete[] y_table;
delete[] uv_table;
delete[] r_v_table;
delete[] g_v_table;
delete[] g_u_table;
delete[] b_u_table;
initialised = false;
}
if ( jpg_dcinfo )
{
jpeg_destroy_decompress( jpg_dcinfo );
delete jpg_dcinfo;
jpg_dcinfo = 0;
}
}
void Image::Initialise()

View File

@ -334,6 +334,8 @@ Monitor::Monitor(
camera( p_camera ),
n_zones( p_n_zones ),
zones( p_zones ),
timestamps( 0 ),
images( 0 ),
iDoNativeMotDet( p_DoNativeMotDet ),
ThePluginManager( p_id )
{
@ -613,6 +615,14 @@ bool Monitor::connect() {
Monitor::~Monitor()
{
if ( timestamps ) {
delete[] timestamps;
timestamps = 0;
}
if ( images ) {
delete[] images;
images = 0;
}
if ( mem_ptr ) {
if ( event )
Info( "%s: %03d - Closing event %d, shutting down", name, image_count, event->Id() );
@ -1306,8 +1316,6 @@ bool Monitor::Analyse()
}
static bool static_undef = true;
static struct timeval **timestamps;
static Image **images;
static int last_section_mod = 0;
static bool last_signal;

View File

@ -286,17 +286,19 @@ protected:
Event *event;
int n_zones;
int n_zones;
Zone **zones;
struct timeval **timestamps;
Image **images;
int iDoNativeMotDet;
#if ZM_PLUGINS_ON
PluginManager ThePluginManager;
#else
int ThePluginManager;
#endif
int n_linked_monitors;
int n_linked_monitors;
MonitorLink **linked_monitors;
public:

View File

@ -63,8 +63,16 @@ void RemoteCamera::Initialise()
auth = host.substr( 0, authIndex );
host.erase( 0, authIndex+1 );
auth64 = base64Encode( auth );
authIndex = auth.rfind( ':' );
username = auth.substr(0,authIndex);
password = auth.substr( authIndex+1, auth.length() );
}
mNeedAuth = false;
mAuthenticator = new Authenticator(username,password);
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;

View File

@ -21,6 +21,7 @@
#define ZM_REMOTE_CAMERA_H
#include "zm_camera.h"
#include "zm_rtsp_auth.h"
#include <string>
#include <sys/types.h>
@ -39,8 +40,17 @@ protected:
std::string port;
std::string path;
std::string auth;
std::string username;
std::string password;
std::string auth64;
// 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;
Authenticator* mAuthenticator;
protected:
struct addrinfo *hp;
@ -53,6 +63,8 @@ public:
const std::string &Port() const { return( port ); }
const std::string &Path() const { return( path ); }
const std::string &Auth() const { return( auth ); }
const std::string &Username() const { return( username ); }
const std::string &Password() const { return( password ); }
virtual void Initialise();
virtual void Terminate() = 0;

View File

@ -62,7 +62,7 @@ void RemoteCameraHttp::Initialise()
{
request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version );
request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION );
request += stringtf( "Host: %s\r\n", host .c_str());
request += stringtf( "Host: %s\r\n", host.c_str());
if ( strcmp( config.http_version, "1.0" ) == 0 )
request += stringtf( "Connection: Keep-Alive\r\n" );
if ( !auth.empty() )
@ -130,6 +130,7 @@ int RemoteCameraHttp::Disconnect()
int RemoteCameraHttp::SendRequest()
{
Debug( 2, "Sending request: %s", request.c_str() );
if ( write( sd, request.data(), request.length() ) < 0 )
{
Error( "Can't write: %s", strerror(errno) );
@ -216,6 +217,8 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, int bytes_expected )
}
while ( total_bytes_to_read );
Debug( 3, buffer );
return( total_bytes_read );
}
@ -280,9 +283,31 @@ int RemoteCameraHttp::GetResponse()
status_code = atoi( status_expr->MatchString( 2 ) );
status_mesg = status_expr->MatchString( 3 );
if ( status_code < 200 || status_code > 299 )
{
Error( "Invalid response status %d: %s", status_code, status_mesg );
if ( status_code == 401 ) {
if ( mNeedAuth ) {
Error( "Failed authentication: " );
return( -1 );
}
mNeedAuth = true;
std::string Header = header;
mAuthenticator->checkAuthResponse(Header);
if ( mAuthenticator->auth_method() == AUTH_DIGEST ) {
Debug( 2, "Need Digest Authentication" );
request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version );
request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION );
request += stringtf( "Host: %s\r\n", host.c_str());
if ( strcmp( config.http_version, "1.0" ) == 0 )
request += stringtf( "Connection: Keep-Alive\r\n" );
request += mAuthenticator->getAuthHeader( "GET", path.c_str() );
request += "\r\n";
Debug( 2, "New request header: %s", request.c_str() );
return( 0 );
}
} else if ( status_code < 200 || status_code > 299 ) {
Error( "Invalid response status %d: %s\n%s", status_code, status_mesg, (char *)buffer );
return( -1 );
}
Debug( 3, "Got status '%d' (%s), http version %s", status_code, status_mesg, http_version );
@ -545,11 +570,13 @@ int RemoteCameraHttp::GetResponse()
static const char *content_length_match = "Content-length:";
static const char *content_type_match = "Content-type:";
static const char *boundary_match = "boundary=";
static const char *authenticate_match = "WWW-Authenticate:";
static int http_match_len = 0;
static int connection_match_len = 0;
static int content_length_match_len = 0;
static int content_type_match_len = 0;
static int boundary_match_len = 0;
static int authenticate_match_len = 0;
if ( !http_match_len )
http_match_len = strlen( http_match );
@ -561,6 +588,8 @@ int RemoteCameraHttp::GetResponse()
content_type_match_len = strlen( content_type_match );
if ( !boundary_match_len )
boundary_match_len = strlen( boundary_match );
if ( !authenticate_match_len )
authenticate_match_len = strlen( authenticate_match );
static int n_headers;
//static char *headers[32];
@ -573,6 +602,7 @@ int RemoteCameraHttp::GetResponse()
static char *content_length_header;
static char *content_type_header;
static char *boundary_header;
static char *authenticate_header;
static char subcontent_length_header[32];
static char subcontent_type_header[64];
@ -596,6 +626,7 @@ int RemoteCameraHttp::GetResponse()
connection_header = 0;
content_length_header = 0;
content_type_header = 0;
authenticate_header = 0;
http_version[0] = '\0';
status_code [0]= '\0';
@ -674,6 +705,12 @@ int RemoteCameraHttp::GetResponse()
content_length_header = header_ptr+content_length_match_len;
Debug( 6, "Got content length header '%s'", header_ptr );
}
else if ( !authenticate_header && (strncasecmp( header_ptr, authenticate_match, authenticate_match_len) == 0) )
{
authenticate_header = header_ptr;
Debug( 6, "Got authenticate header '%s'", header_ptr );
}
else if ( !content_type_header && (strncasecmp( header_ptr, content_type_match, content_type_match_len) == 0) )
{
content_type_header = header_ptr+content_type_match_len;
@ -721,7 +758,36 @@ int RemoteCameraHttp::GetResponse()
start_ptr += strspn( start_ptr, " " );
strcpy( status_mesg, start_ptr );
if ( status < 200 || status > 299 )
if ( status == 401 ) {
if ( mNeedAuth ) {
Error( "Failed authentication: " );
return( -1 );
}
if ( ! authenticate_header ) {
Error( "Failed authentication, but don't have an authentication header: " );
return( -1 );
}
mNeedAuth = true;
std::string Header = authenticate_header;
Debug(2, "Checking for digest auth in %s", authenticate_header );
mAuthenticator->checkAuthResponse(Header);
if ( mAuthenticator->auth_method() == AUTH_DIGEST ) {
Debug( 2, "Need Digest Authentication" );
request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version );
request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION );
request += stringtf( "Host: %s\r\n", host.c_str());
if ( strcmp( config.http_version, "1.0" ) == 0 )
request += stringtf( "Connection: Keep-Alive\r\n" );
request += mAuthenticator->getAuthHeader( "GET", path.c_str() );
request += "\r\n";
Debug( 2, "New request header: %s", request.c_str() );
return( 0 );
} else {
Debug( 2, "Need some other kind of Authentication" );
}
} else if ( status < 200 || status > 299 )
{
Error( "Invalid response status %s: %s", status_code, status_mesg );
return( -1 );

View File

@ -67,34 +67,6 @@ 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 )
@ -120,7 +92,7 @@ bool RtspThread::recvResponse( std::string &response )
if ( respCode == 401)
{
Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry");
checkAuthResponse(response);
mAuthenticator->checkAuthResponse(response);
mNeedAuth = true;
return( false );
}
@ -326,7 +298,7 @@ int RtspThread::run()
// for requested authentication method
if (respCode == 401 && !authTried) {
mNeedAuth = true;
checkAuthResponse(response);
mAuthenticator->checkAuthResponse(response);
Debug(2, "Processed 401 response");
mRtspSocket.close();
if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) )
@ -361,7 +333,7 @@ int RtspThread::run()
int localPorts[2] = { 0, 0 };
// Request supported RTSP commands by the server
message = "OPTIONS * RTSP/1.0\r\n";
message = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
if ( !sendCommand( message ) )
return( -1 );
if ( !recvResponse( response ) )
@ -680,7 +652,7 @@ int RtspThread::run()
select.addReader( &mRtspSocket );
Buffer buffer( ZM_NETWORK_BUFSIZ );
std::string keepaliveMessage = "OPTIONS * RTSP/1.0\r\n";
std::string keepaliveMessage = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
std::string keepaliveResponse = "RTSP/1.0 200 OK\r\n";
while ( !mStop && select.wait() >= 0 )
{

View File

@ -38,6 +38,8 @@ Authenticator::Authenticator(std::string &username, std::string password) {
fAuthMethod = AUTH_UNDEFINED;
fUsername = username;
fPassword = password;
nc = 1;
fCnonce = "0a4f113b";
}
Authenticator::~Authenticator() {
@ -83,8 +85,12 @@ void Authenticator::authHandleHeader(std::string headerData)
fNonce = trimSet( kvPair[1], "\"");
continue;
}
if (key == "qop") {
fQop = trimSet( kvPair[1], "\"");
continue;
}
}
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s", username().c_str(), fRealm.c_str(), fNonce.c_str());
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
}
}
@ -104,8 +110,13 @@ std::string Authenticator::getAuthHeader(std::string method, std::string uri)
{
result += std::string("Digest ") +
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\", " +
"response=\"" + computeDigestResponse(method, uri) + "\"";
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
if ( ! fQop.empty() ) {
result += ", qop=" + fQop;
result += ", nc=" + stringtf("%08x",nc);
result += ", cnonce=" + fCnonce;
}
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
//Authorization: Digest username="zm",
// realm="NC-336PW-HD-1080P",
@ -133,6 +144,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
// Step 1: md5(<username>:<realm>:<password>)
std::string ha1Data = username() + ":" + realm() + ":" + password();
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
#if HAVE_DECL_MD5
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
#elif HAVE_DECL_GNUTLS_FINGERPRINT
@ -148,6 +160,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
// Step 2: md5(<cmd>:<url>)
std::string ha2Data = method + ":" + uri;
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
#if HAVE_DECL_MD5
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
#elif HAVE_DECL_GNUTLS_FINGERPRINT
@ -162,7 +175,14 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
std::string ha2Hash = md5HexBuf;
// Step 3: md5(ha1:<nonce>:ha2)
std::string digestData = ha1Hash + ":" + nonce() + ":" + ha2Hash;
std::string digestData = ha1Hash + ":" + nonce();
if ( ! fQop.empty() ) {
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
nc ++;
// if qop was specified, then we have to include t and a cnonce and an nccount
}
digestData += ":" + ha2Hash;
Debug( 2, "pre-md5: %s", digestData.c_str() );
#if HAVE_DECL_MD5
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
#elif HAVE_DECL_GNUTLS_FINGERPRINT
@ -181,3 +201,28 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
#endif // HAVE_DECL_MD5
return( 0 );
}
void Authenticator::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());
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
} else {
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
}
}

View File

@ -32,10 +32,9 @@
#include <openssl/md5.h>
#endif // HAVE_GCRYPT_H || HAVE_LIBCRYPTO
class Authenticator
{
enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 };
class Authenticator {
public:
typedef enum { AUTH_UNDEFINED, AUTH_BASIC, AUTH_DIGEST } RtspAuthMethod;
Authenticator(std::string &username, std::string password);
virtual ~Authenticator();
void reset();
@ -43,19 +42,24 @@ public:
std::string realm() { return fRealm; }
std::string nonce() { return fNonce; }
std::string username() { return fUsername; }
AuthMethod auth_method() const { return fAuthMethod; }
std::string computeDigestResponse( std::string &cmd, std::string &url );
void authHandleHeader( std::string headerData );
std::string getAuthHeader( std::string method, std::string path );
void checkAuthResponse(std::string &response);
private:
std::string password() { return fPassword; }
RtspAuthMethod fAuthMethod;
AuthMethod fAuthMethod;
std::string fRealm;
std::string fNonce;
std::string fCnonce;
std::string fQop;
std::string fUsername;
std::string fPassword;
std::string quote( std::string src );
int nc;
};
#endif // ZM_RTSP_AUTH_H

View File

@ -296,7 +296,7 @@ int main( int argc, char *argv[] )
{
delete monitors[i];
}
delete monitors;
delete [] monitors;
delete [] alarm_capture_delays;
delete [] capture_delays;
delete [] next_delays;