╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │1358 main_key: [32]u8, // K_2 │ │1359 header_key: [32]u8, // K_1 │ │1360 seq: u64, │ │1350 - idle: ?*IdleState = null, │ │1361 │ │1362 pub fn init(key_material: *const [64]u8, initial_seq: u64) Cipher {│ │1363 return .{ │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1446 const body_len = std.mem.readInt(u32, &length_plain, .big); │ │1447 if (body_len < 8 or body_len > max_packet_len or body_len % 8 != 0) return error.InvalidPacketLength;│ │1448 │ │1439 - const enc_body = try allocator.alloc(u8, body_len); │ │1440 - defer allocator.free(enc_body); │ │1441 - try reader.readSliceAll(enc_body); │ │1449 + // the body allocation becomes the returned payload, so a packet only │ │1450 + // ever costs one allocation │ │1451 + const body = try allocator.alloc(u8, body_len); │ │1452 + errdefer allocator.free(body); │ │1453 + try reader.readSliceAll(body); │ │1454 │ │1455 var tag_recv: [Poly1305.mac_length]u8 = undefined; │ │1456 try reader.readSliceAll(&tag_recv); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │1460 │ │1461 var poly = Poly1305.init(&poly_key); │ │1462 poly.update(&enc_length); │ │1451 - poly.update(enc_body); │ │1463 + poly.update(body); │ │1464 var tag_computed: [Poly1305.mac_length]u8 = undefined; │ │1465 poly.final(&tag_computed); │ │1466 if (!std.crypto.timing_safe.eql([Poly1305.mac_length]u8, tag_recv, tag_computed)) {│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1467 return error.MacVerificationFailed; │ │1468 } │ │1469 │ │1458 - // decrypt the body in place, then copy out the payload (caller owns) │ │1459 - ChaCha20.xor(enc_body, enc_body, 1, self.main_key, nonce); │ │1470 + // decrypt the body in place │ │1471 + ChaCha20.xor(body, body, 1, self.main_key, nonce); │ │1472 │ │1461 - const padding_len = enc_body[0]; │ │1473 + const padding_len = body[0]; │ │1474 if (padding_len < 4 or 1 + @as(u32, padding_len) > body_len) return error.InvalidPadding;│ │1475 const payload_len = body_len - 1 - @as(u32, padding_len); │ │1476 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │1465 - const payload = try allocator.dupe(u8, enc_body[1 .. 1 + payload_len]); │ │1477 + // shift the payload over the padding_length byte, then release the │ │1478 + // trailing padding (caller owns the result) │ │1479 + std.mem.copyForwards(u8, body[0..payload_len], body[1 .. 1 + payload_len]);│ │1480 + const payload = try allocator.realloc(body, payload_len); │ │1481 │ │1482 self.seq += 1; │ │1483 return payload; │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │1621 try conn.sc_cipher.writePacket(conn.io, conn.writer, buf.items); │ │1622 } │ │1623 │ │1609 -/// Append the canonical publickey-signed-data bytes (RFC 4252 §7) to `buf`.│ │1610 -/// Used by the server to recompute the signed input for verification │ │1624 +/// append the canonical publickey-signed-data bytes (RFC 4252 §7) to `buf`.│ │1625 +/// used by the server to recompute the signed input for verification │ │1626 pub fn appendPublickeySignedData( │ │1627 buf: *std.ArrayList(u8), │ │1628 allocator: std.mem.Allocator, │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1742 SSH_MSG_CHANNEL_REQUEST => { │ │1743 const ch = if (channel) |*c| c else continue; │ │1744 const start_request = try handleChannelRequest(conn, ch, packet, &pending_exec);│ │1730 - if (start_request) |req_kind| { │ │1731 - const request: Request = switch (req_kind) { │ │1732 - .shell => .{ .shell = ch.pty }, │ │1733 - .exec => .{ .exec = pending_exec.? }, │ │1734 - }; │ │1745 + if (start_request) |request| { │ │1746 var sess = SessionCtx{ │ │1747 .conn = conn, │ │1748 .channel = ch, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │1799 } │ │1800 } │ │1801 │ │1791 -/// Parse a CHANNEL_OPEN. On a session channel, send CHANNEL_OPEN_CONFIRMATION│ │1792 -/// and return the new Channel. On other types, send CHANNEL_OPEN_FAILURE and │ │1802 +/// parse a CHANNEL_OPEN. on a session channel, send CHANNEL_OPEN_CONFIRMATION│ │1803 +/// and return the new Channel. on other types, send CHANNEL_OPEN_FAILURE and │ │1804 /// return null. │ │1805 fn handleChannelOpen(conn: *Conn, packet: []const u8) !?Channel { │ │1806 const allocator = conn.allocator; │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1867 try conn.sc_cipher.writePacket(conn.io, conn.writer, reply.items); │ │1868 } │ │1869 │ │1859 -const RequestKind = enum { shell, exec }; │ │1860 - │ │1861 -/// Returns the kind of session request if this triggered one, else null. │ │1862 -fn handleChannelRequest(conn: *Conn, ch: *Channel, packet: []const u8, pending_exec: *?[]u8) !?RequestKind {│ │1870 +/// returns the session request if this one triggered it, else null. an exec │ │1871 +/// command is owned by `pending_exec`, which outlives the packet it arrived in. │ │1872 +fn handleChannelRequest(conn: *Conn, ch: *Channel, packet: []const u8, pending_exec: *?[]u8) !?Request { │ │1873 const allocator = conn.allocator; │ │1874 │ │1875 // byte SSH_MSG_CHANNEL_REQUEST │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │1911 │ │1912 if (std.mem.eql(u8, req_type, "shell")) { │ │1913 try replyChannelRequest(conn, ch, want_reply, true);│ │1904 - return .shell; │ │1914 + return .{ .shell = ch.pty }; │ │1915 } │ │1916 │ │1917 if (std.mem.eql(u8, req_type, "exec")) { │ ╰──────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯