af_alg: Pacify --enable-gcc-warnings
[gnulib.git] / lib / af_alg.c
blobc9809c70e9e71310230e8e7ac3bf256cb095cb25
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 /* On Linux < 4.9, the value for an empty stream is wrong (all zeroes).
44 See <https://patchwork.kernel.org/patch/9308641/>. */
45 if (len == 0)
46 return -EAFNOSUPPORT;
48 int cfd = socket (AF_ALG, SOCK_SEQPACKET, 0);
49 if (cfd < 0)
50 return -EAFNOSUPPORT;
52 int result;
53 struct sockaddr_alg salg = {
54 .salg_family = AF_ALG,
55 .salg_type = "hash",
57 /* Avoid calling both strcpy and strlen. */
58 for (int i = 0; (salg.salg_name[i] = alg[i]); i++)
59 if (i == sizeof salg.salg_name - 1)
61 result = -EINVAL;
62 goto out_cfd;
65 int ret = bind (cfd, (struct sockaddr *) &salg, sizeof salg);
66 if (ret != 0)
68 result = -EAFNOSUPPORT;
69 goto out_cfd;
72 int ofd = accept (cfd, NULL, 0);
73 if (ofd < 0)
75 result = -EAFNOSUPPORT;
76 goto out_cfd;
81 ssize_t size = (len > BLOCKSIZE ? BLOCKSIZE : len);
82 if (send (ofd, buffer, size, MSG_MORE) != size)
84 result = -EIO;
85 goto out_ofd;
87 buffer += size;
88 len -= size;
90 while (len > 0);
92 if (read (ofd, resblock, hashlen) != hashlen)
93 result = -EIO;
94 else
95 result = 0;
97 out_ofd:
98 close (ofd);
99 out_cfd:
100 close (cfd);
101 return result;
105 afalg_stream (FILE *stream, const char *alg,
106 void *resblock, ssize_t hashlen)
108 int cfd = socket (AF_ALG, SOCK_SEQPACKET, 0);
109 if (cfd < 0)
110 return -EAFNOSUPPORT;
112 int fd;
113 struct stat st;
115 int result;
116 struct sockaddr_alg salg = {
117 .salg_family = AF_ALG,
118 .salg_type = "hash",
120 /* Avoid calling both strcpy and strlen. */
121 for (int i = 0; (salg.salg_name[i] = alg[i]); i++)
122 if (i == sizeof salg.salg_name - 1)
124 result = -EINVAL;
125 goto out_cfd;
128 int ret = bind (cfd, (struct sockaddr *) &salg, sizeof salg);
129 if (ret != 0)
131 result = -EAFNOSUPPORT;
132 goto out_cfd;
135 int ofd = accept (cfd, NULL, 0);
136 if (ofd < 0)
138 result = -EAFNOSUPPORT;
139 goto out_cfd;
142 /* if file is a regular file, attempt sendfile to pipe the data. */
143 fd = fileno (stream);
144 if (fstat (fd, &st) == 0
145 && (S_ISREG (st.st_mode) || S_TYPEISSHM (&st) || S_TYPEISTMO (&st))
146 && 0 < st.st_size && st.st_size <= SYS_BUFSIZE_MAX)
148 /* Make sure the offset of fileno (stream) reflects how many bytes
149 have been read from stream before this function got invoked.
150 Note: fflush on an input stream after ungetc does not work as expected
151 on some platforms. Therefore this situation is not supported here. */
152 if (fflush (stream))
154 #if defined _WIN32 && ! defined __CYGWIN__
155 result = -EIO;
156 #else
157 result = -errno;
158 #endif
159 goto out_cfd;
162 off_t nbytes = st.st_size - lseek (fd, 0, SEEK_CUR);
163 /* On Linux < 4.9, the value for an empty stream is wrong (all zeroes).
164 See <https://patchwork.kernel.org/patch/9308641/>. */
165 if (nbytes <= 0)
167 result = -EAFNOSUPPORT;
168 goto out_ofd;
170 if (sendfile (ofd, fd, NULL, nbytes) != nbytes)
172 result = -EIO;
173 goto out_ofd;
176 else
178 /* sendfile not possible, do a classic read-write loop. */
179 int non_empty = 0;
180 ssize_t size;
181 char buf[BLOCKSIZE];
182 while ((size = fread (buf, 1, sizeof buf, stream)))
184 non_empty = 1;
185 if (send (ofd, buf, size, MSG_MORE) != size)
187 result = -EIO;
188 goto out_ofd;
191 if (ferror (stream))
193 result = -EIO;
194 goto out_ofd;
196 /* On Linux < 4.9, the value for an empty stream is wrong (all zeroes).
197 See <https://patchwork.kernel.org/patch/9308641/>. */
198 if (!non_empty)
200 result = -EAFNOSUPPORT;
201 goto out_ofd;
205 if (read (ofd, resblock, hashlen) != hashlen)
206 result = -EIO;
207 else
208 result = 0;
210 out_ofd:
211 close (ofd);
212 out_cfd:
213 close (cfd);
214 return result;
217 #endif