Version in base suite: 8.6.8~dfsg.1-2 Base version: condor_8.6.8~dfsg.1-2 Target version: condor_8.6.8~dfsg.1-2+deb10u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/c/condor/condor_8.6.8~dfsg.1-2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/c/condor/condor_8.6.8~dfsg.1-2+deb10u1.dsc changelog | 16 patches/CVE-2019-18823.patch | 710 +++++++++++++++++++++++++++++++++++++++++ patches/CVE-2022-26110_1.patch | 73 ++++ patches/CVE-2022-26110_2.patch | 37 ++ patches/series | 7 5 files changed, 839 insertions(+), 4 deletions(-) diff -Nru condor-8.6.8~dfsg.1/debian/changelog condor-8.6.8~dfsg.1/debian/changelog --- condor-8.6.8~dfsg.1/debian/changelog 2018-03-19 23:28:58.000000000 +0000 +++ condor-8.6.8~dfsg.1/debian/changelog 2022-05-22 13:57:10.000000000 +0000 @@ -1,3 +1,19 @@ +condor (8.6.8~dfsg.1-2+deb10u1) buster-security; urgency=high + + * Non-maintainer upload. + * Fix CVE-2019-18823: + HTCondor has Incorrect Access Control. It is possible to use a different + authentication method to submit a job than the administrator has specified. + If the administrator has configured the READ or WRITE methods to include + CLAIMTOBE, then it is possible to impersonate another user to the + condor_schedd. (For example to submit or remove jobs) + * Fix CVE-2022-26110: + When a user authenticates to an HTCondor daemon via the CLAIMTOBE method, + the user can then impersonate any entity when issuing additional commands + to that daemon. + + -- Markus Koschany Sun, 22 May 2022 15:57:10 +0200 + condor (8.6.8~dfsg.1-2) unstable; urgency=medium * Disable GSOAP (Closes: #890007) (GSOAP will be dropped from HTCondor soon) diff -Nru condor-8.6.8~dfsg.1/debian/patches/CVE-2019-18823.patch condor-8.6.8~dfsg.1/debian/patches/CVE-2019-18823.patch --- condor-8.6.8~dfsg.1/debian/patches/CVE-2019-18823.patch 1970-01-01 00:00:00.000000000 +0000 +++ condor-8.6.8~dfsg.1/debian/patches/CVE-2019-18823.patch 2022-05-22 13:55:54.000000000 +0000 @@ -0,0 +1,710 @@ +From: Markus Koschany +Date: Fri, 20 May 2022 14:08:49 +0200 +Subject: CVE-2019-18823 + +Bug-Debian: https://bugs.debian.org/963777 +Origin: https://github.com/htcondor/htcondor/commit/95eaee86e7ad3852c17df46a1b8b193dabd1fd14 +Origin: https://github.com/htcondor/htcondor/commit/07e33c8b14aa00e04d045d4d79c963db082a3129 +Origin: https://github.com/htcondor/htcondor/commit/cbcb93695a932d511c1c7bd40aed1eabeff01d8d +Origin: https://github.com/htcondor/htcondor/commit/3916209123a8ef762b7a9cd84ca0cf8b2cd99716 +Origin: https://github.com/htcondor/htcondor/commit/5c84c6f0b3db4eda1eec42c2c708069bb9393f0b +--- + src/condor_daemon_client/dc_schedd.cpp | 2 +- + src/condor_daemon_client/dc_startd.cpp | 40 +++++++++++++++++++++++++++--- + src/condor_includes/condor_commands.h | 2 ++ + src/condor_schedd.V6/qmgmt.cpp | 26 +++++++++++++++++--- + src/condor_schedd.V6/qmgmt.h | 3 +++ + src/condor_schedd.V6/qmgmt_receivers.cpp | 14 +++-------- + src/condor_schedd.V6/schedd.cpp | 6 ++--- + src/condor_shadow.V6.1/shadow.cpp | 4 ++- + src/condor_startd.V6/Resource.cpp | 4 +-- + src/condor_startd.V6/command.cpp | 28 ++++++++++++++------- + src/condor_startd.V6/command.h | 2 +- + src/condor_starter.V6.1/baseStarter.cpp | 4 +-- + src/condor_status.V6/prettyPrint.cpp | 2 +- + src/condor_tools/history.cpp | 4 +-- + src/condor_tools/transform_ads.cpp | 4 +-- + src/condor_utils/compat_classad.cpp | 42 +++++++++++++++++++++++++++----- + src/condor_utils/compat_classad.h | 8 +++--- + src/condor_utils/file_transfer.cpp | 8 +++++- + src/condor_who/who.cpp | 4 +-- + 19 files changed, 153 insertions(+), 54 deletions(-) + +diff --git a/src/condor_daemon_client/dc_schedd.cpp b/src/condor_daemon_client/dc_schedd.cpp +index fe935b0..bf291fa 100644 +--- a/src/condor_daemon_client/dc_schedd.cpp ++++ b/src/condor_daemon_client/dc_schedd.cpp +@@ -1878,7 +1878,7 @@ bool DCSchedd::getJobConnectInfo( + + if( IsFulldebug(D_FULLDEBUG) ) { + std::string adstr; +- sPrintAd(adstr, output, true); ++ sPrintAd(adstr, output); + dprintf(D_FULLDEBUG,"Response for GET_JOB_CONNECT_INFO:\n%s\n", + adstr.c_str()); + } +diff --git a/src/condor_daemon_client/dc_startd.cpp b/src/condor_daemon_client/dc_startd.cpp +index be0101f..9e5693c 100644 +--- a/src/condor_daemon_client/dc_startd.cpp ++++ b/src/condor_daemon_client/dc_startd.cpp +@@ -135,6 +135,12 @@ ClaimStartdMsg::writeMsg( DCMessenger * /*messenger*/, Sock *sock ) { + m_job_ad.Assign("_condor_SEND_PAIRED_SLOT", + param_boolean("CLAIM_PAIRED_SLOT",true)); + ++ // Insert an attribute in the request ad to inform the ++ // startd that this schedd is capable of understanding ++ // the newer protocol where any claim id in the response ++ // is encrypted. ++ m_job_ad.Assign("_condor_SECURE_CLAIM_ID", true); ++ + if( !sock->put_secret( m_claim_id.c_str() ) || + !putClassAd( sock, m_job_ad ) || + !sock->put( scheduler_addr_to_send.c_str() ) || +@@ -234,14 +240,29 @@ ClaimStartdMsg::readMsg( DCMessenger * /*messenger*/, Sock *sock ) { + Reply of 4 (REQUEST_CLAIM_PAIR) means claim accepted by a slot + that is paired, and the partner slot ad and claim id will be + sent next. ++ Reply of 5 (REQUEST_CLAIM_LEFTOVERS_2) is the same as 3, but ++ the claim id is encrypted. ++ Reply of 6 (REQUEST_CLAIM_PAIR_2) is the same as 4, but ++ the claim id is encrypted. + */ + + if( m_reply == OK ) { + // no need to log success, because DCMsg::reportSuccess() will + } else if( m_reply == NOT_OK ) { + dprintf( failureDebugLevel(), "Request was NOT accepted for claim %s\n", description() ); +- } else if( m_reply == REQUEST_CLAIM_LEFTOVERS ) { +- if( !sock->get(m_leftover_claim_id) || ++ } else if( m_reply == REQUEST_CLAIM_LEFTOVERS || m_reply == REQUEST_CLAIM_LEFTOVERS_2 ) { ++ bool recv_ok = false; ++ if ( m_reply == REQUEST_CLAIM_LEFTOVERS_2 ) { ++ char *val = NULL; ++ recv_ok = sock->get_secret(val); ++ if ( recv_ok ) { ++ m_leftover_claim_id = val; ++ free(val); ++ } ++ } else { ++ recv_ok = sock->get(m_leftover_claim_id); ++ } ++ if( !recv_ok || + !getClassAd( sock, m_leftover_startd_ad ) ) + { + // failed to read leftover partitionable slot info +@@ -256,8 +277,19 @@ ClaimStartdMsg::readMsg( DCMessenger * /*messenger*/, Sock *sock ) { + // change reply to OK cuz claim was a success + m_reply = OK; + } +- } else if( m_reply == REQUEST_CLAIM_PAIR ) { +- if( !sock->get(m_paired_claim_id) || ++ } else if( m_reply == REQUEST_CLAIM_PAIR || m_reply == REQUEST_CLAIM_PAIR_2 ) { ++ bool recv_ok = false; ++ if ( m_reply == REQUEST_CLAIM_PAIR_2 ) { ++ char *val = NULL; ++ recv_ok = sock->get_secret(val); ++ if ( recv_ok ) { ++ m_paired_claim_id = val; ++ free(val); ++ } ++ } else { ++ recv_ok = sock->get(m_paired_claim_id); ++ } ++ if( !recv_ok || + !getClassAd( sock, m_paired_startd_ad ) ) + { + // failed to read paired slot info +diff --git a/src/condor_includes/condor_commands.h b/src/condor_includes/condor_commands.h +index c6b029c..5656c27 100644 +--- a/src/condor_includes/condor_commands.h ++++ b/src/condor_includes/condor_commands.h +@@ -536,6 +536,8 @@ NAMETABLE_DIRECTIVE:END_SECTION:collector + /* Replies specific to the REQUEST_CLAIM command */ + #define REQUEST_CLAIM_LEFTOVERS 3 + #define REQUEST_CLAIM_PAIR 4 ++#define REQUEST_CLAIM_LEFTOVERS_2 5 ++#define REQUEST_CLAIM_PAIR_2 6 + + /* Replies specific to the SWAP_CLAIM_AND_ACTIVATION command */ + #define SWAP_CLAIM_ALREADY_SWAPPED 4 +diff --git a/src/condor_schedd.V6/qmgmt.cpp b/src/condor_schedd.V6/qmgmt.cpp +index f052531..e6dd116 100644 +--- a/src/condor_schedd.V6/qmgmt.cpp ++++ b/src/condor_schedd.V6/qmgmt.cpp +@@ -158,7 +158,7 @@ static QmgmtPeer *Q_SOCK = NULL; + // running. Used by SuperUserAllowedToSetOwnerTo(). + static HashTable owner_history(MyStringHash); + +-int do_Q_request(ReliSock *,bool &may_fork); ++int do_Q_request(QmgmtPeer &,bool &may_fork); + void FindPrioJob(PROC_ID &); + + static bool qmgmt_was_initialized = false; +@@ -505,6 +505,7 @@ QmgmtPeer::QmgmtPeer() + myendpoint = NULL; + sock = NULL; + transaction = NULL; ++ readonly = false; + allow_protected_attr_changes_by_superuser = true; + + unset(); +@@ -574,6 +575,16 @@ QmgmtPeer::setEffectiveOwner(char const *o) + return true; + } + ++bool ++QmgmtPeer::setReadOnly(bool val) ++{ ++ bool old_val = readonly; ++ ++ readonly = val; ++ ++ return old_val; ++} ++ + void + QmgmtPeer::unset() + { +@@ -598,6 +609,7 @@ QmgmtPeer::unset() + next_proc_num = 0; + active_cluster_num = -1; + xact_start_time = 0; // time at which the current transaction was started ++ readonly = false; + } + + const char* +@@ -1626,6 +1638,12 @@ OwnerCheck(int cluster_id,int proc_id) + bool + OwnerCheck(ClassAd *ad, const char *test_owner) + { ++ if ( Q_SOCK->getReadOnly() ) { ++ errno = EACCES; ++ dprintf( D_FULLDEBUG, "OwnerCheck: reject read-only client\n" ); ++ return false; ++ } ++ + // check if the IP address of the peer has daemon core write permission + // to the schedd. we have to explicitly check here because all queue + // management commands come in via one sole daemon core command which +@@ -1849,7 +1867,7 @@ unsetQSock() + + + int +-handle_q(Service *, int, Stream *sock) ++handle_q(Service *, int cmd, Stream *sock) + { + int rval; + bool all_good; +@@ -1868,13 +1886,15 @@ handle_q(Service *, int, Stream *sock) + } + ASSERT(Q_SOCK); + ++ Q_SOCK->setReadOnly(cmd == QMGMT_READ_CMD); ++ + BeginTransaction(); + + bool may_fork = false; + ForkStatus fork_status = FORK_FAILED; + do { + /* Probably should wrap a timer around this */ +- rval = do_Q_request( Q_SOCK->getReliSock(), may_fork ); ++ rval = do_Q_request( *Q_SOCK, may_fork ); + + if( may_fork && fork_status == FORK_FAILED ) { + fork_status = schedd_forker.NewJob(); +diff --git a/src/condor_schedd.V6/qmgmt.h b/src/condor_schedd.V6/qmgmt.h +index 42e86b2..e090adb 100644 +--- a/src/condor_schedd.V6/qmgmt.h ++++ b/src/condor_schedd.V6/qmgmt.h +@@ -55,6 +55,8 @@ class QmgmtPeer { + bool setAllowProtectedAttrChanges(bool val); + bool getAllowProtectedAttrChanges() { return allow_protected_attr_changes_by_superuser; } + ++ bool setReadOnly(bool val); ++ bool getReadOnly() const { return readonly; } + ReliSock *getReliSock() const { return sock; }; + const CondorVersionInfo *get_peer_version() const { return sock->get_peer_version(); }; + +@@ -68,6 +70,7 @@ class QmgmtPeer { + protected: + + char *owner; ++ bool readonly; + bool allow_protected_attr_changes_by_superuser; + char *fquser; // owner@domain + char *myendpoint; +diff --git a/src/condor_schedd.V6/qmgmt_receivers.cpp b/src/condor_schedd.V6/qmgmt_receivers.cpp +index b71b2b6..7d9b46c 100644 +--- a/src/condor_schedd.V6/qmgmt_receivers.cpp ++++ b/src/condor_schedd.V6/qmgmt_receivers.cpp +@@ -50,11 +50,12 @@ static bool QmgmtMayAccessAttribute( char const *attr_name ) { + } + + int +-do_Q_request(ReliSock *syscall_sock,bool &may_fork) ++do_Q_request(QmgmtPeer &Q_PEER, bool &may_fork) + { + int request_num = -1; + int rval; + ++ ReliSock *syscall_sock = Q_PEER.getReliSock(); + syscall_sock->decode(); + + assert( syscall_sock->code(request_num) ); +@@ -97,12 +98,6 @@ do_Q_request(ReliSock *syscall_sock,bool &may_fork) + { + // dprintf( D_ALWAYS, "InitializeReadOnlyConnection()\n" ); + +- // Since InitializeConnection() does nothing, and we need +- // to record the fact that this is a read-only connection, +- // but we have to do it in the socket (since we don't have +- // any other persistent data structure, and it's probably +- // the right place anyway), set the FQU. +- // + // We need to record if this is a read-only connection so that + // we can avoid expanding $$ in GetJobAd; simply checking if the + // connection is authenticated isn't sufficient, because the +@@ -110,7 +105,7 @@ do_Q_request(ReliSock *syscall_sock,bool &may_fork) + // be authenticated by a previous authenticated connection from + // the same address (when using host-based security) less than + // the expiration period ago. +- syscall_sock->setFullyQualifiedUser( "read-only" ); ++ Q_PEER.setReadOnly(true); + + // same as InitializeConnection but no authenticate() + InitializeConnection( NULL, NULL ); +@@ -819,8 +814,7 @@ do_Q_request(ReliSock *syscall_sock,bool &may_fork) + // Only fetch the jobad for legal values of cluster/proc + if( cluster_id >= 1 ) { + if( proc_id >= 0 ) { +- const char * fqu = syscall_sock->getFullyQualifiedUser(); +- if( fqu != NULL && strcmp( fqu, "read-only" ) != 0 ) { ++ if( !Q_PEER.getReadOnly() ) { + // expand $$() macros in the jobad as required by GridManager. + // The GridManager depends on the fact that the following call + // expands $$ and saves the expansions to disk in case of +diff --git a/src/condor_schedd.V6/schedd.cpp b/src/condor_schedd.V6/schedd.cpp +index b751ced..5a38e9e 100644 +--- a/src/condor_schedd.V6/schedd.cpp ++++ b/src/condor_schedd.V6/schedd.cpp +@@ -2597,7 +2597,7 @@ int Scheduler::guessJobSlotWeight(JobQueueJob * job) + ad->Assign(ATTR_MEMORY, 1024); + ad->Assign(ATTR_DISK, 1024); + dprintf(D_ALWAYS, "Creating slotWeightGuessAd as:\n"); +- dPrintAd(D_ALWAYS, *ad, false); ++ dPrintAd(D_ALWAYS, *ad); + } + } + +@@ -9071,7 +9071,7 @@ Scheduler::spawnJobHandlerRaw( shadow_rec* srec, const char* path, + // handler is now alive and can read from the pipe. + ASSERT( job_ad ); + MyString ad_str; +- sPrintAd(ad_str, *job_ad); ++ sPrintAdWithSecrets(ad_str, *job_ad); + const char* ptr = ad_str.Value(); + int len = ad_str.Length(); + while (len) { +@@ -9705,7 +9705,7 @@ Scheduler::start_sched_universe_job(PROC_ID* job_id) + dprintf ( D_FAILURE|D_ALWAYS, "Open of %s failed (%s, errno=%d).\n", job_ad_path.c_str(), strerror(errno), errno ); + } else { + // fPrindAd does not have any usable error reporting. +- fPrintAd(job_ad_fp, *userJob, true); ++ fPrintAd(job_ad_fp, *userJob); + wrote_job_ad = true; + fclose(job_ad_fp); + } +diff --git a/src/condor_shadow.V6.1/shadow.cpp b/src/condor_shadow.V6.1/shadow.cpp +index e9e4f6d..ff7b09e 100644 +--- a/src/condor_shadow.V6.1/shadow.cpp ++++ b/src/condor_shadow.V6.1/shadow.cpp +@@ -122,7 +122,7 @@ UniShadow::init( ClassAd* job_ad, const char* schedd_addr, const char *xfer_queu + + // In this case we just pass the pointer along... + remRes->setJobAd( jobAd ); +- ++ + // Register command which gets updates from the starter + // on the job's image size, cpu usage, etc. Each kind of + // shadow implements it's own version of this to deal w/ it +@@ -132,6 +132,7 @@ UniShadow::init( ClassAd* job_ad, const char* schedd_addr, const char *xfer_queu + (CommandHandlercpp)&UniShadow::updateFromStarter, + "UniShadow::updateFromStarter", this, DAEMON ); + ++#if ! defined( WIN32 ) + // Register command which the starter uses to fetch a user + // credential if it needs to. + daemonCore-> +@@ -139,6 +140,7 @@ UniShadow::init( ClassAd* job_ad, const char* schedd_addr, const char *xfer_queu + (CommandHandler)&get_cred_handler, + "get_cred_handler", NULL, DAEMON, D_COMMAND, + true /*force authentication*/ ); ++#endif + } + + void +diff --git a/src/condor_startd.V6/Resource.cpp b/src/condor_startd.V6/Resource.cpp +index c78c1ba..27b545b 100644 +--- a/src/condor_startd.V6/Resource.cpp ++++ b/src/condor_startd.V6/Resource.cpp +@@ -3300,9 +3300,9 @@ Resource * initialize_resource(Resource * rip, ClassAd * req_classad, Claim* &le + dprintf(D_MATCH | D_FULLDEBUG, + "STARTD Requirements do not match, %s MODIFY_REQUEST_EXPR_ edits. Job ad was ============================\n", + unmodified_req_classad ? "with" : "w/o"); +- dPrintAd(D_MATCH | D_FULLDEBUG, *req_classad, true); ++ dPrintAd(D_MATCH | D_FULLDEBUG, *req_classad); + dprintf(D_MATCH | D_FULLDEBUG, "Machine ad was ============================\n"); +- dPrintAd(D_MATCH | D_FULLDEBUG, *mach_classad, true); ++ dPrintAd(D_MATCH | D_FULLDEBUG, *mach_classad); + } + if (unmodified_req_classad) { + // our modified req_classad no longer matches, put back the original +diff --git a/src/condor_startd.V6/command.cpp b/src/condor_startd.V6/command.cpp +index ef5fbea..053cbbc 100644 +--- a/src/condor_startd.V6/command.cpp ++++ b/src/condor_startd.V6/command.cpp +@@ -1217,6 +1217,7 @@ request_claim( Resource* rip, Claim *claim, char* id, Stream* stream ) + char *client_addr = NULL; + int interval; + ClaimIdParser idp(id); ++ bool secure_claim_id = false; + + // Used in ABORT macro, yuck + bool new_dynamic_slot = false; +@@ -1321,6 +1322,10 @@ request_claim( Resource* rip, Claim *claim, char* id, Stream* stream ) + ABORT; + } + ++ // If we include a claim id in our response, should it be ++ // encrypted? In the old protocol, it was sent in the clear. ++ req_classad->LookupBool("_condor_SECURE_CLAIM_ID", secure_claim_id); ++ + // At this point, the schedd has registered this socket (stream) + // and likely has gone off to service other requests. Thus, + // we need change the timeout on the socket to the schedd to be +@@ -1530,7 +1535,7 @@ request_claim( Resource* rip, Claim *claim, char* id, Stream* stream ) + // function after the preemption has completed when the startd + // is finally ready to reply to the and finish the claiming + // process. +- accept_request_claim( rip, leftover_claim, and_pair ); ++ accept_request_claim( rip, secure_claim_id, leftover_claim, and_pair ); + + // We always need to return KEEP_STREAM so that daemon core + // doesn't try to delete the stream we've already deleted. +@@ -1569,7 +1574,7 @@ abort_accept_claim( Resource* rip, Stream* stream ) + + + bool +-accept_request_claim( Resource* rip, Claim* leftover_claim, bool and_pair ) ++accept_request_claim( Resource* rip, bool secure_claim_id, Claim* leftover_claim, bool and_pair ) + { + int interval = -1; + char *client_addr = NULL; +@@ -1593,18 +1598,22 @@ accept_request_claim( Resource* rip, Claim* leftover_claim, bool and_pair ) + Reply of 4 (REQUEST_CLAIM_PAIR) means claim accepted by a slot + that is paired, and the partner slot ad and claim id will be + sent next. ++ Reply of 5 (REQUEST_CLAIM_LEFTOVERS_2) is the same as 3, but ++ the claim id is encrypted. ++ Reply of 6 (REQUEST_CLAIM_PAIR_2) is the same as 4, but ++ the claim id is encrypted. + */ + int cmd = OK; + if ( leftover_claim && leftover_claim->id() && + leftover_claim->rip()->r_classad ) + { +- // schedd wants leftovers, send reply code 3 +- cmd = REQUEST_CLAIM_LEFTOVERS; ++ // schedd wants leftovers, send reply code 3 (or 5) ++ cmd = secure_claim_id ? REQUEST_CLAIM_LEFTOVERS_2 : REQUEST_CLAIM_LEFTOVERS; + } + else if (rip->r_pair_name) { + ripb = resmgr->get_by_name(rip->r_pair_name); + if (ripb && and_pair) { +- cmd = REQUEST_CLAIM_PAIR; ++ cmd = secure_claim_id ? REQUEST_CLAIM_PAIR_2 : REQUEST_CLAIM_PAIR; + } + } + +@@ -1616,7 +1625,7 @@ accept_request_claim( Resource* rip, Claim* leftover_claim, bool and_pair ) + abort_accept_claim( rip, stream ); + return false; + } +- if ( cmd == REQUEST_CLAIM_LEFTOVERS ) ++ if ( cmd == REQUEST_CLAIM_LEFTOVERS || cmd == REQUEST_CLAIM_LEFTOVERS_2 ) + { + // schedd just claimed a dynamic slot, and it wants + // us to send back to the classad and the new claim id for +@@ -1625,7 +1634,7 @@ accept_request_claim( Resource* rip, Claim* leftover_claim, bool and_pair ) + + leftover_claim->rip()->r_classad->Assign(ATTR_LAST_SLOT_NAME, rip->r_name); + MyString claimId(leftover_claim->id()); +- if ( !stream->put(claimId) || ++ if ( !(secure_claim_id ? stream->put_secret(claimId.c_str()) : stream->put(claimId)) || + !putClassAd(stream, *leftover_claim->rip()->r_classad) ) + { + rip->dprintf( D_ALWAYS, +@@ -1634,14 +1643,15 @@ accept_request_claim( Resource* rip, Claim* leftover_claim, bool and_pair ) + return false; + } + } +- else if (cmd == REQUEST_CLAIM_PAIR) ++ else if (cmd == REQUEST_CLAIM_PAIR || cmd == REQUEST_CLAIM_PAIR_2) + { + dprintf(D_FULLDEBUG,"Sending paired slot claim to schedd\n"); + //PRAGMA_REMIND("remove these next two dprintfs later") + //dprintf(D_FULLDEBUG,"\tmain slot claim id is %s\n", rip->r_cur->id()); + //dprintf(D_FULLDEBUG,"\tpaired slot claim id is %s\n", ripb->r_cur->id()); + MyString claimId(ripb->r_cur->id()); +- if ( !stream->put(claimId) || ! putClassAd(stream, *ripb->r_classad)) { ++ if ( !(secure_claim_id ? stream->put_secret(claimId.c_str()) : stream->put(claimId)) || ++ ! putClassAd(stream, *ripb->r_classad)) { + rip->dprintf( D_ALWAYS, + "Can't send paired slot claim & ad to schedd.\n" ); + abort_accept_claim( rip, stream ); +diff --git a/src/condor_startd.V6/command.h b/src/condor_startd.V6/command.h +index 540c85d..02eb3f3 100644 +--- a/src/condor_startd.V6/command.h ++++ b/src/condor_startd.V6/command.h +@@ -118,7 +118,7 @@ int match_info( Resource*, char* ); + int request_claim( Resource*, Claim *, char*, Stream* ); + + // Accept claim from schedd agent +-bool accept_request_claim( Resource* , Claim * = NULL, bool and_pair = false ); ++bool accept_request_claim( Resource* , bool secure_claim_id = true, Claim * = NULL, bool and_pair = false ); + + // Activate a claim with a given starter + int activate_claim( Resource*, Stream* ); +diff --git a/src/condor_starter.V6.1/baseStarter.cpp b/src/condor_starter.V6.1/baseStarter.cpp +index 0cd8062..028bea5 100644 +--- a/src/condor_starter.V6.1/baseStarter.cpp ++++ b/src/condor_starter.V6.1/baseStarter.cpp +@@ -3693,7 +3693,7 @@ CStarter::WriteAdFiles() + } + else + { +- fPrintAd(fp, *ad, true); ++ fPrintAd(fp, *ad); + fclose(fp); + } + } +@@ -3718,7 +3718,7 @@ CStarter::WriteAdFiles() + } + else + { +- fPrintAd(fp, *ad, true); ++ fPrintAd(fp, *ad); + fclose(fp); + } + } +diff --git a/src/condor_status.V6/prettyPrint.cpp b/src/condor_status.V6/prettyPrint.cpp +index 9489492..c9cc29c 100644 +--- a/src/condor_status.V6/prettyPrint.cpp ++++ b/src/condor_status.V6/prettyPrint.cpp +@@ -477,7 +477,7 @@ void prettyPrintAd(ppOption pps, ClassAd *ad, int output_index, StringList * whi + classad::References attrs; + classad::References *proj = NULL; + if (PP_IS_LONGish(pps) && ( ! fHashOrder || whitelist)) { +- sGetAdAttrs(attrs, *ad, false, whitelist); ++ sGetAdAttrs(attrs, *ad, true, whitelist); + proj = &attrs; + } + +diff --git a/src/condor_tools/history.cpp b/src/condor_tools/history.cpp +index f841f34..75cc574 100644 +--- a/src/condor_tools/history.cpp ++++ b/src/condor_tools/history.cpp +@@ -743,7 +743,7 @@ main(int argc, char* argv[]) + ad.InsertAttr(ATTR_OWNER, 1); + ad.InsertAttr("StreamResults", true); + dprintf(D_FULLDEBUG, "condor_history: sending streaming ACK header ad:\n"); +- dPrintAd(D_FULLDEBUG, ad, false); ++ dPrintAd(D_FULLDEBUG, ad); + if ( ! putClassAd(socks[0], ad) || ! socks[0]->end_of_message()) { + dprintf(D_ALWAYS, "condor_history: Failed to write streaming ACK header ad\n"); + exit(1); +@@ -768,7 +768,7 @@ main(int argc, char* argv[]) + ad.InsertAttr("MalformedAds", writetosocket_failcount); + ad.InsertAttr("AdCount", adCount); + dprintf(D_FULLDEBUG, "condor_history: sending final ad:\n"); +- dPrintAd(D_FULLDEBUG, ad, false); ++ dPrintAd(D_FULLDEBUG, ad); + if ( ! putClassAd(socks[0], ad) || ! socks[0]->end_of_message()) { + dprintf(D_ALWAYS, "condor_history: Failed to write final ad to client\n"); + exit(1); +diff --git a/src/condor_tools/transform_ads.cpp b/src/condor_tools/transform_ads.cpp +index 5224e9c..5f9160f 100644 +--- a/src/condor_tools/transform_ads.cpp ++++ b/src/condor_tools/transform_ads.cpp +@@ -1740,8 +1740,6 @@ bool ReportSuccess(const ClassAd * job, apply_transform_args & xform_args) + if ( ! job) return false; + if (testing.no_output) return true; + +- StringList * whitelist = NULL; +- + // if we have not yet picked and output format, do that now. + if (DashOutFormat == ClassAdFileParseType::Parse_auto) { + if (xform_args.input_helper) { +@@ -1759,7 +1757,7 @@ bool ReportSuccess(const ClassAd * job, apply_transform_args & xform_args) + classad::References attrs; + classad::References *print_order = NULL; + if ( ! DashOutAttrsInHashOrder) { +- sGetAdAttrs(attrs, *job, false, whitelist); ++ sGetAdAttrs(attrs, *job); + print_order = &attrs; + } + switch (DashOutFormat) { +diff --git a/src/condor_utils/compat_classad.cpp b/src/condor_utils/compat_classad.cpp +index 3fed02e..ca9fafa 100644 +--- a/src/condor_utils/compat_classad.cpp ++++ b/src/condor_utils/compat_classad.cpp +@@ -1731,7 +1731,7 @@ int CondorClassAdListWriter::appendAd(const ClassAd & ad, std::string & output, + classad::References attrs; + classad::References *print_order = NULL; + if ( ! hash_order || whitelist) { +- sGetAdAttrs(attrs, ad, false, whitelist); ++ sGetAdAttrs(attrs, ad, true, whitelist); + print_order = &attrs; + } + +@@ -2574,7 +2574,12 @@ fPrintAd( FILE *file, const classad::ClassAd &ad, bool exclude_private, StringLi + { + MyString buffer; + +- sPrintAd( buffer, ad, exclude_private, attr_white_list ); ++ if( exclude_private ) { ++ sPrintAd( buffer, ad, attr_white_list ); ++ } else { ++ sPrintAdWithSecrets( buffer, ad, attr_white_list ); ++ } ++ + if ( fprintf(file, "%s", buffer.Value()) < 0 ) { + return FALSE; + } else { +@@ -2588,14 +2593,19 @@ dPrintAd( int level, const classad::ClassAd &ad, bool exclude_private ) + if ( IsDebugCatAndVerbosity( level ) ) { + MyString buffer; + +- sPrintAd( buffer, ad, exclude_private ); ++ if( exclude_private ) { ++ sPrintAd( buffer, ad ); ++ } else { ++ sPrintAdWithSecrets( buffer, ad ); ++ } + + dprintf( level|D_NOHEADER, "%s", buffer.Value() ); + } + } + ++ + int +-sPrintAd( MyString &output, const classad::ClassAd &ad, bool exclude_private, StringList *attr_white_list ) ++_sPrintAd( MyString &output, const classad::ClassAd &ad, bool exclude_private, StringList *attr_white_list ) + { + classad::ClassAd::const_iterator itr; + +@@ -2640,10 +2650,30 @@ sPrintAd( MyString &output, const classad::ClassAd &ad, bool exclude_private, St + } + + int +-sPrintAd( std::string &output, const classad::ClassAd &ad, bool exclude_private, StringList *attr_white_list ) ++sPrintAd( MyString &output, const classad::ClassAd &ad, StringList *attr_white_list ) { ++ return _sPrintAd( output, ad, true, attr_white_list ); ++} ++ ++int ++sPrintAdWithSecrets( MyString &output, const classad::ClassAd &ad, StringList *attr_white_list ) { ++ return _sPrintAd( output, ad, false, attr_white_list ); ++} ++ ++ ++int ++sPrintAd( std::string &output, const classad::ClassAd &ad, StringList *attr_white_list ) ++{ ++ MyString myout; ++ int rc = sPrintAd( myout, ad, attr_white_list ); ++ output += myout; ++ return rc; ++} ++ ++int ++sPrintAdWithSecrets( std::string &output, const classad::ClassAd &ad, StringList *attr_white_list ) + { + MyString myout; +- int rc = sPrintAd( myout, ad, exclude_private, attr_white_list ); ++ int rc = sPrintAdWithSecrets( myout, ad, attr_white_list ); + output += myout; + return rc; + } +diff --git a/src/condor_utils/compat_classad.h b/src/condor_utils/compat_classad.h +index 15afd8a..33d41e5 100644 +--- a/src/condor_utils/compat_classad.h ++++ b/src/condor_utils/compat_classad.h +@@ -58,7 +58,7 @@ typedef std::set AttrNameSet; + @param file The file handle to print to. + @return TRUE + */ +-int fPrintAd(FILE *file, const classad::ClassAd &ad, bool exclude_private = false, StringList *attr_white_list = NULL); ++int fPrintAd(FILE *file, const classad::ClassAd &ad, bool exclude_private = true, StringList *attr_white_list = NULL); + + /** Print the ClassAd as an old ClasAd with dprintf + @param level The dprintf level. +@@ -69,13 +69,15 @@ void dPrintAd( int level, const classad::ClassAd &ad, bool exclude_private = tru + @param output The MyString to write into + @return TRUE + */ +-int sPrintAd( MyString &output, const classad::ClassAd &ad, bool exclude_private = false, StringList *attr_white_list = NULL ); ++int sPrintAd( MyString &output, const classad::ClassAd &ad, StringList *attr_white_list = NULL ); ++int sPrintAdWithSecrets( MyString &output, const classad::ClassAd &ad, StringList *attr_white_list = NULL ); + + /** Format the ClassAd as an old ClassAd into the std::string. + @param output The std::string to write into + @return TRUE + */ +-int sPrintAd( std::string &output, const classad::ClassAd &ad, bool exclude_private = false, StringList *attr_white_list = NULL ); ++int sPrintAd( std::string &output, const classad::ClassAd &ad, StringList *attr_white_list = NULL ); ++int sPrintAdWithSecrets( std::string &output, const classad::ClassAd & ad, StringList *attr_white_list = NULL ); + + /** Get a sorted list of attributes that are in the given ad, and also match the given whitelist (if any) + @param attrs the set of attrs to insert into. This is set is NOT cleared first. +diff --git a/src/condor_utils/file_transfer.cpp b/src/condor_utils/file_transfer.cpp +index 3be2e61..6aab8c3 100644 +--- a/src/condor_utils/file_transfer.cpp ++++ b/src/condor_utils/file_transfer.cpp +@@ -3218,7 +3218,13 @@ FileTransfer::DoUpload(filesize_t *total_bytes, ReliSock *s) + return_and_resetpriv( -1 ); + } + +- // compute the size of what we sent ++ // ++ // This comment used to read 'compute the size of what we sent', ++ // but obviously the wire format and the string format of ++ // ClassAds are not the same and can't be expected to be the ++ // same length. Since the size will be wrong anyway, simplify ++ // future security audits but not printing the private attrs. ++ // + MyString junkbuf; + sPrintAd(junkbuf, file_info); + bytes = junkbuf.Length(); +diff --git a/src/condor_who/who.cpp b/src/condor_who/who.cpp +index 7be0d66..19f2a04 100644 +--- a/src/condor_who/who.cpp ++++ b/src/condor_who/who.cpp +@@ -1464,7 +1464,7 @@ main( int argc, char *argv[] ) + std::string tmp; + if (App.print_mask.IsEmpty()) { + classad::References attrs; +- sGetAdAttrs(attrs, ready_ad, false, NULL); ++ sGetAdAttrs(attrs, ready_ad); + sPrintAdAttrs(tmp, ready_ad, attrs); + } else { + if (App.print_mask.has_headings()) { +@@ -1692,7 +1692,7 @@ main( int argc, char *argv[] ) + while (ClassAd *ad = result.Next()) { + tmp.clear(); + classad::References attrs; +- sGetAdAttrs(attrs, *ad, false, NULL); ++ sGetAdAttrs(attrs, *ad); + sPrintAdAttrs(tmp, *ad, attrs); + tmp += "\n"; + fputs(tmp.c_str(), stdout); diff -Nru condor-8.6.8~dfsg.1/debian/patches/CVE-2022-26110_1.patch condor-8.6.8~dfsg.1/debian/patches/CVE-2022-26110_1.patch --- condor-8.6.8~dfsg.1/debian/patches/CVE-2022-26110_1.patch 1970-01-01 00:00:00.000000000 +0000 +++ condor-8.6.8~dfsg.1/debian/patches/CVE-2022-26110_1.patch 2022-05-22 13:55:50.000000000 +0000 @@ -0,0 +1,73 @@ +From: Markus Koschany +Date: Fri, 20 May 2022 14:09:15 +0200 +Subject: CVE-2022-26110_1 + +Origin: https://github.com/htcondor/htcondor/commit/1cae7601d796725e7f5dd73fedf37f6fbbe379ca +--- + src/condor_daemon_core.V6/daemon_command.cpp | 40 ++++++++++++++++++++++++---- + src/condor_includes/condor_attributes.h | 1 + + 2 files changed, 36 insertions(+), 5 deletions(-) + +diff --git a/src/condor_daemon_core.V6/daemon_command.cpp b/src/condor_daemon_core.V6/daemon_command.cpp +index daa86ec..83d4082 100644 +--- a/src/condor_daemon_core.V6/daemon_command.cpp ++++ b/src/condor_daemon_core.V6/daemon_command.cpp +@@ -1408,11 +1408,41 @@ DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::VerifyComman + m_perm = USER_AUTH_FAILURE; + } + else { +- m_perm = daemonCore->Verify( +- command_desc.Value(), +- m_comTable[m_cmd_index].perm, +- m_sock->peer_addr(), +- m_user.Value() ); ++ // Authentication methods can limit the authorizations associated with ++ // a given identity (at time of coding, only TOKEN does this); apply ++ // these limits if present. ++ std::string authz_policy; ++ bool can_attempt = true; ++ if (m_policy && m_policy->EvaluateAttrString(ATTR_SEC_LIMIT_AUTHORIZATION, authz_policy)) { ++ StringList authz_limits(authz_policy.c_str()); ++ authz_limits.rewind(); ++ const char *perm_cstr = PermString(m_comTable[m_cmd_index].perm); ++ const char *authz_name; ++ bool found_limit = false; ++ while ( (authz_name = authz_limits.next()) ) { ++ if (!strcmp(perm_cstr, authz_name)) { ++ found_limit = true; ++ break; ++ } ++ } ++ bool has_allow_perm = !strcmp(perm_cstr, "ALLOW"); ++ if (!found_limit && !has_allow_perm) { ++ can_attempt = false; ++ } ++ } ++ if (can_attempt) { ++ m_perm = daemonCore->Verify( ++ command_desc.Value(), ++ m_comTable[m_cmd_index].perm, ++ m_sock->peer_addr(), ++ m_user.Value() ); ++ } else { ++ dprintf(D_ALWAYS, "DC_AUTHENTICATE: authentication of %s was successful but resulted in a limited authorization which did not include this command (%d %s), so aborting.\n", ++ m_sock->peer_description(), ++ m_req, ++ m_comTable[m_cmd_index].command_descrip); ++ m_perm = USER_AUTH_FAILURE; ++ } + } + + } else { +diff --git a/src/condor_includes/condor_attributes.h b/src/condor_includes/condor_attributes.h +index 7de9120..d230fb3 100644 +--- a/src/condor_includes/condor_attributes.h ++++ b/src/condor_includes/condor_attributes.h +@@ -839,6 +839,7 @@ extern const char ATTR_SEC_AUTHENTICATED_USER []; + #define ATTR_SEC_TRIED_AUTHENTICATION "TriedAuthentication" + #define ATTR_SEC_AUTHORIZATION_SUCCEEDED "AuthorizationSucceeded" + #define ATTR_SEC_RETURN_CODE "ReturnCode" ++#define ATTR_SEC_LIMIT_AUTHORIZATION "LimitAuthorization" + + #define ATTR_MULTIPLE_TASKS_PER_PVMD "MultipleTasksPerPvmd" + diff -Nru condor-8.6.8~dfsg.1/debian/patches/CVE-2022-26110_2.patch condor-8.6.8~dfsg.1/debian/patches/CVE-2022-26110_2.patch --- condor-8.6.8~dfsg.1/debian/patches/CVE-2022-26110_2.patch 1970-01-01 00:00:00.000000000 +0000 +++ condor-8.6.8~dfsg.1/debian/patches/CVE-2022-26110_2.patch 2022-05-22 13:57:04.000000000 +0000 @@ -0,0 +1,37 @@ +From: Markus Koschany +Date: Fri, 20 May 2022 14:09:31 +0200 +Subject: CVE-2022-26110_2 + +Origin: https://github.com/htcondor/htcondor/commit/8568e8ba65c9490f30a1089b6d4f8910e4bfbd6b +--- + src/condor_daemon_core.V6/daemon_command.cpp | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/src/condor_daemon_core.V6/daemon_command.cpp b/src/condor_daemon_core.V6/daemon_command.cpp +index 83d4082..4f2ddac 100644 +--- a/src/condor_daemon_core.V6/daemon_command.cpp ++++ b/src/condor_daemon_core.V6/daemon_command.cpp +@@ -1141,6 +1141,23 @@ DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::Authenticate + + if ( method_used ) { + m_policy->Assign(ATTR_SEC_AUTHENTICATION_METHODS, method_used); ++ ++ // For CLAIMTOBE, explicitly limit the authorized permission ++ // levels to that of the current command and any implied ones. ++ if ( !strcasecmp(method_used, "CLAIMTOBE") ) { ++ std::string perm_list; ++ DCpermissionHierarchy hierarchy( m_comTable[m_cmd_index].perm ); ++ DCpermission const *perms = hierarchy.getImpliedPerms(); ++ ++ // iterate through a list of this perm and all perms implied by it ++ for (DCpermission perm = *(perms++); perm != LAST_PERM; perm = *(perms++)) { ++ if (!perm_list.empty()) { ++ perm_list += ','; ++ } ++ perm_list += PermString(perm); ++ } ++ m_policy->Assign(ATTR_SEC_LIMIT_AUTHORIZATION, perm_list); ++ } + } + if ( m_sock->getAuthenticatedName() ) { + m_policy->Assign(ATTR_SEC_AUTHENTICATED_NAME, m_sock->getAuthenticatedName() ); diff -Nru condor-8.6.8~dfsg.1/debian/patches/series condor-8.6.8~dfsg.1/debian/patches/series --- condor-8.6.8~dfsg.1/debian/patches/series 2018-03-19 21:34:49.000000000 +0000 +++ condor-8.6.8~dfsg.1/debian/patches/series 2022-05-22 13:55:50.000000000 +0000 @@ -1,12 +1,8 @@ -# in order of upstream compatibility and invasiveness default_debian_config debian_init no_java_for_now -#enable_pythonbindings globus-multi-arch -#gsoap_symbols campus_factory_syntax.patch -# should be obsolete in condor 7.7.6+, but cannot confirm fix_condor_run alpha-fix-getpid.patch as-needed-libs-ftbfs.patch @@ -15,3 +11,6 @@ fix_bashisms fix_linker_script notice_file +CVE-2019-18823.patch +CVE-2022-26110_1.patch +CVE-2022-26110_2.patch