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}
@@ -326,4 +327,142 @@ ErrorOr<void> SSHClient::send_user_authentication_success()
326327 return {};
327328}
328329
330+ ErrorOr<void > SSHClient::handle_generic_packet (GenericMessage&& message)
331+ {
332+ switch (message.type ) {
333+ case MessageID::CHANNEL_OPEN:
334+ return handle_channel_open_message (message);
335+ case MessageID::CHANNEL_REQUEST:
336+ return handle_channel_request (message);
337+ default :
338+ dbgln_if (SSH_DEBUG, " Unexpected packet: {}" , to_underlying (message.type ));
339+ return Error::from_string_literal (" Unexpected packet type" );
340+ }
341+ VERIFY_NOT_REACHED ();
342+ }
343+
344+ // 5.1. Opening a Channel
345+ // https://datatracker.ietf.org/doc/html/rfc4254#section-5.1
346+ ErrorOr<void > SSHClient::handle_channel_open_message (GenericMessage& message)
347+ {
348+ auto channel_type = TRY (decode_string (message.payload ));
349+ u32 sender_channel_id = TRY (message.payload .read_value <NetworkOrdered<u32 >>());
350+ u32 initial_window_size = TRY (message.payload .read_value <NetworkOrdered<u32 >>());
351+ u32 maximum_packet_size = TRY (message.payload .read_value <NetworkOrdered<u32 >>());
352+
353+ dbgln_if (SSH_DEBUG, " Channel open request with: {:s} - {} - {} - {}" ,
354+ channel_type.bytes (), sender_channel_id, initial_window_size, maximum_packet_size);
355+
356+ if (channel_type != " session" sv.bytes ())
357+ return Error::from_string_literal (" Unexpected channel type" );
358+
359+ m_sessions.empend (TRY (Session::create (sender_channel_id, initial_window_size, maximum_packet_size)));
360+
361+ TRY (send_channel_open_confirmation (m_sessions.last ()));
362+
363+ return {};
364+ }
365+
366+ // 5.1. Opening a Channel
367+ // https://datatracker.ietf.org/doc/html/rfc4254#section-5.1
368+ ErrorOr<void > SSHClient::send_channel_open_confirmation (Session const & session)
369+ {
370+ AllocatingMemoryStream stream;
371+
372+ // byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
373+ // uint32 recipient channel
374+ // uint32 sender channel
375+ // uint32 initial window size
376+ // uint32 maximum packet size
377+
378+ TRY (stream.write_value (MessageID::CHANNEL_OPEN_CONFIRMATION));
379+ // "The 'recipient channel' is the channel number given in the original
380+ // open request, and 'sender channel' is the channel number allocated by
381+ // the other side."
382+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.sender_channel_id ));
383+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.local_channel_id ));
384+
385+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.window .size ()));
386+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.maximum_packet_size ));
387+
388+ TRY (write_packet (TRY (stream.read_until_eof ())));
389+ return {};
390+ }
391+
392+ ErrorOr<Session*> SSHClient::find_session (u32 sender_channel_id)
393+ {
394+ for (auto & session : m_sessions) {
395+ if (session.sender_channel_id == sender_channel_id)
396+ return &session;
397+ }
398+ return Error::from_string_literal (" Session not found" );
399+ }
400+
401+ // 5.4. Channel-Specific Requests
402+ // https://datatracker.ietf.org/doc/html/rfc4254#section-5.4
403+ ErrorOr<void > SSHClient::handle_channel_request (GenericMessage& message)
404+ {
405+ auto recipient_channel_id = TRY (message.payload .read_value <NetworkOrdered<u32 >>());
406+ auto request_type = TRY (decode_string (message.payload ));
407+ auto want_reply = TRY (message.payload .read_value <bool >());
408+
409+ auto & session = *TRY (find_session (recipient_channel_id));
410+
411+ dbgln_if (SSH_DEBUG, " CHANNEL_REQUEST id({}): {:s}" , session.local_channel_id , request_type.bytes ());
412+
413+ if (request_type == " env" sv.bytes () && !want_reply) {
414+ dbgln (" FIXME: Ignored channel request: {:s}" , request_type.bytes ());
415+ return {};
416+ }
417+
418+ if (request_type == " exec" sv.bytes ()) {
419+ auto command = TRY (decode_string (message.payload ));
420+ Vector<ByteString> args;
421+ args.append (" -c" );
422+ args.append (ByteString (command.bytes ()));
423+
424+ #ifdef AK_OS_SERENITY
425+ auto shell = " /bin/Shell" sv;
426+ #else
427+ auto shell = " /bin/sh" sv;
428+ #endif
429+ auto child = TRY (Core::command (shell, args, {}));
430+
431+ TRY (send_channel_success_message (session));
432+ TRY (send_channel_data (session, child.output ));
433+ TRY (send_channel_close (session));
434+ return {};
435+ }
436+
437+ return Error::from_string_literal (" Unsupported channel request" );
438+ }
439+
440+ ErrorOr<void > SSHClient::send_channel_success_message (Session const & session)
441+ {
442+ AllocatingMemoryStream stream;
443+ TRY (stream.write_value (MessageID::CHANNEL_SUCCESS));
444+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.local_channel_id ));
445+ TRY (write_packet (TRY (stream.read_until_eof ())));
446+ return {};
447+ }
448+
449+ ErrorOr<void > SSHClient::send_channel_data (Session const & session, ByteBuffer const & data)
450+ {
451+ AllocatingMemoryStream stream;
452+ TRY (stream.write_value (MessageID::CHANNEL_DATA));
453+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.local_channel_id ));
454+ TRY (encode_string (stream, data));
455+ TRY (write_packet (TRY (stream.read_until_eof ())));
456+ return {};
457+ }
458+
459+ ErrorOr<void > SSHClient::send_channel_close (Session const & session)
460+ {
461+ AllocatingMemoryStream stream;
462+ TRY (stream.write_value (MessageID::CHANNEL_CLOSE));
463+ TRY (stream.write_value <NetworkOrdered<u32 >>(session.local_channel_id ));
464+ TRY (write_packet (TRY (stream.read_until_eof ())));
465+ return {};
466+ }
467+
329468} // SSHServer
0 commit comments