aboutsummaryrefslogtreecommitdiffstats
path: root/socket.c
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>1999-10-27 00:15:14 +0000
committerEric S. Raymond <esr@thyrsus.com>1999-10-27 00:15:14 +0000
commit0d1e69ae10af5eba7cd3426e802d40d17b6db218 (patch)
treed838573a667ed2ba75e04797f87eb9f45f1df05f /socket.c
parent8d293a3b12a5e2c4ecc6f70bcd59b862294458c8 (diff)
downloadfetchmail-0d1e69ae10af5eba7cd3426e802d40d17b6db218.tar.gz
fetchmail-0d1e69ae10af5eba7cd3426e802d40d17b6db218.tar.bz2
fetchmail-0d1e69ae10af5eba7cd3426e802d40d17b6db218.zip
This preliminary SSL patch goes to Mike.
svn path=/trunk/; revision=2643
Diffstat (limited to 'socket.c')
-rw-r--r--socket.c290
1 files changed, 286 insertions, 4 deletions
diff --git a/socket.c b/socket.c
index db0d7000..6d82c428 100644
--- a/socket.c
+++ b/socket.c
@@ -299,13 +299,35 @@ va_dcl {
}
+#ifdef SSL_ENABLE
+#include "openssl/ssl.h"
+#include "openssl/err.h"
+#include "openssl/pem.h"
+#include "openssl/x509.h"
+
+static SSL_CTX *_ctx = NULL;
+static SSL *_ssl_context[FD_SETSIZE];
+
+SSL *SSLGetContext( int );
+#endif /* SSL_ENABLE */
+
int SockWrite(int sock, char *buf, int len)
{
int n, wrlen = 0;
+#ifdef SSL_ENABLE
+ SSL *ssl;
+#endif
while (len)
{
+#ifdef SSL_ENABLE
+ if( NULL != ( ssl = SSLGetContext( sock ) ) )
+ n = SSL_write(ssl, buf, len);
+ else
+ n = write(sock, buf, len);
+#else
n = write(sock, buf, len);
+#endif
if (n <= 0)
return -1;
len -= n;
@@ -319,6 +341,9 @@ int SockRead(int sock, char *buf, int len)
{
char *newline, *bp = buf;
int n;
+#ifdef SSL_ENABLE
+ SSL *ssl;
+#endif
if (--len < 1)
return(-1);
@@ -329,12 +354,66 @@ int SockRead(int sock, char *buf, int len)
* (2) to return the true length of data read, even if the
* data coming in has embedded NULS.
*/
+#ifdef SSL_ENABLE
+ if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
+ /* Hack alert! */
+ /* OK... SSL_peek works a little different from MSG_PEEK
+ Problem is that SSL_peek can return 0 if there
+ is no data currently available. If, on the other
+ hand, we loose the socket, we also get a zero, but
+ the SSL_read then SEGFAULTS! To deal with this,
+ we'll check the error code any time we get a return
+ of zero from SSL_peek. If we have an error, we bail.
+ If we don't, we read one character in SSL_read and
+ loop. This should continue to work even if they
+ later change the behavior of SSL_peek
+ to "fix" this problem... :-( */
+ if ((n = SSL_peek(ssl, bp, len)) < 0) {
+ return(-1);
+ }
+ if( 0 == n ) {
+ /* SSL_peek says no data... Does he mean no data
+ or did the connection blow up? If we got an error
+ then bail! */
+ if( 0 != ( n = ERR_get_error() ) ) {
+ return -1;
+ }
+ /* We didn't get an error so read at least one
+ character at this point and loop */
+ n = 1;
+ /* Make sure newline start out NULL!
+ * We don't have a string to pass through
+ * the strchr at this point yet */
+ newline = NULL;
+ } else if ((newline = memchr(bp, '\n', n)) != NULL)
+ n = newline - bp + 1;
+ if ((n = SSL_read(ssl, bp, n)) == -1) {
+ return(-1);
+ }
+ /* Check for case where our single character turned out to
+ * be a newline... (It wasn't going to get caught by
+ * the strchr above if it came from the hack... ). */
+ if( NULL == newline && 1 == n && '\n' == *bp ) {
+ /* Got our newline - this will break
+ out of the loop now */
+ newline = bp;
+ }
+ } else {
+ if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
+ return(-1);
+ if ((newline = memchr(bp, '\n', n)) != NULL)
+ n = newline - bp + 1;
+ if ((n = read(sock, bp, n)) == -1)
+ return(-1);
+ }
+#else
if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
return(-1);
if ((newline = memchr(bp, '\n', n)) != NULL)
n = newline - bp + 1;
if ((n = read(sock, bp, n)) == -1)
return(-1);
+#endif
bp += n;
len -= n;
} while
@@ -348,16 +427,219 @@ int SockPeek(int sock)
{
int n;
char ch;
+#ifdef SSL_ENABLE
+ SSL *ssl;
+#endif
- if ((n = recv(sock, &ch, 1, MSG_PEEK)) == -1)
- return -1;
- else
- return(ch);
+#ifdef SSL_ENABLE
+ if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
+ n = SSL_peek(ssl, &ch, 1);
+ if( 0 == n ) {
+ /* This code really needs to implement a "hold back"
+ * to simulate a functioning SSL_peek()... sigh...
+ * Has to be coordinated with the read code above.
+ * Next on the list todo... */
+
+ /* SSL_peek says no data... Does he mean no data
+ or did the connection blow up? If we got an error
+ then bail! */
+ if( 0 != ( n = ERR_get_error() ) ) {
+ return -1;
+ }
+
+ /* Haven't seen this case actually occur, but...
+ if the problem in SockRead can occur, this should
+ be possible... Just not sure what to do here.
+ This should be a safe "punt" the "peek" but don't
+ "punt" the "session"... */
+
+ return 0; /* Give him a '\0' character */
+ }
+ } else {
+ n = recv(sock, &ch, 1, MSG_PEEK);
+ }
+#else
+ n = recv(sock, &ch, 1, MSG_PEEK);
+#endif
+ if (n == -1)
+ return -1;
+ else
+ return(ch);
}
+#ifdef SSL_ENABLE
+
+static char *_ssl_server_cname = NULL;
+
+SSL *SSLGetContext( int sock )
+{
+ /* If SSLOpen has never initialized - just return NULL */
+ if( NULL == _ctx )
+ return NULL;
+
+ if( sock < 0 || sock > FD_SETSIZE )
+ return NULL;
+ return _ssl_context[sock];
+}
+
+
+int SSL_verify_callback( int ok_return, X509_STORE_CTX *ctx )
+{
+ char buf[260];
+ char cbuf[260];
+ char ibuf[260];
+ char *str_ptr;
+ X509 *x509_cert;
+ int err, depth;
+
+ x509_cert = X509_STORE_CTX_get_current_cert(ctx);
+ err = X509_STORE_CTX_get_error(ctx);
+ depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ X509_NAME_oneline(X509_get_subject_name(x509_cert), buf, 256);
+ X509_NAME_oneline(X509_get_issuer_name(x509_cert), ibuf, 256);
+
+ /* Just to be sure those buffers are terminated... I think the
+ X509 libraries do, but... */
+ buf[256] = ibuf[256] = '\0';
+
+ if (depth == 0) {
+ if( ( str_ptr = strstr( ibuf, "/O=" ) ) ) {
+ str_ptr += 3;
+ strcpy( cbuf, str_ptr );
+ if( ( str_ptr = strchr(cbuf, '/' ) ) ) {
+ *str_ptr = '\0';
+ }
+ if (outlevel == O_VERBOSE)
+ report(stdout, "Issuer Organization: %s", cbuf );
+ } else {
+ if (outlevel == O_VERBOSE)
+ report(stdout, "Unknown Organization", cbuf );
+ }
+ if( ( str_ptr = strstr( ibuf, "/CN=" ) ) ) {
+ str_ptr += 4;
+ strcpy( cbuf, str_ptr );
+ if( ( str_ptr = strchr(cbuf, '/' ) ) ) {
+ *str_ptr = '\0';
+ }
+ if (outlevel == O_VERBOSE)
+ report(stdout, "Issuer CommonName: %s", cbuf );
+ } else {
+ if (outlevel == O_VERBOSE)
+ report(stdout, "Unknown Issuer CommonName", cbuf );
+ }
+ if( ( str_ptr = strstr( buf, "/CN=" ) ) ) {
+ str_ptr += 4;
+ strcpy( cbuf, str_ptr );
+ if( ( str_ptr = strchr(cbuf, '/' ) ) ) {
+ *str_ptr = '\0';
+ }
+ if (outlevel == O_VERBOSE)
+ report(stdout, "Server CommonName: %s", cbuf );
+ /* Should we have some wildcarding here? */
+ if ( NULL != _ssl_server_cname && 0 != strcmp( cbuf, _ssl_server_cname ) ) {
+ report(stdout, "Server CommonName mismatch: %s != %s", cbuf, _ssl_server_cname );
+ }
+ } else {
+ if (outlevel == O_VERBOSE)
+ report(stdout, "Unknown Server CommonName", cbuf );
+ }
+ }
+
+ switch (ctx->error) {
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
+ report(stdout, "unknown issuer= %s", buf);
+ break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ report(stderr, "Server Certificate not yet valid");
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ report(stderr, "Server Certificate expired");
+ break;
+ }
+ /* We are not requiring or validating server or issuer id's as yet */
+ /* Always return OK from here */
+ ok_return = 1;
+ return( ok_return );
+}
+
+
+/* performs initial SSL handshake over the connected socket
+ * uses SSL *ssl global variable, which is currently defined
+ * in this file
+ */
+int SSLOpen(int sock, char *mycert, char *mykey, char *servercname )
+{
+ SSL_load_error_strings();
+ SSLeay_add_ssl_algorithms();
+
+ if( sock < 0 || sock > FD_SETSIZE ) {
+ report(stderr, "File descriptor out of range for SSL" );
+ return( -1 );
+ }
+
+ if( ! _ctx ) {
+ /* Be picky and make sure the memory is cleared */
+ memset( _ssl_context, 0, sizeof( _ssl_context ) );
+ _ctx = SSL_CTX_new(SSLv23_client_method());
+ if(_ctx == NULL) {
+ ERR_print_errors_fp(stderr);
+ return(-1);
+ }
+ }
+
+ _ssl_context[sock] = SSL_new(_ctx);
+
+ if(_ssl_context[sock] == NULL) {
+ ERR_print_errors_fp(stderr);
+ return(-1);
+ }
+
+ /* This static is for the verify callback */
+ _ssl_server_cname = servercname;
+
+ SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, SSL_verify_callback);
+
+ if( mycert || mykey ) {
+
+ /* Ok... He has a certificate file defined, so lets declare it. If
+ * he does NOT have a separate certificate and private key file then
+ * assume that it's a combined key and certificate file.
+ */
+ if( !mykey )
+ mykey = mycert;
+ if( !mycert )
+ mycert = mykey;
+ SSL_use_certificate_file(_ssl_context[sock], mycert, SSL_FILETYPE_PEM);
+ SSL_use_RSAPrivateKey_file(_ssl_context[sock], mykey, SSL_FILETYPE_PEM);
+ }
+
+ SSL_set_fd(_ssl_context[sock], sock);
+
+ if(SSL_connect(_ssl_context[sock]) == -1) {
+ ERR_print_errors_fp(stderr);
+ return(-1);
+ }
+
+ return(0);
+}
+#endif
+
int SockClose(int sock)
/* close a socket (someday we may do other cleanup here) */
{
+#ifdef SSL_ENABLE
+ SSL *ssl;
+
+ if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
+ /* Clean up the SSL stack */
+ SSL_free( _ssl_context[sock] );
+ _ssl_context[sock] = NULL;
+ }
+#endif
return(close(sock));
}