1212#include < AK/MemoryStream.h>
1313#include < AK/Random.h>
1414#include < LibCore/Account.h>
15+ #include < LibCore/Command.h>
1516#include < LibCore/Socket.h>
1617#include < LibCrypto/Curves/Ed25519.h>
1718#include < LibCrypto/Curves/X25519.h>
@@ -23,7 +24,6 @@ namespace SSH::Server {
2324
2425ErrorOr<void > SSHClient::handle_data (ByteBuffer& data)
2526{
26-
2727 switch (m_state) {
2828 case State::Constructed:
2929 return handle_protocol_version (data);
@@ -40,7 +40,8 @@ ErrorOr<void> SSHClient::handle_data(ByteBuffer& data)
4040 case State::WaitingForUserAuthentication:
4141 return handle_user_authentication (TRY (unpack_generic_message (data)));
4242 case State::Authentified:
43- return Error::from_string_literal (" Draw the rest of the owl" );
43+ TRY (handle_generic_packet (TRY (unpack_generic_message (data))));
44+ return {};
4445 }
4546 VERIFY_NOT_REACHED ();
4647}
@@ -324,4 +325,162 @@ ErrorOr<void> SSHClient::send_user_authentication_success()
324325 return {};
325326}
326327
328+ ErrorOr<void > SSHClient::handle_generic_packet (GenericMessage&& message)
329+ {
330+ switch (message.type ) {
331+ case MessageID::CHANNEL_OPEN:
332+ return handle_channel_open_message (message);
333+ case MessageID::CHANNEL_REQUEST:
334+ return handle_channel_request (message);
335+ default :
336+ dbgln_if (SSH_DEBUG, " Unexpected packet: {}" , to_underlying (message.type ));
337+ return Error::from_string_literal (" Unexpected packet type" );
338+ }
339+ VERIFY_NOT_REACHED ();
340+ }
341+
342+ // 5.1. Opening a Channel
343+ // https://datatracker.ietf.org/doc/html/rfc4254#section-5.1
344+ ErrorOr<void > SSHClient::handle_channel_open_message (GenericMessage& message)
345+ {
346+ auto channel_type = TRY (decode_string (message.payload ));
347+ u32 sender_channel_id = TRY (message.payload .read_value <NetworkOrdered<u32 >>());
348+ u32 initial_window_size = TRY (message.payload .read_value <NetworkOrdered<u32 >>());
349+ u32 maximum_packet_size = TRY (message.payload .read_value <NetworkOrdered<u32 >>());
350+
351+ dbgln_if (SSH_DEBUG, " Channel open request with: {:s} - {} - {} - {}" ,
352+ channel_type.bytes (), sender_channel_id, initial_window_size, maximum_packet_size);
353+
354+ if (channel_type != " session" sv.bytes ())
355+ return Error::from_string_literal (" Unexpected channel type" );
356+
357+ m_sessions.empend (TRY (Session::create (sender_channel_id, initial_window_size, maximum_packet_size)));
358+
359+ TRY (send_channel_open_confirmation (m_sessions.last ()));
360+
361+ return {};
362+ }
363+
364+ // 5.1. Opening a Channel
365+ // https://datatracker.ietf.org/doc/html/rfc4254#section-5.1
366+ ErrorOr<void > SSHClient::send_channel_open_confirmation (Session const & session)
367+ {
368+ AllocatingMemoryStream stream;
369+
370+ // byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
371+ // uint32 recipient channel
372+ // uint32 sender channel
373+ // uint32 initial window size
374+ // uint32 maximum packet size
375+
376+ TRY (stream.write_value (MessageID::CHANNEL_OPEN_CONFIRMATION));
377+ // "The 'recipient channel' is the channel number given in the original
378+ // open request, and 'sender channel' is the channel number allocated by
379+ // the other side."
380+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.sender_channel_id ));
381+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.local_channel_id ));
382+
383+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.window .size ()));
384+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.maximum_packet_size ));
385+
386+ TRY (write_packet (TRY (stream.read_until_eof ())));
387+ return {};
388+ }
389+
390+ ErrorOr<Session*> SSHClient::find_session (u32 sender_channel_id)
391+ {
392+ for (auto & session : m_sessions) {
393+ if (session.sender_channel_id == sender_channel_id)
394+ return &session;
395+ }
396+ return Error::from_string_literal (" Session not found" );
397+ }
398+
399+ // 5.4. Channel-Specific Requests
400+ // https://datatracker.ietf.org/doc/html/rfc4254#section-5.4
401+ ErrorOr<void > SSHClient::handle_channel_request (GenericMessage& message)
402+ {
403+ auto recipient_channel_id = TRY (message.payload .read_value <NetworkOrdered<u32 >>());
404+ auto request_type = TRY (decode_string (message.payload ));
405+ auto want_reply = TRY (message.payload .read_value <bool >());
406+
407+ auto & session = *TRY (find_session (recipient_channel_id));
408+
409+ dbgln_if (SSH_DEBUG, " CHANNEL_REQUEST id({}): {:s}" , session.local_channel_id , request_type.bytes ());
410+
411+ if (request_type == " env" sv.bytes () && !want_reply) {
412+ dbgln (" FIXME: Ignored channel request: {:s}" , request_type.bytes ());
413+ return {};
414+ }
415+
416+ if (request_type == " exec" sv.bytes ()) {
417+ auto command = TRY (decode_string (message.payload ));
418+
419+ // FIXME: This is a naive implementation, we should stream the result back
420+ // to the user and not block the event loop during the execution of
421+ // the command.
422+ // We should also use the user's shell rather than hardcoding it.
423+
424+ #ifdef AK_OS_SERENITY
425+ auto shell = " /bin/Shell" sv;
426+ #else
427+ auto shell = " /bin/sh" sv;
428+ #endif
429+
430+ Vector<ByteString> args;
431+ args.append (shell);
432+ args.append (" -c" );
433+ args.append (ByteString (command.bytes ()));
434+
435+ Vector<char const *> raw_args;
436+ raw_args.ensure_capacity (args.size () + 1 );
437+ for (auto & arg : args)
438+ raw_args.append (arg.characters ());
439+
440+ raw_args.append (nullptr );
441+
442+ auto child = TRY (Core::Command::create (shell, raw_args.data ()));
443+ auto output = TRY (child->read_all ());
444+ auto status = TRY (child->status ());
445+
446+ if (status != Core::Command::ProcessResult::DoneWithZeroExitCode)
447+ return Error::from_string_literal (" Unable to run command" );
448+
449+ TRY (send_channel_success_message (session));
450+ TRY (send_channel_data (session, output.standard_output ));
451+ TRY (send_channel_close (session));
452+ return {};
453+ }
454+
455+ return Error::from_string_literal (" Unsupported channel request" );
456+ }
457+
458+ ErrorOr<void > SSHClient::send_channel_success_message (Session const & session)
459+ {
460+ AllocatingMemoryStream stream;
461+ TRY (stream.write_value (MessageID::CHANNEL_SUCCESS));
462+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.local_channel_id ));
463+ TRY (write_packet (TRY (stream.read_until_eof ())));
464+ return {};
465+ }
466+
467+ ErrorOr<void > SSHClient::send_channel_data (Session const & session, ByteBuffer const & data)
468+ {
469+ AllocatingMemoryStream stream;
470+ TRY (stream.write_value (MessageID::CHANNEL_DATA));
471+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.local_channel_id ));
472+ TRY (encode_string (stream, data));
473+ TRY (write_packet (TRY (stream.read_until_eof ())));
474+ return {};
475+ }
476+
477+ ErrorOr<void > SSHClient::send_channel_close (Session const & session)
478+ {
479+ AllocatingMemoryStream stream;
480+ TRY (stream.write_value (MessageID::CHANNEL_CLOSE));
481+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.local_channel_id ));
482+ TRY (write_packet (TRY (stream.read_until_eof ())));
483+ return {};
484+ }
485+
327486} // SSHServer
0 commit comments