📄 ssl_unix.c
字号:
static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size, long *contd){ unsigned long n; char *s,*ret,c,d; *contd = NIL; /* assume no continuation */ /* make sure have data */ if (!ssl_getdata (stream)) return NIL; for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) { d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,s,*size = n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } } /* copy partial string from buffer */ memcpy ((ret = (char *) fs_get (n)),s,*size = n); /* get more data from the net */ if (!ssl_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[*size = --n] = '\0'; /* tie off string with null */ } else *contd = LONGT; /* continuation needed */ return ret;}/* SSL receive buffer * Accepts: SSL stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer){ unsigned long n; while (size > 0) { /* until request satisfied */ if (!ssl_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (buffer,stream->iptr,n); buffer += n; /* update pointer */ stream->iptr += n; size -= n; /* update # of bytes to do */ stream->ictr -= n; } buffer[0] = '\0'; /* tie off string */ return T;}/* SSL receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */long ssl_getdata (SSLSTREAM *stream){ int i,sock; fd_set fds,efds; struct timeval tmo; tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL); long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL); time_t t = time (0); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL; /* tcp_unix should have prevented this */ if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()"); (*bn) (BLOCK_TCPREAD,NIL); while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); /* start of request */ time_t now = tl; int ti = ttmo_read ? now + ttmo_read : 0; if (SSL_pending (stream->con)) i = 1; else { if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG); tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (sock,&fds); /* set bit in selection vector */ FD_SET (sock,&efds); /* set bit in error selection vector */ errno = NIL; /* block and read */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); } if (i) { /* non-timeout result from select? */ errno = 0; /* just in case */ if (i > 0) /* read what we can */ while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) && ((errno == EINTR) || (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ))); if (i <= 0) { /* error seen? */ if (tcpdebug) { char *s,tmp[MAILTMPLEN]; if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d", errno,SSL_get_error (stream->con,i)); else s = "SSL data read end of file"; mm_log (s,TCPDEBUG); } return ssl_abort (stream); } stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG); } /* timeout, punt unless told not to */ else if (!tmoh || !(*tmoh) (now - t,now - tl)) { if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG); return ssl_abort (stream); } } (*bn) (BLOCK_NONE,NIL); return T;}/* SSL send string as record * Accepts: SSL stream * string pointer * Returns: T if success else NIL */long ssl_soutr (SSLSTREAM *stream,char *string){ return ssl_sout (stream,string,(unsigned long) strlen (string));}/* SSL send string * Accepts: SSL stream * string pointer * byte count * Returns: T if success else NIL */long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size){ long i; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!stream->con) return NIL; (*bn) (BLOCK_TCPWRITE,NIL); if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG); /* until request satisfied */ for (i = 0; size > 0; string += i,size -= i) /* write as much as we can */ if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) { if (tcpdebug) { char tmp[MAILTMPLEN]; sprintf (tmp,"SSL data write I/O error %d SSL error %d", errno,SSL_get_error (stream->con,i)); mm_log (tmp,TCPDEBUG); } return ssl_abort (stream);/* write failed */ } if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG); (*bn) (BLOCK_NONE,NIL); return LONGT; /* all done */}/* SSL close * Accepts: SSL stream */void ssl_close (SSLSTREAM *stream){ ssl_abort (stream); /* nuke the stream */ fs_give ((void **) &stream); /* flush the stream */}/* SSL abort stream * Accepts: SSL stream * Returns: NIL always */static long ssl_abort (SSLSTREAM *stream){ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (stream->con) { /* close SSL connection */ SSL_shutdown (stream->con); SSL_free (stream->con); stream->con = NIL; } if (stream->context) { /* clean up context */ SSL_CTX_free (stream->context); stream->context = NIL; } if (stream->tcpstream) { /* close TCP stream */ tcp_close (stream->tcpstream); stream->tcpstream = NIL; } (*bn) (BLOCK_NONE,NIL); return NIL;}/* SSL get host name * Accepts: SSL stream * Returns: host name for this stream */char *ssl_host (SSLSTREAM *stream){ return tcp_host (stream->tcpstream);}/* SSL get remote host name * Accepts: SSL stream * Returns: host name for this stream */char *ssl_remotehost (SSLSTREAM *stream){ return tcp_remotehost (stream->tcpstream);}/* SSL return port for this stream * Accepts: SSL stream * Returns: port number for this stream */unsigned long ssl_port (SSLSTREAM *stream){ return tcp_port (stream->tcpstream);}/* SSL get local host name * Accepts: SSL stream * Returns: local host name */char *ssl_localhost (SSLSTREAM *stream){ return tcp_localhost (stream->tcpstream);}/* Start TLS * Accepts: /etc/services service name * Returns: cpystr'd error string if TLS failed, else NIL for success */char *ssl_start_tls (char *server){ char tmp[MAILTMPLEN]; struct stat sbuf; if (sslstdio) return cpystr ("Already in an SSL session"); if (start_tls) return cpystr ("TLS already started"); if (server) { /* build specific certificate/key file name */ sprintf (tmp,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ()); if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */ sprintf (tmp,"%s/%s.pem",SSL_CERT_DIRECTORY,server); if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed"); } start_tls = server; /* switch to STARTTLS mode */ } return NIL;}/* Init server for SSL * Accepts: server name */void ssl_server_init (char *server){ char cert[MAILTMPLEN],key[MAILTMPLEN]; unsigned long i; struct stat sbuf; SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); ssl_onceonlyinit (); /* make sure algorithms added */ ERR_load_crypto_strings (); SSL_load_error_strings (); /* build specific certificate/key file names */ sprintf (cert,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ()); sprintf (key,"%s/%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ()); /* use non-specific name if no specific cert */ if (stat (cert,&sbuf)) sprintf (cert,"%s/%s.pem",SSL_CERT_DIRECTORY,server); if (stat (key,&sbuf)) { /* use non-specific name if no specific key */ sprintf (key,"%s/%s.pem",SSL_KEY_DIRECTORY,server); /* use cert file as fallback for key */ if (stat (key,&sbuf)) strcpy (key,cert); } /* create context */ if (!(stream->context = SSL_CTX_new (start_tls ? TLSv1_server_method () : SSLv23_server_method ()))) syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s", tcp_clienthost ()); else { /* set context options */ SSL_CTX_set_options (stream->context,SSL_OP_ALL); /* set cipher list */ if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST)) syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s", SSLCIPHERLIST,tcp_clienthost ()); /* load certificate */ else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert)) syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s", cert,tcp_clienthost ()); /* load key */ else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key, SSL_FILETYPE_PEM))) syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s", key,tcp_clienthost ()); else { /* generate key if needed */ if (SSL_CTX_need_tmp_RSA (stream->context)) SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey); /* create new SSL connection */ if (!(stream->con = SSL_new (stream->context))) syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s", tcp_clienthost ()); else { /* set file descriptor */ SSL_set_fd (stream->con,0); /* all OK if accepted */ if (SSL_accept (stream->con) < 0) syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s", tcp_clienthost ()); else { /* server set up */ sslstdio = (SSLSTDIOSTREAM *) memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM)); sslstdio->sslstream = stream; /* available space in output buffer */ sslstdio->octr = SSLBUFLEN; /* current output buffer pointer */ sslstdio->optr = sslstdio->obuf; /* allow plaintext if disable value was 2 */ if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1) mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL); /* unhide PLAIN SASL authenticator */ mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"PLAIN"); mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"LOGIN"); return; } } } } while (i = ERR_get_error ()) /* SSL failure */ syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL)); ssl_close (stream); /* punt stream */ exit (1); /* punt this program too */}/* Generate one-time key for server * Accepts: SSL connection * export flag * keylength * Returns: generated key, always */static RSA *ssl_genkey (SSL *con,int export,int keylength){ unsigned long i; static RSA *key = NIL; if (!key) { /* if don't have a key already */ /* generate key */ if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) { syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s", tcp_clienthost ()); while (i = ERR_get_error ()) syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL)); exit (1); } } return key;}/* Wait for stdin input * Accepts: timeout in seconds * Returns: T if have input on stdin, else NIL */long ssl_server_input_wait (long seconds){ int i,sock; fd_set fds,efd; struct timeval tmo; SSLSTREAM *stream; if (!sslstdio) return server_input_wait (seconds); /* input available in buffer */ if (((stream = sslstdio->sslstream)->ictr > 0) || !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT; /* sock ought to be 0 always */ if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()"); /* input available from SSL */ if (SSL_pending (stream->con) && ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) { stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ return LONGT; } FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efd); /* initialize selection vector */ FD_SET (sock,&fds); /* set bit in selection vector */ FD_SET (sock,&efd); /* set bit in selection vector */ tmo.tv_sec = seconds; tmo.tv_usec = 0; /* see if input available from the socket */ return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL;}#include "sslstdio.c"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -