X-Git-Url: http://dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fssl-gnutls.c;h=a181adda62917e31f6f63d607eab0f6252806bf8;hb=a5f636534f95a749b4e0e002cccb3267c53da009;hp=3eca0bb0d7db7c2f1b8c967fd5a9cfefe6756ba6;hpb=e99e87db1f197126909a3c1a944200ce5d502f92;p=ashd.git diff --git a/src/ssl-gnutls.c b/src/ssl-gnutls.c index 3eca0bb..a181add 100644 --- a/src/ssl-gnutls.c +++ b/src/ssl-gnutls.c @@ -56,6 +56,7 @@ struct sslport { int fd; int sport; gnutls_certificate_credentials_t creds; + gnutls_priority_t ciphers; struct namedcreds **ncreds; }; @@ -67,7 +68,117 @@ struct sslconn { struct charbuf in; }; -static gnutls_dh_params_t dhparams; +struct savedsess { + struct savedsess *next, *prev; + gnutls_datum_t key, value; +}; + +static int numconn = 0, numsess = 0; +static struct btree *sessidx = NULL; +static struct savedsess *sesslistf = NULL, *sesslistl = NULL; + +static int sesscmp(void *ap, void *bp) +{ + struct savedsess *a = ap, *b = bp; + + if(a->key.size != b->key.size) + return(a->key.size - b->key.size); + return(memcmp(a->key.data, b->key.data, a->key.size)); +} + +static gnutls_datum_t sessdbfetch(void *uudata, gnutls_datum_t key) +{ + struct savedsess *sess, lkey; + gnutls_datum_t ret; + + memset(&ret, 0, sizeof(ret)); + lkey.key = key; + if((sess = btreeget(sessidx, &lkey, sesscmp)) == NULL) + return(ret); + ret.data = memcpy(gnutls_malloc(ret.size = sess->value.size), sess->value.data, sess->value.size); + return(ret); +} + +static void freesess(struct savedsess *sess) +{ + bbtreedel(&sessidx, sess, sesscmp); + if(sess->next) + sess->next->prev = sess->prev; + if(sess->prev) + sess->prev->next = sess->next; + if(sess == sesslistf) + sesslistf = sess->next; + if(sess == sesslistl) + sesslistl = sess->prev; + free(sess->key.data); + free(sess->value.data); + free(sess); + numsess--; +} + +static int sessdbdel(void *uudata, gnutls_datum_t key) +{ + struct savedsess *sess, lkey; + + lkey.key = key; + if((sess = btreeget(sessidx, &lkey, sesscmp)) == NULL) + return(-1); + freesess(sess); + return(0); +} + +static void cleansess(void) +{ + while(numsess > (max(numconn, 1) * 100)) + freesess(sesslistl); +} + +static int sessdbstore(void *uudata, gnutls_datum_t key, gnutls_datum_t value) +{ + static int cc = 0; + struct savedsess *sess, lkey; + + if((value.data == NULL) || (value.size == 0)) { + sessdbdel(NULL, key); + return(0); + } + lkey.key = key; + if((sess = btreeget(sessidx, &lkey, sesscmp)) == NULL) { + omalloc(sess); + sess->key.data = memcpy(smalloc(sess->key.size = key.size), key.data, key.size); + sess->value.data = memcpy(smalloc(sess->value.size = value.size), value.data, value.size); + bbtreeput(&sessidx, sess, sesscmp); + sess->prev = NULL; + sess->next = sesslistf; + if(sesslistf) + sesslistf->prev = sess; + sesslistf = sess; + if(sesslistl == NULL) + sesslistl = sess; + numsess++; + } else { + free(sess->value.data); + sess->value.data = memcpy(smalloc(sess->value.size = value.size), value.data, value.size); + if(sess != sesslistf) { + if(sess->next) + sess->next->prev = sess->prev; + if(sess->prev) + sess->prev->next = sess->next; + if(sess == sesslistl) + sesslistl = sess->prev; + sess->prev = NULL; + sess->next = sesslistf; + if(sesslistf) + sesslistf->prev = sess; + sesslistf = sess; + } + } + if(cc++ > 100) { + cleansess(); + cc = 0; + } + return(0); +} static int tlsblock(int fd, gnutls_session_t sess, time_t to) { @@ -138,6 +249,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); } @@ -152,16 +266,12 @@ static int initreq(struct conn *conn, struct hthead *req) struct sslconn *ssl = conn->pdata; struct sockaddr_storage sa; socklen_t salen; - char nmbuf[256]; headappheader(req, "X-Ash-Address", formathaddress((struct sockaddr *)&ssl->name, sizeof(sa))); - if(ssl->name.ss_family == AF_INET) { - headappheader(req, "X-Ash-Address", inet_ntop(AF_INET, &((struct sockaddr_in *)&ssl->name)->sin_addr, nmbuf, sizeof(nmbuf))); + if(ssl->name.ss_family == AF_INET) headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in *)&ssl->name)->sin_port))); - } else if(ssl->name.ss_family == AF_INET6) { - headappheader(req, "X-Ash-Address", inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&ssl->name)->sin6_addr, nmbuf, sizeof(nmbuf))); + else if(ssl->name.ss_family == AF_INET6) headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in6 *)&ssl->name)->sin6_port))); - } salen = sizeof(sa); if(!getsockname(ssl->fd, (struct sockaddr *)&sa, &salen)) headappheader(req, "X-Ash-Server-Address", formathaddress((struct sockaddr *)&sa, sizeof(sa))); @@ -209,9 +319,14 @@ static void servessl(struct muth *muth, va_list args) return(0); } + numconn++; fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); gnutls_init(&sess, GNUTLS_SERVER); - gnutls_set_default_priority(sess); + gnutls_priority_set(sess, pd->ciphers); + gnutls_db_set_retrieve_function(sess, sessdbfetch); + gnutls_db_set_store_function(sess, sessdbstore); + gnutls_db_set_remove_function(sess, sessdbdel); + gnutls_db_set_ptr(sess, NULL); gnutls_handshake_set_post_client_hello_function(sess, setcreds); gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t)(intptr_t)fd); while((ret = gnutls_handshake(sess)) != 0) { @@ -235,29 +350,62 @@ static void servessl(struct muth *muth, va_list args) out: gnutls_deinit(sess); close(fd); + numconn--; } static void listenloop(struct muth *muth, va_list args) { vavar(struct sslport *, pd); - int ns; + int i, ns, n; struct sockaddr_storage name; socklen_t namelen; + fcntl(pd->fd, F_SETFL, fcntl(pd->fd, F_GETFL) | O_NONBLOCK); while(1) { namelen = sizeof(name); - block(pd->fd, EV_READ, 0); - ns = accept(pd->fd, (struct sockaddr *)&name, &namelen); - if(ns < 0) { - flog(LOG_ERR, "accept: %s", strerror(errno)); + if(block(pd->fd, EV_READ, 0) == 0) goto out; + n = 0; + while(1) { + ns = accept(pd->fd, (struct sockaddr *)&name, &namelen); + if(ns < 0) { + if(errno == EAGAIN) + break; + if(errno == ECONNABORTED) + continue; + flog(LOG_ERR, "accept: %s", strerror(errno)); + goto out; + } + mustart(servessl, ns, name, pd); + if(++n >= 100) + break; } - mustart(servessl, ns, name, pd); } out: close(pd->fd); free(pd); + for(i = 0; i < listeners.d; i++) { + if(listeners.b[i] == muth) + bufdel(listeners, i); + } +} + +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) @@ -272,11 +420,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) @@ -346,7 +489,7 @@ 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); } @@ -376,15 +519,17 @@ void handlegnussl(int argc, char **argp, char **argv) { int i, ret, port, fd; gnutls_certificate_credentials_t creds; + gnutls_priority_t ciphers; struct ncredbuf ncreds; struct sslport *pd; - char *crtfile, *keyfile; + char *crtfile, *keyfile, *perr; init(); port = 443; bufinit(ncreds); gnutls_certificate_allocate_credentials(&creds); keyfile = crtfile = NULL; + ciphers = NULL; for(i = 0; i < argc; i++) { if(!strcmp(argp[i], "help")) { printf("ssl handler parameters:\n"); @@ -392,6 +537,8 @@ void handlegnussl(int argc, char **argp, char **argv) printf("\t\tThe name of the file to read the certificate from.\n"); printf("\tkey=KEY-FILE [same as CERT-FILE]\n"); printf("\t\tThe name of the file to read the private key from.\n"); + printf("\tprio=PRIORITIES [NORMAL]\n"); + printf("\t\tCiphersuite priorities, as a GnuTLS priority string.\n"); printf("\ttrust=CA-FILE [no default]\n"); printf("\t\tThe name of a file to read trusted certificates from.\n"); printf("\t\tMay be given multiple times.\n"); @@ -421,6 +568,17 @@ void handlegnussl(int argc, char **argp, char **argv) crtfile = argv[i]; } else if(!strcmp(argp[i], "key")) { keyfile = argv[i]; + } else if(!strcmp(argp[i], "prio")) { + if(ciphers != NULL) + gnutls_priority_deinit(ciphers); + ret = gnutls_priority_init(&ciphers, argv[i], (const char **)&perr); + if(ret == GNUTLS_E_INVALID_REQUEST) { + flog(LOG_ERR, "ssl: invalid cipher priority string, at `%s'", perr); + exit(1); + } else if(ret != 0) { + flog(LOG_ERR, "ssl: could not initialize cipher priorities: %s", gnutls_strerror(ret)); + exit(1); + } } else if(!strcmp(argp[i], "trust")) { if((ret = gnutls_certificate_set_x509_trust_file(creds, argv[i], GNUTLS_X509_FMT_PEM)) != 0) { flog(LOG_ERR, "ssl: could not load trust file `%s': %s", argv[i], gnutls_strerror(ret)); @@ -458,27 +616,32 @@ 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)); + if((ciphers == NULL) && ((ret = gnutls_priority_init(&ciphers, "NORMAL", NULL)) != 0)) { + flog(LOG_ERR, "ssl: could not initialize cipher priorities: %s", gnutls_strerror(ret)); exit(1); } + gnutls_certificate_set_dh_params(creds, dhparams()); bufadd(ncreds, NULL); omalloc(pd); pd->fd = fd; pd->sport = port; pd->creds = creds; pd->ncreds = ncreds.b; - mustart(listenloop, pd); - if((fd = listensock6(port)) < 0) { + pd->ciphers = ciphers; + bufadd(listeners, mustart(listenloop, pd)); + if((fd = listensock4(port)) < 0) { if(errno != EADDRINUSE) { - flog(LOG_ERR, "could not listen on IPv6 port (port %i): %s", port, strerror(errno)); + flog(LOG_ERR, "could not listen on IPv4 port (port %i): %s", port, strerror(errno)); exit(1); } } else { @@ -486,7 +649,9 @@ void handlegnussl(int argc, char **argp, char **argv) pd->fd = fd; pd->sport = port; pd->creds = creds; - mustart(listenloop, pd); + pd->ncreds = ncreds.b; + pd->ciphers = ciphers; + bufadd(listeners, mustart(listenloop, pd)); } }