htparser: Fixed SSL memory leak.
[ashd.git] / src / ssl-gnutls.c
index 5247f2a..8e4ea5c 100644 (file)
@@ -20,6 +20,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <fcntl.h>
+#include <dirent.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -66,8 +67,6 @@ struct sslconn {
     struct charbuf in;
 };
 
-static gnutls_dh_params_t dhparams;
-
 static int tlsblock(int fd, gnutls_session_t sess, time_t to)
 {
     if(gnutls_record_get_direction(sess))
@@ -137,6 +136,9 @@ static ssize_t sslwrite(void *cookie, const char *buf, size_t len)
 
 static int sslclose(void *cookie)
 {
+    struct sslconn *ssl = cookie;
+    
+    buffree(ssl->in);
     return(0);
 }
 
@@ -259,6 +261,23 @@ out:
     free(pd);
 }
 
+static gnutls_dh_params_t dhparams(void)
+{
+    static int inited = 0;
+    static gnutls_dh_params_t pars;
+    int ret;
+    
+    if(!inited) {
+       if(((ret = gnutls_dh_params_init(&pars)) != 0) ||
+          ((ret = gnutls_dh_params_generate2(pars, 2048)) != 0)) {
+           flog(LOG_ERR, "GnuTLS could not generate Diffie-Hellman parameters: %s", gnutls_strerror(ret));
+           exit(1);
+       }
+       inited = 1;
+    }
+    return(pars);
+}
+
 static void init(void)
 {
     static int inited = 0;
@@ -271,11 +290,6 @@ static void init(void)
        flog(LOG_ERR, "could not initialize GnuTLS: %s", gnutls_strerror(ret));
        exit(1);
     }
-    if(((ret = gnutls_dh_params_init(&dhparams)) != 0) ||
-       ((ret = gnutls_dh_params_generate2(dhparams, 2048)) != 0)) {
-       flog(LOG_ERR, "GnuTLS could not generate Diffie-Hellman parameters: %s", gnutls_strerror(ret));
-       exit(1);
-    }
 }
 
 static struct namedcreds *readncreds(char *file)
@@ -345,10 +359,32 @@ static struct namedcreds *readncreds(char *file)
        flog(LOG_ERR, "ssl: could not use certificate from %s: %s", file, gnutls_strerror(ret));
        exit(1);
     }
-    gnutls_certificate_set_dh_params(nc->creds, dhparams);
+    gnutls_certificate_set_dh_params(nc->creds, dhparams());
     return(nc);
 }
 
+static void readncdir(struct ncredbuf *buf, char *dir)
+{
+    DIR *d;
+    struct dirent *e;
+    size_t es;
+    
+    if((d = opendir(dir)) == NULL) {
+       flog(LOG_ERR, "ssl: could not read certificate directory %s: %s", dir, strerror(errno));
+       exit(1);
+    }
+    while((e = readdir(d)) != NULL) {
+       if(e->d_name[0] == '.')
+           continue;
+       if((es = strlen(e->d_name)) <= 4)
+           continue;
+       if(strcmp(e->d_name + es - 4, ".crt"))
+           continue;
+       bufadd(*buf, readncreds(sprintf3("%s/%s", dir, e->d_name)));
+    }
+    closedir(d);
+}
+
 void handlegnussl(int argc, char **argp, char **argv)
 {
     int i, ret, port, fd;
@@ -375,11 +411,24 @@ void handlegnussl(int argc, char **argp, char **argv)
            printf("\tcrl=CRL-FILE    [no default]\n");
            printf("\t\tThe name of a file to read revocation lists from.\n");
            printf("\t\tMay be given multiple times.\n");
+           printf("\tncert=CERT-FILE [no default]\n");
+           printf("\t\tThe name of a file to read a named certificate from,\n");
+           printf("\t\tfor use with SNI-enabled clients.\n");
+           printf("\t\tMay be given multiple times.\n");
+           printf("\tncertdir=DIR    [no default]\n");
+           printf("\t\tRead all *.crt files in the given directory as if they\n");
+           printf("\t\twere given with `ncert' options.\n");
+           printf("\t\tMay be given multiple times.\n");
            printf("\tport=PORT       [443]\n");
            printf("\t\tThe TCP port to listen on.\n");
            printf("\n");
            printf("\tAll X.509 data files must be PEM-encoded.\n");
-           printf("\tSee the manpage for information on specifying multiple\n\tcertificates to support SNI operation.\n");
+           printf("\tIf any certificates were given with `ncert' options, they will be\n");
+           printf("\tused if a client explicitly names one of them with a\n");
+           printf("\tserver-name indication. If a client indicates no server name,\n");
+           printf("\tor if a server-name indication does not match any given\n");
+           printf("\tcertificate, the certificate given with the `cert' option will\n");
+           printf("\tbe used instead.\n");
            exit(0);
        } else if(!strcmp(argp[i], "cert")) {
            crtfile = argv[i];
@@ -411,6 +460,8 @@ void handlegnussl(int argc, char **argp, char **argv)
            port = atoi(argv[i]);
        } else if(!strcmp(argp[i], "ncert")) {
            bufadd(ncreds, readncreds(argv[i]));
+       } else if(!strcmp(argp[i], "ncertdir")) {
+           readncdir(&ncreds, argv[i]);
        } else {
            flog(LOG_ERR, "unknown parameter `%s' to ssl handler", argp[i]);
            exit(1);
@@ -420,17 +471,17 @@ void handlegnussl(int argc, char **argp, char **argv)
        flog(LOG_ERR, "ssl: needs certificate file at the very least");
        exit(1);
     }
+    if((fd = listensock6(port)) < 0) {
+       flog(LOG_ERR, "could not listen on IPv6 port (port %i): %s", port, strerror(errno));
+       exit(1);
+    }
     if(keyfile == NULL)
        keyfile = crtfile;
     if((ret = gnutls_certificate_set_x509_key_file(creds, crtfile, keyfile, GNUTLS_X509_FMT_PEM)) != 0) {
        flog(LOG_ERR, "ssl: could not load certificate or key: %s", gnutls_strerror(ret));
        exit(1);
     }
-    gnutls_certificate_set_dh_params(creds, dhparams);
-    if((fd = listensock6(port)) < 0) {
-       flog(LOG_ERR, "could not listen on IPv6 port (port %i): %s", port, strerror(errno));
-       exit(1);
-    }
+    gnutls_certificate_set_dh_params(creds, dhparams());
     bufadd(ncreds, NULL);
     omalloc(pd);
     pd->fd = fd;