af_alg: Pacify --enable-gcc-warnings on GCC 8
[gnulib.git] / lib / af_alg.c
blob81f506e7a100f3b616302dcdf590332a61b1f6e6
1 /* af_alg.c - Functions to compute message digest from file streams using
2 Linux kernel crypto API.
3 Copyright (C) 2018 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option) any
8 later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, see <https://www.gnu.org/licenses/>. */
18 /* Written by Matteo Croce <mcroce@redhat.com>, 2018. */
20 #include <config.h>
22 #if USE_LINUX_CRYPTO_API
24 #include "af_alg.h"
26 #include <unistd.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <linux/if_alg.h>
31 #include <sys/stat.h>
32 #include <sys/sendfile.h>
33 #include <sys/socket.h>
35 #include "sys-limits.h"
37 #define BLOCKSIZE 32768
39 int
40 afalg_buffer (const char *buffer, size_t len, const char *alg,
41 void *resblock, ssize_t hashlen)
43 int ofd;
45 /* On Linux < 4.9, the value for an empty stream is wrong (all zeroes).
46 See <https://patchwork.kernel.org/patch/9308641/>. */
47 if (len == 0)
48 return -EAFNOSUPPORT;
50 int result;
51 struct sockaddr_alg salg = {
52 .salg_family = AF_ALG,
53 .salg_type = "hash",
55 /* Avoid calling both strcpy and strlen. */
56 for (int i = 0; (salg.salg_name[i] = alg[i]); i++)
57 if (i == sizeof salg.salg_name - 1)
58 return -EINVAL;
60 int cfd = socket (AF_ALG, SOCK_SEQPACKET, 0);
61 if (cfd < 0)
62 return -EAFNOSUPPORT;
64 if (bind (cfd, (struct sockaddr *) &salg, sizeof salg) != 0)
66 result = -EAFNOSUPPORT;
67 goto out_cfd;
70 ofd = accept (cfd, NULL, 0);
71 if (ofd < 0)
73 result = -EAFNOSUPPORT;
74 goto out_cfd;
79 ssize_t size = (len > BLOCKSIZE ? BLOCKSIZE : len);
80 if (send (ofd, buffer, size, MSG_MORE) != size)
82 result = -EIO;
83 goto out_ofd;
85 buffer += size;
86 len -= size;
88 while (len > 0);
90 if (read (ofd, resblock, hashlen) != hashlen)
91 result = -EIO;
92 else
93 result = 0;
95 out_ofd:
96 close (ofd);
97 out_cfd:
98 close (cfd);
99 return result;
103 afalg_stream (FILE *stream, const char *alg,
104 void *resblock, ssize_t hashlen)
106 int fd, ofd;
107 struct stat st;
109 int result;
110 struct sockaddr_alg salg = {
111 .salg_family = AF_ALG,
112 .salg_type = "hash",
114 /* Avoid calling both strcpy and strlen. */
115 for (int i = 0; (salg.salg_name[i] = alg[i]); i++)
116 if (i == sizeof salg.salg_name - 1)
117 return -EINVAL;
119 int cfd = socket (AF_ALG, SOCK_SEQPACKET, 0);
120 if (cfd < 0)
121 return -EAFNOSUPPORT;
123 if (bind (cfd, (struct sockaddr *) &salg, sizeof salg) != 0)
125 result = -EAFNOSUPPORT;
126 goto out_cfd;
129 ofd = accept (cfd, NULL, 0);
130 if (ofd < 0)
132 result = -EAFNOSUPPORT;
133 goto out_cfd;
136 /* if file is a regular file, attempt sendfile to pipe the data. */
137 fd = fileno (stream);
138 if (fstat (fd, &st) == 0
139 && (S_ISREG (st.st_mode) || S_TYPEISSHM (&st) || S_TYPEISTMO (&st))
140 && 0 < st.st_size && st.st_size <= SYS_BUFSIZE_MAX)
142 /* Make sure the offset of fileno (stream) reflects how many bytes
143 have been read from stream before this function got invoked.
144 Note: fflush on an input stream after ungetc does not work as expected
145 on some platforms. Therefore this situation is not supported here. */
146 if (fflush (stream))
148 #if defined _WIN32 && ! defined __CYGWIN__
149 result = -EIO;
150 #else
151 result = -errno;
152 #endif
153 goto out_cfd;
156 off_t nbytes = st.st_size - lseek (fd, 0, SEEK_CUR);
157 /* On Linux < 4.9, the value for an empty stream is wrong (all zeroes).
158 See <https://patchwork.kernel.org/patch/9308641/>. */
159 if (nbytes <= 0)
161 result = -EAFNOSUPPORT;
162 goto out_ofd;
164 if (sendfile (ofd, fd, NULL, nbytes) != nbytes)
166 result = -EIO;
167 goto out_ofd;
170 else
172 /* sendfile not possible, do a classic read-write loop. */
173 int non_empty = 0;
174 ssize_t size;
175 char buf[BLOCKSIZE];
176 while ((size = fread (buf, 1, sizeof buf, stream)))
178 non_empty = 1;
179 if (send (ofd, buf, size, MSG_MORE) != size)
181 result = -EIO;
182 goto out_ofd;
185 if (ferror (stream))
187 result = -EIO;
188 goto out_ofd;
190 /* On Linux < 4.9, the value for an empty stream is wrong (all zeroes).
191 See <https://patchwork.kernel.org/patch/9308641/>. */
192 if (!non_empty)
194 result = -EAFNOSUPPORT;
195 goto out_ofd;
199 if (read (ofd, resblock, hashlen) != hashlen)
200 result = -EIO;
201 else
202 result = 0;
204 out_ofd:
205 close (ofd);
206 out_cfd:
207 close (cfd);
208 return result;
211 #endif