(root)/
coreutils-9.4/
lib/
af_alg.c
       1  /* af_alg.c - Compute message digests from file streams and buffers.
       2     Copyright (C) 2018-2023 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)
      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,
      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,
     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