⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ne_openssl.c

📁 linux subdivision ying gai ke yi le ba
💻 C
📖 第 1 页 / 共 2 页
字号:
/*    neon SSL/TLS support using OpenSSL   Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk>   Portions are:   Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>   This library is free software; you can redistribute it and/or   modify it under the terms of the GNU Library General Public   License as published by the Free Software Foundation; either   version 2 of the License, or (at your option) any later version.      This library is distributed in the hope that it will be useful,   but WITHOUT ANY WARRANTY; without even the implied warranty of   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   Library General Public License for more details.   You should have received a copy of the GNU Library General Public   License along with this library; if not, write to the Free   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,   MA 02111-1307, USA*/#include "config.h"#include <sys/types.h>#ifdef HAVE_STRING_H#include <string.h>#endif#include <stdio.h>#include <openssl/ssl.h>#include <openssl/err.h>#include <openssl/pkcs12.h>#include <openssl/x509v3.h>#include <openssl/rand.h>#include "ne_ssl.h"#include "ne_string.h"#include "ne_session.h"#include "ne_i18n.h"#include "ne_private.h"#include "ne_privssl.h"/* OpenSSL 0.9.6 compatibility */#if OPENSSL_VERSION_NUMBER < 0x0090700fL#define PKCS12_unpack_authsafes M_PKCS12_unpack_authsafes#define PKCS12_unpack_p7data M_PKCS12_unpack_p7data/* cast away lack of const-ness */#define OBJ_cmp(a,b) OBJ_cmp((ASN1_OBJECT *)(a), (ASN1_OBJECT *)(b))#endifstruct ne_ssl_dname_s {    X509_NAME *dn;};struct ne_ssl_certificate_s {    ne_ssl_dname subj_dn, issuer_dn;    X509 *subject;    ne_ssl_certificate *issuer;    char *identity;};struct ne_ssl_client_cert_s {    PKCS12 *p12;    int decrypted; /* non-zero if successfully decrypted. */    ne_ssl_certificate cert;    EVP_PKEY *pkey;    char *friendly_name;};char *ne_ssl_readable_dname(const ne_ssl_dname *name){    int n, flag = 0;    ne_buffer *dump = ne_buffer_create();    const ASN1_OBJECT * const cname = OBJ_nid2obj(NID_commonName),	* const email = OBJ_nid2obj(NID_pkcs9_emailAddress);    for (n = X509_NAME_entry_count(name->dn); n > 0; n--) {	X509_NAME_ENTRY *ent = X509_NAME_get_entry(name->dn, n-1);	        /* Skip commonName or emailAddress except if there is no other         * attribute in dname. */	if ((OBJ_cmp(ent->object, cname) && OBJ_cmp(ent->object, email)) ||            (!flag && n == 1)) { 	    if (flag++)		ne_buffer_append(dump, ", ", 2);            switch (ent->value->type) {            case V_ASN1_UTF8STRING:            case V_ASN1_IA5STRING: /* definitely ASCII */            case V_ASN1_VISIBLESTRING: /* probably ASCII */            case V_ASN1_PRINTABLESTRING: /* subset of ASCII */                ne_buffer_append(dump, ent->value->data, ent->value->length);                break;            case V_ASN1_UNIVERSALSTRING:            case V_ASN1_T61STRING: /* let OpenSSL convert it as ISO-8859-1 */            case V_ASN1_BMPSTRING: {                unsigned char *tmp = ""; /* initialize to workaround 0.9.6 bug */                int len;                len = ASN1_STRING_to_UTF8(&tmp, ent->value);                if (len > 0) {                    ne_buffer_append(dump, tmp, len);                    OPENSSL_free(tmp);                    break;                } else {                    ERR_clear_error();                    /* and fall through */                }            }            default:                ne_buffer_zappend(dump, "???");                break;            }                	}    }    return ne_buffer_finish(dump);}int ne_ssl_dname_cmp(const ne_ssl_dname *dn1, const ne_ssl_dname *dn2){    return X509_NAME_cmp(dn1->dn, dn2->dn);}void ne_ssl_clicert_free(ne_ssl_client_cert *cc){    if (cc->p12)        PKCS12_free(cc->p12);    if (cc->decrypted) {        if (cc->cert.identity) ne_free(cc->cert.identity);        EVP_PKEY_free(cc->pkey);        X509_free(cc->cert.subject);    }    if (cc->friendly_name) ne_free(cc->friendly_name);    ne_free(cc);}/* Map a server cert verification into a string. */static void verify_err(ne_session *sess, int failures){    struct {	int bit;	const char *str;    } reasons[] = {	{ NE_SSL_NOTYETVALID, N_("certificate is not yet valid") },	{ NE_SSL_EXPIRED, N_("certificate has expired") },	{ NE_SSL_IDMISMATCH, N_("certificate issued for a different hostname") },	{ NE_SSL_UNTRUSTED, N_("issuer is not trusted") },	{ 0, NULL }    };    int n, flag = 0;    strcpy(sess->error, _("Server certificate verification failed: "));    for (n = 0; reasons[n].bit; n++) {	if (failures & reasons[n].bit) {	    if (flag) strncat(sess->error, ", ", sizeof sess->error);	    strncat(sess->error, _(reasons[n].str), sizeof sess->error);	    flag = 1;	}    }}/* Format an ASN1 time to a string. 'buf' must be at least of size * 'NE_SSL_VDATELEN'. */static void asn1time_to_string(ASN1_TIME *tm, char *buf){    BIO *bio;        strncpy(buf, _("[invalid date]"), NE_SSL_VDATELEN-1);        bio = BIO_new(BIO_s_mem());    if (bio) {	if (ASN1_TIME_print(bio, tm))	    BIO_read(bio, buf, NE_SSL_VDATELEN-1);	BIO_free(bio);    }}void ne_ssl_cert_validity(const ne_ssl_certificate *cert,                          char *from, char *until){    ASN1_TIME *notBefore = X509_get_notBefore(cert->subject);    ASN1_TIME *notAfter = X509_get_notAfter(cert->subject);        if (from) asn1time_to_string(notBefore, from);    if (until) asn1time_to_string(notAfter, until);}/* Return non-zero if hostname from certificate (cn) matches hostname * used for session (hostname).  (Wildcard matching is no longer * mandated by RFC3280, but certs are deployed which use wildcards) */static int match_hostname(char *cn, const char *hostname){    const char *dot;    NE_DEBUG(NE_DBG_SSL, "Match %s on %s...\n", cn, hostname);    dot = strchr(hostname, '.');    if (dot == NULL) {	char *pnt = strchr(cn, '.');	/* hostname is not fully-qualified; unqualify the cn. */	if (pnt != NULL) {	    *pnt = '\0';	}    }    else if (strncmp(cn, "*.", 2) == 0) {	hostname = dot + 1;	cn += 2;    }    return !strcasecmp(cn, hostname);}/* Check certificate identity.  Returns zero if identity matches; 1 if * identity does not match, or <0 if the certificate had no identity. * If 'identity' is non-NULL, store the malloc-allocated identity in * *identity.  If 'server' is non-NULL, it must be the network address * of the server in use, and identity must be NULL. */static int check_identity(const char *hostname, X509 *cert, char **identity,                          const ne_inet_addr *server){    STACK_OF(GENERAL_NAME) *names;    int match = 0, found = 0;        names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);    if (names) {	int n;        /* subjectAltName contains a sequence of GeneralNames */	for (n = 0; n < sk_GENERAL_NAME_num(names) && !match; n++) {	    GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, n);	                /* handle dNSName and iPAddress name extensions only. */	    if (nm->type == GEN_DNS) {		char *name = ne_strndup(nm->d.ia5->data, nm->d.ia5->length);                if (identity && !found) *identity = ne_strdup(name);		match = match_hostname(name, hostname);		ne_free(name);		found = 1;            } else if (nm->type == GEN_IPADD && server) {                /* compare IP address with server IP address. */                ne_inet_addr *ia;                if (nm->d.ip->length == 4)                    ia = ne_iaddr_make(ne_iaddr_ipv4, nm->d.ip->data);                else if (nm->d.ip->length == 16)                    ia = ne_iaddr_make(ne_iaddr_ipv6, nm->d.ip->data);                else                    ia = NULL;                /* ne_iaddr_make returns NULL if address type is unsupported */                if (ia != NULL) { /* address type was supported. */                    match = ne_iaddr_cmp(server, ia) == 0;                    found = 1;                    ne_iaddr_free(ia);                } else {                    NE_DEBUG(NE_DBG_SSL, "iPAddress name with unsupported "                             "address type (length %d), skipped.\n",                             nm->d.ip->length);                }            } /* TODO: handle uniformResourceIdentifier too */	}        /* free the whole stack. */        sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);    }        /* Check against the commonName if no DNS alt. names were found,     * as per RFC3280. */    if (!found) {	X509_NAME *subj = X509_get_subject_name(cert);	X509_NAME_ENTRY *entry;	ASN1_STRING *str;	int idx = -1, lastidx;	char *name;	/* find the most specific commonName attribute. */	do {	    lastidx = idx;	    idx = X509_NAME_get_index_by_NID(subj, NID_commonName, lastidx);	} while (idx >= 0);		if (lastidx < 0)	    return -1;	/* extract the string from the entry */	entry = X509_NAME_get_entry(subj, lastidx);	str = X509_NAME_ENTRY_get_data(entry);	name = ne_strndup(str->data, str->length);        if (identity) *identity = ne_strdup(name);	match = match_hostname(name, hostname);	ne_free(name);    }    NE_DEBUG(NE_DBG_SSL, "Identity match: %s\n", match ? "good" : "bad");    return match ? 0 : 1;}/* Populate an ne_ssl_certificate structure from an X509 object. */static ne_ssl_certificate *populate_cert(ne_ssl_certificate *cert, X509 *x5){    cert->subj_dn.dn = X509_get_subject_name(x5);    cert->issuer_dn.dn = X509_get_issuer_name(x5);    cert->issuer = NULL;    cert->subject = x5;    /* Retrieve the cert identity; pass a dummy hostname to match. */    cert->identity = NULL;    check_identity("", x5, &cert->identity, NULL);    return cert;}/* Return a linked list of certificate objects from an OpenSSL chain. */static ne_ssl_certificate *make_chain(STACK_OF(X509) *chain){    int n, count = sk_X509_num(chain);    ne_ssl_certificate *top = NULL, *current = NULL;        NE_DEBUG(NE_DBG_SSL, "Chain depth: %d\n", count);    for (n = 0; n < count; n++) {        ne_ssl_certificate *cert = ne_malloc(sizeof *cert);        populate_cert(cert, X509_dup(sk_X509_value(chain, n)));#if NE_DEBUGGING        if (ne_debug_mask & NE_DBG_SSL) {            fprintf(ne_debug_stream, "Cert #%d:\n", n);            X509_print_fp(ne_debug_stream, cert->subject);        }#endif        if (top == NULL) {            current = top = cert;        } else {            current->issuer = cert;            current = cert;        }    }    return top;}/* Verifies an SSL server certificate. */static int check_certificate(ne_session *sess, SSL *ssl, ne_ssl_certificate *chain){    X509 *cert = chain->subject;    ASN1_TIME *notBefore = X509_get_notBefore(cert);    ASN1_TIME *notAfter = X509_get_notAfter(cert);    int ret, failures = 0;    long result;    /* check expiry dates */    if (X509_cmp_current_time(notBefore) >= 0)	failures |= NE_SSL_NOTYETVALID;    else if (X509_cmp_current_time(notAfter) <= 0)	failures |= NE_SSL_EXPIRED;    /* Check certificate was issued to this server; pass network     * address of server if a proxy is not in use. */    ret = check_identity(sess->server.hostname, cert, NULL,                          sess->use_proxy ? NULL : sess->server.current);    if (ret < 0) {        ne_set_error(sess, _("Server certificate was missing commonName "                             "attribute in subject name"));        return NE_ERROR;    } else if (ret > 0) failures |= NE_SSL_IDMISMATCH;    /* get the result of the cert verification out of OpenSSL */    result = SSL_get_verify_result(ssl);    NE_DEBUG(NE_DBG_SSL, "Verify result: %ld = %s\n", result,	     X509_verify_cert_error_string(result));    switch (result) {    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:    case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:	/* TODO: and probably more result codes here... */	failures |= NE_SSL_UNTRUSTED;	break;	/* ignore these, since we've already noticed them: */    case X509_V_ERR_CERT_NOT_YET_VALID:    case X509_V_ERR_CERT_HAS_EXPIRED:        /* cert was trusted: */    case X509_V_OK:	break;    default:	/* TODO: tricky to handle the 30-odd failure cases OpenSSL	 * presents here (see x509_vfy.h), and present a useful API to	 * the application so it in turn can then present a meaningful	 * UI to the user.  The only thing to do really would be to	 * pass back the error string, but that's not localisable.  So	 * just fail the verification here - better safe than	 * sorry. */	ne_set_error(sess, _("Certificate verification error: %s"),		     X509_verify_cert_error_string(result));	return NE_ERROR;    }    if (failures == 0) {        /* verified OK! */        ret = NE_OK;    } else {        /* Set up the error string. */	verify_err(sess, failures);        ret = NE_ERROR;        /* Allow manual override */        if (sess->ssl_verify_fn &&             sess->ssl_verify_fn(sess->ssl_verify_ud, failures, chain) == 0)            ret = NE_OK;    }    return ret;}/* Duplicate a client certificate, which must be in the decrypted state. */static ne_ssl_client_cert *dup_client_cert(const ne_ssl_client_cert *cc){    ne_ssl_client_cert *newcc = ne_calloc(sizeof *newcc);        newcc->decrypted = 1;    newcc->pkey = cc->pkey;    if (cc->friendly_name)        newcc->friendly_name = ne_strdup(cc->friendly_name);    populate_cert(&newcc->cert, cc->cert.subject);    cc->cert.subject->references++;    cc->pkey->references++;    return newcc;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -