xdm_netstream.c

Go to the documentation of this file.
00001 /*----------------------------------------------------------------------------
00002  * Copyright (c) 2006, Novell, Inc.
00003  * All rights reserved.
00004  * 
00005  * Redistribution and use in source and binary forms, with or without 
00006  * modification, are permitted provided that the following conditions are 
00007  * met:
00008  * 
00009  *     * Redistributions of source code must retain the above copyright 
00010  *       notice, this list of conditions and the following disclaimer.
00011  *     * Redistributions in binary form must reproduce the above copyright 
00012  *       notice, this list of conditions and the following disclaimer in the 
00013  *       documentation and/or other materials provided with the distribution.
00014  *     * Neither the name of the Novell nor the names of its contributors 
00015  *       may be used to endorse or promote products derived from this 
00016  *       software without specific prior written permission.
00017  * 
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
00019  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
00020  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
00021  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
00022  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00023  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
00024  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
00025  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
00026  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
00027  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *--------------------------------------------------------------------------*/
00030 
00047 #include <string.h>
00048 
00049 #ifdef _WIN32
00050 #  define WIN32_LEAN_AND_MEAN
00051 #  include <windows.h>
00052 #  include <winsock2.h>
00053 #  include <ws2tcpip.h>
00054 #  define XDMAPI __cdecl
00055 #  define XDMEXP __declspec(dllexport)
00056 #  define socket_t SOCKET
00057 #  define strcasecmp _stricmp
00058 #  ifdef ENABLE_SSL
00059 #    define HAVE_LIBSSL 1
00060 #  endif
00061 #else
00062 #  if HAVE_CONFIG_H
00063 #    include <config.h>
00064 #  endif
00065 #  include <sys/types.h>
00066 #  include <sys/socket.h>
00067 #  include <netdb.h>
00068 #  include <errno.h>
00069 #  define XDMAPI
00070 #  define XDMEXP
00071 #  define socket_t int
00072 #  define closesocket close
00073 #  define INVALID_SOCKET -1
00074 #  define WSAECONNRESET ECONNRESET
00075 #  define WSAGetLastError() errno
00076 #endif
00077 
00078 #include <stddef.h>
00079 #include <string.h>
00080 
00081 #define CNF_SECURE      "xdasd.loggers.netstream.secure"
00082 #define CNF_SERVER      "xdasd.loggers.netstream.server"
00083 #define CNF_PORT        "xdasd.loggers.netstream.port"
00084 
00085 #define XDM_DEF_SRVSTR  "localhost"
00086 #define XDM_DEF_PORTSTR "1468"   /* syslog */
00087 
00089 static void (*s_fplogmsg)(int level, const char * msg, ... ) = 0;
00090 
00092 static char * (*s_fpgetcnfstr)(const char *, char *, size_t *) = 0;
00093 
00095 static socket_t s_skt = INVALID_SOCKET;
00096 static char s_srvstr[512];
00097 static char s_portstr[32];
00098 
00099 #if HAVE_LIBSSL
00100 #  include <openssl/ssl.h>
00101 #  ifdef _WIN32
00102 #    include <openssl/rand.h>
00103 #  endif
00104 #  define CNF_TRUSTFILE "xdasd.loggers.netstream.trustfile"
00105 #  define CNF_KEYFILE   "xdasd.loggers.netstream.keyfile"
00106 #  define CNF_KEYPWD    "xdasd.loggers.netstream.keypwd"
00107 #  define CNF_CIPHERS   "xdasd.loggers.netstream.ciphers"
00108 
00110 static SSL_CTX * s_ctx = 0;
00111 static SSL * s_ssl = 0;
00112 static int s_srvauth_required = 0;
00113 
00126 static int pem_passwd_cb(char * buf, int size, int rwflag, void * passwd)
00127 {
00128    (void)rwflag;  /* not used */
00129 
00130    *buf = 0;
00131    if (passwd != 0)
00132       strncpy(buf, (char *)passwd, size);
00133    buf[size - 1] = 0;
00134    return strlen(buf);
00135 }
00136 
00149 static SSL_CTX * initialize_ctx(const char * trustfile, 
00150       const char * keyfile, const char * password, 
00151       const char * ciphers)
00152 {
00153    SSL_CTX * ctx;
00154 
00155    /* Global system initialization */
00156    SSL_library_init();
00157    SSL_load_error_strings();
00158    
00159    /* Create our context */
00160    ctx = SSL_CTX_new(SSLv23_method());
00161    
00162    /* Client authentication configured? */
00163    if (keyfile && *keyfile)
00164    {
00165       /* Load our certificates. */
00166       if (!SSL_CTX_use_certificate_chain_file(ctx, keyfile))
00167       {
00168          s_fplogmsg(0, "netstream: Can't read certificates from %s.\n", keyfile);
00169          SSL_CTX_free(ctx);
00170          return 0;
00171       }
00172 
00173       /* Keyfile has password? */
00174       if (password && *password)
00175       {
00176          /* Set default password callback handler to provide keyfile password. */
00177          SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
00178          SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *)password);
00179       }
00180       
00181       /* Load our keys. */
00182       if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
00183       {
00184          s_fplogmsg(0, "netstream: Can't read keys from %s.\n", keyfile);
00185          SSL_CTX_free(ctx);
00186          return 0;
00187       }
00188    }
00189 
00190    /* Server authentication configured? */
00191    if (trustfile && *trustfile)
00192    {
00193       /* Load the server CAs we trust. */
00194       s_srvauth_required = 1;
00195       if (!SSL_CTX_load_verify_locations(ctx, trustfile, 0))
00196       {
00197          s_fplogmsg(0, "netstream: Can't read CA trust list from %s.\n", trustfile);
00198          SSL_CTX_free(ctx);
00199          return 0;
00200       }
00201    }
00202 
00203    /* Cipher suite configured? */
00204    if (ciphers && *ciphers)
00205    {
00206       /* Configure SSL context with requested ciphers. */
00207       if (!SSL_CTX_set_cipher_list(s_ctx, ciphers))
00208       {
00209          s_fplogmsg(0, "netstream: no requested cipher available.\n");
00210          SSL_CTX_free(ctx);
00211          return 0;
00212       }
00213    }
00214 
00215 #if (OPENSSL_VERSION_NUMBER < 0x00905100L)
00216    SSL_CTX_set_verify_depth(ctx, 1);
00217 #endif
00218    
00219 #ifdef _WIN32
00220    /* add randomness to PRNG with RAND_add - 
00221    TODO: We need a better source of randomness than trustfile. */
00222    RAND_add(trustfile, strlen(trustfile), strlen(trustfile));
00223 #endif
00224 
00225    return ctx;
00226 }
00227 
00239 static int ns_check_cert(SSL * ssl, const char * host)
00240 {
00241    X509 * peer;
00242    char peer_CN[256];
00243    
00244    if (SSL_get_verify_result(ssl) != X509_V_OK)
00245       s_fplogmsg(0, "netstream: Can't verify server (%s) certificate.\n", host);
00246    
00247    /* Check the cert chain. The chain length is automatically checked 
00248       by OpenSSL when we set the verify depth in the ctx */
00249    
00250    /* Acquire the peer certificate */
00251    if ((peer = SSL_get_peer_certificate(ssl)) == 0)
00252    {
00253       s_fplogmsg(0, "netstream: Server (%s) presents no certificate.\n", host);
00254       return -1;
00255    }
00256 
00257    /* Check the common name */
00258    X509_NAME_get_text_by_NID(X509_get_subject_name(peer), 
00259          NID_commonName, peer_CN, 256);
00260    if (strcasecmp(peer_CN, host) != 0)
00261    {
00262       s_fplogmsg(0, "netstream: Common name (%s) doesn't "
00263             "match host name (%s).\n", peer_CN, host);
00264       return -1;
00265    }
00266 
00267    return 0;
00268 }
00269 #endif
00270 
00278 static void ns_disconnect(void)
00279 {
00280 #if HAVE_LIBSSL
00281    if (s_ssl != 0)
00282       SSL_free(s_ssl), s_ssl = 0;
00283 #endif
00284 
00285    if (s_skt != INVALID_SOCKET)
00286       closesocket(s_skt), s_skt = INVALID_SOCKET;
00287 }
00288 
00301 static int ns_connect(const char * host, const char * service)
00302 {
00303    int err;
00304    struct addrinfo hints;
00305    struct addrinfo * alist = 0;
00306 
00307    ns_disconnect();  /* cleanup and reinit in case we're reconnecting */
00308 
00309    memset(&hints, 0, sizeof(hints));
00310    hints.ai_family = AF_INET;
00311    hints.ai_socktype = SOCK_STREAM;
00312    hints.ai_protocol = IPPROTO_TCP;
00313 
00314    /* convert address, open network connection, free addrinfo list */
00315    if ((err = getaddrinfo(host, service, &hints, &alist)) != 0)
00316       s_fplogmsg(0, "netstream: Unable to formulate server address, error %d.\n", err);
00317    else
00318    {
00319       s_skt = socket(alist->ai_family, alist->ai_socktype, alist->ai_protocol);
00320       if (s_skt == INVALID_SOCKET)
00321       {
00322          err = WSAGetLastError();
00323          s_fplogmsg(0, "netstream: socket error %d.\n", err);
00324       }
00325       else 
00326       {
00327          s_fplogmsg(0, "netstream: connecting to %s:%s.\n", host, service);
00328          err = connect(s_skt, (struct sockaddr *)alist->ai_addr, (int)alist->ai_addrlen);
00329          if (err != 0)
00330          {
00331             err = WSAGetLastError();
00332             s_fplogmsg(0, "netstream: connect failed, %d.\n", err);
00333          }
00334       }
00335       freeaddrinfo(alist);
00336    }
00337 
00338 #if HAVE_LIBSSL
00339    if (err != 0 && s_ctx != 0)
00340    {
00341       if ((s_ssl = SSL_new(s_ctx)) == 0)
00342       {
00343          s_fplogmsg(0, "netstream: SSL_new failed.\n");
00344          ns_disconnect();
00345          err = -1;
00346       }
00347       else
00348       {
00349          BIO * sbio;
00350          if ((sbio = BIO_new_socket((int)s_skt, BIO_NOCLOSE)) == 0)
00351          {
00352             s_fplogmsg(0, "netstream: BIO_new_socket failed.\n");
00353             ns_disconnect();
00354             err = -1;
00355          }
00356          else
00357          {
00358             SSL_set_bio(s_ssl, sbio, sbio);
00359             if (SSL_connect(s_ssl) <= 0)
00360             {
00361                s_fplogmsg(0, "netstream: SSL_connect failed, %d.\n", err);
00362                ns_disconnect();
00363                err = -1;
00364             }
00365             else if (s_srvauth_required
00366                   && (err = ns_check_cert(s_ssl, host)) != 0)
00367                ns_disconnect();
00368          }
00369       }
00370    }
00371 #endif
00372 
00373    return err;
00374 }
00375 
00385 static int ns_write(const char * msg, size_t msgsz)
00386 {
00387    int err = 0;
00388    const char * send_api = "send";
00389 
00390 #if HAVE_LIBSSL
00391    if (s_ctx != 0)
00392    {
00393       int r;
00394       send_api = "SSL_write";
00395       if ((r = SSL_write(s_ssl, msg, (int)msgsz)) > 0)
00396           r = SSL_write(s_ssl, "\r\n", 2);
00397       err = SSL_get_error(s_ssl, r);
00398    }
00399    else
00400 #endif
00401    {
00402       if (send(s_skt, msg, (int)msgsz, 0) < 0
00403             || send(s_skt, "\r\n", 2, 0) < 0)
00404          err = WSAGetLastError();
00405    }
00406    if (err != 0)
00407       s_fplogmsg(0, "netstream: %s failed (%d) on record [ %.*s ].\n", 
00408             send_api, err, msgsz, msg);
00409    return err;
00410 }
00411 
00422 XDMEXP int XDMAPI xdm_append(const char ** msgflds)
00423 {
00424    int err;
00425 
00426    /* has socket been permanently closed? */
00427    if (s_skt == INVALID_SOCKET)
00428       return -1;
00429 
00430    if ((err = ns_write(msgflds[0], msgflds[33] - msgflds[0] - 2)) != 0)
00431    {
00432       /* attempt a single reconnect and then try to write the record */
00433       s_fplogmsg(0, "netstream: attempting to reconnect...\n");
00434       if (ns_connect(s_srvstr, s_portstr) < 0)
00435       {
00436          s_fplogmsg(0, "netstream: reconnect failed, closing stream.\n");
00437          return -1;
00438       }
00439       err = ns_write(msgflds[0], msgflds[33] - msgflds[0] - 2);
00440    }
00441    return err;
00442 }
00443 
00448 XDMEXP void XDMAPI xdm_exit(void)
00449 {
00450    ns_disconnect();
00451 
00452 #if HAVE_LIBSSL
00453    if (s_ctx != 0)
00454       SSL_CTX_free(s_ctx), s_ctx = 0;
00455 #endif
00456 
00457 #ifdef _WIN32
00458    WSACleanup();
00459 #endif
00460 }
00461 
00468 XDMEXP int XDMAPI xdm_init(void (*logmsg)(int level, const char * msg, ... ),
00469       char * (*getcnfstr)(const char *, char *, size_t *))
00470 {
00471    int err;
00472    size_t srvstrsz = sizeof(s_srvstr);
00473    size_t portstrsz = sizeof(s_portstr);
00474 
00475    /* save function pointers */
00476    s_fplogmsg = logmsg;
00477    s_fpgetcnfstr = getcnfstr;
00478 
00479 #ifdef _WIN32
00480    {
00481       WSADATA wsadata;
00482       if ((err = WSAStartup(MAKEWORD(2, 2), &wsadata)) != 0)
00483       {
00484          s_fplogmsg(0, "netstream: winsock init failed, %d.\n", err);
00485          return err;
00486       }
00487    }
00488 #endif
00489 
00490 #if HAVE_LIBSSL
00491    {
00492       int secure = 0;
00493       char secstr[32];
00494       size_t secstrsz = sizeof(secstr);
00495 
00496       /* configured to use security? */
00497       s_fpgetcnfstr(CNF_SECURE, secstr, &secstrsz);
00498       if (secstrsz != 0)
00499          secure = (toupper(*secstr) == 'Y' || toupper(*secstr) == 'T');
00500 
00501       if (secure)
00502       {
00503          char trustfile[512];
00504          char keyfile[512];
00505          char keypwd[512];
00506          char ciphers[512];
00507          size_t trustfilesz = sizeof(trustfile);
00508          size_t keyfilesz = sizeof(keyfile);
00509          size_t keypwdsz = sizeof(keypwd);
00510          size_t cipherssz = sizeof(ciphers);
00511 
00512          /* read all configuration parameters into buffers */
00513          *trustfile = *keyfile = *keypwd = *ciphers = 0;
00514          s_fpgetcnfstr(CNF_TRUSTFILE, trustfile, &trustfilesz);
00515          s_fpgetcnfstr(CNF_KEYFILE, keyfile, &keyfilesz);
00516          s_fpgetcnfstr(CNF_KEYPWD, keypwd, &keypwdsz);
00517          s_fpgetcnfstr(CNF_CIPHERS, ciphers, &cipherssz);
00518 
00519          /* build ssl context - s_ctx used later to determine if secure */
00520          if ((s_ctx = initialize_ctx(trustfile, keyfile, keypwd, ciphers)) == 0)
00521          {
00522             s_fplogmsg(0, "netstream: SSL context initialization failed.\n");
00523             xdm_exit();
00524             return -1;
00525          }
00526       }
00527    }
00528 #endif
00529 
00530    /* read configuration criteria here */
00531    s_fpgetcnfstr(CNF_SERVER, s_srvstr, &srvstrsz);
00532    if (srvstrsz == 0)
00533       strcpy(s_srvstr, XDM_DEF_SRVSTR);
00534 
00535    s_fpgetcnfstr(CNF_PORT, s_portstr, &portstrsz);
00536    if (portstrsz == 0)
00537       strcpy(s_portstr, XDM_DEF_PORTSTR);
00538 
00539    /* connect to the specified server on the specified port */
00540    if ((err = ns_connect(s_srvstr, s_portstr)) < 0)
00541       xdm_exit();
00542 
00543    return err;
00544 }
00545 

Generated on Thu Aug 20 22:33:05 2009 for OpenXDAS by  doxygen 1.5.6