2021-04-27 Fred Gleason <fredg@paravelsystems.com>

* Changed the 'RDAIRPLAY_EXIT_PASSWORD' field from 'varchar(41)'
	to 'varchar(48)'.
	* Incremented the database version to 349.
	* Renamed the 'RDSha1Hash()' function to 'RDSha1HashFile()'.
	* Added 'RDSha1HashPassword()' function in 'lib/rdhash.[cpp|h]'.
	* Added 'RDSha1HashCheckPassword()' function in 'lib/rdhash.[cpp|h]'.
	* Changed the hashing algorithm used for the Exit Password for
	rdairplay(1) to salted SHA1.

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason 2021-04-27 16:52:26 -04:00
parent 05c35a208c
commit 9a65658267
17 changed files with 164 additions and 46 deletions

View File

@ -21631,3 +21631,12 @@
* Fixed a regression in 'RDLog::create()' that threw SQL errors.
2021-04-27 Fred Gleason <fredg@paravelsystems.com>
* Fixed a regression in 'RDLogModel' that threw SQL errors.
2021-04-27 Fred Gleason <fredg@paravelsystems.com>
* Changed the 'RDAIRPLAY_EXIT_PASSWORD' field from 'varchar(41)'
to 'varchar(48)'.
* Incremented the database version to 349.
* Renamed the 'RDSha1Hash()' function to 'RDSha1HashFile()'.
* Added 'RDSha1HashPassword()' function in 'lib/rdhash.[cpp|h]'.
* Added 'RDSha1HashCheckPassword()' function in 'lib/rdhash.[cpp|h]'.
* Changed the hashing algorithm used for the Exit Password for
rdairplay(1) to salted SHA1.

View File

@ -26,7 +26,7 @@ DEFAULT_SERVICE varchar(10) From SERVICES.NAME
HOUR_SELECTOR_ENABLED enum('N','Y')
EXIT_CODE int(11) 0=clean, 1=dirty
VIRTUAL_EXIT_CODE int(11) 0=clean, 1=dirty
EXIT_PASSWORD varchar(41)
EXIT_PASSWORD varchar(48)
SKIN_PATH varchar(191)
SHOW_COUNTERS enum('N','Y')
AUDITION_PREROLL int(11)

View File

@ -24,7 +24,7 @@
/*
* Current Database Version
*/
#define RD_VERSION_DATABASE 348
#define RD_VERSION_DATABASE 349
#endif // DBVERSION_H

View File

@ -20,10 +20,11 @@
#include <QObject>
#include "rdairplay_conf.h"
#include "rddb.h"
#include "rdconf.h"
#include "rdairplay_conf.h"
#include "rdescape_string.h"
#include "rdhash.h"
RDAirPlayConf::RDAirPlayConf(const QString &station,const QString &tablename)
{
@ -662,34 +663,40 @@ bool RDAirPlayConf::exitPasswordValid(const QString &passwd) const
{
QString sql;
RDSqlQuery *q;
bool ret=false;
sql=QString("select `EXIT_PASSWORD` from `")+air_tablename+"` where "+
"STATION='"+RDEscapeString(air_station)+"' && "+
"((`EXIT_PASSWORD`=PASSWORD('"+RDEscapeString(passwd)+"'))";
if(passwd.isEmpty()) {
sql+="||(`EXIT_PASSWORD` is null)";
}
sql+=")";
sql=QString("select ")+
"`EXIT_PASSWORD` "+ // 00
"from `"+air_tablename+"` where "+
"`STATION`='"+RDEscapeString(air_station)+"'";
q=new RDSqlQuery(sql);
if(q->size()>0) {
delete q;
return true;
if(q->first()) {
if(passwd.isEmpty()) {
ret=q->value(0).isNull();
}
delete q;
return false;
else {
ret=RDSha1HashCheckPassword(passwd,q->value(0).toString());
}
}
return ret;
}
void RDAirPlayConf::setExitPassword(const QString &passwd) const
{
QString sql;
RDSqlQuery *q;
if(passwd.isEmpty()) {
sql=QString("update `")+air_tablename+"` set "+
"`EXIT_PASSWORD`=PASSWORD('"+RDEscapeString(passwd)+"') where "+
"`EXIT_PASSWORD`=null where "+
"`STATION`='"+RDEscapeString(air_station)+"'";
q=new RDSqlQuery(sql);
delete q;
}
else {
sql=QString("update `")+air_tablename+"` set "+
"`EXIT_PASSWORD`='"+RDEscapeString(RDSha1HashPassword(passwd))+"' where "+
"`STATION`='"+RDEscapeString(air_station)+"'";
}
RDSqlQuery::apply(sql);
}

View File

@ -27,9 +27,28 @@
#include <openssl/sha.h>
#include <QDateTime>
#include "rdhash.h"
QString RDSha1Hash(const QString &filename,bool throttle)
QString __RDSha1Hash_MakePasswordHash(const QString &secret,const QString &salt)
{
SHA_CTX ctx;
unsigned char md[SHA_DIGEST_LENGTH];
SHA1_Init(&ctx);
SHA1_Update(&ctx,salt.toUtf8(),salt.toUtf8().length());
SHA1_Update(&ctx,secret.toUtf8(),secret.toUtf8().length());
SHA1_Final(md,&ctx);
QString ret=salt;
for(int i=0;i<SHA_DIGEST_LENGTH;i++) {
ret+=QString().sprintf("%02x",0xff&md[i]);
}
return ret;
}
QString RDSha1HashFile(const QString &filename,bool throttle)
{
QString ret;
SHA_CTX ctx;
@ -57,3 +76,27 @@ QString RDSha1Hash(const QString &filename,bool throttle)
return ret;
}
QString RDSha1HashPassword(const QString &secret)
{
//
// Create a salt value
//
srand(QDateTime::currentDateTime().toMSecsSinceEpoch());
QString salt=QString().sprintf("%08x",rand());
//
// Generate the hash
//
return __RDSha1Hash_MakePasswordHash(secret,salt);
}
bool RDSha1HashCheckPassword(const QString &secret,const QString &hash)
{
QString salt=secret.left(8);
return __RDSha1Hash_MakePasswordHash(secret,hash.left(8))==hash;
}

View File

@ -23,7 +23,9 @@
#include <qstring.h>
QString RDSha1Hash(const QString &filename,bool throttle=false);
QString RDSha1HashFile(const QString &filename,bool throttle=false);
QString RDSha1HashPassword(const QString &secret);
bool RDSha1HashCheckPassword(const QString &secret,const QString &hash);
#endif // RD_H
#endif // RDHASH_H

View File

@ -2334,7 +2334,7 @@ void MainObject::CheckInRecording(QString cutname,CatchEvent *evt,
s->setChannels(evt->channels());
cut->checkInRecording(rda->config()->stationName(),"",
rda->config()->stationName(),s,msecs);
cut->setSha1Hash(RDSha1Hash(RDCut::pathName(cut->cutName())));
cut->setSha1Hash(RDSha1HashFile(RDCut::pathName(cut->cutName())));
delete s;
cut->autoTrim(RDCut::AudioBoth,-threshold);
RDCart *cart=new RDCart(cut->cartNumber());

View File

@ -133,6 +133,7 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent)
login_button=new QPushButton(this);
login_button->setFont(buttonFont());
login_button->setText(tr("Set User"));
login_button->setDefault(true);
connect(login_button,SIGNAL(clicked()),this,SLOT(loginData()));
//

View File

@ -32,6 +32,8 @@ MainObject::MainObject(QObject *parent)
:QObject(parent)
{
QString filename="";
QString password="";
QString hash="";
//
// Read Command Options
@ -42,27 +44,58 @@ MainObject::MainObject(QObject *parent)
filename=cmd->value(i);
cmd->setProcessed(i,true);
}
if(cmd->key(i)=="--hash") {
hash=cmd->value(i);
cmd->setProcessed(i,true);
}
if(filename.isEmpty()) {
fprintf(stderr,"test_hash: missing --filename\n");
if(cmd->key(i)=="--password") {
password=cmd->value(i);
cmd->setProcessed(i,true);
}
}
if(filename.isEmpty()&&password.isEmpty()) {
fprintf(stderr,"test_hash: missing --filename or --password\n");
exit(256);
}
if((!filename.isEmpty())&&(!password.isEmpty())) {
fprintf(stderr,"test_hash: --filename and --password are mutually exclusive\n");
exit(256);
}
QString hash=RDSha1Hash(filename);
if(!filename.isEmpty()) { // Hash the specified file
hash=RDSha1HashFile(filename);
if(hash.isEmpty()) {
fprintf(stderr,"test_hash: unable to open \"%s\"\n",
filename.toUtf8().constData());
exit(256);
}
printf("%s\n",hash.toUtf8().constData());
exit(0);
}
if((!hash.isEmpty())&&(!password.isEmpty())) { // Check the specified hash
if( RDSha1HashCheckPassword(password,hash)) {
printf("Match!\n");
}
else {
printf("No Match!\n");
}
exit(0);
}
if(!password.isEmpty()) { // Generate password hash
printf("%s\n",RDSha1HashPassword(password).toUtf8().constData());
exit(0);
}
fprintf(stderr,"test_hash: inconsistent arguments given!\n");
exit(256);
}
int main(int argc,char *argv[])
{
QApplication a(argc,argv,false);
QCoreApplication a(argc,argv);
new MainObject();
return a.exec();
}

View File

@ -24,7 +24,7 @@
#include <rdcmd_switch.cpp>
#include <rdhash.h>
#define TEST_HASH_USAGE "[options]\n\nTest SHA1 has generation\n\n--filename=<file-name>\n The name of the file for which to generate a hash.\n\n"
#define TEST_HASH_USAGE "[options]\n\nTest SHA1 hash generation\n\n--filename=<file-name>\n The name of the file for which to generate a hash.\n\n--password=<secret>\n Generate a password hash from <secret>\n\n--hash=<hash>\n When given with --secret, verify <hash>\n\n"
class MainObject : public QObject
{

View File

@ -211,7 +211,7 @@ void MainObject::CheckTableAttributes()
printf(" Database uses default charset/collation %s/%s, should be utf8mb4/%s. Fix? (y/N) ",
q->value(1).toString().toUtf8().constData(),
q->value(2).toString().toUtf8().constData(),
db_config->mysqlCollation());
db_config->mysqlCollation().toUtf8().constData());
fflush(NULL);
if(UserResponse()) {
sql=QString("alter database `")+db_mysql_database+"` "+
@ -400,7 +400,7 @@ void MainObject::RelinkAudio(const QString &srcdir) const
QStringList files=dir.entryList(QDir::Files|QDir::Readable|QDir::Hidden);
for(int i=0;i<files.size();i++) {
QString filename=dir.path()+"/"+files[i];
QString hash=RDSha1Hash(filename);
QString hash=RDSha1HashFile(filename);
QString firstdest;
bool delete_source=true;
@ -913,7 +913,7 @@ void MainObject::RehashCart(unsigned cartnum) const
void MainObject::RehashCut(const QString &cutnum) const
{
QString hash=RDSha1Hash(RDCut::pathName(cutnum),true);
QString hash=RDSha1HashFile(RDCut::pathName(cutnum),true);
if(hash.isEmpty()) {
printf(" Unable to generate hash for \"%s\"\n",
RDCut::pathName(cutnum).toUtf8().constData());

View File

@ -40,6 +40,19 @@ bool MainObject::RevertSchema(int cur_schema,int set_schema,QString *err_msg)
// NEW SCHEMA REVERSIONS GO HERE...
//
// Revert 349
//
if((cur_schema==349)&&(set_schema<cur_schema)) {
sql=QString("alter table `RDAIRPLAY` ")+
"modify column EXIT_PASSWORD varchar(41)";
if(!RDSqlQuery::apply(sql,err_msg)) {
return false;
}
WriteSchemaVersion(--cur_schema);
}
//
// Revert 348
//

View File

@ -159,7 +159,7 @@ void MainObject::InitializeSchemaMap() {
global_version_map["3.3"]=314;
global_version_map["3.4"]=317;
global_version_map["3.5"]=346;
global_version_map["4.0"]=348;
global_version_map["4.0"]=349;
}

View File

@ -10774,6 +10774,16 @@ bool MainObject::UpdateSchema(int cur_schema,int set_schema,QString *err_msg)
WriteSchemaVersion(++cur_schema);
}
if((cur_schema<349)&&(set_schema>cur_schema)) {
sql=QString("alter table `RDAIRPLAY` ")+
"modify column EXIT_PASSWORD varchar(48)";
if(!RDSqlQuery::apply(sql,err_msg)) {
return false;
}
WriteSchemaVersion(++cur_schema);
}
// NEW SCHEMA UPDATES GO HERE...

View File

@ -256,7 +256,7 @@ void Xport::Import()
break;
}
if(resp_code==200) {
cut->setSha1Hash(RDSha1Hash(RDCut::pathName(cut->cutName())));
cut->setSha1Hash(RDSha1HashFile(RDCut::pathName(cut->cutName())));
if(!title.isEmpty()) {
cart->setTitle(title);
}

View File

@ -107,7 +107,7 @@ void Xport::SavePodcast()
delete cast;
XmlExit(err_msg.toUtf8(),500,"podcasts.cpp",LINE_NUMBER);
}
cast->setSha1Hash(RDSha1Hash(destpath));
cast->setSha1Hash(RDSha1HashFile(destpath));
printf("Content-type: text/html; charset: UTF-8\n");
printf("Status: 200\n\n");
@ -172,7 +172,7 @@ void Xport::GetPodcast()
data=new char[st.st_blksize];
n=read(fd,data,st.st_blksize);
while(n>0) {
write(1,data,n);
n=write(1,data,n);
n=read(fd,data,st.st_blksize);
}
delete data;

View File

@ -59,7 +59,7 @@ void Xport::Rehash()
delete cut;
XmlExit("No such cut",404,"rdhash.cpp",LINE_NUMBER);
}
cut->setSha1Hash(RDSha1Hash(RDCut::pathName(cart_number,cut_number)));
cut->setSha1Hash(RDSha1HashFile(RDCut::pathName(cart_number,cut_number)));
delete cut;
XmlExit("OK",200,"rdhash.cpp",LINE_NUMBER);
}