root/maint/gnulib/lib/af_alg.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. alg_socket
  2. afalg_buffer
  3. afalg_stream

   1 /* af_alg.c - Compute message digests from file streams and buffers.
   2    Copyright (C) 2018-2021 Free Software Foundation, Inc.
   3 
   4    This file is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU Lesser General Public License as
   6    published by the Free Software Foundation; either version 2.1 of the
   7    License, or (at your option) any later version.
   8 
   9    This file is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU Lesser General Public License for more details.
  13 
  14    You should have received a copy of the GNU Lesser General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* Written by Matteo Croce <mcroce@redhat.com>, 2018.  */
  18 
  19 #include <config.h>
  20 
  21 #include "af_alg.h"
  22 
  23 #if USE_LINUX_CRYPTO_API
  24 
  25 #include <unistd.h>
  26 #include <string.h>
  27 #include <stdio.h>
  28 #include <errno.h>
  29 #include <linux/if_alg.h>
  30 #include <sys/stat.h>
  31 #include <sys/sendfile.h>
  32 #include <sys/socket.h>
  33 
  34 #include "sys-limits.h"
  35 
  36 #define BLOCKSIZE 32768
  37 
  38 /* Return a newly created socket for ALG.
  39    On error, return a negative error number.  */
  40 static int
  41 alg_socket (char const *alg)
     /* [previous][next][first][last][top][bottom][index][help] */
  42 {
  43   struct sockaddr_alg salg = {
  44     .salg_family = AF_ALG,
  45     .salg_type = "hash",
  46   };
  47   /* Copy alg into salg.salg_name, without calling strcpy nor strlen.  */
  48   for (size_t i = 0; (salg.salg_name[i] = alg[i]) != '\0'; i++)
  49     if (i == sizeof salg.salg_name - 1)
  50       /* alg is too long.  */
  51       return -EINVAL;
  52 
  53   int cfd = socket (AF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
  54   if (cfd < 0)
  55     return -EAFNOSUPPORT;
  56   int ofd = (bind (cfd, (struct sockaddr *) &salg, sizeof salg) == 0
  57              ? accept4 (cfd, NULL, 0, SOCK_CLOEXEC)
  58              : -1);
  59   close (cfd);
  60   return ofd < 0 ? -EAFNOSUPPORT : ofd;
  61 }
  62 
  63 int
  64 afalg_buffer (const char *buffer, size_t len, const char *alg,
     /* [previous][next][first][last][top][bottom][index][help] */
  65               void *resblock, ssize_t hashlen)
  66 {
  67   /* On Linux < 4.9, the value for an empty stream is wrong (all zeroes).
  68      See <https://patchwork.kernel.org/patch/9308641/>.
  69      This was not fixed properly until November 2016,
  70      see <https://patchwork.kernel.org/patch/9434741/>.  */
  71   if (len == 0)
  72     return -EAFNOSUPPORT;
  73 
  74   int ofd = alg_socket (alg);
  75   if (ofd < 0)
  76     return ofd;
  77 
  78   int result;
  79 
  80   for (;;)
  81     {
  82       ssize_t size = (len > BLOCKSIZE ? BLOCKSIZE : len);
  83       if (send (ofd, buffer, size, MSG_MORE) != size)
  84         {
  85           result = -EAFNOSUPPORT;
  86           break;
  87         }
  88       buffer += size;
  89       len -= size;
  90       if (len == 0)
  91         {
  92           result = read (ofd, resblock, hashlen) == hashlen ? 0 : -EAFNOSUPPORT;
  93           break;
  94         }
  95     }
  96 
  97   close (ofd);
  98   return result;
  99 }
 100 
 101 int
 102 afalg_stream (FILE *stream, const char *alg,
     /* [previous][next][first][last][top][bottom][index][help] */
 103               void *resblock, ssize_t hashlen)
 104 {
 105   int ofd = alg_socket (alg);
 106   if (ofd < 0)
 107     return ofd;
 108 
 109   /* If STREAM's size is known and nonzero and not too large, attempt
 110      sendfile to pipe the data.  The nonzero restriction avoids issues
 111      with /proc files that pretend to be empty, and lets the classic
 112      read-write loop work around an empty-input bug noted below.  */
 113   int fd = fileno (stream);
 114   int result;
 115   struct stat st;
 116   off_t off = ftello (stream);
 117   if (0 <= off && fstat (fd, &st) == 0
 118       && (S_ISREG (st.st_mode) || S_TYPEISSHM (&st) || S_TYPEISTMO (&st))
 119       && off < st.st_size && st.st_size - off < SYS_BUFSIZE_MAX)
 120     {
 121       /* Make sure the offset of fileno (stream) reflects how many bytes
 122          have been read from stream before this function got invoked.
 123          Note: fflush on an input stream after ungetc does not work as expected
 124          on some platforms.  Therefore this situation is not supported here.  */
 125       if (fflush (stream))
 126         result = -EIO;
 127       else
 128         {
 129           off_t nbytes = st.st_size - off;
 130           if (sendfile (ofd, fd, &off, nbytes) == nbytes)
 131             {
 132               if (read (ofd, resblock, hashlen) == hashlen)
 133                 {
 134                   /* The input buffers of stream are no longer valid.  */
 135                   if (lseek (fd, off, SEEK_SET) != (off_t)-1)
 136                     result = 0;
 137                   else
 138                     /* The file position of fd has not changed.  */
 139                     result = -EAFNOSUPPORT;
 140                 }
 141               else
 142                 /* The file position of fd has not changed.  */
 143                 result = -EAFNOSUPPORT;
 144             }
 145           else
 146             /* The file position of fd has not changed.  */
 147             result = -EAFNOSUPPORT;
 148        }
 149     }
 150   else
 151     {
 152       /* sendfile not possible, do a classic read-write loop.  */
 153 
 154       /* Number of bytes to seek (backwards) in case of error.  */
 155       off_t nseek = 0;
 156 
 157       for (;;)
 158         {
 159           char buf[BLOCKSIZE];
 160           /* When the stream is not seekable, start with a single-byte block,
 161              so that we can use ungetc() in the case that send() fails.  */
 162           size_t blocksize = (nseek == 0 && off < 0 ? 1 : BLOCKSIZE);
 163           ssize_t size = fread (buf, 1, blocksize, stream);
 164           if (size == 0)
 165             {
 166               /* On Linux < 4.9, the value for an empty stream is wrong (all 0).
 167                  See <https://patchwork.kernel.org/patch/9308641/>.
 168                  This was not fixed properly until November 2016,
 169                  see <https://patchwork.kernel.org/patch/9434741/>.  */
 170               result = ferror (stream) ? -EIO : nseek == 0 ? -EAFNOSUPPORT : 0;
 171               break;
 172             }
 173           nseek -= size;
 174           if (send (ofd, buf, size, MSG_MORE) != size)
 175             {
 176               if (nseek == -1)
 177                 {
 178                   /* 1 byte of pushback buffer is guaranteed on stream, even
 179                      if stream is not seekable.  */
 180                   ungetc ((unsigned char) buf[0], stream);
 181                   result = -EAFNOSUPPORT;
 182                 }
 183               else if (fseeko (stream, nseek, SEEK_CUR) == 0)
 184                 /* The position of stream has been restored.  */
 185                 result = -EAFNOSUPPORT;
 186               else
 187                 result = -EIO;
 188               break;
 189             }
 190 
 191           /* Don't assume that EOF is sticky. See:
 192              <https://sourceware.org/bugzilla/show_bug.cgi?id=19476>.  */
 193           if (feof (stream))
 194             {
 195               result = 0;
 196               break;
 197             }
 198         }
 199 
 200       if (result == 0 && read (ofd, resblock, hashlen) != hashlen)
 201         {
 202           if (nseek == 0 || fseeko (stream, nseek, SEEK_CUR) == 0)
 203             /* The position of stream has been restored.  */
 204             result = -EAFNOSUPPORT;
 205           else
 206             result = -EIO;
 207         }
 208     }
 209   close (ofd);
 210   return result;
 211 }
 212 
 213 #endif

/* [previous][next][first][last][top][bottom][index][help] */