1 /* af_alg.c - Compute message digests from file streams and buffers.
2 Copyright (C) 2018-2023 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Matteo Croce <mcroce@redhat.com>, 2018. */
23 #if USE_LINUX_CRYPTO_API
29 #include <linux/if_alg.h>
31 #include <sys/sendfile.h>
32 #include <sys/socket.h>
34 #include "sys-limits.h"
36 #define BLOCKSIZE 32768
38 /* Return a newly created socket for ALG.
39 On error, return a negative error number. */
41 alg_socket (char const *alg
)
43 struct sockaddr_alg salg
= {
44 .salg_family
= AF_ALG
,
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. */
53 int cfd
= socket (AF_ALG
, SOCK_SEQPACKET
| SOCK_CLOEXEC
, 0);
56 int ofd
= (bind (cfd
, (struct sockaddr
*) &salg
, sizeof salg
) == 0
57 ? accept4 (cfd
, NULL
, 0, SOCK_CLOEXEC
)
60 return ofd
< 0 ? -EAFNOSUPPORT
: ofd
;
64 afalg_buffer (const char *buffer
, size_t len
, const char *alg
,
65 void *resblock
, ssize_t hashlen
)
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/>. */
74 int ofd
= alg_socket (alg
);
82 ssize_t size
= (len
> BLOCKSIZE
? BLOCKSIZE
: len
);
83 if (send (ofd
, buffer
, size
, MSG_MORE
) != size
)
85 result
= -EAFNOSUPPORT
;
92 result
= read (ofd
, resblock
, hashlen
) == hashlen
? 0 : -EAFNOSUPPORT
;
102 afalg_stream (FILE *stream
, const char *alg
,
103 void *resblock
, ssize_t hashlen
)
105 int ofd
= alg_socket (alg
);
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
);
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
)
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. */
129 off_t nbytes
= st
.st_size
- off
;
130 if (sendfile (ofd
, fd
, &off
, nbytes
) == nbytes
)
132 if (read (ofd
, resblock
, hashlen
) == hashlen
)
134 /* The input buffers of stream are no longer valid. */
135 if (lseek (fd
, off
, SEEK_SET
) != (off_t
)-1)
138 /* The file position of fd has not changed. */
139 result
= -EAFNOSUPPORT
;
142 /* The file position of fd has not changed. */
143 result
= -EAFNOSUPPORT
;
146 /* The file position of fd has not changed. */
147 result
= -EAFNOSUPPORT
;
152 /* sendfile not possible, do a classic read-write loop. */
154 /* Number of bytes to seek (backwards) in case of error. */
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
);
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;
174 if (send (ofd
, buf
, size
, MSG_MORE
) != size
)
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
;
183 else if (fseeko (stream
, nseek
, SEEK_CUR
) == 0)
184 /* The position of stream has been restored. */
185 result
= -EAFNOSUPPORT
;
191 /* Don't assume that EOF is sticky. See:
192 <https://sourceware.org/bugzilla/show_bug.cgi?id=19476>. */
200 if (result
== 0 && read (ofd
, resblock
, hashlen
) != hashlen
)
202 if (nseek
== 0 || fseeko (stream
, nseek
, SEEK_CUR
) == 0)
203 /* The position of stream has been restored. */
204 result
= -EAFNOSUPPORT
;