From 19370379b49bceea2cc1747ba2c6ac2c677510ec Mon Sep 17 00:00:00 2001 From: Fred Gleason Date: Mon, 19 Sep 2022 13:07:48 -0400 Subject: [PATCH] 2022-09-19 Fred Gleason * 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 --- ChangeLog | 3 ++ lib/rdupload.cpp | 31 ++++++++++------ lib/rdupload.h | 7 ++-- rdcatch/edit_download.cpp | 76 +++++++++++++++++++++++---------------- rdcatch/edit_download.h | 2 ++ rdcatchd/batch.cpp | 14 ++++---- rdcatchd/catch_event.cpp | 13 +++++++ rdcatchd/catch_event.h | 3 ++ rdcatchd/rdcatchd.cpp | 34 +++++++++++++----- 9 files changed, 124 insertions(+), 59 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9487e033..de54fc22 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23339,3 +23339,6 @@ 2022-09-19 Fred Gleason * Fixed a regression in rdcatch(1) that caused a segfault when attempting to add or delete an event. +2022-09-19 Fred Gleason + * Added support for using a ssh(1) identity file for authentication + to the 'Upload' and 'Download' event types in rdcatch(1). diff --git a/lib/rdupload.cpp b/lib/rdupload.cpp index 2d253af2..71c87e75 100644 --- a/lib/rdupload.cpp +++ b/lib/rdupload.cpp @@ -2,7 +2,7 @@ // // Upload a File // -// (C) Copyright 2010-2021 Fred Gleason +// (C) Copyright 2010-2022 Fred Gleason // // 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 @@ -41,8 +41,8 @@ // // CURL Progress Callback // -int UploadProgressCallback(void *clientp,double dltotal,double dlnow, - double ultotal,double ulnow) +int __RDUpload_UploadProgressCallback(void *clientp,double dltotal,double dlnow, + double ultotal,double ulnow) { RDUpload *conv=(RDUpload *)clientp; 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, - void *clientp) +int __RDUpload_UploadErrorCallback(CURL *curl,curl_infotype type,char *msg, + size_t size,void *clientp) { char str[1000]; @@ -160,7 +160,7 @@ RDUpload::ErrorCode RDUpload::runUpload(const QString &username, url.replace("#","%23"); // - // Authentication + // Authentication Parameters // if((conv_dst_url.scheme().toLower()=="sftp")&& (!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, id_filename.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 { strncpy(userpwd,(username+":"+password).toUtf8(),255); 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_READDATA,f); curl_easy_setopt(curl,CURLOPT_INFILESIZE,(long)conv_src_size); 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_NOPROGRESS,0); curl_easy_setopt(curl,CURLOPT_USERAGENT, - (const char *)rda->config()->userAgent().toUtf8()); + rda->config()->userAgent().toUtf8().constData()); + + // + // Debug Parameters + // if(log_debug) { 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) { RDCheckExitCode("RDUpload::runUpload setegid",setegid(user->gid())); diff --git a/lib/rdupload.h b/lib/rdupload.h index 3b41cc3c..2ccbaaa2 100644 --- a/lib/rdupload.h +++ b/lib/rdupload.h @@ -2,7 +2,7 @@ // // Upload a File // -// (C) Copyright 2010-2020 Fred Gleason +// (C) Copyright 2010-2022 Fred Gleason // // 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 @@ -56,8 +56,9 @@ class RDUpload : public RDTransfer private: void UpdateProgress(int step); - friend int UploadProgressCallback(void *clientp,double dltotal,double dlnow, - double ultotal,double ulnow); + friend int __RDUpload_UploadProgressCallback(void *clientp,double dltotal, + double dlnow,double ultotal, + double ulnow); QString conv_src_filename; QUrl conv_dst_url; bool conv_aborting; diff --git a/rdcatch/edit_download.cpp b/rdcatch/edit_download.cpp index 32857fba..81780da3 100644 --- a/rdcatch/edit_download.cpp +++ b/rdcatch/edit_download.cpp @@ -46,12 +46,6 @@ EditDownload::EditDownload(QString *filter,QWidget *parent) // 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 // @@ -98,6 +92,15 @@ EditDownload::EditDownload(QString *filter,QWidget *parent) edit_password_label->setFont(labelFont()); 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 // @@ -235,7 +238,7 @@ EditDownload::~EditDownload() 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 *adds) edit_url_edit->setText(edit_recording->url()); edit_username_edit->setText(edit_recording->urlUsername()); edit_password_edit->setText(edit_recording->urlPassword()); + edit_use_id_file_check->setChecked(edit_recording->urlUseIdFile()); edit_cutname=edit_recording->cutName(); edit_destination_edit->setText("Cut "+edit_cutname); 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_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_label->setGeometry(275,97,80,20); - edit_destination_edit->setGeometry(115,124,size().width()-195,20); - edit_destination_button->setGeometry(size().width()-70,122,60,24); - edit_destination_label->setGeometry(10,127,100,19); + edit_use_id_file_check->setGeometry(120,122,15,15); + edit_use_id_file_label->setGeometry(140,120,size().width()-150,20); - edit_channels_box->setGeometry(190,149,40,20); - edit_channels_label->setGeometry(120,149,70,20); + edit_destination_edit->setGeometry(115,148,size().width()-195,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_autotrim_label_label->setGeometry(140,173,80,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_channels_box->setGeometry(190,175,40,20); + edit_channels_label->setGeometry(120,175,70,20); - edit_normalize_label_label->setGeometry(140,197,80,20); - edit_normalize_box->setGeometry(120,199,15,15); - edit_normalize_spin->setGeometry(265,197,40,20); - edit_normalize_label->setGeometry(220,197,40,20); - edit_normalize_unit->setGeometry(310,197,40,20); + edit_autotrim_box->setGeometry(120,203,15,15); + edit_autotrim_label_label->setGeometry(140,201,80,20); + edit_autotrim_spin->setGeometry(265,201,40,20); + edit_autotrim_label->setGeometry(220,201,40,20); + edit_autotrim_unit->setGeometry(310,201,40,20); - edit_metadata_box->setGeometry(120,222,15,15); - edit_metadata_label->setGeometry(140,222,160,20); + edit_normalize_label_label->setGeometry(140,227,80,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_oneshot_box->setGeometry(20,335,15,15); - edit_oneshot_label->setGeometry(40,333,115,20); + edit_oneshot_box->setGeometry(20,359,15,15); + edit_oneshot_label->setGeometry(40,357,115,20); - edit_eventoffset_spin->setGeometry(245,333,45,20); - edit_eventoffset_label->setGeometry(140,333,100,20); - edit_eventoffset_unit->setGeometry(295,333,40,20); + edit_eventoffset_spin->setGeometry(245,357,45,20); + edit_eventoffset_label->setGeometry(140,357,100,20); + edit_eventoffset_unit->setGeometry(295,357,40,20); edit_saveas_button->setGeometry(size().width()-300,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->setUrlUsername(edit_username_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_dow_selector->toRecording(edit_recording->id()); edit_recording->setEventdateOffset(edit_eventoffset_spin->value()); diff --git a/rdcatch/edit_download.h b/rdcatch/edit_download.h index d359a539..83eaa7bb 100644 --- a/rdcatch/edit_download.h +++ b/rdcatch/edit_download.h @@ -76,6 +76,8 @@ class EditDownload : public RDDialog QLineEdit *edit_username_edit; QLabel *edit_password_label; QLineEdit *edit_password_edit; + QCheckBox *edit_use_id_file_check;; + QLabel *edit_use_id_file_label; QString edit_cutname; QLabel *edit_destination_label; QLineEdit *edit_destination_edit; diff --git a/rdcatchd/batch.cpp b/rdcatchd/batch.cpp index 7e41cf1b..615b3df7 100644 --- a/rdcatchd/batch.cpp +++ b/rdcatchd/batch.cpp @@ -203,15 +203,14 @@ void MainObject::RunDownload(CatchEvent *evt) conv->setDestinationFile(evt->tempName()); QString url_username=evt->urlUsername(); QString url_password=evt->urlPassword(); + QString id_filename=rda->station()->sshIdentityFile(); if(url_username.isEmpty()&& (QUrl(evt->resolvedUrl()).scheme().toLower()=="ftp")) { url_username=RD_ANON_FTP_USERNAME; url_password=QString(RD_ANON_FTP_PASSWORD)+"-"+VERSION; } - // - // FIXME: Finish implementing public key support! - // - switch((conv_err=conv->runDownload(url_username,url_password,"",false, + switch((conv_err=conv->runDownload(url_username,url_password,id_filename, + evt->useSshIdentity(), rda->config()->logXloadDebugData()))) { case RDDownload::ErrorOk: 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()); QString url_username=evt->urlUsername(); QString url_password=evt->urlPassword(); + QString id_filename=rda->station()->sshIdentityFile(); if(url_username.isEmpty()&& (QUrl(evt->resolvedUrl()).scheme().toLower()=="ftp")) { url_username=RD_ANON_FTP_USERNAME; url_password=QString(RD_ANON_FTP_PASSWORD)+"-"+VERSION; } - // - // FIXME: Finish implementing ssh(1) identity keys! - // - switch((conv_err=conv->runUpload(url_username,url_password,"",false, + switch((conv_err=conv->runUpload(url_username,url_password,id_filename, + evt->useSshIdentity(), rda->config()->logXloadDebugData()))) { case RDUpload::ErrorOk: catch_connect->setExitCode(evt->id(),RDRecording::Ok,tr("Ok")); diff --git a/rdcatchd/catch_event.cpp b/rdcatchd/catch_event.cpp index 73c07f36..8a806b66 100644 --- a/rdcatchd/catch_event.cpp +++ b/rdcatchd/catch_event.cpp @@ -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 { return catch_temp_length; @@ -709,6 +721,7 @@ void CatchEvent::clear() catch_resolved_url=""; catch_url_username=""; catch_url_password=""; + catch_use_ssh_identity=false; catch_enable_metadata=false; catch_temp_length=0; catch_final_length=0; diff --git a/rdcatchd/catch_event.h b/rdcatchd/catch_event.h index 49700872..f9d959d3 100644 --- a/rdcatchd/catch_event.h +++ b/rdcatchd/catch_event.h @@ -117,6 +117,8 @@ class CatchEvent void setUrlUsername(const QString &name); QString urlPassword() const; void setUrlPassword(const QString &passwd); + bool useSshIdentity() const; + void setUseSshIdentity(bool state); bool enableMetadata() const; void setEnableMetadata(bool state); unsigned tempLength() const; @@ -181,6 +183,7 @@ class CatchEvent QString catch_resolved_url; QString catch_url_username; QString catch_url_password; + bool catch_use_ssh_identity; bool catch_enable_metadata; unsigned catch_temp_length; unsigned catch_final_length; diff --git a/rdcatchd/rdcatchd.cpp b/rdcatchd/rdcatchd.cpp index 0a295140..5e14668d 100644 --- a/rdcatchd/rdcatchd.cpp +++ b/rdcatchd/rdcatchd.cpp @@ -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 // @@ -1892,6 +1908,7 @@ QString MainObject::LoadEventSql() "`URL`,"+ // 37 "`URL_USERNAME`,"+ // 38 "`URL_PASSWORD`,"+ // 39 + "`URL_USE_ID_FILE`,"+ // 40 "`QUALITY`,"+ // 40 "`NORMALIZE_LEVEL`,"+ // 41 "`ALLOW_MULT_RECS`,"+ // 42 @@ -1932,9 +1949,10 @@ void MainObject::LoadEvent(RDSqlQuery *q,CatchEvent *e,bool add) e->setUrl(q->value(37).toString()); e->setUrlUsername(q->value(38).toString()); e->setUrlPassword(q->value(39).toString()); - e->setQuality(q->value(40).toInt()); - e->setNormalizeLevel(q->value(41).toInt()); - e->setFeedId(q->value(45).toUInt()); + e->setUseSshIdentity(q->value(40).toString()=='Y'); + e->setQuality(q->value(41).toInt()); + e->setNormalizeLevel(q->value(42).toInt()); + e->setFeedId(q->value(46).toUInt()); e->setMacroCart(q->value(23).toInt()); e->setSwitchInput(q->value(24).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->setEndMatrix(q->value(35).toInt()); e->setEndLine(q->value(36).toInt()); - e->setAllowMultipleRecordings(RDBool(q->value(42).toString())); - e->setMaxGpiRecordLength(q->value(43).toUInt()); - e->setDescription(q->value(44).toString()); - e->setEventdateOffset(q->value(46).toInt()); - e->setEnableMetadata(RDBool(q->value(47).toString())); + e->setAllowMultipleRecordings(RDBool(q->value(43).toString())); + e->setMaxGpiRecordLength(q->value(44).toUInt()); + e->setDescription(q->value(45).toString()); + e->setEventdateOffset(q->value(47).toInt()); + e->setEnableMetadata(RDBool(q->value(48).toString())); if(add) { if(e->startType()==RDRecording::GpiStart) {