#include <sys_defs.h>
#ifdef USE_TLS
#include <msg.h>
#include <mail_params.h>
#ifdef __APPLE_OS_X_SERVER__
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
typedef struct
{
int len;
char key[ FILENAME_MAX ];
int reserved;
} CallbackUserData;
int apple_password_callback ( char *in_buf, int in_size, int in_rwflag, void *in_user_data );
static CallbackUserData *s_user_data = NULL;
#endif
#define TLS_INTERNAL
#include <tls.h>
#ifdef __APPLE_OS_X_SERVER__
int apple_password_callback ( char *in_buf, int in_size, int in_rwflag, void *in_user_data )
{
int status;
char *buf = NULL;
ssize_t len = 0;
int fd[ 2 ];
char *args[ 4 ];
CallbackUserData *cb_data = (CallbackUserData *)in_user_data;
if ( (cb_data == NULL) || strlen( cb_data->key ) == 0 ||
(cb_data->len >= FILENAME_MAX) || (cb_data->len == 0) || !in_buf )
{
msg_error("invalid arguments in callback" );
return( 0 );
}
pipe( fd );
pid_t pid = fork();
if ( pid == 0 )
{
close(0);
close(1);
dup2( fd[1], 1 );
args[ 0 ] = "/usr/sbin/certadmin";
args[ 1 ] = "--get-private-key-passphrase";
args[ 2 ] = cb_data->key;
args[ 3 ] = NULL;
execv("/usr/sbin/certadmin", args);
exit( 0 );
}
else if ( pid > 0 )
{
wait( &status );
if ( !NORMAL_EXIT_STATUS(status) ) {
if ( WIFEXITED(status) )
msg_warn("command /usr/sbin/certadmin exit status %d",
WEXITSTATUS(status) );
if ( WIFSIGNALED(status) )
msg_warn("command /usr/sbin/certadmin killed by signal %d",
WTERMSIG(status) );
return( 0 );
}
len = 0;
buf = malloc( in_size );
if ( buf == NULL )
{
msg_error( "memory allocation error" );
return( 0 );
}
len = read( fd[0], buf, in_size);
if ( len != 0 )
{
strncpy( in_buf, buf, len - 1 );
in_buf[ len - 1 ] = '\0';
}
}
return( strlen(in_buf ) );
}
#endif
int tls_set_ca_certificate_info(SSL_CTX *ctx, const char *CAfile,
const char *CApath)
{
if (*CAfile == 0)
CAfile = 0;
if (*CApath == 0)
CApath = 0;
if (CAfile || CApath) {
if (!SSL_CTX_load_verify_locations(ctx, CAfile, CApath)) {
msg_info("cannot load Certificate Authority data: "
"disabling TLS support");
tls_print_errors();
return (-1);
}
if (var_tls_append_def_CA && !SSL_CTX_set_default_verify_paths(ctx)) {
msg_info("cannot set certificate verification paths: "
"disabling TLS support");
tls_print_errors();
return (-1);
}
}
return (0);
}
static int set_cert_stuff(SSL_CTX *ctx, const char *cert_type,
const char *cert_file,
const char *key_file)
{
ERR_clear_error();
if (SSL_CTX_use_certificate_chain_file(ctx, cert_file) <= 0) {
msg_warn("cannot get %s certificate from file %s: "
"disabling TLS support", cert_type, cert_file);
tls_print_errors();
return (0);
}
#ifdef __APPLE_OS_X_SERVER__
if ( strlen( key_file ) < FILENAME_MAX )
{
if ( s_user_data == NULL )
{
s_user_data = malloc( sizeof(CallbackUserData) );
if ( s_user_data != NULL )
{
memset( s_user_data, 0, sizeof(CallbackUserData) );
}
}
if ( s_user_data != NULL )
{
snprintf( s_user_data->key, FILENAME_MAX, "%s", key_file );
s_user_data->len = strlen( s_user_data->key );
SSL_CTX_set_default_passwd_cb_userdata( ctx, (void *)s_user_data );
SSL_CTX_set_default_passwd_cb( ctx, &apple_password_callback );
}
else
{
msg_info( "Could not allocate memory for custom callback: %s", key_file );
}
}
else
{
msg_info( "Key file path too big for custom callback: %s", key_file );
}
#endif
if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) {
msg_warn("cannot get %s private key from file %s: "
"disabling TLS support", cert_type, key_file);
tls_print_errors();
return (0);
}
if (!SSL_CTX_check_private_key(ctx)) {
msg_warn("%s private key in %s does not match public key in %s: "
"disabling TLS support", cert_type, key_file, cert_file);
return (0);
}
return (1);
}
int tls_set_my_certificate_key_info(SSL_CTX *ctx,
const char *cert_file,
const char *key_file,
const char *dcert_file,
const char *dkey_file,
const char *eccert_file,
const char *eckey_file)
{
if (*cert_file && !set_cert_stuff(ctx, "RSA", cert_file, key_file))
return (-1);
if (*dcert_file && !set_cert_stuff(ctx, "DSA", dcert_file, dkey_file))
return (-1);
#if OPENSSL_VERSION_NUMBER >= 0x1000000fL && !defined(OPENSSL_NO_ECDH)
if (*eccert_file && !set_cert_stuff(ctx, "ECDSA", eccert_file, eckey_file))
return (-1);
#else
if (*eccert_file)
msg_warn("ECDSA not supported. Ignoring ECDSA certificate file \"%s\"",
eccert_file);
#endif
return (0);
}
#endif