diff options
author | Eric S. Raymond <esr@thyrsus.com> | 1999-10-27 00:15:14 +0000 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 1999-10-27 00:15:14 +0000 |
commit | 0d1e69ae10af5eba7cd3426e802d40d17b6db218 (patch) | |
tree | d838573a667ed2ba75e04797f87eb9f45f1df05f /socket.c | |
parent | 8d293a3b12a5e2c4ecc6f70bcd59b862294458c8 (diff) | |
download | fetchmail-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.c | 290 |
1 files changed, 286 insertions, 4 deletions
@@ -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)); } |