io-ssl.cc (8646B)
1 /* -*- Mode: c++; -*- */ 2 /* -------------------------------------------------------------------- 3 * Filename: 4 * io-ssl.cc 5 * 6 * Description: 7 * Implementation of the IO class. 8 * 9 * Authors: 10 * Andreas Aardal Hanssen <andreas-binc curly bincimap spot org> 11 * 12 * Bugs: 13 * 14 * ChangeLog: 15 * 16 * -------------------------------------------------------------------- 17 * Copyright 2002-2005 Andreas Aardal Hanssen 18 * 19 * This program is free software; you can redistribute it and/or modify 20 * it under the terms of the GNU General Public License as published by 21 * the Free Software Foundation; either version 2 of the License, or 22 * (at your option) any later version. 23 * 24 * This program is distributed in the hope that it will be useful, 25 * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 * GNU General Public License for more details. 28 * 29 * You should have received a copy of the GNU General Public License 30 * along with this program; if not, write to the Free Software 31 * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. 32 * -------------------------------------------------------------------- 33 */ 34 #ifdef HAVE_CONFIG_H 35 #include <config.h> 36 #endif 37 38 #ifdef WITH_SSL 39 40 #include <string> 41 42 #include <openssl/ssl.h> 43 44 #include "session.h" 45 #include "io-ssl.h" 46 47 using namespace ::std; 48 using namespace Binc; 49 50 //------------------------------------------------------------------------ 51 SSLEnabledIO::SSLEnabledIO(void) 52 { 53 } 54 55 //------------------------------------------------------------------------ 56 SSLEnabledIO::SSLEnabledIO(FILE *fp) : IO(fp) 57 { 58 } 59 60 //------------------------------------------------------------------------ 61 SSLEnabledIO::~SSLEnabledIO(void) 62 { 63 switch (mode) { 64 case MODE_SSL: 65 SSL_shutdown(ssl); 66 SSL_shutdown(ssl); 67 68 SSL_free(ssl); 69 SSL_CTX_free(ctx); 70 71 break; 72 case MODE_SYSLOG: 73 closelog(); 74 break; 75 default: 76 break; 77 } 78 } 79 80 81 //------------------------------------------------------------------------ 82 bool SSLEnabledIO::setModeSSL(void) 83 { 84 SSL_library_init(); 85 SSL_load_error_strings(); 86 87 OpenSSL_add_ssl_algorithms(); 88 89 if ((ctx = SSL_CTX_new(SSLv23_server_method())) == 0) { 90 setLastError("SSL error: internal error when creating CTX: " 91 + string(ERR_error_string(ERR_get_error(), 0))); 92 return false; 93 } 94 95 SSL_CTX_set_options(ctx, SSL_OP_ALL); 96 97 Session &session = Session::getInstance(); 98 99 string clist = session.globalconfig["SSL"]["cipher list"]; 100 if (clist != "" && !SSL_CTX_set_cipher_list(ctx, clist.c_str())) { 101 setLastError("SSL error: cannot load cipher list " + clist); 102 return false; 103 } 104 105 string CAfile = session.globalconfig["SSL"]["ca file"]; 106 if (CAfile == "") CAfile == "/usr/share/ssl/certs/.crt"; 107 108 string CApath = session.globalconfig["SSL"]["ca path"]; 109 if (CApath == "") CApath == "/usr/share/ssl/certs/"; 110 111 SSL_CTX_set_default_verify_paths(ctx); 112 113 string pemname = session.globalconfig["SSL"]["pem file"]; 114 if (pemname == "") pemname = "/usr/share/ssl/certs/stunnel.pem"; 115 116 if (!SSL_CTX_use_certificate_chain_file(ctx, pemname.c_str())) { 117 setLastError("SSL error: unable to use certificate in PEM file: " 118 + pemname + ": " 119 + string(ERR_error_string(ERR_get_error(), 0))); 120 return false; 121 } 122 123 if (!SSL_CTX_use_PrivateKey_file(ctx, pemname.c_str(), SSL_FILETYPE_PEM)) { 124 setLastError("SSL error: unable to use private key in PEM file: " 125 + pemname + ": " 126 + string(ERR_error_string(ERR_get_error(), 0))); 127 return false; 128 } 129 130 if (!SSL_CTX_check_private_key(ctx)) { 131 setLastError("SSL error: public and private key in PEM file" 132 " don't match: " 133 + pemname + ": " 134 + string(ERR_error_string(ERR_get_error(), 0))); 135 return false; 136 } 137 138 const char *CAfileptr = CAfile == "" ? 0 : CAfile.c_str(); 139 const char *CApathptr = CApath == "" ? 0 : CApath.c_str(); 140 if (CAfileptr || CApathptr) { 141 if (!SSL_CTX_load_verify_locations(ctx, CAfileptr, CApathptr)) { 142 setLastError("SSL error: unable to load CA file or path: " 143 + string(ERR_error_string(ERR_get_error(), 0))); 144 return false; 145 } 146 } 147 148 if (session.globalconfig["SSL"]["verify peer"] == "yes") 149 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); 150 else 151 SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0); 152 153 SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CAfile.c_str())); 154 155 SSL *ssl = SSL_new(ctx); 156 if (ssl == 0) { 157 setLastError("SSL error: when creating SSL object: " 158 + string(ERR_error_string(ERR_get_error(), 0))); 159 return false; 160 } 161 162 SSL_clear(ssl); 163 164 SSL_set_rfd(ssl, 0); 165 SSL_set_wfd(ssl, 1); 166 SSL_set_accept_state(ssl); 167 168 fflush(stdout); 169 170 int result = SSL_accept(ssl); 171 if (result <= 0) { 172 string errstr; 173 switch (SSL_get_error(ssl, 0)) { 174 case SSL_ERROR_NONE: errstr = "Unknown error"; break; 175 case SSL_ERROR_ZERO_RETURN: errstr = "Connection closed"; break; 176 case SSL_ERROR_WANT_READ: 177 case SSL_ERROR_WANT_WRITE: 178 case SSL_ERROR_WANT_CONNECT: errstr = "Operation did not complete"; break; 179 case SSL_ERROR_WANT_X509_LOOKUP: errstr = "X509 lookup requested"; break; 180 case SSL_ERROR_SYSCALL: errstr = "Unexpected EOF"; break; 181 case SSL_ERROR_SSL: errstr = "Internal SSL error: "; 182 errstr += string(ERR_error_string(ERR_get_error(), 0)); break; 183 } 184 185 setLastError(errstr); 186 return false; 187 } 188 189 this->ssl = ssl; 190 mode = MODE_SSL; 191 return true; 192 } 193 194 //------------------------------------------------------------------------ 195 bool SSLEnabledIO::isModeSSL(void) 196 { 197 return mode == MODE_SSL; 198 } 199 200 //------------------------------------------------------------------------ 201 void SSLEnabledIO::writeStr(const string s) 202 { 203 if (s == "") 204 return; 205 206 if (mode == MODE_PLAIN) { 207 // Set transfer timeout 208 alarm(transfertimeout); 209 int n = fwrite(s.c_str(), 1, s.length(), fpout); 210 fflush(fpout); 211 alarm(0); 212 213 Session::getInstance().addWriteBytes(n); 214 215 if (n != (int) s.length()) { 216 setLastError("Output error: fwrite wrote too few bytes"); 217 return; 218 } 219 } else if (mode == MODE_SYSLOG) { 220 // Set transfer timeout 221 alarm(transfertimeout); 222 syslog(LOG_INFO, "%s", s.c_str()); 223 alarm(0); 224 } else if (mode == MODE_SSL) { 225 do { 226 alarm(transfertimeout); 227 int retval = SSL_write(ssl, s.c_str(), s.length()); 228 alarm(0); 229 230 if (retval > 0) { 231 Session::getInstance().addWriteBytes(retval); 232 break; 233 } else if (retval == 0) { 234 /* call get_error */ 235 setLastError("SSL_write returned 0"); 236 return; 237 } else if (retval < 0) { 238 int err = SSL_get_error(ssl, retval); 239 240 if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { 241 setLastError("SSL_write returned < 0"); 242 return; 243 } else { 244 // retry when we get this error. 245 } 246 } 247 } while (1); 248 } 249 } 250 251 //------------------------------------------------------------------------ 252 int SSLEnabledIO::fillBuffer(int timeout, bool retry) 253 { 254 if (mode == MODE_PLAIN) 255 return IO::fillBuffer(timeout, retry); 256 257 fd_set rfds; 258 FD_ZERO(&rfds); 259 FD_SET(0, &rfds); 260 261 for (;;) { 262 fd_set rfds; 263 FD_ZERO(&rfds); 264 FD_SET(0, &rfds); 265 266 struct timeval tv; 267 tv.tv_sec = timeout; 268 tv.tv_usec = 0; 269 270 if (pending() == 0) { 271 int r = ::select(fileno(stdin) + 1, &rfds, 0, 0, timeout ? &tv : 0); 272 if (r == 0) { 273 setLastError("Reading from client timed out."); 274 return -2; 275 } 276 277 if (r < 0) { 278 setLastError("Error when reading from client"); 279 return -1; 280 } 281 } 282 283 char buf[1024]; 284 unsigned int readBytes = SSL_read(ssl, buf, sizeof(buf)); 285 if (readBytes > 0) { 286 287 Session::getInstance().addReadBytes(readBytes); 288 289 for (unsigned int i = 0; i < readBytes; ++i) 290 inputBuffer.push_front(buf[i]); 291 292 return readBytes; 293 } 294 295 if (readBytes == 0) { 296 setLastError("Client disconnected"); 297 return -1; 298 } 299 300 if (readBytes < 0) { 301 int err = SSL_get_error(ssl, readBytes); 302 if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { 303 setLastError("SSL error: read returned " 304 + toString(readBytes) + ": " 305 + string(ERR_error_string(ERR_get_error(), 0))); 306 return -1; 307 } 308 309 if (timeout == 0 || !retry) { 310 setLastError("Reading from client timed out."); 311 return -2; 312 } 313 } 314 } 315 } 316 317 //------------------------------------------------------------------------ 318 int SSLEnabledIO::pending(void) const 319 { 320 int tmp = (mode == MODE_PLAIN ? 0 : SSL_pending(ssl)); 321 if (tmp) 322 return tmp; 323 else 324 return inputBuffer.size(); 325 } 326 327 #endif