Back to index

courier  0.68.2
tlscache.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2002 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 #include      "config.h"
00006 #include      "numlib/numlib.h"
00007 #include      "liblock/config.h"
00008 #include      "liblock/liblock.h"
00009 #include      <stdio.h>
00010 #include      <string.h>
00011 #include      <stdlib.h>
00012 #include      <errno.h>
00013 #include      <ctype.h>
00014 
00015 #if HAVE_SYS_STAT_H
00016 #include      <sys/stat.h>
00017 #endif
00018 #if HAVE_FCNTL_H
00019 #include      <fcntl.h>
00020 #endif
00021 
00022 #include      "tlscache.h"
00023 
00024 
00025 /*
00026 ** The cache file begins with the following record:
00027 */
00028 
00029 struct hdr {
00030        off_t filesize;             /* Size of the file, don't trust fstat */
00031        off_t head, tail;    /* Head and tail ptrs */
00032        off_t first;         /* See below */
00033 };
00034 
00035 #ifndef TLSCACHEMINSIZE
00036 #define TLSCACHEMINSIZE 16384
00037 #endif
00038 
00039 #define BORK(p) BORK2(__LINE__, (p))
00040 
00041 static void BORK2(int l, const char *p)
00042 {
00043        fprintf(stderr, "ALERT: tlscache.c(%d): corruption detected in %s\n",
00044               (l), (p));
00045 }
00046 
00047 /*
00048 ** Cached SSL session objects are written starting at the end of the file
00049 ** the file, growing to the beginning of the file.  The head pointer
00050 ** points to the most recently added cached object.  When the beginning of
00051 ** the file is reached, it's wrapped around.
00052 **
00053 ** After the wraparound, old SSL session objects are freed. starting with
00054 ** the tail ptr, to make room for newer object.  There may be unused space
00055 ** between struct hdr, and the first cached object, the 'first' pointer
00056 ** helps me find the first cached object, when searching.
00057 ** When searching for a cached object, begin at the head ptr (most recent,
00058 ** and continue until we find an object referenced by the tail ptr).
00059 **
00060 ** Each cached object carries the following header.
00061 */
00062 
00063 struct obj {
00064        size_t prev_size;    /* Size of the previous cached object,
00065                             ** this must be the first member of this
00066                             ** struct.
00067                             */
00068 
00069        size_t my_size;             /* Size of this cached object */
00070 };
00071 
00072 /* Read cnt number of bytes, or else */
00073 
00074 static int my_read(int fd, void *buffer, size_t cnt)
00075 {
00076        char *p=(char *)buffer;
00077 
00078        while (cnt > 0)
00079        {
00080               int n=read(fd, p, cnt);
00081 
00082               if (n <= 0)
00083                      return n;
00084               p += n;
00085               cnt -= n;
00086        }
00087        return 1;
00088 }
00089 
00090 /* Write cnt number of bytes, or else */
00091 
00092 static int my_write(int fd, const void *buffer, size_t cnt)
00093 {
00094        const char *p=(const char *)buffer;
00095 
00096        while (cnt > 0)
00097        {
00098               int n=write(fd, p, cnt);
00099 
00100               if (n <= 0)
00101                      return -1;
00102               p += n;
00103               cnt -= n;
00104        }
00105        return 0;
00106 }
00107 
00108 static int init(struct CACHE *, off_t s);
00109 
00110 void tls_cache_close(struct CACHE *p)
00111 {
00112        if (p->filename != NULL)
00113               free(p->filename);
00114        if (p->fd >= 0)
00115               close(p->fd);
00116        free(p);
00117 }
00118 
00119 /*
00120 ** Open a cache file, creating one if necessary
00121 */
00122 
00123 struct CACHE *tls_cache_open(const char *filename, off_t req_size)
00124 {
00125        struct CACHE *p=malloc(sizeof(struct CACHE));
00126        struct hdr h;
00127        int rc;
00128 
00129        if (!p) return NULL;
00130 
00131        if ((p->fd=open(filename, O_RDWR|O_CREAT, 0600)) < 0)
00132        {
00133               free(p);
00134               return NULL;
00135        }
00136 
00137        if ((p->filename=strdup(filename)) == NULL)
00138        {
00139               close(p->fd);
00140               free(p);
00141               return (NULL);
00142        }
00143 
00144        rc=my_read(p->fd, &h, sizeof(h));
00145 
00146        if (rc < 0)
00147        {
00148               tls_cache_close(p);
00149               return (NULL);
00150        }
00151 
00152        if (rc == 0 || h.filesize == 0)
00153        {
00154               /* Once again, but this time lock it */
00155 
00156               if (ll_lock_ex(p->fd) < 0 ||
00157                   lseek(p->fd, 0, SEEK_SET) < 0)
00158               {
00159                      tls_cache_close(p);
00160                      return (NULL);
00161               }
00162 
00163               rc=my_read(p->fd, &h, sizeof(h));
00164 
00165               if (rc < 0)
00166               {
00167                      tls_cache_close(p);
00168                      return (NULL);
00169               }
00170 
00171               if (rc == 0 || h.filesize == 0)
00172               {
00173                      if (init(p, req_size))
00174                      {
00175                             tls_cache_close(p);
00176                             return (NULL);
00177                      }
00178               }
00179               ll_unlock_ex(p->fd);
00180        }
00181        return p;
00182 }
00183 
00184 static int doadd(struct CACHE *p, const char *val, size_t vallen);
00185 
00186 int tls_cache_add(struct CACHE *p, const char *val, size_t vallen)
00187 {
00188        int rc;
00189 
00190        if (p->fd < 0)
00191               return (0);   /* Previous error invalidated obj */
00192 
00193        if (ll_lock_ex(p->fd) < 0)
00194        {
00195               close(p->fd);
00196               p->fd= -1;
00197               return (-1);
00198        }
00199 
00200        rc=doadd(p, val, vallen);
00201 
00202        if (rc < 0 && p->fd >= 0)
00203        {
00204               close(p->fd);
00205               p->fd= -1;
00206               unlink(p->filename); /* Blow it away, something's wrong */
00207               perror("ALERT: tlscache.c: ");
00208               fprintf(stderr, "ALERT: tlscache.c: removing %s\n",
00209                      p->filename);
00210        }
00211 
00212        if (p->fd >= 0 && ll_unlock_ex(p->fd) < 0)
00213        {
00214               close(p->fd);
00215               p->fd= -1;
00216               rc= -1;
00217        }
00218 
00219        if (rc != 0)
00220               rc= -1;
00221 
00222        return rc;
00223 }
00224 
00225 /*
00226 ** Read the header, and do a simple sanity check
00227 */
00228 
00229 static int readhdr(struct CACHE *p, struct hdr *h)
00230 {
00231        if (lseek(p->fd, 0, SEEK_SET) < 0 ||
00232            my_read(p->fd, h, sizeof(*h)) <= 0)
00233        {
00234               BORK(p->filename);
00235               return (-1);
00236        }
00237 
00238        if (h->filesize < TLSCACHEMINSIZE || h->head >= h->filesize ||
00239            h->tail >= h->filesize || h->first >= h->filesize ||
00240            h->head < 0 || h->tail < 0 || h->first < 0 ||
00241            h->first > h->head || h->first > h->tail)
00242        {
00243               BORK(p->filename);
00244               return (-1);  /* Sanity check */
00245        }
00246        return (0);
00247 }
00248 
00249 static int writehdr(struct CACHE *p, const struct hdr *h)
00250 {
00251        if (lseek(p->fd, 0, SEEK_SET) < 0 ||
00252            my_write(p->fd, h, sizeof(*h)) < 0)
00253        {
00254               BORK(p->filename);
00255               return -1;
00256        }
00257        return 0;
00258 }
00259 
00260 /*
00261 ** Read the header of a cached object, and do a sanity check
00262 */
00263 
00264 static int readobj(struct CACHE *p, off_t w,
00265                  const struct hdr *h, struct obj *o)
00266 {
00267        if (lseek(p->fd, w, SEEK_SET) < 0 ||
00268            my_read(p->fd, o, sizeof(*o)) <= 0)
00269        {
00270               BORK(p->filename);
00271               return (-1);
00272        }
00273 
00274        if (o->prev_size < sizeof(*o) || o->my_size < sizeof(*o) ||
00275            o->prev_size >= h->filesize || o->my_size >= h->filesize ||
00276            h->filesize - w < o->my_size)
00277        {
00278               BORK(p->filename);
00279               errno=EIO;
00280               return (-1);  /* Sanity check */
00281        }
00282        return 0;
00283 }
00284 
00285 
00286 static int doadd(struct CACHE *p, const char *val, size_t vallen)
00287 {
00288        struct hdr h;
00289        struct obj o;
00290        int timer=0;
00291        int first=0;
00292        char *buf;
00293 
00294        if (readhdr(p, &h))
00295               return -1;
00296 
00297        /* Keep trying to allocate sufficient space in the cache file */
00298 
00299        for (;;)
00300        {
00301               if (++timer > 100)
00302               {
00303                      BORK(p->filename);
00304                      errno=EIO;
00305                      return (-1);  /* Sanity check */
00306               }
00307 
00308               if (h.head == 0 && h.tail == 0)    /* First time */
00309               {
00310                      if (vallen + sizeof(struct obj) +
00311                          sizeof(struct hdr) > h.filesize)
00312                      {
00313                             errno=ENOSPC;
00314                             return 1;
00315                      }
00316 
00317                      /* First cached object goes at the end */
00318 
00319                      h.head=h.tail=h.filesize - vallen - sizeof(struct obj);
00320                      first=1;
00321                      break;
00322               }
00323 
00324               if (h.head <= h.tail)       /* Not wrapped around */
00325               {
00326                      if (h.head >= sizeof(struct hdr) +
00327                          sizeof(struct obj) + vallen)
00328                      {
00329                             h.head -= sizeof(struct obj) + vallen;
00330                             break;
00331                             /* Room earlier in the file */
00332                      }
00333 
00334                      /*
00335                      ** No room before, we must now wrap around.  Find
00336                      ** where the last object ends, and see if there's
00337                      ** enough room between the end of the last object,
00338                      ** and the end of the file, to save the new object.
00339                      */
00340 
00341                      if (readobj(p, h.tail, &h, &o) < 0)
00342                             return -1;
00343 
00344                      if (h.filesize - h.tail - o.my_size >=
00345                          sizeof(struct obj) + vallen)
00346                      {
00347                             h.first=h.head;
00348                             h.head=h.filesize - vallen -
00349                                    sizeof(struct obj);
00350                             /* Room to wrap around */
00351 
00352                             break;
00353                      }
00354               }
00355               else   /* We're currently wrapped around, so all the free
00356                      ** space is from tail to head.
00357                      */
00358               {
00359                      if (readobj(p, h.tail, &h, &o) < 0)
00360                             return -1;
00361 
00362                      if (h.head >= h.tail + o.my_size +
00363                          sizeof(struct obj) + vallen)
00364                      {
00365                             h.head -= sizeof(struct obj) + vallen;
00366                             break;
00367                      }
00368               }
00369 
00370               if (h.head == h.tail)       /* Sanity check */
00371               {
00372                      errno=ENOSPC;
00373                      return 1;
00374               }
00375 
00376               /* Pop one off tail */
00377 
00378               if (readobj(p, h.tail, &h, &o))
00379                      return -1;
00380 
00381               if (sizeof(h) + o.prev_size <= h.tail)
00382               {
00383                      h.tail -= o.prev_size;
00384 
00385                      if (writehdr(p, &h))
00386                             return (-1);
00387                      continue;
00388               }
00389 
00390               if (h.tail != h.first)
00391               {
00392                      BORK(p->filename);
00393                      errno=EIO;
00394                      return (-1);  /* Sanity check */
00395               }
00396 
00397               h.first=0;
00398               h.tail=h.filesize - o.prev_size;
00399 
00400               if (h.tail < h.first)
00401               {
00402                      BORK(p->filename);
00403                      errno=EIO;
00404                      return (-1);  /* Sanity check */
00405               }
00406 
00407               if (writehdr(p, &h))
00408                      return (-1);
00409        }
00410 
00411        buf=malloc(vallen + sizeof(o) + sizeof(o.prev_size));
00412 
00413        if (!buf)
00414               return (1);
00415 
00416        o.prev_size=0;
00417        o.my_size=vallen + sizeof(o);
00418        memcpy(buf, &o, sizeof(o));
00419        memcpy(buf + sizeof(o), val, vallen);
00420        o.prev_size=o.my_size;
00421        memcpy(buf + sizeof(o) + vallen, &o.prev_size, sizeof(o.prev_size));
00422 
00423        if (lseek(p->fd, h.head, SEEK_SET) < 0)
00424               return (-1);
00425 
00426        if (h.head + sizeof(o) + vallen < h.filesize)
00427        {
00428               if (my_write(p->fd, buf, sizeof(o)+vallen+sizeof(o.prev_size)) < 0)
00429                      return -1;
00430        }
00431        else
00432        {
00433               if (my_write(p->fd, buf, sizeof(o)+vallen) < 0 ||
00434                   (!first && (lseek(p->fd, h.first, SEEK_SET) < 0 ||
00435                             my_write(p->fd, &o.prev_size,
00436                                     sizeof(o.prev_size)) < 0)))
00437                      return -1;
00438        }
00439                             
00440        return writehdr(p, &h);
00441 }
00442 
00443 static int init(struct CACHE *p, off_t size)
00444 {
00445        char buffer[BUFSIZ];
00446        off_t c;
00447        struct hdr h;
00448 
00449        if (size < TLSCACHEMINSIZE)
00450        {
00451               errno=EINVAL;
00452               return -1;
00453        }
00454 
00455        if (lseek(p->fd, 0, SEEK_SET) < 0)
00456               return -1;
00457 
00458        memset(buffer, 0, sizeof(buffer));
00459 
00460        c=size;
00461 
00462        while (c > 0)
00463        {
00464               int i;
00465 
00466               off_t n=c;
00467 
00468               if (n > sizeof(buffer))
00469                      n=sizeof(buffer);
00470 
00471               i=write(p->fd, buffer, n);
00472 
00473               if (i <= 0)
00474                      return -1;
00475 
00476               c -= i;
00477        }
00478 
00479        memset(&h, 0, sizeof(h));
00480        h.filesize=size;
00481 
00482        if (lseek(p->fd, 0, SEEK_SET) < 0 ||
00483            my_write(p->fd, &h, sizeof(h)))
00484               return (-1);
00485        return (0);
00486 }
00487 
00488 static int dowalk(struct CACHE *cache,
00489                 int (*walk_func)(void *rec, size_t recsize,
00490                                int *doupdate,
00491                                void *arg),
00492                 void *arg);
00493 
00494 int tls_cache_walk(struct CACHE *p,
00495                  int (*walk_func)(void *rec, size_t recsize,
00496                                 int *doupdate,
00497                                 void *arg),
00498                  void *arg)
00499 {
00500        int rc;
00501 
00502        if (p->fd < 0)
00503               return (0);   /* Previous error invalidated obj */
00504 
00505        if (ll_lockfd(p->fd, ll_readlock||ll_whence_start|ll_wait, 0, 0) < 0)
00506        {
00507               /* Some locking methods don't support readonly locks */
00508 
00509               if (ll_lock_ex(p->fd) < 0)
00510               {
00511                      close(p->fd);
00512                      p->fd= -1;
00513                      return (-1);
00514               }
00515        }
00516 
00517        rc=dowalk(p, walk_func, arg);
00518 
00519        if (rc < 0 && p->fd >= 0)
00520        {
00521               close(p->fd);
00522               p->fd= -1;
00523               unlink(p->filename);
00524               perror("ALERT: tlscache.c: ");
00525               fprintf(stderr, "ALERT: tlscache.c: removing %s\n",
00526                      p->filename);
00527        }
00528 
00529        if (p->fd >= 0 && ll_unlock_ex(p->fd) < 0)
00530        {
00531               close(p->fd);
00532               p->fd= -1;
00533               rc= -1;
00534        }
00535 
00536        return rc;
00537 }
00538 
00539 /* Buffered reads when searching, for speed */
00540 
00541 struct walkbuf {
00542        char buffer[BUFSIZ];
00543        char *bufptr;
00544        int left;
00545 };
00546 
00547 static int buf_read(int fd, struct walkbuf *w,
00548                   const void *buffer, size_t cnt)
00549 {
00550        char *p=(char *)buffer;
00551 
00552        while (cnt > 0)
00553        {
00554               if (w->left <= 0)
00555               {
00556                      w->left=read(fd, w->buffer, sizeof(w->buffer));
00557 
00558                      if (w->left <= 0)
00559                             return -1;
00560                      w->bufptr=w->buffer;
00561               }
00562 
00563               *p++ = *w->bufptr++;
00564               --w->left;
00565               --cnt;
00566        }
00567        return 1;
00568 }
00569 
00570 static int dowalk(struct CACHE *p,
00571                 int (*walk_func)(void *rec, size_t recsize,
00572                                int *doupdate, void *arg),
00573                 void *arg)
00574 {
00575        struct hdr h;
00576        struct obj o;
00577        char *buf=NULL;
00578        size_t bufsize=0;
00579        int rc;
00580        int counter;
00581        struct walkbuf wb;
00582        int updateflag;
00583 
00584        off_t pos;
00585        off_t lastpos;
00586 
00587        if (readhdr(p, &h))
00588               return -1;
00589 
00590        if (h.head == 0 && h.tail == 0)    /* First time */
00591               return (0);
00592 
00593        pos=h.head;
00594        if (lseek(p->fd, pos, SEEK_SET) < 0)
00595               return (-1);
00596 
00597        counter=0;
00598        wb.left=0;
00599        for (;;)
00600        {
00601               if (++counter > h.filesize / sizeof(o))
00602               {
00603                      BORK(p->filename);
00604                      return (-1);
00605               }
00606 
00607               if (h.filesize - pos < sizeof(o))
00608               {
00609                      BORK(p->filename);
00610                      errno=EIO;
00611                      if (buf)
00612                             free(buf);
00613                      return (-1);  /* Sanity check */
00614               }
00615 
00616               if (buf_read(p->fd, &wb, &o, sizeof(o)) <= 0)
00617               {
00618                      if (buf)
00619                             free(buf);
00620                      return (-1);
00621               }
00622 
00623               if (h.filesize - pos < o.my_size || o.my_size < sizeof(o))
00624               {
00625                      BORK(p->filename);
00626                      errno=EIO;
00627                      if (buf)
00628                             free(buf);
00629                      return (-1);  /* Sanity check */
00630               }
00631 
00632               if (buf == NULL || bufsize < o.my_size - sizeof(o)+1)
00633               {
00634                      char *newbuf;
00635 
00636                      bufsize=o.my_size - sizeof(o)+1;
00637 
00638                      if ((newbuf=buf ? realloc(buf, bufsize):
00639                           malloc(bufsize)) == NULL)
00640                      {
00641                             free(buf);
00642                             return (-1);
00643                      }
00644                      buf=newbuf;
00645               }
00646 
00647               if (buf_read(p->fd, &wb, buf, o.my_size - sizeof(o)) <= 0)
00648               {
00649                      free(buf);
00650                      return (-1);
00651               }
00652 
00653               updateflag=0;
00654 
00655               rc= (*walk_func)(buf, o.my_size - sizeof(o), &updateflag, arg);
00656 
00657               if (updateflag && rc >= 0)
00658               {
00659                      if (lseek(p->fd, pos + sizeof(o), SEEK_SET) < 0 ||
00660                          my_write(p->fd, buf, o.my_size - sizeof(o)) < 0)
00661                      {
00662                             free(buf);
00663                             return (-1);
00664                      }
00665                      wb.left=0;
00666               }
00667 
00668               if (rc != 0)
00669               {
00670                      free(buf);
00671                      if (rc < 0)
00672                             rc=1;
00673                      return (rc);
00674               }
00675 
00676               if (pos == h.tail)
00677                      break;
00678 
00679               lastpos=pos;
00680               pos += o.my_size;
00681 
00682               if (pos < h.filesize)
00683               {
00684                      if (lastpos < h.tail && pos > h.tail)
00685                      {
00686                             BORK(p->filename);
00687                             errno=EIO;
00688                             free(buf);
00689                             return (-1);
00690                      }
00691               }
00692               else
00693               {
00694                      pos=h.first;
00695                      if (h.first < sizeof(h))
00696                      {
00697                             BORK(p->filename);
00698                             free(buf);
00699                             errno=EIO;
00700                             return (-1);
00701                      }
00702 
00703                      if (lseek(p->fd, pos, SEEK_SET) < 0)
00704                      {
00705                             free(buf);
00706                             return (-1);
00707                      }
00708                      wb.left=0;
00709               }
00710        }
00711        if (buf)
00712               free(buf);
00713        return (0);
00714 }