文件
PazeSSH/apps/sshd/main.c
T

199 行
6.5 KiB
C

/* pssh sshd —— SSH2 服务端 (平替 sshd)
*
* 用户库: ~/.pssh/users.conf (可用 -u <file> 覆盖)
* 行格式: 用户名:SHA256hex(64):权限 (权限 0=受限 1=普通 2=管理员)
* 可用 pssh useradd 添加用户
*/
#include "paze/ssh.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <winsock2.h>
typedef SOCKET sock_t;
#define SOCK_INVALID INVALID_SOCKET
typedef int socklen_t;
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
typedef int sock_t;
#define SOCK_INVALID (-1)
#define closesocket close
typedef socklen_t socklen_t;
#endif
#define MAX_USERS 64
static int tcp_read(void *ctx, uint8_t *buf, size_t n) {
sock_t s = *(sock_t *)ctx;
return (int)recv(s, (char *)buf, (int)n, 0);
}
static int tcp_write(void *ctx, const uint8_t *buf, size_t n) {
sock_t s = *(sock_t *)ctx;
return (int)send(s, (char *)buf, (int)n, 0);
}
static sock_t tcp_listen(int port) {
#ifdef _WIN32
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
#endif
sock_t s = socket(AF_INET, SOCK_STREAM, 0);
if (s == SOCK_INVALID) return SOCK_INVALID;
int opt = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
closesocket(s); return SOCK_INVALID;
}
if (listen(s, 5) < 0) {
closesocket(s); return SOCK_INVALID;
}
return s;
}
/* 读取整个文件到堆缓冲(末尾补 NUL);失败返回 NULL */
static char *read_file(const char *path) {
FILE *fp = fopen(path, "rb");
if (!fp) return NULL;
long sz;
if (fseek(fp, 0, SEEK_END) != 0 || (sz = ftell(fp)) < 0 ||
fseek(fp, 0, SEEK_SET) != 0) {
fclose(fp);
return NULL;
}
char *buf = (char *)malloc((size_t)sz + 1);
if (!buf) { fclose(fp); return NULL; }
size_t rd = fread(buf, 1, (size_t)sz, fp);
fclose(fp);
buf[rd] = '\0';
return buf;
}
int pssh_cmd_sshd(int argc, char **argv) {
setvbuf(stderr, NULL, _IONBF, 0);
if (argc < 2) {
fprintf(stderr,
"用法: pssh sshd [-p <port>] [-u <users-file>] [-a <authorized_keys>]\n"
" -p <port> 监听端口 (默认 2222)\n"
" -u <file> 用户库文件 (默认 ~/.pssh/users.conf)\n"
" -a <file> authorized_keys 文件(启用公钥认证)\n"
" -v 详细日志(认证/通道调试)\n"
" (前台运行,Ctrl+C 退出)\n");
return 1;
}
int port = 2222;
int verbose = 0;
char users_path[1024] = "";
char ak_path[1024] = "";
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) port = atoi(argv[++i]);
else if (strcmp(argv[i], "-u") == 0 && i + 1 < argc) {
snprintf(users_path, sizeof(users_path), "%s", argv[++i]);
} else if (strcmp(argv[i], "-a") == 0 && i + 1 < argc) {
snprintf(ak_path, sizeof(ak_path), "%s", argv[++i]);
} else if (strcmp(argv[i], "-v") == 0) {
verbose = 1;
}
}
/* 默认用户库路径: ~/.pssh/users.conf */
if (users_path[0] == '\0') {
const char *home = getenv("USERPROFILE");
if (!home) home = getenv("HOME");
if (home) snprintf(users_path, sizeof(users_path), "%s/.pssh/users.conf", home);
else snprintf(users_path, sizeof(users_path), ".pssh/users.conf");
}
/* 加载用户库 */
static ssh_user_t users[MAX_USERS];
int nusers = 0;
char *text = read_file(users_path);
if (!text) {
fprintf(stderr,
"paze-sshd: 找不到用户库 %s\n"
" 请先创建用户, 例如: pssh useradd root -pwd <密码> -perm 2\n"
" 或指定其它文件: pssh sshd -u <file>\n",
users_path);
return 1;
}
nusers = ssh_userdb_parse(text, users, MAX_USERS);
free(text);
if (nusers < 0) {
fprintf(stderr, "paze-sshd: 用户库格式错误: %s\n", users_path);
return 1;
}
if (nusers == 0) {
fprintf(stderr, "paze-sshd: 用户库为空: %s\n", users_path);
return 1;
}
fprintf(stderr, "paze-sshd: 加载 %d 个用户 (库: %s)\n", nusers, users_path);
/* 加载 authorized_keys(可选,启用 publickey 认证) */
char *ak_text = NULL;
size_t ak_len = 0;
if (ak_path[0] != '\0') {
ak_text = read_file(ak_path);
if (!ak_text) {
fprintf(stderr, "paze-sshd: 找不到 authorized_keys %s\n", ak_path);
return 1;
}
ak_len = strlen(ak_text);
fprintf(stderr, "paze-sshd: 加载 authorized_keys %s (%zu 字节)\n",
ak_path, ak_len);
} else {
/* 默认尝试 ~/.ssh/authorized_keys */
const char *home = getenv("USERPROFILE");
if (!home) home = getenv("HOME");
char def_ak[1024];
if (home) snprintf(def_ak, sizeof(def_ak), "%s/.ssh/authorized_keys", home);
else snprintf(def_ak, sizeof(def_ak), ".ssh/authorized_keys");
ak_text = read_file(def_ak);
if (ak_text) {
ak_len = strlen(ak_text);
fprintf(stderr, "paze-sshd: 加载 authorized_keys %s (%zu 字节)\n",
def_ak, ak_len);
}
}
fprintf(stderr, "paze-sshd: listening on port %d\n", port);
sock_t listen_sock = tcp_listen(port);
if (listen_sock == SOCK_INVALID) {
fprintf(stderr, "paze-sshd: listen failed\n");
return 1;
}
for (;;) {
struct sockaddr_in client;
socklen_t clen = sizeof(client);
sock_t conn = accept(listen_sock, (struct sockaddr *)&client, &clen);
if (conn == SOCK_INVALID) continue;
fprintf(stderr, "paze-sshd: accepted connection\n");
sock_t *ps = (sock_t *)malloc(sizeof(sock_t));
*ps = conn;
ssh_session_t *s = ssh_session_new(1);
if (!s) { closesocket(conn); free(ps); continue; }
ssh_session_set_io(s, ps, tcp_read, tcp_write);
ssh_session_set_users(s, users, nusers);
if (verbose) ssh_session_set_verbose(s, 1);
ssh_server_loop(s, (const uint8_t *)ak_text, ak_len);
ssh_session_free(s);
closesocket(conn);
free(ps);
fprintf(stderr, "paze-sshd: connection closed\n");
}
closesocket(listen_sock);
return 0;
}