Merge pull request #1885 from franzs/feature_proxy_protocol_for_check_smtp
[monitoring-plugins.git] / gl / af_alg.c
blob9f022ce52186cad720d5b388b71f7f35219a325a
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. */
19 #include <config.h>
21 #include "af_alg.h"
23 #if USE_LINUX_CRYPTO_API
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>
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. */
40 static int
41 alg_socket (char const *alg)
43 struct sockaddr_alg salg = {
44 .salg_family = AF_ALG,
45 .salg_type = "hash",
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;
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;
63 int
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/>. */
71 if (len == 0)
72 return -EAFNOSUPPORT;
74 int ofd = alg_socket (alg);
75 if (ofd < 0)
76 return ofd;
78 int result;
80 for (;;)
82 ssize_t size = (len > BLOCKSIZE ? BLOCKSIZE : len);
83 if (send (ofd, buffer, size, MSG_MORE) != size)
85 result = -EAFNOSUPPORT;
86 break;
88 buffer += size;
89 len -= size;
90 if (len == 0)
92 result = read (ofd, resblock, hashlen) == hashlen ? 0 : -EAFNOSUPPORT;
93 break;
97 close (ofd);
98 return result;
102 afalg_stream (FILE *stream, const char *alg,
103 void *resblock, ssize_t hashlen)
105 int ofd = alg_socket (alg);
106 if (ofd < 0)
107 return ofd;
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)
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
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)
136 result = 0;
137 else
138 /* The file position of fd has not changed. */
139 result = -EAFNOSUPPORT;
141 else
142 /* The file position of fd has not changed. */
143 result = -EAFNOSUPPORT;
145 else
146 /* The file position of fd has not changed. */
147 result = -EAFNOSUPPORT;
150 else
152 /* sendfile not possible, do a classic read-write loop. */
154 /* Number of bytes to seek (backwards) in case of error. */
155 off_t nseek = 0;
157 for (;;)
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)
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;
173 nseek -= size;
174 if (send (ofd, buf, size, MSG_MORE) != size)
176 if (nseek == -1)
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;
186 else
187 result = -EIO;
188 break;
191 /* Don't assume that EOF is sticky. See:
192 <https://sourceware.org/bugzilla/show_bug.cgi?id=19476>. */
193 if (feof (stream))
195 result = 0;
196 break;
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;
205 else
206 result = -EIO;
209 close (ofd);
210 return result;
213 #endif