libssh
1.SSH概念
ssh(secure shell),安全外壳协议,由IETF的网络小组所制定。ssh为建立在应用层基础上的安全协议。SSH是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用SSH协议可以有效防止远程管理过程中的信息泄露问题。SSH最初是UNIX系统上的一个程序,后来迅速扩展到其他操作平台。SSH在正确使用时可以弥补网络中的漏洞。
1.1 ssh功能
传统的网络服务程序,如ftp、pop和telnet在本质上都是不安全的,因为它们在网络上使用明文传送口令和数据。而且,这些服务程序的安全验证方式也是有其弱点的,极易受到中间人攻击。通过使用SSH,可以把所有传输的数据进行加密,这样中间人攻击方式就不可能实现了,而且也能防止DNS欺骗和IP欺骗。使用SSH,还有一个额外的好处是传输的数据是经过压缩的,所以可以加快传输的速度。SSH有很多功能,它既可以代替TELNET,又可以为ftp、pop甚至为ppp提供一个安全的通道。
1.2 ssh验证
从客户端来看,ssh提供两种级别的安全验证。
第一种级别(基于口令的安全验证)
只要你知道自己账号和口令,就可以登录到远程主机。所有传输的数据都会被加密,但是不能保证你正在连接的服务器是你想连接的服务器。可能会有别的服务器在冒充真正的服务器,也就是受到“中间人攻击”。
第二种级别(基于密钥的安全验证)
需要依靠密钥,也就是你必须为自己创建一对密钥,并把公用密钥放在需要访问的服务器上。如果你想要连接到SSH服务器上,客户端软件就会向服务器发出请求,请求用你的密钥进行安全验证。服务器收到请求之后,先到该服务器上你的主目录下寻找你的公用密钥,然后把它和你发送过来的公用密钥进行比较。如果两个密钥一致,服务器就用公钥密钥加密“质询(challenge)”并把它发送给客户端软件。客户端软件收到“质询”之后就可以用你的私人密钥解密再把它发送给服务器。
用这种方式,你必须知道自己密钥口令,但是,与第一种级别相比,第二种级别不需要再网络上传送口令。
第二种级别不仅加密所有传输的数据,而且中间人攻击不能实现,因为他没有你的私人密钥。但是整个登陆过程可能需要10秒。
1.3 ssh结构
ssh由客户端和服务端软件组成,有两个不兼容的版本分别是:1.x和2.x。用ssh2.x的客户程序不能连接到ssh1.x的服务程序上去。openssh2.x同时支持ssh1.x和2.x。
服务端是一个守护进程,它在后台运行并响应来自客户端的连接请求。服务端一般是sshd进程,提供了对远程连接的处理,一般包括公共密钥认证、密钥交换、对称密钥加密和非安全连接。
客户端包含ssh程序以及像scp(远程拷贝)、slogin(远程登录)、sftp(安全文件传输)等其他的应用程序。
他们的工作机制大致是本地的客户端发送一个连接请求到远程的服务端,服务端检查申请的包和IP地址再发送密钥给SSH的客户端,本地再将密钥发回给服务端,自此连接建立。SSH1.x和SSH2.x在连接协议上有一些差异。
一旦建立一个安全传输层连接,客户机就发送一个服务请求。当用户认证完成之后,会发送第二个服务请求。这样就允许新定义的协议可以与上述协议共存。
启动SSH服务器后,sshd运行起来并在默认的22端口进行监听,当请求到来的时候SSH守护进程会产生一个子进程,该子进程进行这次的连接处理。
1.4 扩展
SSH协议框架中设计了大量可扩展的溶于能力,比如用户自定义算法、客户自定义密钥规则、高层扩展功能性应用协议。这些扩展大多遵循IANA的有关规定,特别是在重要的部分,像命名规则和消息编码方面。
SSH采用面向连接的TCP协议传输,应用22端口,安全系数较高。
2. libssh2
libssh2是一个用C库实现了SSH2协议的客户端。
2.1 libssh2特性
libssh2提供用于开发基于SSH的应用的API。一些特性如下:
- 密钥交换方式:diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, diffie-hellman-group-exchange-sha1, diffie-hellman-group-exchange-sha256;
- 主机密钥类型:ssh-rsa,ssh-dss;
- 加密器:aes256-ctr, aes192-ctr, aes128-ctr, aes256-cbc (rijndael-cbc@lysator.liu.se), aes192-cbc, aes128-cbc, 3des-cbc, blowfish-cbc, cast128-cbc, arcfour, arcfour128, none;
- 压缩方式:zlib, zlib@openssh.com, none;
- MAC 哈希:hmac-sha2-256, hmac-sha2-512, hmac-sha1, hmac-sha1-96, hmac-md5, hmac-md5-96, hmac-ripemd160 (hmac-ripemd160@openssh.com), none;
- 认证方式:none, password, public-key, hostbased, keyboard-interactive;
- 通道:shell, exec (incl. SCP wrapper), direct-tcpip, subsystem;
- 全局请求:tcpid-forward;
- 通道请求:x11, pty, exit-signal, keepalive@openssh.com;
- 子系统:sftp(version 3), publickey(version 2);
- SFTP:statvfs@openssh.com, fstatvfs@openssh.com;
- 线程安全:只是不要同时共享句柄;
- 非阻塞:可以使用阻塞和非阻塞两种;
- sockets:app在socket上调用,调用select()等;
- 加密后端:OpenSSL、libgcrypt,mbedTLS
2.2 libssh2版本
libssh2 1.8.0
改变:
- 添加了一个基础的dockerised测试套件;
- crypto:添加了mbedTLS的后端支持。
bug修复:
- libgcrypt:修复了OOM上的一个NULL指针解除引用;
- VMS:不能将%zd用于off_t格式;
- VMS:更新了 vms/libssh2_config.h;
- windows:与crypt32.lib链接;
- libssh2_channel_open:在通道错误消息中修复了speeling错误;
- msvc:修复了14个编译警告;
- tests:HAVE_NETINET_IN_H未正确定义;
- openssl:添加OpenSSL 1.1.0兼容性;
- cmake:添加CLEAR_MEMORY选项,类似于autoconf的选项;
- configure:使用 --with-*选项覆盖OpenSSL的默认值;
- libssh2_wait_socket:设置err_msg错误;
- libssh2_wait_socket:修复用毫秒与api_timeout的比较。
2.3 libssh2应用例子
工作思路是为session以一种工作模式(加密、压缩和MAC层)启动一个libssh2例程,然后在经过身份认证后接着进行文件传输或其他服务,在完成服务后,关闭session和socket,最后退出libssh2。
2.3.1 在sftp的应用
unsigned long hostaddr; int sock, i, auth_pw = 0; struct sockaddr_in sin; const char *fingerprint; char *userauthlist; LIBSSH2_SESSION * session; int rc; LIBSSH2_SFTP *sftp_session; LIBSSH2_HANDLE *sftp_handle;
步骤:
(1)创建socket并建立连接;
(2)创建一个会话实例;
/* Create a session instance */ session = libssh2_session_init(); if(!session) return -1;
(3)如果设置非阻塞模式,通知libssh2我们正在阻塞
libssh2_session_set_blocking(session, 1);
(4)启动它。操作包括:交易欢迎横幅、交换密钥并设置加密、压缩和MAC层。
rc = libssh2_session_handshake(session, sock); if(rc) { fprintf(stderr, "Failure establishing SSH session: %d\n", rc); return -1; }
(5)此时尚未通过身份验证。要做的第一件事是检查主机密钥对我们已知的主机的指纹(您的应用程序可能已经硬编码,可能会转到文件,可能会将其呈现给用户,这是你的通话)。
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
(6)检查可用的认证方法。
userauthlist = libssh2_userauth_list(session, username, strlen(username)); if(strstr(userauthlist, "password") != NULL) { auth_pw |= 1; } if(strstr(userauthlist, "keyboard-interactive") != NULL) { auth_pw |= 2; } if(strstr(userauthlist, "publickey") != NULL) { auth_pw |= 4; }
(7)根据用户参数-i、-p、-k选择认证方式
if (auth_pw & 1) { /* We could authenticate via password */ if (libssh2_userauth_password(session, username, password)) { fprintf(stderr, "Authentication by password failed.\n"); goto shutdown; } } else if (auth_pw & 2) { /* Or via keyboard-interactive */ if (libssh2_userauth_keyboard_interactive(session, username, &kbd_callback) ) { fprintf(stderr, "\tAuthentication by keyboard-interactive failed!\n"); goto shutdown; } else { fprintf(stderr, "\tAuthentication by keyboard-interactive succeeded.\n"); } } else if (auth_pw & 4) { /* Or by public key */ if (libssh2_userauth_publickey_fromfile(session, username, keyfile1, keyfile2, password)) { fprintf(stderr, "\tAuthentication by public key failed!\n"); goto shutdown; } else { fprintf(stderr, "\tAuthentication by public key succeeded.\n"); } } else { fprintf(stderr, "No supported authentication methods found!\n"); goto shutdown; }
(8)通过认证后 ,重启一个会话
sftp_session = libssh2_sftp_init(session);
if (!sftp_session) { fprintf(stderr, "Unable to init SFTP session\n"); goto shutdown; }
(9)通过SFTP请求一个文件
sftp_handle = libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);
...
do
{
char mem[1024];
rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));
if(rc >0)
{
write(1, mem, rc);
}
else
{
break;
}
}while(1);
(10)关闭sftp句柄和sftp会话
libssh2_sftp_close(sftp_handle);
libssh2_sftp_shutdown(sftp_session);
(11)shutdown:断开会话连接并释放会话
libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); libssh2_session_free(session);
(12)关闭sock并退出libssh2
close(sock);
libssh2_exit();