209 行
9.5 KiB
C
209 行
9.5 KiB
C
/* ssh.h —— PazeSSH 公共 API
|
|
*
|
|
* 目标:仅依赖 libc + 内部 crypto,跨平台 SSH-2 客户端/服务器。
|
|
* 预算:SSH 全量 ≤ 25,000 行。
|
|
*
|
|
* 支持:
|
|
* - KEX: curve25519-sha256@libssh.org, ecdh-sha2-nistp256
|
|
* - Host keys: ssh-ed25519, ecdsa-sha2-nistp256, rsa-sha2-256
|
|
* - Encryption: chacha20-poly1305@openssh.com, aes128-gcm@openssh.com
|
|
* - MAC: hmac-sha2-256
|
|
* - Auth: publickey, password
|
|
* - Channels: session, shell, exec, pty-req
|
|
*/
|
|
#ifndef PAZE_SSH_H
|
|
#define PAZE_SSH_H
|
|
|
|
#include "paze/paze_types.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* Opaque SSH session */
|
|
typedef struct ssh_session ssh_session_t;
|
|
|
|
/* Factory: allocate and initialize a new SSH session.
|
|
* is_server: 0=client, 1=server */
|
|
ssh_session_t *ssh_session_new(int is_server);
|
|
void ssh_session_free(ssh_session_t *s);
|
|
|
|
/* Set custom I/O callbacks */
|
|
void ssh_session_set_io(ssh_session_t *s, void *ctx,
|
|
int (*read_fn)(void *, uint8_t *, size_t),
|
|
int (*write_fn)(void *, const uint8_t *, size_t));
|
|
/* Set peer IP(服务端 accept 后设置,用于认证失败日志的 fail2ban 兼容) */
|
|
void ssh_session_set_peer_ip(ssh_session_t *s, const char *ip);
|
|
|
|
/* Return the underlying socket handle for event loops:
|
|
* Windows: SOCKET, POSIX: int fd. -1 if none. */
|
|
long ssh_session_socket(ssh_session_t *s);
|
|
|
|
/* Return the I/O context set via ssh_session_set_io (or the socket pointer
|
|
* set by ssh_client_connect). For proxy-jump sessions the caller allocated
|
|
* this context and is responsible for freeing it. */
|
|
void *ssh_session_io_ctx(ssh_session_t *s);
|
|
|
|
/* StrictHostKeyChecking mode:
|
|
* mode: 0 = ask (default), 1 = accept-new, 2 = no (skip check) */
|
|
void ssh_session_set_hostkey_check(ssh_session_t *s, int mode);
|
|
|
|
/* -v: 打开详细日志 (on != 0) */
|
|
void ssh_session_set_verbose(ssh_session_t *s, int on);
|
|
/* -c: 指定加密算法,如 "chacha20-poly1305@openssh.com";cipher 为 NULL
|
|
* 或空串时恢复默认协商列表。 */
|
|
void ssh_session_set_cipher(ssh_session_t *s, const char *cipher);
|
|
|
|
/* ---- 用户库 (pssh sshd) ----
|
|
* 每个用户: 用户名 + SHA256(密码) + 权限级别。
|
|
* users.conf 行格式: 用户名:SHA256hex(64):权限
|
|
* 权限: 0=受限(仅通道回显), 1=普通(允许 shell/exec/pty-req),
|
|
* 2=管理员(全部, 含端口转发) */
|
|
#define PSSH_PERM_GUEST 0
|
|
#define PSSH_PERM_USER 1
|
|
#define PSSH_PERM_ADMIN 2
|
|
|
|
typedef struct {
|
|
char name[64];
|
|
uint8_t pwhash[32]; /* SHA256(password) 原始字节 */
|
|
int perm;
|
|
} ssh_user_t;
|
|
|
|
/* 解析 users.conf 文本,最多 max 个用户;返回用户数(负数=格式错误) */
|
|
int ssh_userdb_parse(const char *text, ssh_user_t *users, int max);
|
|
/* 校验密码:0=通过, -1=不通过 */
|
|
int ssh_userdb_check(const ssh_user_t *u, const char *password);
|
|
/* 服务端绑定用户库(认证用);users 由调用方持有,会话生命周期内须有效 */
|
|
void ssh_session_set_users(ssh_session_t *s, const ssh_user_t *users, int nusers);
|
|
|
|
/* Return the server host key blob received during KEX (for ssh-keyscan).
|
|
* Pointer is valid until the session is freed; returns NULL if unavailable. */
|
|
const uint8_t *ssh_session_server_key(const ssh_session_t *s, size_t *len);
|
|
|
|
/* ---- Client ---- */
|
|
int ssh_client_connect(ssh_session_t *s, const char *host, uint16_t port);
|
|
/* 在已通过 ssh_session_set_io 设置的传输上完成握手(代理跳转用) */
|
|
int ssh_client_connect_io(ssh_session_t *s, const char *host, uint16_t port);
|
|
|
|
/* 发送 keepalive(keepalive@openssh.com global request,fire-and-forget)。
|
|
* 用于保持空闲连接不被 NAT/防火墙回收;对端回复仅表明连接存活。 */
|
|
int ssh_keepalive_send(ssh_session_t *s);
|
|
|
|
/* Authenticate with password. Returns 0 on success. */
|
|
int ssh_auth_client_loop(ssh_session_t *s,
|
|
const char *username,
|
|
const char *password,
|
|
const uint8_t *privkey_blob, size_t privkey_blob_len);
|
|
|
|
struct ssh_agent; /* paze/ssh_agent.h */
|
|
|
|
/* Authenticate via an SSH agent (tries each identity, falls back to password). */
|
|
int ssh_auth_client_agent_loop(ssh_session_t *s,
|
|
const char *username,
|
|
const char *password,
|
|
struct ssh_agent *agent);
|
|
|
|
/* ---- Channels ---- */
|
|
int ssh_channel_open(ssh_session_t *s, uint32_t *local_id,
|
|
const char *type, uint32_t window, uint32_t max_pkt);
|
|
int ssh_channel_request_pty(ssh_session_t *s, uint32_t local_id,
|
|
const char *term, int cols, int rows);
|
|
int ssh_channel_request_shell(ssh_session_t *s, uint32_t local_id);
|
|
int ssh_channel_request_exec(ssh_session_t *s, uint32_t local_id,
|
|
const char *cmd);
|
|
int ssh_channel_send_data(ssh_session_t *s, uint32_t local_id,
|
|
const uint8_t *data, size_t len);
|
|
int ssh_channel_recv_data(ssh_session_t *s,
|
|
uint32_t *remote_id, uint8_t *data, size_t *len);
|
|
int ssh_channel_close(ssh_session_t *s, uint32_t local_id);
|
|
/* 发送 CHANNEL_EOF:通知对端本方不再发送数据(exec 收尾必需) */
|
|
int ssh_channel_send_eof(ssh_session_t *s, uint32_t local_id);
|
|
/* 读取通道退出状态;未收到 exit-status 返回 -1 */
|
|
int ssh_channel_exit_status(ssh_session_t *s, uint32_t local_id, int *status);
|
|
|
|
/* ---- TCP/IP 转发 (RFC 4254 §7) ---- */
|
|
/* 打开 direct-tcpip 通道(本地转发 -L / 动态转发 -D 用) */
|
|
int ssh_channel_open_direct(ssh_session_t *s, uint32_t *local_id,
|
|
const char *host, uint32_t port,
|
|
const char *origin, uint32_t oport);
|
|
/* 接收服务器主动打开的 forwarded-tcpip 通道(-R 用)。
|
|
* 非 CHANNEL_OPEN 时放回内部缓冲并返回 -2。 */
|
|
int ssh_channel_accept_forwarded(ssh_session_t *s, uint32_t *local_id,
|
|
char *host, size_t hlen, uint32_t *port);
|
|
/* 请求服务器开启远程转发 tcpip-forward(-R 用) */
|
|
int ssh_global_request_tcpip_forward(ssh_session_t *s, const char *bind,
|
|
uint32_t port, uint32_t *actual_port);
|
|
/* 事件循环:SSH socket 是否有完整包可读(1=有,0=无,-1=错误) */
|
|
int ssh_session_data_ready(ssh_session_t *s);
|
|
|
|
/* ---- SCP (传统 rcp 协议客户端, 与 OpenSSH scp -t/-f 兼容) ---- */
|
|
/* 上传本地文件/目录 (dest_name 不含 '/'); recursive 启用目录递归 */
|
|
int ssh_scp_upload(ssh_session_t *s, uint32_t ch, const char *local_path,
|
|
const char *dest_name, int recursive, int preserve);
|
|
/* 下载到本地目录; save_name 非 NULL 时作为单文件显式目标名 */
|
|
int ssh_scp_download(ssh_session_t *s, uint32_t ch, const char *local_dir,
|
|
const char *save_name, int preserve);
|
|
|
|
/* ---- SFTP 子系统客户端 (与 OpenSSH sftp-server / scp -s 兼容) ---- */
|
|
typedef struct ssh_sftp ssh_sftp_t;
|
|
|
|
/* open pflags (SFTP v3) */
|
|
#define SSH_FXF_READ 0x00000001
|
|
#define SSH_FXF_WRITE 0x00000002
|
|
#define SSH_FXF_APPEND 0x00000004
|
|
#define SSH_FXF_CREAT 0x00000008
|
|
#define SSH_FXF_TRUNC 0x00000010
|
|
#define SSH_FXF_EXCL 0x00000020
|
|
|
|
/* 打开 SFTP 会话:请求 subsystem "sftp" 并协商版本;成功返回 0 */
|
|
int ssh_sftp_start(ssh_sftp_t **out, ssh_session_t *s, uint32_t ch);
|
|
void ssh_sftp_free(ssh_sftp_t *sf);
|
|
|
|
/* 打开/关闭文件。handle 为服务端返回的不透明字节串 */
|
|
int ssh_sftp_open(ssh_sftp_t *sf, const char *path, uint32_t pflags,
|
|
uint8_t *handle, size_t *hlen);
|
|
int ssh_sftp_close(ssh_sftp_t *sf, const uint8_t *handle, size_t hlen);
|
|
|
|
/* 读写:返回实际传输字节数(0=EOF),-1=错误(见 *code) */
|
|
int ssh_sftp_read(ssh_sftp_t *sf, const uint8_t *handle, size_t hlen,
|
|
uint64_t off, uint8_t *buf, uint32_t len, int *code);
|
|
int ssh_sftp_write(ssh_sftp_t *sf, const uint8_t *handle, size_t hlen,
|
|
uint64_t off, const uint8_t *buf, uint32_t len, int *code);
|
|
|
|
/* 目录:opendir 后反复 readdir(1=有条目,0=目录结束) */
|
|
int ssh_sftp_opendir(ssh_sftp_t *sf, const char *path,
|
|
uint8_t *handle, size_t *hlen);
|
|
typedef struct {
|
|
char name[512];
|
|
uint32_t flags; /* attrs flags */
|
|
uint64_t size;
|
|
uint32_t perms; /* 高 4 位为文件类型(S_IFDIR 等) */
|
|
uint32_t atime, mtime;
|
|
} ssh_sftp_ent_t;
|
|
int ssh_sftp_readdir(ssh_sftp_t *sf, const uint8_t *handle, size_t hlen,
|
|
ssh_sftp_ent_t *ent);
|
|
|
|
/* 路径操作:stat/lstat(attrs 置位情况同 ssh_sftp_ent_t),realpath 输出绝对路径 */
|
|
int ssh_sftp_stat(ssh_sftp_t *sf, const char *path, ssh_sftp_ent_t *ent);
|
|
int ssh_sftp_lstat(ssh_sftp_t *sf, const char *path, ssh_sftp_ent_t *ent);
|
|
int ssh_sftp_realpath(ssh_sftp_t *sf, const char *path, char *out, size_t n);
|
|
int ssh_sftp_mkdir(ssh_sftp_t *sf, const char *path, uint32_t perms);
|
|
int ssh_sftp_rmdir(ssh_sftp_t *sf, const char *path);
|
|
int ssh_sftp_remove(ssh_sftp_t *sf, const char *path);
|
|
int ssh_sftp_rename(ssh_sftp_t *sf, const char *oldp, const char *newp);
|
|
int ssh_sftp_setstat(ssh_sftp_t *sf, const char *path,
|
|
uint32_t atime, uint32_t mtime);
|
|
int ssh_sftp_fsetstat(ssh_sftp_t *sf, const uint8_t *handle, size_t hlen,
|
|
uint32_t atime, uint32_t mtime);
|
|
|
|
/* ---- Server ---- */
|
|
/* 单连接服务端:版本/KEX/认证(password+publickey)后进入通道循环。
|
|
* authorized_keys 非空时启用 publickey 认证(OpenSSH 文本格式)。 */
|
|
int ssh_server_loop(ssh_session_t *s,
|
|
const uint8_t *authorized_keys, size_t ak_len);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif /* PAZE_SSH_H */
|