common: Add new function gnupg_gmtime.
[gnupg.git] / common / iobuf.c
index 97f3202..ca74bd7 100644 (file)
@@ -40,6 +40,9 @@
 #include <fcntl.h>
 #include <unistd.h>
 #ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_WINSOCK2_H
+#  include <winsock2.h>
+# endif
 # include <windows.h>
 #endif
 #ifdef __riscos__
    test "armored_key_8192" in armor.test! */
 #define IOBUF_BUFFER_SIZE  8192
 
+/* To avoid a potential DoS with compression packets we better limit
+   the number of filters in a chain.  */
+#define MAX_NESTING_FILTER 64
+
 /*-- End configurable part.  --*/
 
 
@@ -241,7 +248,7 @@ fd_cache_synchronize (const char *fname)
 
 
 static gnupg_fd_t
-direct_open (const char *fname, const char *mode)
+direct_open (const char *fname, const char *mode, int mode700)
 {
 #ifdef HAVE_W32_SYSTEM
   unsigned long da, cd, sm;
@@ -292,9 +299,14 @@ direct_open (const char *fname, const char *mode)
   hfile = CreateFile (fname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL);
 #endif
   return hfile;
+
 #else /*!HAVE_W32_SYSTEM*/
+
   int oflag;
-  int cflag = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  int cflag = S_IRUSR | S_IWUSR;
+
+  if (!mode700)
+    cflag |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
 
   /* Note, that we do not handle all mode combinations */
   if (strchr (mode, '+'))
@@ -317,21 +329,18 @@ direct_open (const char *fname, const char *mode)
   if (strchr (mode, 'b'))
     oflag |= O_BINARY;
 #endif
-  /* No we need to distinguish between POSIX and RISC OS.  */
-#ifndef __riscos__
-  return open (fname, oflag, cflag);
-#else
+
+#ifdef __riscos__
   {
     struct stat buf;
-    int rc = stat (fname, &buf);
 
     /* Don't allow iobufs on directories */
-    if (!rc && S_ISDIR (buf.st_mode) && !S_ISREG (buf.st_mode))
+    if (!stat (fname, &buf) && S_ISDIR (buf.st_mode) && !S_ISREG (buf.st_mode))
       return __set_errno (EISDIR);
-    else
-      return open (fname, oflag, cflag);
   }
 #endif
+  return open (fname, oflag, cflag);
+
 #endif /*!HAVE_W32_SYSTEM*/
 }
 
@@ -414,7 +423,7 @@ fd_cache_open (const char *fname, const char *mode)
     }
   if (DBG_IOBUF)
     log_debug ("fd_cache_open (%s) not cached\n", fname);
-  return direct_open (fname, mode);
+  return direct_open (fname, mode, 0);
 }
 
 
@@ -862,7 +871,7 @@ block_filter (void *opaque, int control, iobuf_t chain, byte * buffer,
                    }
                  else if (c == 255)
                    {
-                     a->size = iobuf_get (chain) << 24;
+                     a->size = (size_t)iobuf_get (chain) << 24;
                      a->size |= iobuf_get (chain) << 16;
                      a->size |= iobuf_get (chain) << 8;
                      if ((c = iobuf_get (chain)) == -1)
@@ -1074,7 +1083,7 @@ print_chain (iobuf_t a)
        a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL,
                   (byte *) & desc, &dummy_len);
 
-      log_debug ("iobuf chain: %d.%d `%s' filter_eof=%d start=%d len=%d\n",
+      log_debug ("iobuf chain: %d.%d '%s' filter_eof=%d start=%d len=%d\n",
                 a->no, a->subno, desc?desc:"?", a->filter_eof,
                 (int) a->d.start, (int) a->d.len);
     }
@@ -1132,7 +1141,7 @@ iobuf_close (iobuf_t a)
        log_error ("iobuf_flush failed on close: %s\n", gpg_strerror (rc));
 
       if (DBG_IOBUF)
-       log_debug ("iobuf-%d.%d: close `%s'\n", a->no, a->subno,
+       log_debug ("iobuf-%d.%d: close '%s'\n", a->no, a->subno,
                    a->desc?a->desc:"?");
       if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_FREE,
                                        a->chain, NULL, &dummy_len)))
@@ -1219,9 +1228,12 @@ iobuf_t
 iobuf_temp_with_content (const char *buffer, size_t length)
 {
   iobuf_t a;
+  int i;
 
   a = iobuf_alloc (3, length);
-  memcpy (a->d.buf, buffer, length);
+  /* memcpy (a->d.buf, buffer, length); */
+  for (i=0; i < length; i++)
+    a->d.buf[i] = buffer[i];
   a->d.len = length;
 
   return a;
@@ -1292,7 +1304,7 @@ iobuf_open (const char *fname)
   iobuf_t a;
   gnupg_fd_t fp;
   file_filter_ctx_t *fcx;
-  size_t len;
+  size_t len = 0;
   int print_only = 0;
   int fd;
 
@@ -1318,7 +1330,7 @@ iobuf_open (const char *fname)
   file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: open `%s' fd=%d\n",
+    log_debug ("iobuf-%d.%d: open '%s' fd=%d\n",
               a->no, a->subno, fname, FD2INT (fcx->fp));
 
   return a;
@@ -1346,7 +1358,7 @@ do_iobuf_fdopen (int fd, const char *mode, int keep_open)
   file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: fdopen%s `%s'\n",
+    log_debug ("iobuf-%d.%d: fdopen%s '%s'\n",
                a->no, a->subno, keep_open? "_nc":"", fcx->fname);
   iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
   return a;
@@ -1386,7 +1398,7 @@ iobuf_esopen (estream_t estream, const char *mode, int keep_open)
   file_es_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: esopen%s `%s'\n",
+    log_debug ("iobuf-%d.%d: esopen%s '%s'\n",
                a->no, a->subno, keep_open? "_nc":"", fcx->fname);
   return a;
 }
@@ -1410,7 +1422,7 @@ iobuf_sockopen (int fd, const char *mode)
   sock_filter (scx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: sockopen `%s'\n", a->no, a->subno, scx->fname);
+    log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname);
   iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
 #else
   a = iobuf_fdopen (fd, mode);
@@ -1419,10 +1431,11 @@ iobuf_sockopen (int fd, const char *mode)
 }
 
 /****************
- * create an iobuf for writing to a file; the file will be created.
+ * Create an iobuf for writing to a file; the file will be created.
+ * With MODE700 set the file is created with that mode (Unix only).
  */
 iobuf_t
-iobuf_create (const char *fname)
+iobuf_create (const char *fname, int mode700)
 {
   iobuf_t a;
   gnupg_fd_t fp;
@@ -1439,7 +1452,7 @@ iobuf_create (const char *fname)
     }
   else if ((fd = check_special_filename (fname)) != -1)
     return iobuf_fdopen (translate_file_handle (fd, 1), "wb");
-  else if ((fp = direct_open (fname, "wb")) == GNUPG_INVALID_FD)
+  else if ((fp = direct_open (fname, "wb", mode700)) == GNUPG_INVALID_FD)
     return NULL;
   a = iobuf_alloc (2, IOBUF_BUFFER_SIZE);
   fcx = xmalloc (sizeof *fcx + strlen (fname));
@@ -1453,7 +1466,7 @@ iobuf_create (const char *fname)
   file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: create `%s'\n", a->no, a->subno,
+    log_debug ("iobuf-%d.%d: create '%s'\n", a->no, a->subno,
                a->desc?a->desc:"?");
 
   return a;
@@ -1470,7 +1483,7 @@ iobuf_openrw (const char *fname)
 
   if (!fname)
     return NULL;
-  else if ((fp = direct_open (fname, "r+b")) == GNUPG_INVALID_FD)
+  else if ((fp = direct_open (fname, "r+b", 0)) == GNUPG_INVALID_FD)
     return NULL;
   a = iobuf_alloc (2, IOBUF_BUFFER_SIZE);
   fcx = xmalloc (sizeof *fcx + strlen (fname));
@@ -1482,7 +1495,7 @@ iobuf_openrw (const char *fname)
   file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: openrw `%s'\n", a->no, a->subno,
+    log_debug ("iobuf-%d.%d: openrw '%s'\n", a->no, a->subno,
                a->desc?a->desc:"?");
 
   return a;
@@ -1498,7 +1511,7 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
          the past by http.c; this ioctl is not directly used
          anymore.  */
       if (DBG_IOBUF)
-       log_debug ("iobuf-%d.%d: ioctl `%s' keep_open=%d\n",
+       log_debug ("iobuf-%d.%d: ioctl '%s' keep_open=%d\n",
                   a ? a->no : -1, a ? a->subno : -1,
                    a && a->desc ? a->desc : "?",
                   intval);
@@ -1521,7 +1534,7 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
   else if (cmd == IOBUF_IOCTL_INVALIDATE_CACHE)
     {
       if (DBG_IOBUF)
-       log_debug ("iobuf-*.*: ioctl `%s' invalidate\n",
+       log_debug ("iobuf-*.*: ioctl '%s' invalidate\n",
                   ptrval ? (char *) ptrval : "?");
       if (!a && !intval && ptrval)
        {
@@ -1533,7 +1546,7 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
   else if (cmd == IOBUF_IOCTL_NO_CACHE)
     {
       if (DBG_IOBUF)
-       log_debug ("iobuf-%d.%d: ioctl `%s' no_cache=%d\n",
+       log_debug ("iobuf-%d.%d: ioctl '%s' no_cache=%d\n",
                   a ? a->no : -1, a ? a->subno : -1,
                    a && a->desc? a->desc : "?",
                   intval);
@@ -1558,7 +1571,7 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
       /* Do a fsync on the open fd and return any errors to the caller
          of iobuf_ioctl.  Note that we work on a file name here. */
       if (DBG_IOBUF)
-        log_debug ("iobuf-*.*: ioctl `%s' fsync\n",
+        log_debug ("iobuf-*.*: ioctl '%s' fsync\n",
                    ptrval? (const char*)ptrval:"<null>");
 
        if (!a && !intval && ptrval)
@@ -1599,6 +1612,13 @@ iobuf_push_filter2 (iobuf_t a,
 
   if (a->use == 2 && (rc = iobuf_flush (a)))
     return rc;
+
+  if (a->subno >= MAX_NESTING_FILTER)
+    {
+      log_error ("i/o filter too deeply nested - corrupted data?\n");
+      return GPG_ERR_BAD_DATA;
+    }
+
   /* make a copy of the current stream, so that
    * A is the new stream and B the original one.
    * The contents of the buffers are transferred to the
@@ -1650,7 +1670,7 @@ iobuf_push_filter2 (iobuf_t a,
 
   if (DBG_IOBUF)
     {
-      log_debug ("iobuf-%d.%d: push `%s'\n", a->no, a->subno,
+      log_debug ("iobuf-%d.%d: push '%s'\n", a->no, a->subno,
                  a->desc?a->desc:"?");
       print_chain (a);
     }
@@ -1678,7 +1698,7 @@ pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
     BUG ();
 
   if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: pop `%s'\n", a->no, a->subno,
+    log_debug ("iobuf-%d.%d: pop '%s'\n", a->no, a->subno,
                a->desc?a->desc:"?");
   if (!a->filter)
     {                          /* this is simple */
@@ -1765,7 +1785,7 @@ underflow (iobuf_t a)
        {
          iobuf_t b = a->chain;
          if (DBG_IOBUF)
-           log_debug ("iobuf-%d.%d: pop `%s' in underflow\n",
+           log_debug ("iobuf-%d.%d: pop '%s' in underflow\n",
                       a->no, a->subno, a->desc?a->desc:"?");
          xfree (a->d.buf);
          xfree (a->real_fname);
@@ -2311,7 +2331,7 @@ iobuf_seek (iobuf_t a, off_t newpos)
        }
       clearerr (fp);
     }
-  else
+  else if (a->use != 3)  /* Not a temp stream.  */
     {
       for (; a; a = a->chain)
        {
@@ -2338,7 +2358,8 @@ iobuf_seek (iobuf_t a, off_t newpos)
        }
 #endif
     }
-  a->d.len = 0;                        /* discard buffer */
+  if (a->use != 3)
+    a->d.len = 0;      /* Discard the buffer  unless it is a temp stream.  */
   a->d.start = 0;
   a->nbytes = 0;
   a->nlimit = 0;