From 9c45d9ad13fdf439d44d7443ae75da15ea0223ed Mon Sep 17 00:00:00 2001 From: Sam Varshavchik Date: Mon, 19 Aug 2013 16:39:41 -0400 Subject: Initial checkin Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy. --- tcpd/tlscache.c | 714 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 714 insertions(+) create mode 100644 tcpd/tlscache.c (limited to 'tcpd/tlscache.c') diff --git a/tcpd/tlscache.c b/tcpd/tlscache.c new file mode 100644 index 0000000..0a4cd5d --- /dev/null +++ b/tcpd/tlscache.c @@ -0,0 +1,714 @@ +/* +** Copyright 2002 Double Precision, Inc. +** See COPYING for distribution information. +*/ +#include "config.h" +#include "numlib/numlib.h" +#include "liblock/config.h" +#include "liblock/liblock.h" +#include +#include +#include +#include +#include + +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_FCNTL_H +#include +#endif + +#include "tlscache.h" + + +/* +** The cache file begins with the following record: +*/ + +struct hdr { + off_t filesize; /* Size of the file, don't trust fstat */ + off_t head, tail; /* Head and tail ptrs */ + off_t first; /* See below */ +}; + +#ifndef TLSCACHEMINSIZE +#define TLSCACHEMINSIZE 16384 +#endif + +#define BORK(p) BORK2(__LINE__, (p)) + +static void BORK2(int l, const char *p) +{ + fprintf(stderr, "ALERT: tlscache.c(%d): corruption detected in %s\n", + (l), (p)); +} + +/* +** Cached SSL session objects are written starting at the end of the file +** the file, growing to the beginning of the file. The head pointer +** points to the most recently added cached object. When the beginning of +** the file is reached, it's wrapped around. +** +** After the wraparound, old SSL session objects are freed. starting with +** the tail ptr, to make room for newer object. There may be unused space +** between struct hdr, and the first cached object, the 'first' pointer +** helps me find the first cached object, when searching. +** When searching for a cached object, begin at the head ptr (most recent, +** and continue until we find an object referenced by the tail ptr). +** +** Each cached object carries the following header. +*/ + +struct obj { + size_t prev_size; /* Size of the previous cached object, + ** this must be the first member of this + ** struct. + */ + + size_t my_size; /* Size of this cached object */ +}; + +/* Read cnt number of bytes, or else */ + +static int my_read(int fd, void *buffer, size_t cnt) +{ + char *p=(char *)buffer; + + while (cnt > 0) + { + int n=read(fd, p, cnt); + + if (n <= 0) + return n; + p += n; + cnt -= n; + } + return 1; +} + +/* Write cnt number of bytes, or else */ + +static int my_write(int fd, const void *buffer, size_t cnt) +{ + const char *p=(const char *)buffer; + + while (cnt > 0) + { + int n=write(fd, p, cnt); + + if (n <= 0) + return -1; + p += n; + cnt -= n; + } + return 0; +} + +static int init(struct CACHE *, off_t s); + +void tls_cache_close(struct CACHE *p) +{ + if (p->filename != NULL) + free(p->filename); + if (p->fd >= 0) + close(p->fd); + free(p); +} + +/* +** Open a cache file, creating one if necessary +*/ + +struct CACHE *tls_cache_open(const char *filename, off_t req_size) +{ + struct CACHE *p=malloc(sizeof(struct CACHE)); + struct hdr h; + int rc; + + if (!p) return NULL; + + if ((p->fd=open(filename, O_RDWR|O_CREAT, 0600)) < 0) + { + free(p); + return NULL; + } + + if ((p->filename=strdup(filename)) == NULL) + { + close(p->fd); + free(p); + return (NULL); + } + + rc=my_read(p->fd, &h, sizeof(h)); + + if (rc < 0) + { + tls_cache_close(p); + return (NULL); + } + + if (rc == 0 || h.filesize == 0) + { + /* Once again, but this time lock it */ + + if (ll_lock_ex(p->fd) < 0 || + lseek(p->fd, 0, SEEK_SET) < 0) + { + tls_cache_close(p); + return (NULL); + } + + rc=my_read(p->fd, &h, sizeof(h)); + + if (rc < 0) + { + tls_cache_close(p); + return (NULL); + } + + if (rc == 0 || h.filesize == 0) + { + if (init(p, req_size)) + { + tls_cache_close(p); + return (NULL); + } + } + ll_unlock_ex(p->fd); + } + return p; +} + +static int doadd(struct CACHE *p, const char *val, size_t vallen); + +int tls_cache_add(struct CACHE *p, const char *val, size_t vallen) +{ + int rc; + + if (p->fd < 0) + return (0); /* Previous error invalidated obj */ + + if (ll_lock_ex(p->fd) < 0) + { + close(p->fd); + p->fd= -1; + return (-1); + } + + rc=doadd(p, val, vallen); + + if (rc < 0 && p->fd >= 0) + { + close(p->fd); + p->fd= -1; + unlink(p->filename); /* Blow it away, something's wrong */ + perror("ALERT: tlscache.c: "); + fprintf(stderr, "ALERT: tlscache.c: removing %s\n", + p->filename); + } + + if (p->fd >= 0 && ll_unlock_ex(p->fd) < 0) + { + close(p->fd); + p->fd= -1; + rc= -1; + } + + if (rc != 0) + rc= -1; + + return rc; +} + +/* +** Read the header, and do a simple sanity check +*/ + +static int readhdr(struct CACHE *p, struct hdr *h) +{ + if (lseek(p->fd, 0, SEEK_SET) < 0 || + my_read(p->fd, h, sizeof(*h)) <= 0) + { + BORK(p->filename); + return (-1); + } + + if (h->filesize < TLSCACHEMINSIZE || h->head >= h->filesize || + h->tail >= h->filesize || h->first >= h->filesize || + h->head < 0 || h->tail < 0 || h->first < 0 || + h->first > h->head || h->first > h->tail) + { + BORK(p->filename); + return (-1); /* Sanity check */ + } + return (0); +} + +static int writehdr(struct CACHE *p, const struct hdr *h) +{ + if (lseek(p->fd, 0, SEEK_SET) < 0 || + my_write(p->fd, h, sizeof(*h)) < 0) + { + BORK(p->filename); + return -1; + } + return 0; +} + +/* +** Read the header of a cached object, and do a sanity check +*/ + +static int readobj(struct CACHE *p, off_t w, + const struct hdr *h, struct obj *o) +{ + if (lseek(p->fd, w, SEEK_SET) < 0 || + my_read(p->fd, o, sizeof(*o)) <= 0) + { + BORK(p->filename); + return (-1); + } + + if (o->prev_size < sizeof(*o) || o->my_size < sizeof(*o) || + o->prev_size >= h->filesize || o->my_size >= h->filesize || + h->filesize - w < o->my_size) + { + BORK(p->filename); + errno=EIO; + return (-1); /* Sanity check */ + } + return 0; +} + + +static int doadd(struct CACHE *p, const char *val, size_t vallen) +{ + struct hdr h; + struct obj o; + int timer=0; + int first=0; + char *buf; + + if (readhdr(p, &h)) + return -1; + + /* Keep trying to allocate sufficient space in the cache file */ + + for (;;) + { + if (++timer > 100) + { + BORK(p->filename); + errno=EIO; + return (-1); /* Sanity check */ + } + + if (h.head == 0 && h.tail == 0) /* First time */ + { + if (vallen + sizeof(struct obj) + + sizeof(struct hdr) > h.filesize) + { + errno=ENOSPC; + return 1; + } + + /* First cached object goes at the end */ + + h.head=h.tail=h.filesize - vallen - sizeof(struct obj); + first=1; + break; + } + + if (h.head <= h.tail) /* Not wrapped around */ + { + if (h.head >= sizeof(struct hdr) + + sizeof(struct obj) + vallen) + { + h.head -= sizeof(struct obj) + vallen; + break; + /* Room earlier in the file */ + } + + /* + ** No room before, we must now wrap around. Find + ** where the last object ends, and see if there's + ** enough room between the end of the last object, + ** and the end of the file, to save the new object. + */ + + if (readobj(p, h.tail, &h, &o) < 0) + return -1; + + if (h.filesize - h.tail - o.my_size >= + sizeof(struct obj) + vallen) + { + h.first=h.head; + h.head=h.filesize - vallen - + sizeof(struct obj); + /* Room to wrap around */ + + break; + } + } + else /* We're currently wrapped around, so all the free + ** space is from tail to head. + */ + { + if (readobj(p, h.tail, &h, &o) < 0) + return -1; + + if (h.head >= h.tail + o.my_size + + sizeof(struct obj) + vallen) + { + h.head -= sizeof(struct obj) + vallen; + break; + } + } + + if (h.head == h.tail) /* Sanity check */ + { + errno=ENOSPC; + return 1; + } + + /* Pop one off tail */ + + if (readobj(p, h.tail, &h, &o)) + return -1; + + if (sizeof(h) + o.prev_size <= h.tail) + { + h.tail -= o.prev_size; + + if (writehdr(p, &h)) + return (-1); + continue; + } + + if (h.tail != h.first) + { + BORK(p->filename); + errno=EIO; + return (-1); /* Sanity check */ + } + + h.first=0; + h.tail=h.filesize - o.prev_size; + + if (h.tail < h.first) + { + BORK(p->filename); + errno=EIO; + return (-1); /* Sanity check */ + } + + if (writehdr(p, &h)) + return (-1); + } + + buf=malloc(vallen + sizeof(o) + sizeof(o.prev_size)); + + if (!buf) + return (1); + + o.prev_size=0; + o.my_size=vallen + sizeof(o); + memcpy(buf, &o, sizeof(o)); + memcpy(buf + sizeof(o), val, vallen); + o.prev_size=o.my_size; + memcpy(buf + sizeof(o) + vallen, &o.prev_size, sizeof(o.prev_size)); + + if (lseek(p->fd, h.head, SEEK_SET) < 0) + return (-1); + + if (h.head + sizeof(o) + vallen < h.filesize) + { + if (my_write(p->fd, buf, sizeof(o)+vallen+sizeof(o.prev_size)) < 0) + return -1; + } + else + { + if (my_write(p->fd, buf, sizeof(o)+vallen) < 0 || + (!first && (lseek(p->fd, h.first, SEEK_SET) < 0 || + my_write(p->fd, &o.prev_size, + sizeof(o.prev_size)) < 0))) + return -1; + } + + return writehdr(p, &h); +} + +static int init(struct CACHE *p, off_t size) +{ + char buffer[BUFSIZ]; + off_t c; + struct hdr h; + + if (size < TLSCACHEMINSIZE) + { + errno=EINVAL; + return -1; + } + + if (lseek(p->fd, 0, SEEK_SET) < 0) + return -1; + + memset(buffer, 0, sizeof(buffer)); + + c=size; + + while (c > 0) + { + int i; + + off_t n=c; + + if (n > sizeof(buffer)) + n=sizeof(buffer); + + i=write(p->fd, buffer, n); + + if (i <= 0) + return -1; + + c -= i; + } + + memset(&h, 0, sizeof(h)); + h.filesize=size; + + if (lseek(p->fd, 0, SEEK_SET) < 0 || + my_write(p->fd, &h, sizeof(h))) + return (-1); + return (0); +} + +static int dowalk(struct CACHE *cache, + int (*walk_func)(void *rec, size_t recsize, + int *doupdate, + void *arg), + void *arg); + +int tls_cache_walk(struct CACHE *p, + int (*walk_func)(void *rec, size_t recsize, + int *doupdate, + void *arg), + void *arg) +{ + int rc; + + if (p->fd < 0) + return (0); /* Previous error invalidated obj */ + + if (ll_lockfd(p->fd, ll_readlock||ll_whence_start|ll_wait, 0, 0) < 0) + { + /* Some locking methods don't support readonly locks */ + + if (ll_lock_ex(p->fd) < 0) + { + close(p->fd); + p->fd= -1; + return (-1); + } + } + + rc=dowalk(p, walk_func, arg); + + if (rc < 0 && p->fd >= 0) + { + close(p->fd); + p->fd= -1; + unlink(p->filename); + perror("ALERT: tlscache.c: "); + fprintf(stderr, "ALERT: tlscache.c: removing %s\n", + p->filename); + } + + if (p->fd >= 0 && ll_unlock_ex(p->fd) < 0) + { + close(p->fd); + p->fd= -1; + rc= -1; + } + + return rc; +} + +/* Buffered reads when searching, for speed */ + +struct walkbuf { + char buffer[BUFSIZ]; + char *bufptr; + int left; +}; + +static int buf_read(int fd, struct walkbuf *w, + const void *buffer, size_t cnt) +{ + char *p=(char *)buffer; + + while (cnt > 0) + { + if (w->left <= 0) + { + w->left=read(fd, w->buffer, sizeof(w->buffer)); + + if (w->left <= 0) + return -1; + w->bufptr=w->buffer; + } + + *p++ = *w->bufptr++; + --w->left; + --cnt; + } + return 1; +} + +static int dowalk(struct CACHE *p, + int (*walk_func)(void *rec, size_t recsize, + int *doupdate, void *arg), + void *arg) +{ + struct hdr h; + struct obj o; + char *buf=NULL; + size_t bufsize=0; + int rc; + int counter; + struct walkbuf wb; + int updateflag; + + off_t pos; + off_t lastpos; + + if (readhdr(p, &h)) + return -1; + + if (h.head == 0 && h.tail == 0) /* First time */ + return (0); + + pos=h.head; + if (lseek(p->fd, pos, SEEK_SET) < 0) + return (-1); + + counter=0; + wb.left=0; + for (;;) + { + if (++counter > h.filesize / sizeof(o)) + { + BORK(p->filename); + return (-1); + } + + if (h.filesize - pos < sizeof(o)) + { + BORK(p->filename); + errno=EIO; + if (buf) + free(buf); + return (-1); /* Sanity check */ + } + + if (buf_read(p->fd, &wb, &o, sizeof(o)) <= 0) + { + if (buf) + free(buf); + return (-1); + } + + if (h.filesize - pos < o.my_size || o.my_size < sizeof(o)) + { + BORK(p->filename); + errno=EIO; + if (buf) + free(buf); + return (-1); /* Sanity check */ + } + + if (buf == NULL || bufsize < o.my_size - sizeof(o)+1) + { + char *newbuf; + + bufsize=o.my_size - sizeof(o)+1; + + if ((newbuf=buf ? realloc(buf, bufsize): + malloc(bufsize)) == NULL) + { + free(buf); + return (-1); + } + buf=newbuf; + } + + if (buf_read(p->fd, &wb, buf, o.my_size - sizeof(o)) <= 0) + { + free(buf); + return (-1); + } + + updateflag=0; + + rc= (*walk_func)(buf, o.my_size - sizeof(o), &updateflag, arg); + + if (updateflag && rc >= 0) + { + if (lseek(p->fd, pos + sizeof(o), SEEK_SET) < 0 || + my_write(p->fd, buf, o.my_size - sizeof(o)) < 0) + { + free(buf); + return (-1); + } + wb.left=0; + } + + if (rc != 0) + { + free(buf); + if (rc < 0) + rc=1; + return (rc); + } + + if (pos == h.tail) + break; + + lastpos=pos; + pos += o.my_size; + + if (pos < h.filesize) + { + if (lastpos < h.tail && pos > h.tail) + { + BORK(p->filename); + errno=EIO; + free(buf); + return (-1); + } + } + else + { + pos=h.first; + if (h.first < sizeof(h)) + { + BORK(p->filename); + free(buf); + errno=EIO; + return (-1); + } + + if (lseek(p->fd, pos, SEEK_SET) < 0) + { + free(buf); + return (-1); + } + wb.left=0; + } + } + if (buf) + free(buf); + return (0); +} -- cgit v1.2.3