1/*++
2/* NAME
3/*	tls_certkey 3
4/* SUMMARY
5/*	public key certificate and private key loader
6/* SYNOPSIS
7/*	#define TLS_INTERNAL
8/*	#include <tls.h>
9/*
10/*	int	tls_set_ca_certificate_info(ctx, CAfile, CApath)
11/*	SSL_CTX	*ctx;
12/*	const char *CAfile;
13/*	const char *CApath;
14/*
15/*	int	tls_set_my_certificate_key_info(ctx, cert_file, key_file,
16/*						dcert_file, dkey_file,
17/*						eccert_file, eckey_file)
18/*	SSL_CTX	*ctx;
19/*	const char *cert_file;
20/*	const char *key_file;
21/*	const char *dcert_file;
22/*	const char *dkey_file;
23/*	const char *eccert_file;
24/*	const char *eckey_file;
25/* DESCRIPTION
26/*	OpenSSL supports two options to specify CA certificates:
27/*	either one file CAfile that contains all CA certificates,
28/*	or a directory CApath with separate files for each
29/*	individual CA, with symbolic links named after the hash
30/*	values of the certificates. The second option is not
31/*	convenient with a chrooted process.
32/*
33/*	tls_set_ca_certificate_info() loads the CA certificate
34/*	information for the specified TLS server or client context.
35/*	The result is -1 on failure, 0 on success.
36/*
37/*	tls_set_my_certificate_key_info() loads the public key
38/*	certificates and private keys for the specified TLS server
39/*	or client context. Up to 3 pairs of key pairs (RSA, DSA and
40/*	ECDSA) may be specified; each certificate and key pair must
41/*	match.  The result is -1 on failure, 0 on success.
42/* LICENSE
43/* .ad
44/* .fi
45/*	This software is free. You can do with it whatever you want.
46/*	The original author kindly requests that you acknowledge
47/*	the use of his software.
48/* AUTHOR(S)
49/*	Originally written by:
50/*	Lutz Jaenicke
51/*	BTU Cottbus
52/*	Allgemeine Elektrotechnik
53/*	Universitaetsplatz 3-4
54/*	D-03044 Cottbus, Germany
55/*
56/*	Updated by:
57/*	Wietse Venema
58/*	IBM T.J. Watson Research
59/*	P.O. Box 704
60/*	Yorktown Heights, NY 10598, USA
61/*--*/
62
63/* System library. */
64
65#include <sys_defs.h>
66
67#ifdef USE_TLS
68
69/* Utility library. */
70
71#include <msg.h>
72
73/* Global library. */
74
75#include <mail_params.h>
76
77#ifdef __APPLE_OS_X_SERVER__
78#include <stdio.h>
79#include <sys/types.h>
80#include <sys/stat.h>
81
82#include <Security/SecKeychain.h>
83#include <Security/SecKeychainItem.h>
84
85typedef struct
86{
87	int		len;
88	char	key[ FILENAME_MAX ];
89	int		reserved;
90} CallbackUserData;
91
92int apple_password_callback ( char *in_buf, int in_size, int in_rwflag, void *in_user_data );
93
94static CallbackUserData *s_user_data = NULL;
95
96static const char cert_admin_path[] = "/Applications/Server.app/Contents/ServerRoot/usr/sbin/certadmin";
97#endif
98
99/* TLS library. */
100
101#define TLS_INTERNAL
102#include <tls.h>
103
104#ifdef __APPLE_OS_X_SERVER__
105/* -----------------------------------------------------------------
106        apple_password_callback ()
107   ----------------------------------------------------------------- */
108
109int apple_password_callback ( char *in_buf, int in_size, int in_rwflag, void *in_user_data )
110{
111	int					status;
112	char			   *buf		= NULL;
113	ssize_t				len		= 0;
114	int					fd[ 2 ];
115	char			   *args[ 4 ];
116	CallbackUserData   *cb_data	= (CallbackUserData *)in_user_data;
117
118	if ( (cb_data == NULL) || strlen( cb_data->key ) == 0 ||
119		 (cb_data->len >= FILENAME_MAX) || (cb_data->len == 0) || !in_buf ) {
120		msg_error("invalid arguments in callback" );
121		return( 0 );
122	}
123
124	/* open a pipe */
125	pipe( fd );
126
127	/* fork the child */
128	pid_t pid = fork();
129	if ( pid == 0 ) {
130		/* child: exec certadmin tool */
131		close(0);
132		close(1);
133
134		dup2( fd[1], 1 );
135
136		/* set up the args list */
137		args[ 0 ] = cert_admin_path;
138		args[ 1 ] = "--get-private-key-passphrase";
139		args[ 2 ] = cb_data->key;
140		args[ 3 ] = NULL;
141
142		/* get the passphrase */
143		execv(cert_admin_path, args);
144		exit( 1 );
145	} else if ( pid > 0 ) {
146		/* parent: wait for the child to terminate */
147		wait( &status );
148		if ( !NORMAL_EXIT_STATUS(status) ) {
149			if ( WIFEXITED(status) )
150				msg_warn("command: %s exit status %d",
151					cert_admin_path, WEXITSTATUS(status) );
152			if ( WIFSIGNALED(status) )
153				msg_warn("command: %s killed by signal %d",
154					cert_admin_path, WTERMSIG(status) );
155
156			return( 0 );
157		}
158
159		/* parent: read passphrase */
160		len = 0;
161		buf = malloc( in_size );
162		if ( buf == NULL ) {
163			msg_error( "memory allocation error" );
164			return( 0 );
165		}
166
167		len = read( fd[0], buf, in_size);
168		if ( len != 0 ) {
169			/* copy passphrase into buffer & strip off /n */
170			strncpy( in_buf, buf, len - 1 );
171			in_buf[ len - 1 ] = '\0';
172		}
173	}
174
175	return( strlen(in_buf ) );
176} /* apple_password_callback */
177
178#endif
179
180
181/* tls_set_ca_certificate_info - load certificate authority certificates */
182
183int     tls_set_ca_certificate_info(SSL_CTX *ctx, const char *CAfile,
184				            const char *CApath)
185{
186    if (*CAfile == 0)
187	CAfile = 0;
188    if (*CApath == 0)
189	CApath = 0;
190    if (CAfile || CApath) {
191	if (!SSL_CTX_load_verify_locations(ctx, CAfile, CApath)) {
192	    msg_info("cannot load Certificate Authority data: "
193		     "disabling TLS support");
194	    tls_print_errors();
195	    return (-1);
196	}
197	if (var_tls_append_def_CA && !SSL_CTX_set_default_verify_paths(ctx)) {
198	    msg_info("cannot set certificate verification paths: "
199		     "disabling TLS support");
200	    tls_print_errors();
201	    return (-1);
202	}
203    }
204    return (0);
205}
206
207/* set_cert_stuff - specify certificate and key information */
208
209static int set_cert_stuff(SSL_CTX *ctx, const char *cert_type,
210			          const char *cert_file,
211			          const char *key_file)
212{
213
214    /*
215     * We need both the private key (in key_file) and the public key
216     * certificate (in cert_file). Both may specify the same file.
217     *
218     * Code adapted from OpenSSL apps/s_cb.c.
219     */
220    ERR_clear_error();
221    if (SSL_CTX_use_certificate_chain_file(ctx, cert_file) <= 0) {
222	msg_warn("cannot get %s certificate from file %s: "
223		 "disabling TLS support", cert_type, cert_file);
224	tls_print_errors();
225	return (0);
226    }
227
228#ifdef __APPLE_OS_X_SERVER__
229	struct stat st;
230	if ( stat(cert_admin_path, &st) == 0 ) {
231		if ( strlen( key_file ) < FILENAME_MAX ) {
232			if ( s_user_data == NULL ) {
233				s_user_data = malloc( sizeof(CallbackUserData) );
234				if ( s_user_data != NULL )
235					memset( s_user_data, 0, sizeof(CallbackUserData) );
236			}
237
238			if ( s_user_data != NULL ) {
239				snprintf( s_user_data->key, FILENAME_MAX, "%s", key_file );
240				s_user_data->len = strlen( s_user_data->key );
241
242				SSL_CTX_set_default_passwd_cb_userdata( ctx, (void *)s_user_data );
243				SSL_CTX_set_default_passwd_cb( ctx, &apple_password_callback );
244			} else
245				msg_info( "Could not allocate memory for custom callback: %s", key_file );
246		} else
247			msg_info( "Key file path too big for custom callback: %s", key_file );
248	}
249#endif
250
251    if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) {
252	msg_warn("cannot get %s private key from file %s: "
253		 "disabling TLS support", cert_type, key_file);
254	tls_print_errors();
255	return (0);
256    }
257
258    /*
259     * Sanity check.
260     */
261    if (!SSL_CTX_check_private_key(ctx)) {
262	msg_warn("%s private key in %s does not match public key in %s: "
263		 "disabling TLS support", cert_type, key_file, cert_file);
264	return (0);
265    }
266    return (1);
267}
268
269/* tls_set_my_certificate_key_info - load client or server certificates/keys */
270
271int     tls_set_my_certificate_key_info(SSL_CTX *ctx,
272					        const char *cert_file,
273					        const char *key_file,
274					        const char *dcert_file,
275					        const char *dkey_file,
276					        const char *eccert_file,
277					        const char *eckey_file)
278{
279
280    /*
281     * Lack of certificates is fine so long as we are prepared to use
282     * anonymous ciphers.
283     */
284    if (*cert_file && !set_cert_stuff(ctx, "RSA", cert_file, key_file))
285	return (-1);			/* logged */
286    if (*dcert_file && !set_cert_stuff(ctx, "DSA", dcert_file, dkey_file))
287	return (-1);				/* logged */
288#if OPENSSL_VERSION_NUMBER >= 0x1000000fL && !defined(OPENSSL_NO_ECDH)
289    if (*eccert_file && !set_cert_stuff(ctx, "ECDSA", eccert_file, eckey_file))
290	return (-1);				/* logged */
291#else
292    if (*eccert_file)
293	msg_warn("ECDSA not supported. Ignoring ECDSA certificate file \"%s\"",
294		 eccert_file);
295#endif
296    return (0);
297}
298
299#endif
300