Bug Summary

File:src/usr.sbin/smtpd/smtpd/../crypto.c
Warning:line 162, column 7
Although the value stored to 'r' is used in the enclosing expression, the value is never actually read from 'r'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name crypto.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/smtpd/smtpd/.. -D IO_TLS -D QUEUE_PROFILING -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/smtpd/smtpd/../crypto.c
1/* $OpenBSD: crypto.c,v 1.10 2021/06/14 17:58:15 eric Exp $ */
2
3/*
4 * Copyright (c) 2013 Gilles Chehade <gilles@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/stat.h>
20
21#include <openssl/evp.h>
22#include <string.h>
23
24#define CRYPTO_BUFFER_SIZE16384 16384
25
26#define GCM_TAG_SIZE16 16
27#define IV_SIZE12 12
28#define KEY_SIZE32 32
29
30/* bump if we ever switch from aes-256-gcm to anything else */
31#define API_VERSION1 1
32
33
34int crypto_setup(const char *, size_t);
35int crypto_encrypt_file(FILE *, FILE *);
36int crypto_decrypt_file(FILE *, FILE *);
37size_t crypto_encrypt_buffer(const char *, size_t, char *, size_t);
38size_t crypto_decrypt_buffer(const char *, size_t, char *, size_t);
39
40static struct crypto_ctx {
41 unsigned char key[KEY_SIZE32];
42} cp;
43
44int
45crypto_setup(const char *key, size_t len)
46{
47 if (len != KEY_SIZE32)
48 return 0;
49
50 memset(&cp, 0, sizeof cp);
51
52 /* openssl rand -hex 16 */
53 memcpy(cp.key, key, sizeof cp.key);
54
55 return 1;
56}
57
58int
59crypto_encrypt_file(FILE * in, FILE * out)
60{
61 EVP_CIPHER_CTX *ctx;
62 uint8_t ibuf[CRYPTO_BUFFER_SIZE16384];
63 uint8_t obuf[CRYPTO_BUFFER_SIZE16384];
64 uint8_t iv[IV_SIZE12];
65 uint8_t tag[GCM_TAG_SIZE16];
66 uint8_t version = API_VERSION1;
67 size_t r;
68 int len;
69 int ret = 0;
70 struct stat sb;
71
72 /* XXX - Do NOT encrypt files bigger than 64GB */
73 if (fstat(fileno(in)(!__isthreaded ? ((in)->_file) : (fileno)(in)), &sb) == -1)
74 return 0;
75 if (sb.st_size >= 0x1000000000LL)
76 return 0;
77
78 /* prepend version byte*/
79 if (fwrite(&version, 1, sizeof version, out) != sizeof version)
80 return 0;
81
82 /* generate and prepend IV */
83 memset(iv, 0, sizeof iv);
84 arc4random_buf(iv, sizeof iv);
85 if (fwrite(iv, 1, sizeof iv, out) != sizeof iv)
86 return 0;
87
88 ctx = EVP_CIPHER_CTX_new();
89 if (ctx == NULL((void *)0))
90 return 0;
91
92 EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL((void *)0), cp.key, iv);
93
94 /* encrypt until end of file */
95 while ((r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE16384, in)) != 0) {
96 if (!EVP_EncryptUpdate(ctx, obuf, &len, ibuf, r))
97 goto end;
98 if (len && fwrite(obuf, len, 1, out) != 1)
99 goto end;
100 }
101 if (!feof(in)(!__isthreaded ? (((in)->_flags & 0x0020) != 0) : (feof
)(in))
)
102 goto end;
103
104 /* finalize and write last chunk if any */
105 if (!EVP_EncryptFinal_ex(ctx, obuf, &len))
106 goto end;
107 if (len && fwrite(obuf, len, 1, out) != 1)
108 goto end;
109
110 /* get and append tag */
111 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG0x10, sizeof tag, tag);
112 if (fwrite(tag, sizeof tag, 1, out) != 1)
113 goto end;
114
115 fflush(out);
116 ret = 1;
117
118end:
119 EVP_CIPHER_CTX_free(ctx);
120 return ret;
121}
122
123int
124crypto_decrypt_file(FILE * in, FILE * out)
125{
126 EVP_CIPHER_CTX *ctx;
127 uint8_t ibuf[CRYPTO_BUFFER_SIZE16384];
128 uint8_t obuf[CRYPTO_BUFFER_SIZE16384];
129 uint8_t iv[IV_SIZE12];
130 uint8_t tag[GCM_TAG_SIZE16];
131 uint8_t version;
132 size_t r;
133 off_t sz;
134 int len;
135 int ret = 0;
136 struct stat sb;
137
138 /* input file too small to be an encrypted file */
139 if (fstat(fileno(in)(!__isthreaded ? ((in)->_file) : (fileno)(in)), &sb) == -1)
140 return 0;
141 if (sb.st_size <= (off_t) (sizeof version + sizeof tag + sizeof iv))
142 return 0;
143 sz = sb.st_size;
144
145 /* extract tag */
146 if (fseek(in, -sizeof(tag), SEEK_END2) == -1)
147 return 0;
148 if ((r = fread(tag, 1, sizeof tag, in)) != sizeof tag)
149 return 0;
150
151 if (fseek(in, 0, SEEK_SET0) == -1)
152 return 0;
153
154 /* extract version */
155 if ((r = fread(&version, 1, sizeof version, in)) != sizeof version)
156 return 0;
157 if (version != API_VERSION1)
158 return 0;
159
160 /* extract IV */
161 memset(iv, 0, sizeof iv);
162 if ((r = fread(iv, 1, sizeof iv, in)) != sizeof iv)
Although the value stored to 'r' is used in the enclosing expression, the value is never actually read from 'r'
163 return 0;
164
165 /* real ciphertext length */
166 sz -= sizeof version;
167 sz -= sizeof iv;
168 sz -= sizeof tag;
169
170 ctx = EVP_CIPHER_CTX_new();
171 if (ctx == NULL((void *)0))
172 return 0;
173
174 EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL((void *)0), cp.key, iv);
175
176 /* set expected tag */
177 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG0x11, sizeof tag, tag);
178
179 /* decrypt until end of ciphertext */
180 while (sz) {
181 if (sz > CRYPTO_BUFFER_SIZE16384)
182 r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE16384, in);
183 else
184 r = fread(ibuf, 1, sz, in);
185 if (!r)
186 break;
187 if (!EVP_DecryptUpdate(ctx, obuf, &len, ibuf, r))
188 goto end;
189 if (len && fwrite(obuf, len, 1, out) != 1)
190 goto end;
191 sz -= r;
192 }
193 if (ferror(in)(!__isthreaded ? (((in)->_flags & 0x0040) != 0) : (ferror
)(in))
)
194 goto end;
195
196 /* finalize, write last chunk if any and perform authentication check */
197 if (!EVP_DecryptFinal_ex(ctx, obuf, &len))
198 goto end;
199 if (len && fwrite(obuf, len, 1, out) != 1)
200 goto end;
201
202 fflush(out);
203 ret = 1;
204
205end:
206 EVP_CIPHER_CTX_free(ctx);
207 return ret;
208}
209
210size_t
211crypto_encrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
212{
213 EVP_CIPHER_CTX *ctx;
214 uint8_t iv[IV_SIZE12];
215 uint8_t tag[GCM_TAG_SIZE16];
216 uint8_t version = API_VERSION1;
217 off_t sz;
218 int olen;
219 int len = 0;
220 int ret = 0;
221
222 /* output buffer does not have enough room */
223 if (outlen < inlen + sizeof version + sizeof tag + sizeof iv)
224 return 0;
225
226 /* input should not exceed 64GB */
227 sz = inlen;
228 if (sz >= 0x1000000000LL)
229 return 0;
230
231 /* prepend version */
232 *out = version;
233 len++;
234
235 /* generate IV */
236 memset(iv, 0, sizeof iv);
237 arc4random_buf(iv, sizeof iv);
238 memcpy(out + len, iv, sizeof iv);
239 len += sizeof iv;
240
241 ctx = EVP_CIPHER_CTX_new();
242 if (ctx == NULL((void *)0))
243 return 0;
244
245 EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL((void *)0), cp.key, iv);
246
247 /* encrypt buffer */
248 if (!EVP_EncryptUpdate(ctx, out + len, &olen, in, inlen))
249 goto end;
250 len += olen;
251
252 /* finalize and write last chunk if any */
253 if (!EVP_EncryptFinal_ex(ctx, out + len, &olen))
254 goto end;
255 len += olen;
256
257 /* get and append tag */
258 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG0x10, sizeof tag, tag);
259 memcpy(out + len, tag, sizeof tag);
260 ret = len + sizeof tag;
261
262end:
263 EVP_CIPHER_CTX_free(ctx);
264 return ret;
265}
266
267size_t
268crypto_decrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
269{
270 EVP_CIPHER_CTX *ctx;
271 uint8_t iv[IV_SIZE12];
272 uint8_t tag[GCM_TAG_SIZE16];
273 int olen;
274 int len = 0;
275 int ret = 0;
276
277 /* out does not have enough room */
278 if (outlen < inlen - sizeof tag + sizeof iv)
279 return 0;
280
281 /* extract tag */
282 memcpy(tag, in + inlen - sizeof tag, sizeof tag);
283 inlen -= sizeof tag;
284
285 /* check version */
286 if (*in != API_VERSION1)
287 return 0;
288 in++;
289 inlen--;
290
291 /* extract IV */
292 memset(iv, 0, sizeof iv);
293 memcpy(iv, in, sizeof iv);
294 inlen -= sizeof iv;
295 in += sizeof iv;
296
297 ctx = EVP_CIPHER_CTX_new();
298 if (ctx == NULL((void *)0))
299 return 0;
300
301 EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL((void *)0), cp.key, iv);
302
303 /* set expected tag */
304 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG0x11, sizeof tag, tag);
305
306 /* decrypt buffer */
307 if (!EVP_DecryptUpdate(ctx, out, &olen, in, inlen))
308 goto end;
309 len += olen;
310
311 /* finalize, write last chunk if any and perform authentication check */
312 if (!EVP_DecryptFinal_ex(ctx, out + len, &olen))
313 goto end;
314 ret = len + olen;
315
316end:
317 EVP_CIPHER_CTX_free(ctx);
318 return ret;
319}
320
321#if 0
322int
323main(int argc, char *argv[])
324{
325 if (argc != 3) {
326 printf("usage: crypto <key> <buffer>\n");
327 return 1;
328 }
329
330 if (!crypto_setup(argv[1], strlen(argv[1]))) {
331 printf("crypto_setup failed\n");
332 return 1;
333 }
334
335 {
336 char encbuffer[4096];
337 size_t enclen;
338 char decbuffer[4096];
339 size_t declen;
340
341 printf("encrypt/decrypt buffer: ");
342 enclen = crypto_encrypt_buffer(argv[2], strlen(argv[2]),
343 encbuffer, sizeof encbuffer);
344
345 /* uncomment below to provoke integrity check failure */
346 /*
347 * encbuffer[13] = 0x42;
348 * encbuffer[14] = 0x42;
349 * encbuffer[15] = 0x42;
350 * encbuffer[16] = 0x42;
351 */
352
353 declen = crypto_decrypt_buffer(encbuffer, enclen,
354 decbuffer, sizeof decbuffer);
355 if (declen != 0 && !strncmp(argv[2], decbuffer, declen))
356 printf("ok\n");
357 else
358 printf("nope\n");
359 }
360
361 {
362 FILE *fpin;
363 FILE *fpout;
364 printf("encrypt/decrypt file: ");
365
366 fpin = fopen("/etc/passwd", "r");
367 fpout = fopen("/tmp/passwd.enc", "w");
368 if (!crypto_encrypt_file(fpin, fpout)) {
369 printf("encryption failed\n");
370 return 1;
371 }
372 fclose(fpin);
373 fclose(fpout);
374
375 /* uncomment below to provoke integrity check failure */
376 /*
377 * fpin = fopen("/tmp/passwd.enc", "a");
378 * fprintf(fpin, "borken");
379 * fclose(fpin);
380 */
381 fpin = fopen("/tmp/passwd.enc", "r");
382 fpout = fopen("/tmp/passwd.dec", "w");
383 if (!crypto_decrypt_file(fpin, fpout))
384 printf("nope\n");
385 else
386 printf("ok\n");
387 fclose(fpin);
388 fclose(fpout);
389 }
390
391
392 return 0;
393}
394#endif