<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2011 Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

// //////////////////////////////////////////////////////////////////////
//
// Implementation of DaemonCore.
//
//
// //////////////////////////////////////////////////////////////////////

#include "condor_common.h"
#ifdef HAVE_EXT_GSOAP
#include "soap_core.h"
#endif
#include "authentication.h"
#include "reli_sock.h"
#include "condor_daemon_core.h"
#include "condor_io.h"
#include "condor_rw.h"
#include "condor_config.h"
#include "condor_threads.h"
#include "condor_version.h"
#include "ipv6_hostname.h"
#include "daemon_command.h"


static unsigned int ZZZZZ = 0;
static int ZZZ_always_increase() {
	return ZZZZZ++;
}

DaemonCommandProtocol::DaemonCommandProtocol(Stream *sock,bool is_command_sock):
#ifdef HAVE_EXT_GSOAP
	m_is_http_post(false),
	m_is_http_get(false),
#endif
	m_nonblocking(!is_command_sock), // cannot re-register command sockets for non-blocking read
	m_delete_sock(!is_command_sock), // must not delete registered command sockets
	m_sock_had_no_deadline(false),
	m_is_tcp(0),
	m_req(0),
	m_reqFound(FALSE),
	m_result(FALSE),
	m_perm(USER_AUTH_FAILURE),
	m_policy(NULL),
	m_key(NULL),
	m_sid(NULL),
	m_async_waiting_time(0),
	m_real_cmd(0),
	m_auth_cmd(0),
	m_new_session(false),
	m_will_enable_encryption(SecMan::SEC_FEAT_ACT_UNDEFINED),
	m_will_enable_integrity(SecMan::SEC_FEAT_ACT_UNDEFINED)
{
	m_sock = dynamic_cast<Sock *>(sock);

	m_sec_man = daemonCore->getSecMan();
	m_comTable = daemonCore->comTable;

	m_handle_req_start_time.getTime();

	ASSERT(m_sock);

	switch ( m_sock->type() ) {
		case Stream::reli_sock :
			m_is_tcp = TRUE;
			m_state = CommandProtocolAcceptTCPRequest;
			break;
		case Stream::safe_sock :
			m_is_tcp = FALSE;
			m_state = CommandProtocolAcceptUDPRequest;
			break;
		default:
			// unrecognized Stream sock
			EXCEPT("DaemonCore: HandleReq(): unrecognized Stream sock");
	}
}

DaemonCommandProtocol::~DaemonCommandProtocol()
{
	if (m_policy) {
		delete m_policy;
	}
	if (m_key) {
		delete m_key;
	}
	if (m_sid) {
		free(m_sid);
	}
}

int DaemonCommandProtocol::doProtocol()
{

	CommandProtocolResult what_next = CommandProtocolContinue;

	if( m_sock ) {
		if( m_sock->deadline_expired() ) {
			MyString msg;
			dprintf(D_ALWAYS,"DaemonCommandProtocol: deadline for security handshake with %s has expired.\n",
					m_sock->peer_description());

			m_result = FALSE;
			what_next = CommandProtocolFinished;
		}
		else if( m_nonblocking && m_sock->is_connect_pending() ) {
			what_next = WaitForSocketData();
		}
		else if( m_is_tcp && !m_sock->is_connected()) {
			MyString msg;
			dprintf(D_ALWAYS,"DaemonCommandProtocol: TCP connection to %s failed.\n",
					m_sock->peer_description());

			m_result = FALSE;
			what_next = CommandProtocolFinished;
		}
	}

	while( what_next == CommandProtocolContinue ) {
		switch(m_state) {
		case CommandProtocolAcceptTCPRequest:
			what_next = AcceptTCPRequest();
			break;
		case CommandProtocolAcceptUDPRequest:
			what_next = AcceptUDPRequest();
			break;
		case CommandProtocolReadCommand:
			what_next = ReadCommand();
			break;
		case CommandProtocolAuthenticate:
			what_next = Authenticate();
			break;
		case CommandProtocolPostAuthenticate:
			what_next = PostAuthenticate();
			break;
		case CommandProtocolExecCommand:
			what_next = ExecCommand();
			break;
		}
	}

	if( what_next == CommandProtocolInProgress ) {
		return KEEP_STREAM;
	}

	return finalize();
}

// Register the socket to call us back when data is available to read.
DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::WaitForSocketData()
{
	if( m_sock->get_deadline() == 0 ) {
		int TCP_SESSION_DEADLINE = param_integer("SEC_TCP_SESSION_DEADLINE",120);
		m_sock->set_deadline_timeout(TCP_SESSION_DEADLINE);
		m_sock_had_no_deadline = true;
	}

	int reg_rc = daemonCore->Register_Socket(
		m_sock,
		m_sock->peer_description(),
		(SocketHandlercpp)&DaemonCommandProtocol::SocketCallback,
		"DaemonCommandProtocol::WaitForSocketData",
		this,
		ALLOW);

	if(reg_rc < 0) {
		dprintf(D_ALWAYS, "DaemonCommandProtocol failed to process command from %s because "
				"Register_Socket returned %d.\n",
				m_sock->get_sinful_peer(),
				reg_rc);

		m_result = FALSE;
		return CommandProtocolFinished;
	}

		// Do not allow ourselves to be deleted until after
		// SocketCallback is called.
	incRefCount();

	m_async_waiting_start_time.getTime();

	return CommandProtocolInProgress;
}

// This function is called by DaemonCore when incoming data is
// available to read, or the socket has become disconnected,
// or the deadline has expired.
int
DaemonCommandProtocol::SocketCallback( Stream *stream )
{
	UtcTime async_waiting_stop_time;
	async_waiting_stop_time.getTime();
	m_async_waiting_time += async_waiting_stop_time.difference(&m_async_waiting_start_time);

	daemonCore->Cancel_Socket( stream );

	int rc = doProtocol();

		// get rid of ref counted when callback was registered
	decRefCount();

	return rc;
}

// This is the first thing we do on an incoming TCP command socket.
// Once this function is finished, m_sock will point to the socket
// from which we should read the command.
DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::AcceptTCPRequest()
{

	m_state = CommandProtocolReadCommand;

		// we have just accepted a socket or perhaps been given a
		// socket from HandleReqAsync().  if there is nothing
		// available yet to read on this socket, we don't want to
		// block when reading the command, so register it
	if ( m_nonblocking && ((ReliSock *)m_sock)->bytes_available_to_read() < 4 ) {
		return WaitForSocketData();
	}

	return CommandProtocolContinue;
}

// This is the first thing we do on an incoming UDP command socket.
// Once this function is finished, m_sock will point to the socket
// from which we should read the command.  If the Condor UDP
// packet header indicates that this command is using an existing
// security session, we handle that here.
DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::AcceptUDPRequest()
{
	std::string who;

		// in UDP we cannot display who the command is from until
		// we read something off the socket, so we display who from
		// after we read the command below...

		dprintf ( D_SECURITY,
				  "DC_AUTHENTICATE: received UDP packet from %s.\n",
				  m_sock->peer_description());


		// get the info, if there is any
		const char * cleartext_info = ((SafeSock*)m_sock)->isIncomingDataMD5ed();
		char * sess_id = NULL;
		char * return_address_ss = NULL;

		if (cleartext_info) {
			StringList info_list(cleartext_info);
			char * tmp = NULL;

			info_list.rewind();
			tmp = info_list.next();
			if (tmp) {
				sess_id = strdup(tmp);
				tmp = info_list.next();
				if (tmp) {
					return_address_ss = strdup(tmp);
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: packet from %s uses MD5 session %s.\n",
							return_address_ss, sess_id);
				} else {
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: packet uses MD5 session %s.\n", sess_id);
				}

			} else {
				// protocol violation... StringList didn't give us anything!
				// this is unlikely to work, but we may as well try... so, we
				// don't fail here.
			}
		}

		if (sess_id) {
			KeyCacheEntry *session = NULL;
			bool found_sess = m_sec_man->session_cache->lookup(sess_id, session);

			if (!found_sess) {
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: session %s NOT FOUND; this session was requested by %s with return address %s\n", sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				// no session... we outta here!

				// but first, we should be nice and send a message back to
				// the people who sent us the wrong session id.
				daemonCore->send_invalidate_session ( return_address_ss, sess_id );

				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			}

			session->renewLease();

			if (!session->key()) {
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: session %s is missing the key! This session was requested by %s with return address %s\n", sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				// uhm, there should be a key here!
				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			}

			if (!m_sock->set_MD_mode(MD_ALWAYS_ON, session->key())) {
				dprintf (D_ALWAYS, "DC_AUTHENTICATE: unable to turn on message authenticator for session %s, failing; this session was requested by %s with return address %s\n",sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			} else {
				dprintf (D_SECURITY, "DC_AUTHENTICATE: message authenticator enabled with key id %s.\n", sess_id);
				m_sec_man->key_printf (D_SECURITY, session->key());
			}

			// Lookup remote user
			session->policy()->LookupString(ATTR_SEC_USER, who);

			free( sess_id );

			if (return_address_ss) {
				free( return_address_ss );
			}
		}


		// get the info, if there is any
		cleartext_info = ((SafeSock*)m_sock)->isIncomingDataEncrypted();
		sess_id = NULL;
		return_address_ss = NULL;

		if (cleartext_info) {
			StringList info_list(cleartext_info);
			char * tmp = NULL;

			info_list.rewind();
			tmp = info_list.next();
			if (tmp) {
				sess_id = strdup(tmp);

				tmp = info_list.next();
				if (tmp) {
					return_address_ss = strdup(tmp);
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: packet from %s uses crypto session %s.\n",
							return_address_ss, sess_id);
				} else {
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: packet uses crypto session %s.\n", sess_id);
				}

			} else {
				// protocol violation... StringList didn't give us anything!
				// this is unlikely to work, but we may as well try... so, we
				// don't fail here.
			}
		}


		if (sess_id) {
			KeyCacheEntry *session = NULL;
			bool found_sess = m_sec_man->session_cache->lookup(sess_id, session);

			if (!found_sess) {
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: session %s NOT FOUND; this session was requested by %s with return address %s\n", sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				// no session... we outta here!

				// but first, see above behavior in MD5 code above.
				daemonCore->send_invalidate_session( return_address_ss, sess_id );

				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			}

			session->renewLease();

			if (!session->key()) {
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: session %s is missing the key! This session was requested by %s with return address %s\n", sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				// uhm, there should be a key here!
				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			}

				// NOTE: prior to 7.1.3, we _always_ set the encryption
				// mode to "on" here, regardless of what was previously
				// negotiated.  However, as of now, the sender never
				// sends an encryption id in the UDP packet header unless
				// we did negotiate to use encryption by default.  Once we
				// no longer care about backwards compatibility with
				// versions older than 7.1.3, we could allow the sender to
				// set the encryption id and trust that we will set the mode
				// as was previously negotiated.
			SecMan::sec_feat_act will_enable_encryption = m_sec_man->sec_lookup_feat_act(*session->policy(), ATTR_SEC_ENCRYPTION);
			bool turn_encryption_on = will_enable_encryption == SecMan::SEC_FEAT_ACT_YES;

			if (!m_sock->set_crypto_key(turn_encryption_on, session->key())) {
				dprintf (D_ALWAYS, "DC_AUTHENTICATE: unable to turn on encryption for session %s, failing; this session was requested by %s with return address %s\n",sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			} else {
				dprintf (D_SECURITY,
					"DC_AUTHENTICATE: encryption enabled with key id %s%s.\n",
					sess_id,
					turn_encryption_on ? "" : " (but encryption mode is off by default for this packet)");
				m_sec_man->key_printf (D_SECURITY, session->key());
			}
			// Lookup user if necessary
			if (who.empty()) {
				session->policy()->LookupString(ATTR_SEC_USER, who);
			}
			bool tried_authentication = false;
			session->policy()->LookupBool(ATTR_SEC_TRIED_AUTHENTICATION,tried_authentication);
			m_sock->setTriedAuthentication(tried_authentication);

			free( sess_id );
			if (return_address_ss) {
				free( return_address_ss );
			}
		}

		if (!who.empty()) {
			m_sock->setFullyQualifiedUser(who.c_str());
			dprintf (D_SECURITY, "DC_AUTHENTICATE: UDP message is from %s.\n", who.c_str());
		}

		m_state = CommandProtocolReadCommand;
		return CommandProtocolContinue;
}

// Read the command.  This function will either be followed by
// Authenticate or ExecCommand(), depending on whether authentication
// was requested.  Soap requests are also handled here.
DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::ReadCommand()
{
	CondorError errstack;

	m_sock->decode();

		// Determine if incoming socket is HTTP over TCP, or if it is CEDAR.
		// For better or worse, we figure this out by seeing if the socket
		// starts w/ a GET or POST.  Hopefully this does not correspond to
		// a daemoncore command int!  [not ever likely, since CEDAR ints are
		// exapanded out to 8 bytes]  Still, in a perfect world we would replace
		// with a more foolproof method.
	char tmpbuf[5];
	memset(tmpbuf,0,sizeof(tmpbuf));
	if ( m_is_tcp ) {
			// TODO Should we be ignoring the return value of condor_read?
		condor_read(m_sock->peer_description(), m_sock->get_file_desc(),
			tmpbuf, sizeof(tmpbuf) - 1, 1, MSG_PEEK);
	}
#ifdef HAVE_EXT_GSOAP
	if ( strstr(tmpbuf,"GET") ) {
		if ( param_boolean("ENABLE_WEB_SERVER",false) ) {
			// mini-web server requires READ authorization.
			if ( daemonCore->Verify("HTTP GET", READ,m_sock->peer_addr(),NULL) ) {
				m_is_http_get = true;
			}
		} else {
			dprintf(D_ALWAYS,"Received HTTP GET connection from %s -- "
							 "DENIED because ENABLE_WEB_SERVER=FALSE\n",
							 m_sock->peer_description());
		}
	} else {
		if ( strstr(tmpbuf,"POST") ) {
			if ( param_boolean("ENABLE_SOAP",false) ) {
				// SOAP requires SOAP authorization.
				if ( daemonCore->Verify("HTTP POST",SOAP_PERM,m_sock->peer_addr(),NULL) ) {
					m_is_http_post = true;
				}
			} else {
				dprintf(D_ALWAYS,"Received HTTP POST connection from %s -- "
							 "DENIED because ENABLE_SOAP=FALSE\n",
							 m_sock->peer_description());
			}
		}
	}
	if ( m_is_http_post || m_is_http_get )
	{
		struct soap *cursoap;

			// Socket appears to be HTTP, so deal with it.
		dprintf(D_ALWAYS, "Received HTTP %s connection from %s\n",
			m_is_http_get ? "GET" : "POST",
			m_sock->peer_description() );


		ASSERT( daemonCore->soap );
		cursoap = dc_soap_accept(m_sock, daemonCore->soap);

			// Now, process the Soap RPC request and dispatch it
		dprintf(D_ALWAYS,"About to serve HTTP request...\n");
		dc_soap_serve(cursoap);
		dc_soap_free(cursoap);
		dprintf(D_ALWAYS, "Completed servicing HTTP request\n");

			// gsoap already closed the socket.  so set the socket in
			// the underlying CEDAR object to INVALID_SOCKET, so
			// CEDAR won't close it again when we delete the object.
		m_sock->invalidateSock();

		m_result = TRUE;
		return CommandProtocolFinished;
	}
#endif // HAVE_EXT_GSOAP

	// read in the command from the sock with a timeout value of just 1 second,
	// since we know there is already some data waiting for us.
	m_sock->timeout(1);
	m_result = m_sock->code(m_req);
	// For now, lets set a 20 second timeout, so all command handlers are called with
	// a timeout of 20 seconds on their socket.
	m_sock->timeout(20);
	if(!m_result) {
		char const *ip = m_sock->peer_ip_str();
		if(!ip) {
			ip = "unknown address";
		}
		dprintf(D_ALWAYS,
			"DaemonCore: Can't receive command request from %s (perhaps a timeout?)\n", ip);
		m_result = FALSE;
		return CommandProtocolFinished;
	}

	if (m_req == DC_AUTHENTICATE) {

		// Allow thread to yield during all the authentication network round-trips
		ScopedEnableParallel(true);

		m_sock->decode();

		dprintf (D_SECURITY, "DC_AUTHENTICATE: received DC_AUTHENTICATE from %s\n", m_sock->peer_description());

		if( !m_auth_info.initFromStream(*m_sock)) {
			dprintf (D_ALWAYS, "ERROR: DC_AUTHENTICATE unable to "
					 "receive auth_info from %s!\n", m_sock->peer_description());
			m_result = FALSE;
			return CommandProtocolFinished;
		}

		if ( m_is_tcp && !m_sock->end_of_message()) {
			dprintf (D_ALWAYS, "ERROR: DC_AUTHENTICATE is TCP, unable to "
					   "receive eom!\n");
			m_result = FALSE;
			return CommandProtocolFinished;
		}

		if (IsDebugVerbose(D_SECURITY)) {
			dprintf (D_SECURITY, "DC_AUTHENTICATE: received following ClassAd:\n");
			m_auth_info.dPrint (D_SECURITY);
		}

		MyString peer_version;
		if( m_auth_info.LookupString( ATTR_SEC_REMOTE_VERSION, peer_version ) ) {
			CondorVersionInfo ver_info( peer_version.Value() );
			m_sock->set_peer_version( &ver_info );
		}

		// look at the ad.  get the command number.
		m_real_cmd = 0;
		m_auth_cmd = 0;
		m_auth_info.LookupInteger(ATTR_SEC_COMMAND, m_real_cmd);

		if (m_real_cmd == DC_AUTHENTICATE) {
			// we'll set m_auth_cmd temporarily to
			m_auth_info.LookupInteger(ATTR_SEC_AUTH_COMMAND, m_auth_cmd);
		} else {
			m_auth_cmd = m_real_cmd;
		}

		// get the auth level of this command
		// locate the hash table entry
		int cmd_index = 0;
		m_reqFound = daemonCore->CommandNumToTableIndex(m_auth_cmd,&cmd_index);

		if (!m_reqFound) {
			// we have no idea what command they want to send.
			// too bad, bye bye

			dprintf(D_ALWAYS,
					"Received %s command (%d) (%s) from %s %s\n",
					(m_is_tcp) ? "TCP" : "UDP",
					m_auth_cmd,
					"UNREGISTERED COMMAND!",
					m_user.Value(),
					m_sock->peer_description());

			m_result = FALSE;
			return CommandProtocolFinished;
		}

		m_new_session        = false;
		bool using_cookie       = false;
		bool valid_cookie		= false;

		// check if we are using a cookie
		char *incoming_cookie   = NULL;
		if( m_auth_info.LookupString(ATTR_SEC_COOKIE, &incoming_cookie)) {
			// compare it to the one we have internally

			valid_cookie = daemonCore->cookie_is_valid((unsigned char*)incoming_cookie);
			free (incoming_cookie);

			if ( valid_cookie ) {
				// we have a match... trust this command.
				using_cookie = true;
			} else {
				// bad cookie!!!
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: recieved invalid cookie from %s!!!\n", m_sock->peer_description());
				m_result = FALSE;
				return CommandProtocolFinished;
			}
		}

		// check if we are restarting a cached session

		if (!using_cookie) {

			if ( m_sec_man->sec_lookup_feat_act(m_auth_info, ATTR_SEC_USE_SESSION) == SecMan::SEC_FEAT_ACT_YES) {

				KeyCacheEntry *session = NULL;

				if( ! m_auth_info.LookupString(ATTR_SEC_SID, &m_sid)) {
					dprintf (D_ALWAYS, "ERROR: DC_AUTHENTICATE unable to "
							 "extract auth_info.%s from %s!\n", ATTR_SEC_SID,
							 m_sock->peer_description());
					m_result = FALSE;
					return CommandProtocolFinished;
				}

				// lookup the suggested key
				if (!m_sec_man->session_cache->lookup(m_sid, session)) {

					// the key id they sent was not in our cache.  this is a
					// problem.

					char * return_addr = NULL;
					m_auth_info.LookupString(ATTR_SEC_SERVER_COMMAND_SOCK, &return_addr);

					dprintf (D_ALWAYS, "DC_AUTHENTICATE: attempt to open "
							   "invalid session %s, failing; this session was requested by %s with return address %s\n", m_sid, m_sock->peer_description(), return_addr ? return_addr : "(none)");

					if( return_addr ) {
						daemonCore->send_invalidate_session( return_addr, m_sid );
						free (return_addr);
					}

					// consume the rejected message
					m_sock->decode();
					m_sock->end_of_message();

					// close the connection.
					m_result = FALSE;
					return CommandProtocolFinished;

				} else {
					// the session->id() and the_sid strings should be identical.

					if (IsDebugLevel(D_SECURITY)) {
						char *return_addr = NULL;
						if(session->policy()) {
							session->policy()->LookupString(ATTR_SEC_SERVER_COMMAND_SOCK,&return_addr);
						}
						dprintf (D_SECURITY, "DC_AUTHENTICATE: resuming session id %s%s%s:\n",
								 session->id(),
								 return_addr ? " with return address " : "",
								 return_addr ? return_addr : "");
						free(return_addr);
					}
				}

				session->renewLease();

				if (session->key()) {
					// copy this to the HandleReq() scope
					m_key = new KeyInfo(*session->key());
				}

				if (session->policy()) {
					// copy this to the HandleReq() scope
					m_policy = new ClassAd(*session->policy());
					if (IsDebugVerbose(D_SECURITY)) {
						dprintf (D_SECURITY, "DC_AUTHENTICATE: Cached Session:\n");
						m_policy->dPrint (D_SECURITY);
					}
				}

				// grab the user out of the policy.
				if (m_policy) {
					char *the_user  = NULL;
					m_policy->LookupString( ATTR_SEC_USER, &the_user);
					if (the_user) {
						// copy this to the HandleReq() scope
						m_user = the_user;
						free( the_user );
						the_user = NULL;
					}

					bool tried_authentication=false;
					m_policy->LookupBool(ATTR_SEC_TRIED_AUTHENTICATION,tried_authentication);
					m_sock->setTriedAuthentication(tried_authentication);
				}
				m_new_session = false;

			} // end of case: using existing session
			else {
					// they did not request a cached session.  see if they
					// want to start one.  look at our security policy.
				ClassAd our_policy;
				if( ! m_sec_man->FillInSecurityPolicyAd(
					m_comTable[cmd_index].perm,
					&our_policy,
					false,
					false,
					m_comTable[cmd_index].force_authentication ) )
				{
						// our policy is invalid even without the other
						// side getting involved.
					dprintf( D_ALWAYS, "DC_AUTHENTICATE: "
							 "Our security policy is invalid!\n" );
					m_result = FALSE;
					return CommandProtocolFinished;
				}

				if (IsDebugVerbose(D_SECURITY)) {
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: our_policy:\n" );
					our_policy.dPrint(D_SECURITY);
				}

				// reconcile.  if unable, close socket.
				m_policy = m_sec_man->ReconcileSecurityPolicyAds( m_auth_info,
																  our_policy );

				if (!m_policy) {
					dprintf(D_ALWAYS, "DC_AUTHENTICATE: Unable to reconcile!\n");
					m_result = FALSE;
					return CommandProtocolFinished;
				} else {
					if (IsDebugVerbose(D_SECURITY)) {
						dprintf ( D_SECURITY, "DC_AUTHENTICATE: the_policy:\n" );
						m_policy->dPrint(D_SECURITY);
					}
				}

				// add our version to the policy to be sent over
				m_policy->Assign(ATTR_SEC_REMOTE_VERSION, CondorVersion());

				// handy policy vars
				SecMan::sec_feat_act will_authenticate      = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_AUTHENTICATION);

				if (m_sec_man->sec_lookup_feat_act(m_auth_info, ATTR_SEC_NEW_SESSION) == SecMan::SEC_FEAT_ACT_YES) {

					// generate a new session

					// generate a unique ID.
					MyString tmpStr;
					tmpStr.formatstr( "%s:%i:%i:%i", 
									get_local_hostname().Value(), daemonCore->mypid,
							 (int)time(0), ZZZ_always_increase() );
					assert (m_sid == NULL);
					m_sid = strdup(tmpStr.Value());

					if (will_authenticate == SecMan::SEC_FEAT_ACT_YES) {

						char *crypto_method = NULL;
						if (!m_policy->LookupString(ATTR_SEC_CRYPTO_METHODS, &crypto_method)) {
							dprintf ( D_ALWAYS, "DC_AUTHENTICATE: tried to enable encryption for request from %s, but we have none!\n", m_sock->peer_description() );
							m_result = FALSE;
							return CommandProtocolFinished;
						}

						unsigned char* rkey = Condor_Crypt_Base::randomKey(24);
						unsigned char  rbuf[24];
						if (rkey) {
							memcpy (rbuf, rkey, 24);
							// this was malloced in randomKey
							free (rkey);
						} else {
							memset (rbuf, 0, 24);
							dprintf ( D_ALWAYS, "DC_AUTHENTICATE: unable to generate key for request from %s - no crypto available!\n", m_sock->peer_description() );							
							free( crypto_method );
							crypto_method = NULL;
							m_result = FALSE;
							return CommandProtocolFinished;
						}

						switch (toupper(crypto_method[0])) {
							case 'B': // blowfish
								dprintf (D_SECURITY, "DC_AUTHENTICATE: generating BLOWFISH key for session %s...\n", m_sid);
								m_key = new KeyInfo(rbuf, 24, CONDOR_BLOWFISH);
								break;
							case '3': // 3des
							case 'T': // Tripledes
								dprintf (D_SECURITY, "DC_AUTHENTICATE: generating 3DES key for session %s...\n", m_sid);
								m_key = new KeyInfo(rbuf, 24, CONDOR_3DES);
								break;
							default:
								dprintf (D_SECURITY, "DC_AUTHENTICATE: generating RANDOM key for session %s...\n", m_sid);
								m_key = new KeyInfo(rbuf, 24);
								break;
						}

						free( crypto_method );
						crypto_method = NULL;

						if (!m_key) {
							m_result = FALSE;
							return CommandProtocolFinished;
						}

						m_sec_man->key_printf (D_SECURITY, m_key);
					}

					m_new_session = true;
				}

				// if they asked, tell them
				if (m_is_tcp && (m_sec_man->sec_lookup_feat_act(m_auth_info, ATTR_SEC_ENACT) == SecMan::SEC_FEAT_ACT_NO)) {
					if (IsDebugVerbose(D_SECURITY)) {
						dprintf (D_SECURITY, "SECMAN: Sending following response ClassAd:\n");
						m_policy->dPrint( D_SECURITY );
					}
					m_sock->encode();
					if (!m_policy->put(*m_sock) ||
						!m_sock->end_of_message()) {
						dprintf (D_ALWAYS, "SECMAN: Error sending response classad to %s!\n", m_sock->peer_description());
						m_auth_info.dPrint (D_ALWAYS);
						m_result = FALSE;
						return CommandProtocolFinished;
					}
					m_sock->decode();
				} else {
					dprintf( D_SECURITY, "SECMAN: Enact was '%s', not sending response.\n",
						SecMan::sec_feat_act_rev[m_sec_man->sec_lookup_feat_act(m_auth_info, ATTR_SEC_ENACT)] );
				}

			} // end of case: consider establishing a new security session

			if( !m_is_tcp ) {
					// For UDP, if encryption is not on by default,
					// configure it with the session key so that it
					// can be programmatically toggled on and off for
					// portions of the message (e.g. for secret stuff
					// like claimids).  If encryption _is_ on by
					// default, then it will have already been turned
					// on by now, because the UDP header contains the
					// encryption key in that case.

				SecMan::sec_feat_act will_enable_encryption = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_ENCRYPTION);

				if( will_enable_encryption != SecMan::SEC_FEAT_ACT_YES
					&& m_key )
				{
					m_sock->set_crypto_key(false, m_key);
					dprintf(D_SECURITY, "DC_AUTHENTICATE: encryption enabled with session key id %s (but encryption mode is off by default for this packet).\n", m_sid ? m_sid : "(null)");
				}
			}

			if (m_is_tcp) {

				// do what we decided

				// handy policy vars
				SecMan::sec_feat_act will_authenticate      = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_AUTHENTICATION);
				m_will_enable_encryption = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_ENCRYPTION);
				m_will_enable_integrity  = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_INTEGRITY);


				// protocol fix:
				//
				// up to and including 6.6.0, will_authenticate would be set to
				// true if we are resuming a session that was authenticated.
				// this is not necessary.
				//
				// so, as of 6.6.1, if we are resuming a session (as determined
				// by the expression (!m_new_session), AND the other side is
				// 6.6.1 or higher, we will force will_authenticate to
				// SEC_FEAT_ACT_NO.

				if ( will_authenticate == SecMan::SEC_FEAT_ACT_YES ) {
					if ((!m_new_session)) {
						char * remote_version = NULL;
						m_policy->LookupString(ATTR_SEC_REMOTE_VERSION, &remote_version);
						if(remote_version) {
							// this attribute was added in 6.6.1.  it's mere
							// presence means that the remote side is 6.6.1 or
							// higher, so no need to instantiate a CondorVersionInfo.
							dprintf( D_SECURITY, "SECMAN: other side is %s, NOT reauthenticating.\n", remote_version );
							will_authenticate = SecMan::SEC_FEAT_ACT_NO;

							free (remote_version);
						} else {
							dprintf( D_SECURITY, "SECMAN: other side is pre 6.6.1, reauthenticating.\n" );
						}
					} else {
						dprintf( D_SECURITY, "SECMAN: new session, doing initial authentication.\n" );
					}
				}



				if (m_is_tcp && (will_authenticate == SecMan::SEC_FEAT_ACT_YES)) {

					m_state = CommandProtocolAuthenticate;
					return CommandProtocolContinue;


				} else {
					if (IsDebugVerbose(D_SECURITY)) {
						dprintf (D_SECURITY, "DC_AUTHENTICATE: not authenticating.\n");
					}
					m_state = CommandProtocolPostAuthenticate;
					return CommandProtocolContinue;
				}
			} // end is_tcp
		} // end !using_cookie
	} // end DC_AUTHENTICATE
	m_state = CommandProtocolExecCommand;
	return CommandProtocolContinue;
}

DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::Authenticate()
{
	CondorError errstack;

	if( m_nonblocking && !m_sock->readReady() ) {
		return WaitForSocketData();
	}

	// we know the ..METHODS_LIST attribute exists since it was put
	// in by us.  pre 6.5.0 protocol does not put it in.
	char * auth_methods = NULL;
	m_policy->LookupString(ATTR_SEC_AUTHENTICATION_METHODS_LIST, &auth_methods);

	if (!auth_methods) {
		dprintf (D_SECURITY, "DC_AUTHENTICATE: no auth methods in response ad from %s, failing!\n", m_sock->peer_description());
		m_result = FALSE;
		return CommandProtocolFinished;
	}

	if (IsDebugVerbose(D_SECURITY)) {
		dprintf (D_SECURITY, "DC_AUTHENTICATE: authenticating RIGHT NOW.\n");
	}

	int cmd_index = 0;
	if( !daemonCore->CommandNumToTableIndex(m_auth_cmd,&cmd_index) ) {
		dprintf(D_ALWAYS, "DC_AUTHENTICATE: UNREGISTERED COMMAND %d in Authenticate()\n",m_auth_cmd);
		m_result = FALSE;
		return CommandProtocolFinished;
	}

	int auth_timeout = daemonCore->getSecMan()->getSecTimeout( m_comTable[cmd_index].perm );

	char *method_used = NULL;
	bool auth_success = m_sock->authenticate(m_key, auth_methods, &errstack, auth_timeout, &method_used);

	if ( method_used ) {
		m_policy->Assign(ATTR_SEC_AUTHENTICATION_METHODS, method_used);
	}

	free( auth_methods );
	free( method_used );<commit_msg>Fix small memory leak when not authenticate an unknown command<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2011 Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

// //////////////////////////////////////////////////////////////////////
//
// Implementation of DaemonCore.
//
//
// //////////////////////////////////////////////////////////////////////

#include "condor_common.h"
#ifdef HAVE_EXT_GSOAP
#include "soap_core.h"
#endif
#include "authentication.h"
#include "reli_sock.h"
#include "condor_daemon_core.h"
#include "condor_io.h"
#include "condor_rw.h"
#include "condor_config.h"
#include "condor_threads.h"
#include "condor_version.h"
#include "ipv6_hostname.h"
#include "daemon_command.h"


static unsigned int ZZZZZ = 0;
static int ZZZ_always_increase() {
	return ZZZZZ++;
}

DaemonCommandProtocol::DaemonCommandProtocol(Stream *sock,bool is_command_sock):
#ifdef HAVE_EXT_GSOAP
	m_is_http_post(false),
	m_is_http_get(false),
#endif
	m_nonblocking(!is_command_sock), // cannot re-register command sockets for non-blocking read
	m_delete_sock(!is_command_sock), // must not delete registered command sockets
	m_sock_had_no_deadline(false),
	m_is_tcp(0),
	m_req(0),
	m_reqFound(FALSE),
	m_result(FALSE),
	m_perm(USER_AUTH_FAILURE),
	m_policy(NULL),
	m_key(NULL),
	m_sid(NULL),
	m_async_waiting_time(0),
	m_real_cmd(0),
	m_auth_cmd(0),
	m_new_session(false),
	m_will_enable_encryption(SecMan::SEC_FEAT_ACT_UNDEFINED),
	m_will_enable_integrity(SecMan::SEC_FEAT_ACT_UNDEFINED)
{
	m_sock = dynamic_cast<Sock *>(sock);

	m_sec_man = daemonCore->getSecMan();
	m_comTable = daemonCore->comTable;

	m_handle_req_start_time.getTime();

	ASSERT(m_sock);

	switch ( m_sock->type() ) {
		case Stream::reli_sock :
			m_is_tcp = TRUE;
			m_state = CommandProtocolAcceptTCPRequest;
			break;
		case Stream::safe_sock :
			m_is_tcp = FALSE;
			m_state = CommandProtocolAcceptUDPRequest;
			break;
		default:
			// unrecognized Stream sock
			EXCEPT("DaemonCore: HandleReq(): unrecognized Stream sock");
	}
}

DaemonCommandProtocol::~DaemonCommandProtocol()
{
	if (m_policy) {
		delete m_policy;
	}
	if (m_key) {
		delete m_key;
	}
	if (m_sid) {
		free(m_sid);
	}
}

int DaemonCommandProtocol::doProtocol()
{

	CommandProtocolResult what_next = CommandProtocolContinue;

	if( m_sock ) {
		if( m_sock->deadline_expired() ) {
			MyString msg;
			dprintf(D_ALWAYS,"DaemonCommandProtocol: deadline for security handshake with %s has expired.\n",
					m_sock->peer_description());

			m_result = FALSE;
			what_next = CommandProtocolFinished;
		}
		else if( m_nonblocking && m_sock->is_connect_pending() ) {
			what_next = WaitForSocketData();
		}
		else if( m_is_tcp && !m_sock->is_connected()) {
			MyString msg;
			dprintf(D_ALWAYS,"DaemonCommandProtocol: TCP connection to %s failed.\n",
					m_sock->peer_description());

			m_result = FALSE;
			what_next = CommandProtocolFinished;
		}
	}

	while( what_next == CommandProtocolContinue ) {
		switch(m_state) {
		case CommandProtocolAcceptTCPRequest:
			what_next = AcceptTCPRequest();
			break;
		case CommandProtocolAcceptUDPRequest:
			what_next = AcceptUDPRequest();
			break;
		case CommandProtocolReadCommand:
			what_next = ReadCommand();
			break;
		case CommandProtocolAuthenticate:
			what_next = Authenticate();
			break;
		case CommandProtocolPostAuthenticate:
			what_next = PostAuthenticate();
			break;
		case CommandProtocolExecCommand:
			what_next = ExecCommand();
			break;
		}
	}

	if( what_next == CommandProtocolInProgress ) {
		return KEEP_STREAM;
	}

	return finalize();
}

// Register the socket to call us back when data is available to read.
DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::WaitForSocketData()
{
	if( m_sock->get_deadline() == 0 ) {
		int TCP_SESSION_DEADLINE = param_integer("SEC_TCP_SESSION_DEADLINE",120);
		m_sock->set_deadline_timeout(TCP_SESSION_DEADLINE);
		m_sock_had_no_deadline = true;
	}

	int reg_rc = daemonCore->Register_Socket(
		m_sock,
		m_sock->peer_description(),
		(SocketHandlercpp)&DaemonCommandProtocol::SocketCallback,
		"DaemonCommandProtocol::WaitForSocketData",
		this,
		ALLOW);

	if(reg_rc < 0) {
		dprintf(D_ALWAYS, "DaemonCommandProtocol failed to process command from %s because "
				"Register_Socket returned %d.\n",
				m_sock->get_sinful_peer(),
				reg_rc);

		m_result = FALSE;
		return CommandProtocolFinished;
	}

		// Do not allow ourselves to be deleted until after
		// SocketCallback is called.
	incRefCount();

	m_async_waiting_start_time.getTime();

	return CommandProtocolInProgress;
}

// This function is called by DaemonCore when incoming data is
// available to read, or the socket has become disconnected,
// or the deadline has expired.
int
DaemonCommandProtocol::SocketCallback( Stream *stream )
{
	UtcTime async_waiting_stop_time;
	async_waiting_stop_time.getTime();
	m_async_waiting_time += async_waiting_stop_time.difference(&m_async_waiting_start_time);

	daemonCore->Cancel_Socket( stream );

	int rc = doProtocol();

		// get rid of ref counted when callback was registered
	decRefCount();

	return rc;
}

// This is the first thing we do on an incoming TCP command socket.
// Once this function is finished, m_sock will point to the socket
// from which we should read the command.
DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::AcceptTCPRequest()
{

	m_state = CommandProtocolReadCommand;

		// we have just accepted a socket or perhaps been given a
		// socket from HandleReqAsync().  if there is nothing
		// available yet to read on this socket, we don't want to
		// block when reading the command, so register it
	if ( m_nonblocking && ((ReliSock *)m_sock)->bytes_available_to_read() < 4 ) {
		return WaitForSocketData();
	}

	return CommandProtocolContinue;
}

// This is the first thing we do on an incoming UDP command socket.
// Once this function is finished, m_sock will point to the socket
// from which we should read the command.  If the Condor UDP
// packet header indicates that this command is using an existing
// security session, we handle that here.
DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::AcceptUDPRequest()
{
	std::string who;

		// in UDP we cannot display who the command is from until
		// we read something off the socket, so we display who from
		// after we read the command below...

		dprintf ( D_SECURITY,
				  "DC_AUTHENTICATE: received UDP packet from %s.\n",
				  m_sock->peer_description());


		// get the info, if there is any
		const char * cleartext_info = ((SafeSock*)m_sock)->isIncomingDataMD5ed();
		char * sess_id = NULL;
		char * return_address_ss = NULL;

		if (cleartext_info) {
			StringList info_list(cleartext_info);
			char * tmp = NULL;

			info_list.rewind();
			tmp = info_list.next();
			if (tmp) {
				sess_id = strdup(tmp);
				tmp = info_list.next();
				if (tmp) {
					return_address_ss = strdup(tmp);
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: packet from %s uses MD5 session %s.\n",
							return_address_ss, sess_id);
				} else {
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: packet uses MD5 session %s.\n", sess_id);
				}

			} else {
				// protocol violation... StringList didn't give us anything!
				// this is unlikely to work, but we may as well try... so, we
				// don't fail here.
			}
		}

		if (sess_id) {
			KeyCacheEntry *session = NULL;
			bool found_sess = m_sec_man->session_cache->lookup(sess_id, session);

			if (!found_sess) {
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: session %s NOT FOUND; this session was requested by %s with return address %s\n", sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				// no session... we outta here!

				// but first, we should be nice and send a message back to
				// the people who sent us the wrong session id.
				daemonCore->send_invalidate_session ( return_address_ss, sess_id );

				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			}

			session->renewLease();

			if (!session->key()) {
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: session %s is missing the key! This session was requested by %s with return address %s\n", sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				// uhm, there should be a key here!
				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			}

			if (!m_sock->set_MD_mode(MD_ALWAYS_ON, session->key())) {
				dprintf (D_ALWAYS, "DC_AUTHENTICATE: unable to turn on message authenticator for session %s, failing; this session was requested by %s with return address %s\n",sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			} else {
				dprintf (D_SECURITY, "DC_AUTHENTICATE: message authenticator enabled with key id %s.\n", sess_id);
				m_sec_man->key_printf (D_SECURITY, session->key());
			}

			// Lookup remote user
			session->policy()->LookupString(ATTR_SEC_USER, who);

			free( sess_id );

			if (return_address_ss) {
				free( return_address_ss );
			}
		}


		// get the info, if there is any
		cleartext_info = ((SafeSock*)m_sock)->isIncomingDataEncrypted();
		sess_id = NULL;
		return_address_ss = NULL;

		if (cleartext_info) {
			StringList info_list(cleartext_info);
			char * tmp = NULL;

			info_list.rewind();
			tmp = info_list.next();
			if (tmp) {
				sess_id = strdup(tmp);

				tmp = info_list.next();
				if (tmp) {
					return_address_ss = strdup(tmp);
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: packet from %s uses crypto session %s.\n",
							return_address_ss, sess_id);
				} else {
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: packet uses crypto session %s.\n", sess_id);
				}

			} else {
				// protocol violation... StringList didn't give us anything!
				// this is unlikely to work, but we may as well try... so, we
				// don't fail here.
			}
		}


		if (sess_id) {
			KeyCacheEntry *session = NULL;
			bool found_sess = m_sec_man->session_cache->lookup(sess_id, session);

			if (!found_sess) {
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: session %s NOT FOUND; this session was requested by %s with return address %s\n", sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				// no session... we outta here!

				// but first, see above behavior in MD5 code above.
				daemonCore->send_invalidate_session( return_address_ss, sess_id );

				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			}

			session->renewLease();

			if (!session->key()) {
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: session %s is missing the key! This session was requested by %s with return address %s\n", sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				// uhm, there should be a key here!
				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			}

				// NOTE: prior to 7.1.3, we _always_ set the encryption
				// mode to "on" here, regardless of what was previously
				// negotiated.  However, as of now, the sender never
				// sends an encryption id in the UDP packet header unless
				// we did negotiate to use encryption by default.  Once we
				// no longer care about backwards compatibility with
				// versions older than 7.1.3, we could allow the sender to
				// set the encryption id and trust that we will set the mode
				// as was previously negotiated.
			SecMan::sec_feat_act will_enable_encryption = m_sec_man->sec_lookup_feat_act(*session->policy(), ATTR_SEC_ENCRYPTION);
			bool turn_encryption_on = will_enable_encryption == SecMan::SEC_FEAT_ACT_YES;

			if (!m_sock->set_crypto_key(turn_encryption_on, session->key())) {
				dprintf (D_ALWAYS, "DC_AUTHENTICATE: unable to turn on encryption for session %s, failing; this session was requested by %s with return address %s\n",sess_id, m_sock->peer_description(), return_address_ss ? return_address_ss : "(none)");
				if( return_address_ss ) {
					free( return_address_ss );
					return_address_ss = NULL;
				}
				free( sess_id );
				sess_id = NULL;
				m_result = FALSE;
				return CommandProtocolFinished;
			} else {
				dprintf (D_SECURITY,
					"DC_AUTHENTICATE: encryption enabled with key id %s%s.\n",
					sess_id,
					turn_encryption_on ? "" : " (but encryption mode is off by default for this packet)");
				m_sec_man->key_printf (D_SECURITY, session->key());
			}
			// Lookup user if necessary
			if (who.empty()) {
				session->policy()->LookupString(ATTR_SEC_USER, who);
			}
			bool tried_authentication = false;
			session->policy()->LookupBool(ATTR_SEC_TRIED_AUTHENTICATION,tried_authentication);
			m_sock->setTriedAuthentication(tried_authentication);

			free( sess_id );
			if (return_address_ss) {
				free( return_address_ss );
			}
		}

		if (!who.empty()) {
			m_sock->setFullyQualifiedUser(who.c_str());
			dprintf (D_SECURITY, "DC_AUTHENTICATE: UDP message is from %s.\n", who.c_str());
		}

		m_state = CommandProtocolReadCommand;
		return CommandProtocolContinue;
}

// Read the command.  This function will either be followed by
// Authenticate or ExecCommand(), depending on whether authentication
// was requested.  Soap requests are also handled here.
DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::ReadCommand()
{
	CondorError errstack;

	m_sock->decode();

		// Determine if incoming socket is HTTP over TCP, or if it is CEDAR.
		// For better or worse, we figure this out by seeing if the socket
		// starts w/ a GET or POST.  Hopefully this does not correspond to
		// a daemoncore command int!  [not ever likely, since CEDAR ints are
		// exapanded out to 8 bytes]  Still, in a perfect world we would replace
		// with a more foolproof method.
	char tmpbuf[5];
	memset(tmpbuf,0,sizeof(tmpbuf));
	if ( m_is_tcp ) {
			// TODO Should we be ignoring the return value of condor_read?
		condor_read(m_sock->peer_description(), m_sock->get_file_desc(),
			tmpbuf, sizeof(tmpbuf) - 1, 1, MSG_PEEK);
	}
#ifdef HAVE_EXT_GSOAP
	if ( strstr(tmpbuf,"GET") ) {
		if ( param_boolean("ENABLE_WEB_SERVER",false) ) {
			// mini-web server requires READ authorization.
			if ( daemonCore->Verify("HTTP GET", READ,m_sock->peer_addr(),NULL) ) {
				m_is_http_get = true;
			}
		} else {
			dprintf(D_ALWAYS,"Received HTTP GET connection from %s -- "
							 "DENIED because ENABLE_WEB_SERVER=FALSE\n",
							 m_sock->peer_description());
		}
	} else {
		if ( strstr(tmpbuf,"POST") ) {
			if ( param_boolean("ENABLE_SOAP",false) ) {
				// SOAP requires SOAP authorization.
				if ( daemonCore->Verify("HTTP POST",SOAP_PERM,m_sock->peer_addr(),NULL) ) {
					m_is_http_post = true;
				}
			} else {
				dprintf(D_ALWAYS,"Received HTTP POST connection from %s -- "
							 "DENIED because ENABLE_SOAP=FALSE\n",
							 m_sock->peer_description());
			}
		}
	}
	if ( m_is_http_post || m_is_http_get )
	{
		struct soap *cursoap;

			// Socket appears to be HTTP, so deal with it.
		dprintf(D_ALWAYS, "Received HTTP %s connection from %s\n",
			m_is_http_get ? "GET" : "POST",
			m_sock->peer_description() );


		ASSERT( daemonCore->soap );
		cursoap = dc_soap_accept(m_sock, daemonCore->soap);

			// Now, process the Soap RPC request and dispatch it
		dprintf(D_ALWAYS,"About to serve HTTP request...\n");
		dc_soap_serve(cursoap);
		dc_soap_free(cursoap);
		dprintf(D_ALWAYS, "Completed servicing HTTP request\n");

			// gsoap already closed the socket.  so set the socket in
			// the underlying CEDAR object to INVALID_SOCKET, so
			// CEDAR won't close it again when we delete the object.
		m_sock->invalidateSock();

		m_result = TRUE;
		return CommandProtocolFinished;
	}
#endif // HAVE_EXT_GSOAP

	// read in the command from the sock with a timeout value of just 1 second,
	// since we know there is already some data waiting for us.
	m_sock->timeout(1);
	m_result = m_sock->code(m_req);
	// For now, lets set a 20 second timeout, so all command handlers are called with
	// a timeout of 20 seconds on their socket.
	m_sock->timeout(20);
	if(!m_result) {
		char const *ip = m_sock->peer_ip_str();
		if(!ip) {
			ip = "unknown address";
		}
		dprintf(D_ALWAYS,
			"DaemonCore: Can't receive command request from %s (perhaps a timeout?)\n", ip);
		m_result = FALSE;
		return CommandProtocolFinished;
	}

	if (m_req == DC_AUTHENTICATE) {

		// Allow thread to yield during all the authentication network round-trips
		ScopedEnableParallel(true);

		m_sock->decode();

		dprintf (D_SECURITY, "DC_AUTHENTICATE: received DC_AUTHENTICATE from %s\n", m_sock->peer_description());

		if( !m_auth_info.initFromStream(*m_sock)) {
			dprintf (D_ALWAYS, "ERROR: DC_AUTHENTICATE unable to "
					 "receive auth_info from %s!\n", m_sock->peer_description());
			m_result = FALSE;
			return CommandProtocolFinished;
		}

		if ( m_is_tcp && !m_sock->end_of_message()) {
			dprintf (D_ALWAYS, "ERROR: DC_AUTHENTICATE is TCP, unable to "
					   "receive eom!\n");
			m_result = FALSE;
			return CommandProtocolFinished;
		}

		if (IsDebugVerbose(D_SECURITY)) {
			dprintf (D_SECURITY, "DC_AUTHENTICATE: received following ClassAd:\n");
			m_auth_info.dPrint (D_SECURITY);
		}

		MyString peer_version;
		if( m_auth_info.LookupString( ATTR_SEC_REMOTE_VERSION, peer_version ) ) {
			CondorVersionInfo ver_info( peer_version.Value() );
			m_sock->set_peer_version( &ver_info );
		}

		// look at the ad.  get the command number.
		m_real_cmd = 0;
		m_auth_cmd = 0;
		m_auth_info.LookupInteger(ATTR_SEC_COMMAND, m_real_cmd);

		if (m_real_cmd == DC_AUTHENTICATE) {
			// we'll set m_auth_cmd temporarily to
			m_auth_info.LookupInteger(ATTR_SEC_AUTH_COMMAND, m_auth_cmd);
		} else {
			m_auth_cmd = m_real_cmd;
		}

		// get the auth level of this command
		// locate the hash table entry
		int cmd_index = 0;
		m_reqFound = daemonCore->CommandNumToTableIndex(m_auth_cmd,&cmd_index);

		if (!m_reqFound) {
			// we have no idea what command they want to send.
			// too bad, bye bye

			dprintf(D_ALWAYS,
					"Received %s command (%d) (%s) from %s %s\n",
					(m_is_tcp) ? "TCP" : "UDP",
					m_auth_cmd,
					"UNREGISTERED COMMAND!",
					m_user.Value(),
					m_sock->peer_description());

			m_result = FALSE;
			return CommandProtocolFinished;
		}

		m_new_session        = false;
		bool using_cookie       = false;
		bool valid_cookie		= false;

		// check if we are using a cookie
		char *incoming_cookie   = NULL;
		if( m_auth_info.LookupString(ATTR_SEC_COOKIE, &incoming_cookie)) {
			// compare it to the one we have internally

			valid_cookie = daemonCore->cookie_is_valid((unsigned char*)incoming_cookie);
			free (incoming_cookie);

			if ( valid_cookie ) {
				// we have a match... trust this command.
				using_cookie = true;
			} else {
				// bad cookie!!!
				dprintf ( D_ALWAYS, "DC_AUTHENTICATE: recieved invalid cookie from %s!!!\n", m_sock->peer_description());
				m_result = FALSE;
				return CommandProtocolFinished;
			}
		}

		// check if we are restarting a cached session

		if (!using_cookie) {

			if ( m_sec_man->sec_lookup_feat_act(m_auth_info, ATTR_SEC_USE_SESSION) == SecMan::SEC_FEAT_ACT_YES) {

				KeyCacheEntry *session = NULL;

				if( ! m_auth_info.LookupString(ATTR_SEC_SID, &m_sid)) {
					dprintf (D_ALWAYS, "ERROR: DC_AUTHENTICATE unable to "
							 "extract auth_info.%s from %s!\n", ATTR_SEC_SID,
							 m_sock->peer_description());
					m_result = FALSE;
					return CommandProtocolFinished;
				}

				// lookup the suggested key
				if (!m_sec_man->session_cache->lookup(m_sid, session)) {

					// the key id they sent was not in our cache.  this is a
					// problem.

					char * return_addr = NULL;
					m_auth_info.LookupString(ATTR_SEC_SERVER_COMMAND_SOCK, &return_addr);

					dprintf (D_ALWAYS, "DC_AUTHENTICATE: attempt to open "
							   "invalid session %s, failing; this session was requested by %s with return address %s\n", m_sid, m_sock->peer_description(), return_addr ? return_addr : "(none)");

					if( return_addr ) {
						daemonCore->send_invalidate_session( return_addr, m_sid );
						free (return_addr);
					}

					// consume the rejected message
					m_sock->decode();
					m_sock->end_of_message();

					// close the connection.
					m_result = FALSE;
					return CommandProtocolFinished;

				} else {
					// the session->id() and the_sid strings should be identical.

					if (IsDebugLevel(D_SECURITY)) {
						char *return_addr = NULL;
						if(session->policy()) {
							session->policy()->LookupString(ATTR_SEC_SERVER_COMMAND_SOCK,&return_addr);
						}
						dprintf (D_SECURITY, "DC_AUTHENTICATE: resuming session id %s%s%s:\n",
								 session->id(),
								 return_addr ? " with return address " : "",
								 return_addr ? return_addr : "");
						free(return_addr);
					}
				}

				session->renewLease();

				if (session->key()) {
					// copy this to the HandleReq() scope
					m_key = new KeyInfo(*session->key());
				}

				if (session->policy()) {
					// copy this to the HandleReq() scope
					m_policy = new ClassAd(*session->policy());
					if (IsDebugVerbose(D_SECURITY)) {
						dprintf (D_SECURITY, "DC_AUTHENTICATE: Cached Session:\n");
						m_policy->dPrint (D_SECURITY);
					}
				}

				// grab the user out of the policy.
				if (m_policy) {
					char *the_user  = NULL;
					m_policy->LookupString( ATTR_SEC_USER, &the_user);
					if (the_user) {
						// copy this to the HandleReq() scope
						m_user = the_user;
						free( the_user );
						the_user = NULL;
					}

					bool tried_authentication=false;
					m_policy->LookupBool(ATTR_SEC_TRIED_AUTHENTICATION,tried_authentication);
					m_sock->setTriedAuthentication(tried_authentication);
				}
				m_new_session = false;

			} // end of case: using existing session
			else {
					// they did not request a cached session.  see if they
					// want to start one.  look at our security policy.
				ClassAd our_policy;
				if( ! m_sec_man->FillInSecurityPolicyAd(
					m_comTable[cmd_index].perm,
					&our_policy,
					false,
					false,
					m_comTable[cmd_index].force_authentication ) )
				{
						// our policy is invalid even without the other
						// side getting involved.
					dprintf( D_ALWAYS, "DC_AUTHENTICATE: "
							 "Our security policy is invalid!\n" );
					m_result = FALSE;
					return CommandProtocolFinished;
				}

				if (IsDebugVerbose(D_SECURITY)) {
					dprintf ( D_SECURITY, "DC_AUTHENTICATE: our_policy:\n" );
					our_policy.dPrint(D_SECURITY);
				}

				// reconcile.  if unable, close socket.
				m_policy = m_sec_man->ReconcileSecurityPolicyAds( m_auth_info,
																  our_policy );

				if (!m_policy) {
					dprintf(D_ALWAYS, "DC_AUTHENTICATE: Unable to reconcile!\n");
					m_result = FALSE;
					return CommandProtocolFinished;
				} else {
					if (IsDebugVerbose(D_SECURITY)) {
						dprintf ( D_SECURITY, "DC_AUTHENTICATE: the_policy:\n" );
						m_policy->dPrint(D_SECURITY);
					}
				}

				// add our version to the policy to be sent over
				m_policy->Assign(ATTR_SEC_REMOTE_VERSION, CondorVersion());

				// handy policy vars
				SecMan::sec_feat_act will_authenticate      = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_AUTHENTICATION);

				if (m_sec_man->sec_lookup_feat_act(m_auth_info, ATTR_SEC_NEW_SESSION) == SecMan::SEC_FEAT_ACT_YES) {

					// generate a new session

					// generate a unique ID.
					MyString tmpStr;
					tmpStr.formatstr( "%s:%i:%i:%i", 
									get_local_hostname().Value(), daemonCore->mypid,
							 (int)time(0), ZZZ_always_increase() );
					assert (m_sid == NULL);
					m_sid = strdup(tmpStr.Value());

					if (will_authenticate == SecMan::SEC_FEAT_ACT_YES) {

						char *crypto_method = NULL;
						if (!m_policy->LookupString(ATTR_SEC_CRYPTO_METHODS, &crypto_method)) {
							dprintf ( D_ALWAYS, "DC_AUTHENTICATE: tried to enable encryption for request from %s, but we have none!\n", m_sock->peer_description() );
							m_result = FALSE;
							return CommandProtocolFinished;
						}

						unsigned char* rkey = Condor_Crypt_Base::randomKey(24);
						unsigned char  rbuf[24];
						if (rkey) {
							memcpy (rbuf, rkey, 24);
							// this was malloced in randomKey
							free (rkey);
						} else {
							memset (rbuf, 0, 24);
							dprintf ( D_ALWAYS, "DC_AUTHENTICATE: unable to generate key for request from %s - no crypto available!\n", m_sock->peer_description() );							
							free( crypto_method );
							crypto_method = NULL;
							m_result = FALSE;
							return CommandProtocolFinished;
						}

						switch (toupper(crypto_method[0])) {
							case 'B': // blowfish
								dprintf (D_SECURITY, "DC_AUTHENTICATE: generating BLOWFISH key for session %s...\n", m_sid);
								m_key = new KeyInfo(rbuf, 24, CONDOR_BLOWFISH);
								break;
							case '3': // 3des
							case 'T': // Tripledes
								dprintf (D_SECURITY, "DC_AUTHENTICATE: generating 3DES key for session %s...\n", m_sid);
								m_key = new KeyInfo(rbuf, 24, CONDOR_3DES);
								break;
							default:
								dprintf (D_SECURITY, "DC_AUTHENTICATE: generating RANDOM key for session %s...\n", m_sid);
								m_key = new KeyInfo(rbuf, 24);
								break;
						}

						free( crypto_method );
						crypto_method = NULL;

						if (!m_key) {
							m_result = FALSE;
							return CommandProtocolFinished;
						}

						m_sec_man->key_printf (D_SECURITY, m_key);
					}

					m_new_session = true;
				}

				// if they asked, tell them
				if (m_is_tcp && (m_sec_man->sec_lookup_feat_act(m_auth_info, ATTR_SEC_ENACT) == SecMan::SEC_FEAT_ACT_NO)) {
					if (IsDebugVerbose(D_SECURITY)) {
						dprintf (D_SECURITY, "SECMAN: Sending following response ClassAd:\n");
						m_policy->dPrint( D_SECURITY );
					}
					m_sock->encode();
					if (!m_policy->put(*m_sock) ||
						!m_sock->end_of_message()) {
						dprintf (D_ALWAYS, "SECMAN: Error sending response classad to %s!\n", m_sock->peer_description());
						m_auth_info.dPrint (D_ALWAYS);
						m_result = FALSE;
						return CommandProtocolFinished;
					}
					m_sock->decode();
				} else {
					dprintf( D_SECURITY, "SECMAN: Enact was '%s', not sending response.\n",
						SecMan::sec_feat_act_rev[m_sec_man->sec_lookup_feat_act(m_auth_info, ATTR_SEC_ENACT)] );
				}

			} // end of case: consider establishing a new security session

			if( !m_is_tcp ) {
					// For UDP, if encryption is not on by default,
					// configure it with the session key so that it
					// can be programmatically toggled on and off for
					// portions of the message (e.g. for secret stuff
					// like claimids).  If encryption _is_ on by
					// default, then it will have already been turned
					// on by now, because the UDP header contains the
					// encryption key in that case.

				SecMan::sec_feat_act will_enable_encryption = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_ENCRYPTION);

				if( will_enable_encryption != SecMan::SEC_FEAT_ACT_YES
					&& m_key )
				{
					m_sock->set_crypto_key(false, m_key);
					dprintf(D_SECURITY, "DC_AUTHENTICATE: encryption enabled with session key id %s (but encryption mode is off by default for this packet).\n", m_sid ? m_sid : "(null)");
				}
			}

			if (m_is_tcp) {

				// do what we decided

				// handy policy vars
				SecMan::sec_feat_act will_authenticate      = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_AUTHENTICATION);
				m_will_enable_encryption = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_ENCRYPTION);
				m_will_enable_integrity  = m_sec_man->sec_lookup_feat_act(*m_policy, ATTR_SEC_INTEGRITY);


				// protocol fix:
				//
				// up to and including 6.6.0, will_authenticate would be set to
				// true if we are resuming a session that was authenticated.
				// this is not necessary.
				//
				// so, as of 6.6.1, if we are resuming a session (as determined
				// by the expression (!m_new_session), AND the other side is
				// 6.6.1 or higher, we will force will_authenticate to
				// SEC_FEAT_ACT_NO.

				if ( will_authenticate == SecMan::SEC_FEAT_ACT_YES ) {
					if ((!m_new_session)) {
						char * remote_version = NULL;
						m_policy->LookupString(ATTR_SEC_REMOTE_VERSION, &remote_version);
						if(remote_version) {
							// this attribute was added in 6.6.1.  it's mere
							// presence means that the remote side is 6.6.1 or
							// higher, so no need to instantiate a CondorVersionInfo.
							dprintf( D_SECURITY, "SECMAN: other side is %s, NOT reauthenticating.\n", remote_version );
							will_authenticate = SecMan::SEC_FEAT_ACT_NO;

							free (remote_version);
						} else {
							dprintf( D_SECURITY, "SECMAN: other side is pre 6.6.1, reauthenticating.\n" );
						}
					} else {
						dprintf( D_SECURITY, "SECMAN: new session, doing initial authentication.\n" );
					}
				}



				if (m_is_tcp && (will_authenticate == SecMan::SEC_FEAT_ACT_YES)) {

					m_state = CommandProtocolAuthenticate;
					return CommandProtocolContinue;


				} else {
					if (IsDebugVerbose(D_SECURITY)) {
						dprintf (D_SECURITY, "DC_AUTHENTICATE: not authenticating.\n");
					}
					m_state = CommandProtocolPostAuthenticate;
					return CommandProtocolContinue;
				}
			} // end is_tcp
		} // end !using_cookie
	} // end DC_AUTHENTICATE
	m_state = CommandProtocolExecCommand;
	return CommandProtocolContinue;
}

DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::Authenticate()
{
	CondorError errstack;

	if( m_nonblocking && !m_sock->readReady() ) {
		return WaitForSocketData();
	}

	// we know the ..METHODS_LIST attribute exists since it was put
	// in by us.  pre 6.5.0 protocol does not put it in.
	char * auth_methods = NULL;
	m_policy->LookupString(ATTR_SEC_AUTHENTICATION_METHODS_LIST, &auth_methods);

	if (!auth_methods) {
		dprintf (D_SECURITY, "DC_AUTHENTICATE: no auth methods in response ad from %s, failing!\n", m_sock->peer_description());
		m_result = FALSE;
		return CommandProtocolFinished;
	}

	if (IsDebugVerbose(D_SECURITY)) {
		dprintf (D_SECURITY, "DC_AUTHENTICATE: authenticating RIGHT NOW.\n");
	}

	int cmd_index = 0;
	if( !daemonCore->CommandNumToTableIndex(m_auth_cmd,&cmd_index) ) {
		dprintf(D_ALWAYS, "DC_AUTHENTICATE: UNREGISTERED COMMAND %d in Authenticate()\n",m_auth_cmd);
		m_result = FALSE;
		free( auth_methods );
		return CommandProtocolFinished;
	}

	int auth_timeout = daemonCore->getSecMan()->getSecTimeout( m_comTable[cmd_index].perm );

	char *method_used = NULL;
	bool auth_success = m_sock->authenticate(m_key, auth_methods, &errstack, auth_timeout, &method_used);

	if ( method_used ) {
		m_policy->Assign(ATTR_SEC_AUTHENTICATION_METHODS, method_used);
	}

	free( auth_methods );
	free( method_used );
{'fraction_non_alphanumeric': 0.11624551001521496, 'fraction_numerical': 0.0029488808369802205, 'mean_word_length': 3.409905236217749, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 271, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3823411', 'n_tokens_mistral': 25645, 'n_tokens_neox': 21086, 'n_words': 5487}