【问题标题】:SSL Socket in C cannot connect to some sitesC 中的 SSL 套接字无法连接到某些站点
【发布时间】:2017-06-26 17:32:28
【问题描述】:

我的代码部分失败,因为我可以建立到 www.hp.com 和 www.google.com 等位置的非安全连接,但是在将 SSL 添加到套接字时,我无法再连接到www.google.com,即使我仍然可以连接到 www.hp.com

对此,我进行了相当多的研究,并得出了不同的答案。我尝试测试不同的站点,结果参差不齐。所以我觉得我在这里遗漏了 SSL 握手的一个关键部分,这并不明显(至少对我来说)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <errno.h>

#define SW_SUCCESS  1
#define SW_ERROR    0

typedef struct socket_info
{
    int SocketHandle;
    unsigned long AddressLong;
    unsigned short Port;
    char Host[256];
    char Address[16];
    char Request[256];
    char Agent[128];
    char Headers[512];
    SSL *SecureHandle;
    SSL_CTX *SecureContext;
    const SSL_METHOD *SecureMethod;
    BIO *SecureCertificate;
} SOCKETINFO, *PSOCKETINFO, *LPSOCKETINFO;


int secinit()
{
    OpenSSL_add_all_algorithms();
    ERR_load_BIO_strings();
    ERR_load_crypto_strings();
    SSL_load_error_strings();
    return SW_SUCCESS;
}


int rawclose(struct socket_info *psi)
{
    return (!close(psi->SocketHandle) ? SW_SUCCESS : SW_ERROR);
}

int secclose(struct socket_info *psi)
{
    SSL_free(psi->SecureHandle);
    SSL_CTX_free(psi->SecureContext);
    return (rawclose(psi) ? SW_SUCCESS : SW_ERROR);
}


int rawconnect(struct socket_info *psi)
{
    struct hostent *host;
    struct sockaddr_in addr;

    if ( (host = gethostbyname(psi->Host)) == NULL ) return SW_ERROR;

    if ( (psi->SocketHandle = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 )
    {
        psi->SocketHandle = 0;
        return SW_ERROR;
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(psi->Port);
    addr.sin_addr.s_addr = *(long *)host->h_addr; 

    if ( connect(psi->SocketHandle, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1 )
    {
        psi->SocketHandle = 0;
        return SW_ERROR;
    }

    return SW_SUCCESS;
}



int secconnect(struct socket_info *psi)
{
    // Initialise certificate BIO.
    psi->SecureCertificate = BIO_new(BIO_s_file());
    if ( SSL_library_init() < 0 ) return SW_ERROR;

    // Initialise SSL method and deprecate SSLv2.
    psi->SecureMethod = SSLv23_client_method();
    if ( (psi->SecureContext = SSL_CTX_new(psi->SecureMethod)) == NULL ) return SW_ERROR;
    SSL_CTX_set_options(psi->SecureContext, SSL_OP_NO_SSLv2);
    psi->SecureHandle = SSL_new(psi->SecureContext);

    // Connect to host with raw socket.
    if ( !rawconnect(psi) )
    {
        SSL_free(psi->SecureHandle);
        SSL_CTX_free(psi->SecureContext);
        return SW_ERROR;
    }

    // Upgrade socket to SSL enabled.
    SSL_set_fd(psi->SecureHandle, psi->SocketHandle);
    if ( SSL_connect(psi->SecureHandle) != 1 )
    {
        SSL_free(psi->SecureHandle);
        close(psi->SocketHandle);
        SSL_CTX_free(psi->SecureContext);
        return SW_ERROR;
    }

    return SW_SUCCESS;
}


int main(int argc, char **argv)
{
    SOCKETINFO si;
    int ret;

    // SSLWrap module initialisation.
    if ( secinit() != SW_SUCCESS ) return SW_ERROR;

    memset(&si, 0, sizeof(si));
    strncpy(si.Host, "www.google.com", 10);
    si.Port = 80;
    if ( (ret = rawconnect(&si)) != SW_SUCCESS ) return SW_ERROR;
    if ( (ret = rawclose(&si)) != SW_SUCCESS ) return SW_ERROR;

    memset(&si, 0, sizeof(si));
    strncpy(si.Host, "www.google.com", 10);
    si.Port = 443;
    if ( (ret = secconnect(&si)) != SW_SUCCESS ) return SW_ERROR;
    if ( (ret = secclose(&si)) != SW_SUCCESS ) return SW_ERROR;

    return 0;
}

【问题讨论】:

  • 什么是失败?你有任何回应吗?
  • 检查errno
  • @DanaVaughne,你说你的代码在connect()ECONNREFUSED 中失败了。因此,这与 SSL 无关——它是在任何 SSL 握手启动之前发生的 TCP 级故障。这就是为什么我提出了错误端口的可能性。
  • @JohnBollinger 是正确的,根据您的错误报告,该问题与 SSL 无关。但是,google.com 肯定在端口 443 上运行。如果同一台机器上的 Web 浏览器可以正常连接到该站点,我只能想象它们通过使用不同的 IP 地址(可能是 IPv6?您的代码仅使用 IPv4)成功。顺便说一句,whois 与测试与站点的连接性无关,并且 ping 不会在 TCP 级别测试与特定端口的连接性。你可以试试openssl s_client -connect www.google.com:443
  • @DanaVaughne,如果您可以通过nc 连接但不能通过您自己的程序连接,那么您的程序正在做不同的事情。如果它没有尝试连接到不同的端口,那么唯一的其他重要变量就是地址。你是怎么选择的?

标签: c linux sockets openssl network-programming


【解决方案1】:

strncpy(si.Host, "www.google.com", 10); 10 太短了;只需在此处使用strcpy()

[也是一个强烈的建议:永远不要使用 strncpy() ,至少在你知道它的作用之前...]


why is strncpy() considered evil? 主要是因为它令人困惑,至少对于新手用户而言。怎么样?

使用strncpy(dst, src, siz);

  • 如果目标缓冲区 (strlen(src) &lt; siz) 中有足够的空间,则缓冲区的其余部分将被空字节填充
  • 如果没有足够的空间(strlen(src) &gt;= siz),不会超过sizbytes将写入*dst,但复制的字符串将为空- 终止。

第一种情况并不那么重要,几个周期被浪费在写入额外的空值上。如果您期望生成的字符串以空值结尾,则第二种情况可能是灾难性的。 (大多数人都这样做)这就是strncpy() 发出危险信号的原因。


如何避免strncpy()?有几种方法。最简单的一种:

len = strlen(src);
if (len >=siz) len=siz-1;
memcpy(dst, src, len);
dst[len]= 0;

【讨论】:

  • 答案真的应该交给 John,因为他建议将我的帖子更新为 MCVE 标准,这最初向我揭示了错误。谢谢你的目光。 :)
  • 我很抱歉约翰,那么。我刚刚阅读了这个问题,浏览了 cmets,他们似乎主要关注网络方面的内容。
  • "... 在这里使用 strcpy()... 强烈建议:永远不要使用 strncpy()..." - 糟糕的建议。 strcpy 造成的 40 或 50 年的安全漏洞还不够吗?如果用0 回填太难吃,他们可以寻找替代方法,比如更安全的字符串函数之一。另见Safe String Functions In Mac OS X and Linux
  • (你省略了:至少在你知道它的作用之前)对strncpy() 的虚假信任怎么样? ...货物崇拜大错特错...
猜你喜欢
  • 1970-01-01
  • 2016-11-25
  • 1970-01-01
  • 1970-01-01
  • 2015-08-21
  • 2017-05-10
  • 2013-09-18
  • 2018-01-03
  • 1970-01-01
相关资源
最近更新 更多