2022-09-19 Fred Gleason <fredg@paravelsystems.com>

* Added support for using a ssh(1) identity file for authentication
	to the 'Upload' and 'Download' event types in rdcatch(1).

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason 2022-09-19 13:07:48 -04:00
parent a1ecaa871d
commit 19370379b4
9 changed files with 124 additions and 59 deletions

View File

@ -23339,3 +23339,6 @@
2022-09-19 Fred Gleason <fredg@paravelsystems.com> 2022-09-19 Fred Gleason <fredg@paravelsystems.com>
* Fixed a regression in rdcatch(1) that caused a segfault when * Fixed a regression in rdcatch(1) that caused a segfault when
attempting to add or delete an event. attempting to add or delete an event.
2022-09-19 Fred Gleason <fredg@paravelsystems.com>
* Added support for using a ssh(1) identity file for authentication
to the 'Upload' and 'Download' event types in rdcatch(1).

View File

@ -2,7 +2,7 @@
// //
// Upload a File // Upload a File
// //
// (C) Copyright 2010-2021 Fred Gleason <fredg@paravelsystems.com> // (C) Copyright 2010-2022 Fred Gleason <fredg@paravelsystems.com>
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as // it under the terms of the GNU General Public License version 2 as
@ -41,8 +41,8 @@
// //
// CURL Progress Callback // CURL Progress Callback
// //
int UploadProgressCallback(void *clientp,double dltotal,double dlnow, int __RDUpload_UploadProgressCallback(void *clientp,double dltotal,double dlnow,
double ultotal,double ulnow) double ultotal,double ulnow)
{ {
RDUpload *conv=(RDUpload *)clientp; RDUpload *conv=(RDUpload *)clientp;
conv->UpdateProgress(ulnow); conv->UpdateProgress(ulnow);
@ -54,8 +54,8 @@ int UploadProgressCallback(void *clientp,double dltotal,double dlnow,
} }
int UploadErrorCallback(CURL *curl,curl_infotype type,char *msg,size_t size, int __RDUpload_UploadErrorCallback(CURL *curl,curl_infotype type,char *msg,
void *clientp) size_t size,void *clientp)
{ {
char str[1000]; char str[1000];
@ -160,7 +160,7 @@ RDUpload::ErrorCode RDUpload::runUpload(const QString &username,
url.replace("#","%23"); url.replace("#","%23");
// //
// Authentication // Authentication Parameters
// //
if((conv_dst_url.scheme().toLower()=="sftp")&& if((conv_dst_url.scheme().toLower()=="sftp")&&
(!id_filename.isEmpty())&&use_id_filename) { (!id_filename.isEmpty())&&use_id_filename) {
@ -168,25 +168,36 @@ RDUpload::ErrorCode RDUpload::runUpload(const QString &username,
curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE, curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE,
id_filename.toUtf8().constData()); id_filename.toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_KEYPASSWD,password.toUtf8().constData()); curl_easy_setopt(curl,CURLOPT_KEYPASSWD,password.toUtf8().constData());
rda->syslog(LOG_DEBUG,"using ssh key at \"%s\"",
id_filename.toUtf8().constData());
} }
else { else {
strncpy(userpwd,(username+":"+password).toUtf8(),255); strncpy(userpwd,(username+":"+password).toUtf8(),255);
curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd); curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd);
} }
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0); // Don't verify host key
curl_easy_setopt(curl,CURLOPT_URL,(const char *)url); //
// Transfer Parameters
//
curl_easy_setopt(curl,CURLOPT_URL,url.constData());
curl_easy_setopt(curl,CURLOPT_UPLOAD,1); curl_easy_setopt(curl,CURLOPT_UPLOAD,1);
curl_easy_setopt(curl,CURLOPT_READDATA,f); curl_easy_setopt(curl,CURLOPT_READDATA,f);
curl_easy_setopt(curl,CURLOPT_INFILESIZE,(long)conv_src_size); curl_easy_setopt(curl,CURLOPT_INFILESIZE,(long)conv_src_size);
curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT); curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT);
curl_easy_setopt(curl,CURLOPT_PROGRESSFUNCTION,UploadProgressCallback); curl_easy_setopt(curl,CURLOPT_PROGRESSFUNCTION,
__RDUpload_UploadProgressCallback);
curl_easy_setopt(curl,CURLOPT_PROGRESSDATA,this); curl_easy_setopt(curl,CURLOPT_PROGRESSDATA,this);
curl_easy_setopt(curl,CURLOPT_NOPROGRESS,0); curl_easy_setopt(curl,CURLOPT_NOPROGRESS,0);
curl_easy_setopt(curl,CURLOPT_USERAGENT, curl_easy_setopt(curl,CURLOPT_USERAGENT,
(const char *)rda->config()->userAgent().toUtf8()); rda->config()->userAgent().toUtf8().constData());
//
// Debug Parameters
//
if(log_debug) { if(log_debug) {
curl_easy_setopt(curl,CURLOPT_VERBOSE,1); curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
curl_easy_setopt(curl,CURLOPT_DEBUGFUNCTION,UploadErrorCallback); curl_easy_setopt(curl,CURLOPT_DEBUGFUNCTION,__RDUpload_UploadErrorCallback);
} }
if(user!=NULL) { if(user!=NULL) {
RDCheckExitCode("RDUpload::runUpload setegid",setegid(user->gid())); RDCheckExitCode("RDUpload::runUpload setegid",setegid(user->gid()));

View File

@ -2,7 +2,7 @@
// //
// Upload a File // Upload a File
// //
// (C) Copyright 2010-2020 Fred Gleason <fredg@paravelsystems.com> // (C) Copyright 2010-2022 Fred Gleason <fredg@paravelsystems.com>
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as // it under the terms of the GNU General Public License version 2 as
@ -56,8 +56,9 @@ class RDUpload : public RDTransfer
private: private:
void UpdateProgress(int step); void UpdateProgress(int step);
friend int UploadProgressCallback(void *clientp,double dltotal,double dlnow, friend int __RDUpload_UploadProgressCallback(void *clientp,double dltotal,
double ultotal,double ulnow); double dlnow,double ultotal,
double ulnow);
QString conv_src_filename; QString conv_src_filename;
QUrl conv_dst_url; QUrl conv_dst_url;
bool conv_aborting; bool conv_aborting;

View File

@ -46,12 +46,6 @@ EditDownload::EditDownload(QString *filter,QWidget *parent)
// //
RDTextValidator *validator=new RDTextValidator(this,"validator"); RDTextValidator *validator=new RDTextValidator(this,"validator");
//
// Dialogs
//
// edit_cut_dialog=new RDCutDialog(edit_filter,&edit_group,&edit_schedcode,
// false,true,true,"RDCatch",false,this);
// //
// Event Widget // Event Widget
// //
@ -98,6 +92,15 @@ EditDownload::EditDownload(QString *filter,QWidget *parent)
edit_password_label->setFont(labelFont()); edit_password_label->setFont(labelFont());
edit_password_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); edit_password_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
//
// Use ssh(1) ID File
//
edit_use_id_file_label=
new QLabel(tr("Authenticate with local identity file"),this);
edit_use_id_file_label->setFont(labelFont());
edit_use_id_file_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
edit_use_id_file_check=new QCheckBox(this);
// //
// Destination // Destination
// //
@ -235,7 +238,7 @@ EditDownload::~EditDownload()
QSize EditDownload::sizeHint() const QSize EditDownload::sizeHint() const
{ {
return QSize(edit_event_widget->sizeHint().width(),432); return QSize(edit_event_widget->sizeHint().width(),456);
} }
@ -264,6 +267,7 @@ int EditDownload::exec(int record_id,std::vector<int> *adds)
edit_url_edit->setText(edit_recording->url()); edit_url_edit->setText(edit_recording->url());
edit_username_edit->setText(edit_recording->urlUsername()); edit_username_edit->setText(edit_recording->urlUsername());
edit_password_edit->setText(edit_recording->urlPassword()); edit_password_edit->setText(edit_recording->urlPassword());
edit_use_id_file_check->setChecked(edit_recording->urlUseIdFile());
edit_cutname=edit_recording->cutName(); edit_cutname=edit_recording->cutName();
edit_destination_edit->setText("Cut "+edit_cutname); edit_destination_edit->setText("Cut "+edit_cutname);
edit_channels_box->setCurrentIndex(edit_recording->channels()-1); edit_channels_box->setCurrentIndex(edit_recording->channels()-1);
@ -311,6 +315,14 @@ void EditDownload::urlChangedData(const QString &str)
edit_password_label->setDisabled(true); edit_password_label->setDisabled(true);
edit_password_edit->setDisabled(true); edit_password_edit->setDisabled(true);
} }
if((scheme=="sftp")&&(!rda->station()->sshIdentityFile().isEmpty())) {
edit_use_id_file_check->setEnabled(true);
edit_use_id_file_label->setEnabled(true);
}
else {
edit_use_id_file_check->setDisabled(true);
edit_use_id_file_label->setDisabled(true);
}
} }
@ -405,37 +417,40 @@ void EditDownload::resizeEvent(QResizeEvent *e)
edit_password_edit->setGeometry(360,97,size().width()-370,20); edit_password_edit->setGeometry(360,97,size().width()-370,20);
edit_password_label->setGeometry(275,97,80,20); edit_password_label->setGeometry(275,97,80,20);
edit_destination_edit->setGeometry(115,124,size().width()-195,20); edit_use_id_file_check->setGeometry(120,122,15,15);
edit_destination_button->setGeometry(size().width()-70,122,60,24); edit_use_id_file_label->setGeometry(140,120,size().width()-150,20);
edit_destination_label->setGeometry(10,127,100,19);
edit_channels_box->setGeometry(190,149,40,20); edit_destination_edit->setGeometry(115,148,size().width()-195,20);
edit_channels_label->setGeometry(120,149,70,20); edit_destination_button->setGeometry(size().width()-70,146,60,24);
edit_destination_label->setGeometry(10,151,100,19);
edit_autotrim_box->setGeometry(120,175,15,15); edit_channels_box->setGeometry(190,175,40,20);
edit_autotrim_label_label->setGeometry(140,173,80,20); edit_channels_label->setGeometry(120,175,70,20);
edit_autotrim_spin->setGeometry(265,173,40,20);
edit_autotrim_label->setGeometry(220,173,40,20);
edit_autotrim_unit->setGeometry(310,173,40,20);
edit_normalize_label_label->setGeometry(140,197,80,20); edit_autotrim_box->setGeometry(120,203,15,15);
edit_normalize_box->setGeometry(120,199,15,15); edit_autotrim_label_label->setGeometry(140,201,80,20);
edit_normalize_spin->setGeometry(265,197,40,20); edit_autotrim_spin->setGeometry(265,201,40,20);
edit_normalize_label->setGeometry(220,197,40,20); edit_autotrim_label->setGeometry(220,201,40,20);
edit_normalize_unit->setGeometry(310,197,40,20); edit_autotrim_unit->setGeometry(310,201,40,20);
edit_metadata_box->setGeometry(120,222,15,15); edit_normalize_label_label->setGeometry(140,227,80,20);
edit_metadata_label->setGeometry(140,222,160,20); edit_normalize_box->setGeometry(120,229,15,15);
edit_normalize_spin->setGeometry(265,227,40,20);
edit_normalize_label->setGeometry(220,227,40,20);
edit_normalize_unit->setGeometry(310,227,40,20);
edit_dow_selector->setGeometry(10,257,edit_dow_selector->sizeHint().width(), edit_metadata_box->setGeometry(120,254,15,15);
edit_metadata_label->setGeometry(140,252,160,20);
edit_dow_selector->setGeometry(10,281,edit_dow_selector->sizeHint().width(),
edit_dow_selector->sizeHint().height()); edit_dow_selector->sizeHint().height());
edit_oneshot_box->setGeometry(20,335,15,15); edit_oneshot_box->setGeometry(20,359,15,15);
edit_oneshot_label->setGeometry(40,333,115,20); edit_oneshot_label->setGeometry(40,357,115,20);
edit_eventoffset_spin->setGeometry(245,333,45,20); edit_eventoffset_spin->setGeometry(245,357,45,20);
edit_eventoffset_label->setGeometry(140,333,100,20); edit_eventoffset_label->setGeometry(140,357,100,20);
edit_eventoffset_unit->setGeometry(295,333,40,20); edit_eventoffset_unit->setGeometry(295,357,40,20);
edit_saveas_button->setGeometry(size().width()-300,size().height()-60,80,50); edit_saveas_button->setGeometry(size().width()-300,size().height()-60,80,50);
edit_ok_button->setGeometry(size().width()-180,size().height()-60,80,50); edit_ok_button->setGeometry(size().width()-180,size().height()-60,80,50);
@ -486,6 +501,7 @@ void EditDownload::Save()
edit_recording->setUrl(edit_url_edit->text()); edit_recording->setUrl(edit_url_edit->text());
edit_recording->setUrlUsername(edit_username_edit->text()); edit_recording->setUrlUsername(edit_username_edit->text());
edit_recording->setUrlPassword(edit_password_edit->text()); edit_recording->setUrlPassword(edit_password_edit->text());
edit_recording->setUrlUseIdFile(edit_use_id_file_check->isChecked());
edit_recording->setEnableMetadata(edit_metadata_box->isChecked()); edit_recording->setEnableMetadata(edit_metadata_box->isChecked());
edit_dow_selector->toRecording(edit_recording->id()); edit_dow_selector->toRecording(edit_recording->id());
edit_recording->setEventdateOffset(edit_eventoffset_spin->value()); edit_recording->setEventdateOffset(edit_eventoffset_spin->value());

View File

@ -76,6 +76,8 @@ class EditDownload : public RDDialog
QLineEdit *edit_username_edit; QLineEdit *edit_username_edit;
QLabel *edit_password_label; QLabel *edit_password_label;
QLineEdit *edit_password_edit; QLineEdit *edit_password_edit;
QCheckBox *edit_use_id_file_check;;
QLabel *edit_use_id_file_label;
QString edit_cutname; QString edit_cutname;
QLabel *edit_destination_label; QLabel *edit_destination_label;
QLineEdit *edit_destination_edit; QLineEdit *edit_destination_edit;

View File

@ -203,15 +203,14 @@ void MainObject::RunDownload(CatchEvent *evt)
conv->setDestinationFile(evt->tempName()); conv->setDestinationFile(evt->tempName());
QString url_username=evt->urlUsername(); QString url_username=evt->urlUsername();
QString url_password=evt->urlPassword(); QString url_password=evt->urlPassword();
QString id_filename=rda->station()->sshIdentityFile();
if(url_username.isEmpty()&& if(url_username.isEmpty()&&
(QUrl(evt->resolvedUrl()).scheme().toLower()=="ftp")) { (QUrl(evt->resolvedUrl()).scheme().toLower()=="ftp")) {
url_username=RD_ANON_FTP_USERNAME; url_username=RD_ANON_FTP_USERNAME;
url_password=QString(RD_ANON_FTP_PASSWORD)+"-"+VERSION; url_password=QString(RD_ANON_FTP_PASSWORD)+"-"+VERSION;
} }
// switch((conv_err=conv->runDownload(url_username,url_password,id_filename,
// FIXME: Finish implementing public key support! evt->useSshIdentity(),
//
switch((conv_err=conv->runDownload(url_username,url_password,"",false,
rda->config()->logXloadDebugData()))) { rda->config()->logXloadDebugData()))) {
case RDDownload::ErrorOk: case RDDownload::ErrorOk:
rda->syslog(LOG_INFO,"finished download of %s to %s, id=%d", rda->syslog(LOG_INFO,"finished download of %s to %s, id=%d",
@ -305,15 +304,14 @@ void MainObject::RunUpload(CatchEvent *evt)
conv->setDestinationUrl(evt->resolvedUrl()); conv->setDestinationUrl(evt->resolvedUrl());
QString url_username=evt->urlUsername(); QString url_username=evt->urlUsername();
QString url_password=evt->urlPassword(); QString url_password=evt->urlPassword();
QString id_filename=rda->station()->sshIdentityFile();
if(url_username.isEmpty()&& if(url_username.isEmpty()&&
(QUrl(evt->resolvedUrl()).scheme().toLower()=="ftp")) { (QUrl(evt->resolvedUrl()).scheme().toLower()=="ftp")) {
url_username=RD_ANON_FTP_USERNAME; url_username=RD_ANON_FTP_USERNAME;
url_password=QString(RD_ANON_FTP_PASSWORD)+"-"+VERSION; url_password=QString(RD_ANON_FTP_PASSWORD)+"-"+VERSION;
} }
// switch((conv_err=conv->runUpload(url_username,url_password,id_filename,
// FIXME: Finish implementing ssh(1) identity keys! evt->useSshIdentity(),
//
switch((conv_err=conv->runUpload(url_username,url_password,"",false,
rda->config()->logXloadDebugData()))) { rda->config()->logXloadDebugData()))) {
case RDUpload::ErrorOk: case RDUpload::ErrorOk:
catch_connect->setExitCode(evt->id(),RDRecording::Ok,tr("Ok")); catch_connect->setExitCode(evt->id(),RDRecording::Ok,tr("Ok"));

View File

@ -546,6 +546,18 @@ void CatchEvent::setUrlPassword(const QString &passwd)
} }
bool CatchEvent::useSshIdentity() const
{
return catch_use_ssh_identity;
}
void CatchEvent::setUseSshIdentity(bool state)
{
catch_use_ssh_identity=state;
}
unsigned CatchEvent::tempLength() const unsigned CatchEvent::tempLength() const
{ {
return catch_temp_length; return catch_temp_length;
@ -709,6 +721,7 @@ void CatchEvent::clear()
catch_resolved_url=""; catch_resolved_url="";
catch_url_username=""; catch_url_username="";
catch_url_password=""; catch_url_password="";
catch_use_ssh_identity=false;
catch_enable_metadata=false; catch_enable_metadata=false;
catch_temp_length=0; catch_temp_length=0;
catch_final_length=0; catch_final_length=0;

View File

@ -117,6 +117,8 @@ class CatchEvent
void setUrlUsername(const QString &name); void setUrlUsername(const QString &name);
QString urlPassword() const; QString urlPassword() const;
void setUrlPassword(const QString &passwd); void setUrlPassword(const QString &passwd);
bool useSshIdentity() const;
void setUseSshIdentity(bool state);
bool enableMetadata() const; bool enableMetadata() const;
void setEnableMetadata(bool state); void setEnableMetadata(bool state);
unsigned tempLength() const; unsigned tempLength() const;
@ -181,6 +183,7 @@ class CatchEvent
QString catch_resolved_url; QString catch_resolved_url;
QString catch_url_username; QString catch_url_username;
QString catch_url_password; QString catch_url_password;
bool catch_use_ssh_identity;
bool catch_enable_metadata; bool catch_enable_metadata;
unsigned catch_temp_length; unsigned catch_temp_length;
unsigned catch_final_length; unsigned catch_final_length;

View File

@ -185,6 +185,22 @@ MainObject::MainObject(QObject *parent)
} }
} }
//
// Drop root permissions
//
/*
if(getuid()==0) {
if(setgid(rda->config()->gid())<0) {
perror("rdcatchd");
exit(1);
}
if(setuid(rda->config()->uid())<0) {
perror("rdcatchd");
exit(1);
}
}
*/
// //
// Initialize Data Structures // Initialize Data Structures
// //
@ -1892,6 +1908,7 @@ QString MainObject::LoadEventSql()
"`URL`,"+ // 37 "`URL`,"+ // 37
"`URL_USERNAME`,"+ // 38 "`URL_USERNAME`,"+ // 38
"`URL_PASSWORD`,"+ // 39 "`URL_PASSWORD`,"+ // 39
"`URL_USE_ID_FILE`,"+ // 40
"`QUALITY`,"+ // 40 "`QUALITY`,"+ // 40
"`NORMALIZE_LEVEL`,"+ // 41 "`NORMALIZE_LEVEL`,"+ // 41
"`ALLOW_MULT_RECS`,"+ // 42 "`ALLOW_MULT_RECS`,"+ // 42
@ -1932,9 +1949,10 @@ void MainObject::LoadEvent(RDSqlQuery *q,CatchEvent *e,bool add)
e->setUrl(q->value(37).toString()); e->setUrl(q->value(37).toString());
e->setUrlUsername(q->value(38).toString()); e->setUrlUsername(q->value(38).toString());
e->setUrlPassword(q->value(39).toString()); e->setUrlPassword(q->value(39).toString());
e->setQuality(q->value(40).toInt()); e->setUseSshIdentity(q->value(40).toString()=='Y');
e->setNormalizeLevel(q->value(41).toInt()); e->setQuality(q->value(41).toInt());
e->setFeedId(q->value(45).toUInt()); e->setNormalizeLevel(q->value(42).toInt());
e->setFeedId(q->value(46).toUInt());
e->setMacroCart(q->value(23).toInt()); e->setMacroCart(q->value(23).toInt());
e->setSwitchInput(q->value(24).toInt()); e->setSwitchInput(q->value(24).toInt());
e->setSwitchOutput(q->value(25).toInt()); e->setSwitchOutput(q->value(25).toInt());
@ -1950,11 +1968,11 @@ void MainObject::LoadEvent(RDSqlQuery *q,CatchEvent *e,bool add)
e->setEndLength(q->value(34).toInt()); e->setEndLength(q->value(34).toInt());
e->setEndMatrix(q->value(35).toInt()); e->setEndMatrix(q->value(35).toInt());
e->setEndLine(q->value(36).toInt()); e->setEndLine(q->value(36).toInt());
e->setAllowMultipleRecordings(RDBool(q->value(42).toString())); e->setAllowMultipleRecordings(RDBool(q->value(43).toString()));
e->setMaxGpiRecordLength(q->value(43).toUInt()); e->setMaxGpiRecordLength(q->value(44).toUInt());
e->setDescription(q->value(44).toString()); e->setDescription(q->value(45).toString());
e->setEventdateOffset(q->value(46).toInt()); e->setEventdateOffset(q->value(47).toInt());
e->setEnableMetadata(RDBool(q->value(47).toString())); e->setEnableMetadata(RDBool(q->value(48).toString()));
if(add) { if(add) {
if(e->startType()==RDRecording::GpiStart) { if(e->startType()==RDRecording::GpiStart) {