/*$Id: tlsops.c 1282 2006-11-28 15:49:10Z miconda $
*
* Copyright (C) 2006 nic.at
* Copyright (C) 2001-2003 FhG Fokus
*
* This file is part of openser, a free SIP server.
*
* openser is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* openser 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* History:
* -------
* 2006-01-26 initial version
*
* tls module, it implements the following commands:
* is_peer_verified(): returns 1 if the message is received via TLS
* and the peer was verified during TLS connection handshake,
* otherwise it returns -1
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/ssl.h>
#include "tlsops.h"
#include "tls_select.h"
#include "../../tcp_conn.h" /* struct tcp_connection */
#include "../../tcp_server.h" /* tcpconn_get() */
#include "../../sr_module.h"
#include "../../items.h"
MODULE_VERSION
int tcp_con_lifetime=DEFAULT_TCP_CONNECTION_LIFETIME;
/* definition of exported functions */
static int is_peer_verified(struct sip_msg*, char*, char*);
/* definition of internal functions */
static int mod_init(void);
static void mod_destroy(void);
/*
* Module parameter variables
*/
/*
* Exported functions
*/
static cmd_export_t cmds[]={
{"is_peer_verified", is_peer_verified, 0, 0,
REQUEST_ROUTE},
{0,0,0,0,0}
};
/*
* Exported parameters
*/
static param_export_t params[] = {
{0,0,0}
};
/*
* pseudo variables
*/
static item_export_t mod_items[] = {
/* TLS session parameters */
{"tls_version", tlsops_version, 0,
{{0, 0}, 0} },
{"tls_description", tlsops_desc, 0,
{{0, 0}, 0} },
{"tls_cipher_info", tlsops_cipher, 0,
{{0, 0}, 0} },
{"tls_cipher_bits", tlsops_bits, 0,
{{0, 0}, 0} },
/* general certificate parameters for peer and local */
{"tls_peer_version", tlsops_cert_version, 0,
{{0, 0}, CERT_PEER} },
{"tls_my_version", tlsops_cert_version, 0,
{{0, 0}, CERT_LOCAL} },
{"tls_peer_serial", tlsops_sn, 0,
{{0, 0}, CERT_PEER} },
{"tls_my_serial", tlsops_sn, 0,
{{0, 0}, CERT_LOCAL} },
/* certificate parameters for peer and local, for subject and issuer*/
{"tls_peer_subject", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_SUBJECT} },
{"tls_peer_issuer", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_ISSUER} },
{"tls_my_subject", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_SUBJECT} },
{"tls_my_issuer", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_ISSUER} },
{"tls_peer_subject_cn", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_SUBJECT | COMP_CN} },
{"tls_peer_issuer_cn", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_ISSUER | COMP_CN} },
{"tls_my_subject_cn", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_SUBJECT | COMP_CN} },
{"tls_my_issuer_cn", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_ISSUER | COMP_CN} },
{"tls_peer_subject_locality", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_SUBJECT | COMP_L} },
{"tls_peer_issuer_locality", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_ISSUER | COMP_L} },
{"tls_my_subject_locality", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_SUBJECT | COMP_L} },
{"tls_my_issuer_locality", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_ISSUER | COMP_L} },
{"tls_peer_subject_country", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_SUBJECT | COMP_C} },
{"tls_peer_issuer_country", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_ISSUER | COMP_C} },
{"tls_my_subject_country", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_SUBJECT | COMP_C} },
{"tls_my_issuer_country", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_ISSUER | COMP_C} },
{"tls_peer_subject_state", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_SUBJECT | COMP_ST} },
{"tls_peer_issuer_state", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_ISSUER | COMP_ST} },
{"tls_my_subject_state", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_SUBJECT | COMP_ST} },
{"tls_my_issuer_state", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_ISSUER | COMP_ST} },
{"tls_peer_subject_organization", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_SUBJECT | COMP_O} },
{"tls_peer_issuer_organization", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_ISSUER | COMP_O} },
{"tls_my_subject_organization", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_SUBJECT | COMP_O} },
{"tls_my_issuer_organization", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_ISSUER | COMP_O} },
{"tls_peer_subject_unit", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_SUBJECT | COMP_OU} },
{"tls_peer_issuer_unit", tlsops_comp, 0,
{{0, 0}, CERT_PEER | CERT_ISSUER | COMP_OU} },
{"tls_my_subject_unit", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_SUBJECT | COMP_OU} },
{"tls_my_issuer_unit", tlsops_comp, 0,
{{0, 0}, CERT_LOCAL | CERT_ISSUER | COMP_OU} },
/* subject alternative name parameters for peer and local */
{"tls_peer_san_email", tlsops_alt, 0,
{{0, 0}, CERT_PEER | COMP_E} },
{"tls_my_san_email", tlsops_alt, 0,
{{0, 0}, CERT_LOCAL | COMP_E} },
{"tls_peer_san_hostname", tlsops_alt, 0,
{{0, 0}, CERT_PEER | COMP_HOST} },
{"tls_my_san_hostname", tlsops_alt, 0,
{{0, 0}, CERT_LOCAL | COMP_HOST} },
{"tls_peer_san_uri", tlsops_alt, 0,
{{0, 0}, CERT_PEER | COMP_URI} },
{"tls_my_san_uri", tlsops_alt, 0,
{{0, 0}, CERT_LOCAL | COMP_URI} },
{"tls_peer_san_ip", tlsops_alt, 0,
{{0, 0}, CERT_PEER | COMP_IP} },
{"tls_my_san_ip", tlsops_alt, 0,
{{0, 0}, CERT_LOCAL | COMP_IP} },
/* peer certificate validation parameters */
{"tls_peer_verified", tlsops_check_cert, 0,
{{0, 0}, CERT_VERIFIED} },
{"tls_peer_revoked", tlsops_check_cert, 0,
{{0, 0}, CERT_REVOKED} },
{"tls_peer_expired", tlsops_check_cert, 0,
{{0, 0}, CERT_EXPIRED} },
{"tls_peer_selfsigned", tlsops_check_cert, 0,
{{0, 0}, CERT_SELFSIGNED} },
{"tls_peer_notBefore", tlsops_validity, 0,
{{0, 0}, CERT_NOTBEFORE} },
{"tls_peer_notAfter", tlsops_validity, 0,
{{0, 0}, CERT_NOTAFTER} },
{0,0,0,{{0, 0},0}}
};
/*
* Module interface
*/
struct module_exports exports = {
"tlsops",
DEFAULT_DLFLAGS, /* dlopen flags */
cmds, /* Exported functions */
params, /* Exported parameters */
0, /* exported statistics */
0, /* exported MI functions */
mod_items, /* exported pseudo-variables */
mod_init, /* module initialization function */
0, /* response function */
mod_destroy, /* destroy function */
0 /* child initialization function */
};
static int mod_init(void)
{
DBG("%s module - initializing...\n", exports.name);
return 0;
}
static void mod_destroy(void)
{
DBG("%s module - shutting down...\n", exports.name);
}
static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2)
{
struct tcp_connection *c;
SSL *ssl;
long ssl_verify;
X509 *x509_cert;
LOG(L_DBG, "tlsops:is_peer_verified: is_peer_verified() started...\n");
if (msg->rcv.proto != PROTO_TLS) {
LOG(L_ERR, "tlsops:is_peer_verified: ERROR: proto != TLS -->"
" peer can't be verified, return -1\n");
return -1;
}
LOG(L_DBG, "tlsops:is_peer_verified: trying to find TCP connection "
"of received message...\n");
/* what if we have multiple connections to the same remote socket? e.g. we can have
connection 1: localIP1:localPort1 <--> remoteIP:remotePort
connection 2: localIP2:localPort2 <--> remoteIP:remotePort
but I think the is very unrealistic */
c=tcpconn_get(0, &(msg->rcv.src_ip), msg->rcv.src_port, tcp_con_lifetime);
if (!c) {
LOG(L_ERR, "tlsops:is_peer_verified: ERROR: no corresponding TLS/TCP "
"connection found. This should not happen... return -1\n");
return -1;
}
LOG(L_DBG, "tlsops:is_peer_verified: corresponding TLS/TCP connection "
"found. s=%d, fd=%d, id=%d\n", c->s, c->fd, c->id);
if (!c->extra_data) {
LOG(L_ERR, "tlsops:is_peer_verified: ERROR: no extra_data specified "
"in TLS/TCP connection found. This should not happen... "
"return -1\n");
tcpconn_put(c);
return -1;
}
ssl = (SSL *) c->extra_data;
ssl_verify = SSL_get_verify_result(ssl);
if ( ssl_verify != X509_V_OK ) {
LOG(L_WARN, "tlsops:is_peer_verified: WARNING: verification of "
"presented certificate failed... return -1\n");
tcpconn_put(c);
return -1;
}
/* now, we have only valid peer certificates or peers without certificates.
* Thus we have to check for the existence of a peer certificate
*/
x509_cert = SSL_get_peer_certificate(ssl);
if ( x509_cert == NULL ) {
LOG(L_WARN, "tlsops:is_peer_verified: WARNING: peer did not presented "
"a certificate. Thus it could not be verified... return -1\n");
tcpconn_put(c);
return -1;
}
X509_free(x509_cert);
tcpconn_put(c);
LOG(L_DBG, "tlsops:is_peer_verified: peer is successfuly verified"
"...done\n");
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1