summaryrefslogtreecommitdiffstats
path: root/tcpd/tlscache.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /tcpd/tlscache.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'tcpd/tlscache.c')
-rw-r--r--tcpd/tlscache.c714
1 files changed, 714 insertions, 0 deletions
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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#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);
+}