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