| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- package ssh
- import (
- "crypto/rand"
- "crypto/rsa"
- "encoding/binary"
- "golang.org/x/crypto/ssh"
- )
- func generateSigner() (ssh.Signer, error) {
- key, err := rsa.GenerateKey(rand.Reader, 2048)
- if err != nil {
- return nil, err
- }
- return ssh.NewSignerFromKey(key)
- }
- func parsePtyRequest(payload []byte) (pty Pty, ok bool) {
- // See https://datatracker.ietf.org/doc/html/rfc4254#section-6.2
- // 6.2. Requesting a Pseudo-Terminal
- // A pseudo-terminal can be allocated for the session by sending the
- // following message.
- // byte SSH_MSG_CHANNEL_REQUEST
- // uint32 recipient channel
- // string "pty-req"
- // boolean want_reply
- // string TERM environment variable value (e.g., vt100)
- // uint32 terminal width, characters (e.g., 80)
- // uint32 terminal height, rows (e.g., 24)
- // uint32 terminal width, pixels (e.g., 640)
- // uint32 terminal height, pixels (e.g., 480)
- // string encoded terminal modes
- // The payload starts from the TERM variable.
- term, rem, ok := parseString(payload)
- if !ok {
- return
- }
- win, rem, ok := parseWindow(rem)
- if !ok {
- return
- }
- modes, ok := parseTerminalModes(rem)
- if !ok {
- return
- }
- pty = Pty{
- Term: term,
- Window: win,
- Modes: modes,
- }
- return
- }
- func parseTerminalModes(in []byte) (modes ssh.TerminalModes, ok bool) {
- // See https://datatracker.ietf.org/doc/html/rfc4254#section-8
- // 8. Encoding of Terminal Modes
- //
- // All 'encoded terminal modes' (as passed in a pty request) are encoded
- // into a byte stream. It is intended that the coding be portable
- // across different environments. The stream consists of opcode-
- // argument pairs wherein the opcode is a byte value. Opcodes 1 to 159
- // have a single uint32 argument. Opcodes 160 to 255 are not yet
- // defined, and cause parsing to stop (they should only be used after
- // any other data). The stream is terminated by opcode TTY_OP_END
- // (0x00).
- //
- // The client SHOULD put any modes it knows about in the stream, and the
- // server MAY ignore any modes it does not know about. This allows some
- // degree of machine-independence, at least between systems that use a
- // POSIX-like tty interface. The protocol can support other systems as
- // well, but the client may need to fill reasonable values for a number
- // of parameters so the server pty gets set to a reasonable mode (the
- // server leaves all unspecified mode bits in their default values, and
- // only some combinations make sense).
- _, rem, ok := parseUint32(in)
- if !ok {
- return
- }
- const ttyOpEnd = 0
- for len(rem) > 0 {
- if modes == nil {
- modes = make(ssh.TerminalModes)
- }
- code := uint8(rem[0])
- rem = rem[1:]
- if code == ttyOpEnd || code > 160 {
- break
- }
- var val uint32
- val, rem, ok = parseUint32(rem)
- if !ok {
- return
- }
- modes[code] = val
- }
- ok = true
- return
- }
- func parseWindow(s []byte) (win Window, rem []byte, ok bool) {
- // See https://datatracker.ietf.org/doc/html/rfc4254#section-6.7
- // 6.7. Window Dimension Change Message
- // When the window (terminal) size changes on the client side, it MAY
- // send a message to the other side to inform it of the new dimensions.
- // byte SSH_MSG_CHANNEL_REQUEST
- // uint32 recipient channel
- // string "window-change"
- // boolean FALSE
- // uint32 terminal width, columns
- // uint32 terminal height, rows
- // uint32 terminal width, pixels
- // uint32 terminal height, pixels
- wCols, rem, ok := parseUint32(s)
- if !ok {
- return
- }
- hRows, rem, ok := parseUint32(rem)
- if !ok {
- return
- }
- wPixels, rem, ok := parseUint32(rem)
- if !ok {
- return
- }
- hPixels, rem, ok := parseUint32(rem)
- if !ok {
- return
- }
- win = Window{
- Width: int(wCols),
- Height: int(hRows),
- WidthPixels: int(wPixels),
- HeightPixels: int(hPixels),
- }
- return
- }
- func parseString(in []byte) (out string, rem []byte, ok bool) {
- length, rem, ok := parseUint32(in)
- if uint32(len(rem)) < length || !ok {
- ok = false
- return
- }
- out, rem = string(rem[:length]), rem[length:]
- ok = true
- return
- }
- func parseUint32(in []byte) (uint32, []byte, bool) {
- if len(in) < 4 {
- return 0, nil, false
- }
- return binary.BigEndian.Uint32(in), in[4:], true
- }
|