Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2020-2022 Yubico AB. All rights reserved. |
3 | | * Use of this source code is governed by a BSD-style |
4 | | * license that can be found in the LICENSE file. |
5 | | */ |
6 | | |
7 | | #include <zlib.h> |
8 | | #include "fido.h" |
9 | | |
10 | 1.01k | #define BOUND (1024UL * 1024UL) |
11 | | |
12 | | /* zlib inflate (raw + headers) */ |
13 | | static int |
14 | | rfc1950_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz) |
15 | 13 | { |
16 | 13 | u_long ilen, olen; |
17 | 13 | int z; |
18 | | |
19 | 13 | memset(out, 0, sizeof(*out)); |
20 | | |
21 | 13 | if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND || |
22 | 13 | origsiz > ULONG_MAX || (olen = (u_long)origsiz) > BOUND) { |
23 | 0 | fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__, |
24 | 0 | in->len, origsiz); |
25 | 0 | return FIDO_ERR_INVALID_ARGUMENT; |
26 | 0 | } |
27 | | |
28 | 13 | if ((out->ptr = calloc(1, olen)) == NULL) |
29 | 1 | return FIDO_ERR_INTERNAL; |
30 | 12 | out->len = olen; |
31 | | |
32 | 12 | if ((z = uncompress(out->ptr, &olen, in->ptr, ilen)) != Z_OK || |
33 | 12 | olen > SIZE_MAX || olen != out->len) { |
34 | 3 | fido_log_debug("%s: uncompress: %d, olen=%lu, out->len=%zu", |
35 | 3 | __func__, z, olen, out->len); |
36 | 3 | fido_blob_reset(out); |
37 | 3 | return FIDO_ERR_COMPRESS; |
38 | 3 | } |
39 | | |
40 | 9 | return FIDO_OK; |
41 | 12 | } |
42 | | |
43 | | /* raw inflate */ |
44 | | static int |
45 | | rfc1951_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz) |
46 | 4 | { |
47 | 4 | z_stream zs; |
48 | 4 | u_int ilen, olen; |
49 | 4 | int r, z; |
50 | | |
51 | 4 | memset(&zs, 0, sizeof(zs)); |
52 | 4 | memset(out, 0, sizeof(*out)); |
53 | | |
54 | 4 | if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND || |
55 | 4 | origsiz > UINT_MAX || (olen = (u_int)origsiz) > BOUND) { |
56 | 0 | fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__, |
57 | 0 | in->len, origsiz); |
58 | 0 | return FIDO_ERR_INVALID_ARGUMENT; |
59 | 0 | } |
60 | 4 | if ((z = inflateInit2(&zs, -MAX_WBITS)) != Z_OK) { |
61 | 0 | fido_log_debug("%s: inflateInit2: %d", __func__, z); |
62 | 0 | return FIDO_ERR_COMPRESS; |
63 | 0 | } |
64 | | |
65 | 4 | if ((out->ptr = calloc(1, olen)) == NULL) { |
66 | 1 | r = FIDO_ERR_INTERNAL; |
67 | 1 | goto fail; |
68 | 1 | } |
69 | 3 | out->len = olen; |
70 | 3 | zs.next_in = in->ptr; |
71 | 3 | zs.avail_in = ilen; |
72 | 3 | zs.next_out = out->ptr; |
73 | 3 | zs.avail_out = olen; |
74 | | |
75 | 3 | if ((z = inflate(&zs, Z_FINISH)) != Z_STREAM_END) { |
76 | 1 | fido_log_debug("%s: inflate: %d", __func__, z); |
77 | 1 | r = FIDO_ERR_COMPRESS; |
78 | 1 | goto fail; |
79 | 1 | } |
80 | 2 | if (zs.avail_out != 0) { |
81 | 0 | fido_log_debug("%s: %u != 0", __func__, zs.avail_out); |
82 | 0 | r = FIDO_ERR_COMPRESS; |
83 | 0 | goto fail; |
84 | 0 | } |
85 | | |
86 | 2 | r = FIDO_OK; |
87 | 4 | fail: |
88 | 4 | if ((z = inflateEnd(&zs)) != Z_OK) { |
89 | 0 | fido_log_debug("%s: inflateEnd: %d", __func__, z); |
90 | 0 | r = FIDO_ERR_COMPRESS; |
91 | 0 | } |
92 | 4 | if (r != FIDO_OK) |
93 | 2 | fido_blob_reset(out); |
94 | | |
95 | 4 | return r; |
96 | 2 | } |
97 | | |
98 | | /* raw deflate */ |
99 | | static int |
100 | | rfc1951_deflate(fido_blob_t *out, const fido_blob_t *in) |
101 | 481 | { |
102 | 481 | z_stream zs; |
103 | 481 | u_int ilen, olen; |
104 | 481 | int r, z; |
105 | | |
106 | 481 | memset(&zs, 0, sizeof(zs)); |
107 | 481 | memset(out, 0, sizeof(*out)); |
108 | | |
109 | 481 | if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND) { |
110 | 0 | fido_log_debug("%s: in->len=%zu", __func__, in->len); |
111 | 0 | return FIDO_ERR_INVALID_ARGUMENT; |
112 | 0 | } |
113 | 481 | if ((z = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, |
114 | 481 | -MAX_WBITS, 8, Z_DEFAULT_STRATEGY)) != Z_OK) { |
115 | 1 | fido_log_debug("%s: deflateInit2: %d", __func__, z); |
116 | 1 | return FIDO_ERR_COMPRESS; |
117 | 1 | } |
118 | | |
119 | 480 | olen = BOUND; |
120 | 480 | if ((out->ptr = calloc(1, olen)) == NULL) { |
121 | 1 | r = FIDO_ERR_INTERNAL; |
122 | 1 | goto fail; |
123 | 1 | } |
124 | 479 | out->len = olen; |
125 | 479 | zs.next_in = in->ptr; |
126 | 479 | zs.avail_in = ilen; |
127 | 479 | zs.next_out = out->ptr; |
128 | 479 | zs.avail_out = olen; |
129 | | |
130 | 479 | if ((z = deflate(&zs, Z_FINISH)) != Z_STREAM_END) { |
131 | 1 | fido_log_debug("%s: inflate: %d", __func__, z); |
132 | 1 | r = FIDO_ERR_COMPRESS; |
133 | 1 | goto fail; |
134 | 1 | } |
135 | 478 | if (zs.avail_out >= out->len) { |
136 | 1 | fido_log_debug("%s: %u > %zu", __func__, zs.avail_out, |
137 | 1 | out->len); |
138 | 1 | r = FIDO_ERR_COMPRESS; |
139 | 1 | goto fail; |
140 | 1 | } |
141 | 477 | out->len -= zs.avail_out; |
142 | | |
143 | 477 | r = FIDO_OK; |
144 | 480 | fail: |
145 | 480 | if ((z = deflateEnd(&zs)) != Z_OK) { |
146 | 0 | fido_log_debug("%s: deflateEnd: %d", __func__, z); |
147 | 0 | r = FIDO_ERR_COMPRESS; |
148 | 0 | } |
149 | 480 | if (r != FIDO_OK) |
150 | 3 | fido_blob_reset(out); |
151 | | |
152 | 480 | return r; |
153 | 477 | } |
154 | | |
155 | | int |
156 | | fido_compress(fido_blob_t *out, const fido_blob_t *in) |
157 | 481 | { |
158 | 481 | return rfc1951_deflate(out, in); |
159 | 481 | } |
160 | | |
161 | | int |
162 | | fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz) |
163 | 13 | { |
164 | 13 | if (rfc1950_inflate(out, in, origsiz) == FIDO_OK) |
165 | 9 | return FIDO_OK; /* backwards compat with libfido2 < 1.11 */ |
166 | 4 | return rfc1951_inflate(out, in, origsiz); |
167 | 13 | } |