1 /* librepo - A library providing (libcURL like) API to downloading repository
2 * Copyright (C) 2012 Tomas Mlcoch
3 *
4 * Licensed under the GNU Lesser General Public License Version 2.1
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <glib.h>
22 #include <glib/gprintf.h>
23 #include <assert.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include "xattr.h"
30 #include <unistd.h>
31 #include <openssl/evp.h>
32
33 #include "cleanup.h"
34 #include "checksum.h"
35 #include "rcodes.h"
36 #include "util.h"
37 #include "xattr_internal.h"
38
39 #define BUFFER_SIZE 2048
40 #define MAX_CHECKSUM_NAME_LEN 7
41
42 LrChecksumType
lr_checksum_type(const char * type)43 lr_checksum_type(const char *type)
44 {
45 size_t len;
46 char type_lower[MAX_CHECKSUM_NAME_LEN+1];
47
48 if (!type)
49 return LR_CHECKSUM_UNKNOWN;
50
51 len = strlen(type);
52 if (len > MAX_CHECKSUM_NAME_LEN)
53 return LR_CHECKSUM_UNKNOWN;
54
55 for (size_t x = 0; x <= len; x++)
56 type_lower[x] = tolower(type[x]);
57
58 if (!strncmp(type_lower, "md", 2)) {
59 // MD* family
60 char *md_type = type_lower + 2;
61 if (!strcmp(md_type, "5"))
62 return LR_CHECKSUM_MD5;
63 } else if (!strncmp(type_lower, "sha", 3)) {
64 // SHA* family
65 char *sha_type = type_lower + 3;
66 if (!strcmp(sha_type, ""))
67 return LR_CHECKSUM_SHA1;
68 else if (!strcmp(sha_type, "1"))
69 return LR_CHECKSUM_SHA1;
70 else if (!strcmp(sha_type, "224"))
71 return LR_CHECKSUM_SHA224;
72 else if (!strcmp(sha_type, "256"))
73 return LR_CHECKSUM_SHA256;
74 else if (!strcmp(sha_type, "384"))
75 return LR_CHECKSUM_SHA384;
76 else if (!strcmp(sha_type, "512"))
77 return LR_CHECKSUM_SHA512;
78 }
79
80 return LR_CHECKSUM_UNKNOWN;
81 }
82
83 const char *
lr_checksum_type_to_str(LrChecksumType type)84 lr_checksum_type_to_str(LrChecksumType type)
85 {
86 switch (type) {
87 case LR_CHECKSUM_UNKNOWN:
88 return "Unknown checksum";
89 case LR_CHECKSUM_MD5:
90 return "md5";
91 case LR_CHECKSUM_SHA1:
92 return "sha1";
93 case LR_CHECKSUM_SHA224:
94 return "sha224";
95 case LR_CHECKSUM_SHA256:
96 return "sha256";
97 case LR_CHECKSUM_SHA384:
98 return "sha384";
99 case LR_CHECKSUM_SHA512:
100 return "sha512";
101 }
102 return NULL;
103 }
104
105 char *
lr_checksum_fd(LrChecksumType type,int fd,GError ** err)106 lr_checksum_fd(LrChecksumType type, int fd, GError **err)
107 {
108 unsigned int len;
109 ssize_t readed;
110 char buf[BUFFER_SIZE];
111 unsigned char raw_checksum[EVP_MAX_MD_SIZE];
112 char *checksum;
113 EVP_MD_CTX *ctx;
114 const EVP_MD *ctx_type;
115
116 assert(fd > -1);
117 assert(!err || *err == NULL);
118
119 switch (type) {
120 case LR_CHECKSUM_MD5: ctx_type = EVP_md5(); break;
121 case LR_CHECKSUM_SHA1: ctx_type = EVP_sha1(); break;
122 case LR_CHECKSUM_SHA224: ctx_type = EVP_sha224(); break;
123 case LR_CHECKSUM_SHA256: ctx_type = EVP_sha256(); break;
124 case LR_CHECKSUM_SHA384: ctx_type = EVP_sha384(); break;
125 case LR_CHECKSUM_SHA512: ctx_type = EVP_sha512(); break;
126 case LR_CHECKSUM_UNKNOWN:
127 default:
128 g_debug("%s: Unknown checksum type", __func__);
129 assert(0);
130 g_set_error(err, LR_CHECKSUM_ERROR, LRE_BADFUNCARG,
131 "Unknown checksum type: %d", type);
132 return NULL;
133 }
134
135 ctx = EVP_MD_CTX_create();
136 if (!ctx) {
137 g_set_error(err, LR_CHECKSUM_ERROR, LRE_OPENSSL,
138 "EVP_MD_CTX_create() failed");
139 return NULL;
140 }
141
142 if (!EVP_DigestInit_ex(ctx, ctx_type, NULL)) {
143 g_set_error(err, LR_CHECKSUM_ERROR, LRE_OPENSSL,
144 "EVP_DigestInit_ex() failed");
145 EVP_MD_CTX_destroy(ctx);
146 return NULL;
147 }
148
149 if (lseek(fd, 0, SEEK_SET) == -1) {
150 g_set_error(err, LR_CHECKSUM_ERROR, LRE_IO,
151 "Cannot seek to the begin of the file. "
152 "lseek(%d, 0, SEEK_SET) error: %s", fd, g_strerror(errno));
153 return NULL;
154 }
155
156 while ((readed = read(fd, buf, BUFFER_SIZE)) > 0)
157 if (!EVP_DigestUpdate(ctx, buf, readed)) {
158 g_set_error(err, LR_CHECKSUM_ERROR, LRE_OPENSSL,
159 "EVP_DigestUpdate() failed");
160 return NULL;
161 }
162
163 if (readed == -1) {
164 EVP_MD_CTX_destroy(ctx);
165 g_set_error(err, LR_CHECKSUM_ERROR, LRE_IO,
166 "read(%d) failed: %s", fd, g_strerror(errno));
167 return NULL;
168 }
169
170 if (!EVP_DigestFinal_ex(ctx, raw_checksum, &len)) {
171 g_set_error(err, LR_CHECKSUM_ERROR, LRE_OPENSSL,
172 "EVP_DigestFinal_ex() failed");
173 return NULL;
174 }
175
176 EVP_MD_CTX_destroy(ctx);
177
178 checksum = lr_malloc0(sizeof(char) * (len * 2 + 1));
179 for (size_t x = 0; x < len; x++)
180 sprintf(checksum+(x*2), "%02x", raw_checksum[x]);
181
182 return checksum;
183 }
184
185 gboolean
lr_checksum_fd_cmp(LrChecksumType type,int fd,const char * expected,gboolean caching,gboolean * matches,GError ** err)186 lr_checksum_fd_cmp(LrChecksumType type,
187 int fd,
188 const char *expected,
189 gboolean caching,
190 gboolean *matches,
191 GError **err)
192 {
193 return lr_checksum_fd_compare(type, fd, expected, caching,
194 matches, NULL, err);
195 }
196
197
198 gboolean
lr_checksum_fd_compare(LrChecksumType type,int fd,const char * expected,gboolean caching,gboolean * matches,gchar ** calculated,GError ** err)199 lr_checksum_fd_compare(LrChecksumType type,
200 int fd,
201 const char *expected,
202 gboolean caching,
203 gboolean *matches,
204 gchar **calculated,
205 GError **err)
206 {
207 _cleanup_free_ gchar *checksum = NULL;
208
209 assert(fd >= 0);
210 assert(!err || *err == NULL);
211
212 *matches = FALSE;
213
214 if (!expected) {
215 g_set_error(err, LR_CHECKSUM_ERROR, LRE_BADFUNCARG,
216 "No expected checksum passed");
217 return FALSE;
218 }
219
220 time_t timestamp = -1;
221
222 if (caching) {
223 struct stat st;
224 if (fstat(fd, &st) == 0) {
225 timestamp = st.st_mtime;
226 }
227 }
228
229 _cleanup_free_ gchar *timestamp_str = g_strdup_printf("%lli", (long long)timestamp);
230 const char *type_str = lr_checksum_type_to_str(type);
231 _cleanup_free_ gchar *timestamp_key = g_strconcat(XATTR_CHKSUM_PREFIX, "mtime", NULL);
232 _cleanup_free_ gchar *checksum_key = g_strconcat(XATTR_CHKSUM_PREFIX, type_str, NULL);
233
234 if (caching && timestamp != -1) {
235 // Load cached checksum if enabled and used
236 char buf[256];
237 ssize_t attr_size;
238 attr_size = FGETXATTR(fd, timestamp_key, &buf, sizeof(buf)-1);
239 if (attr_size != -1) {
240 buf[attr_size] = 0;
241 // check that mtime stored in xattr is the same as timestamp
242 if (strcmp(timestamp_str, buf) == 0) {
243 g_debug("%s: Using mtime cached in xattr: [%s] %s", __func__, timestamp_key, buf);
244 attr_size = FGETXATTR(fd, checksum_key, &buf, sizeof(buf)-1);
245 if (attr_size != -1) {
246 buf[attr_size] = 0;
247 // Cached checksum found
248 g_debug("%s: Using checksum cached in xattr: [%s] %s",
249 __func__, checksum_key, buf);
250 *matches = (strcmp(expected, buf) == 0);
251 if (calculated)
252 *calculated = g_strdup(buf);
253 return TRUE;
254 }
255 } else {
256 // timestamp stored in xattr is different => checksums are no longer valid
257 lr_checksum_clear_cache(fd);
258 }
259 }
260 }
261
262 checksum = lr_checksum_fd(type, fd, err);
263 if (!checksum)
264 return FALSE;
265
266 *matches = (strcmp(expected, checksum)) ? FALSE : TRUE;
267
268 if (fsync(fd) != 0) {
269 if (errno == EROFS || errno == EINVAL) {
270 g_debug("fsync failed: %s", strerror(errno));
271 } else {
272 g_set_error(err, LR_CHECKSUM_ERROR, LRE_FILE,
273 "fsync failed: %s", strerror(errno));
274 return FALSE;
275 }
276 }
277
278 if (caching && *matches && timestamp != -1) {
279 // Store timestamp and checksum as extended file attribute if caching is enabled
280 FSETXATTR(fd, timestamp_key, timestamp_str, strlen(timestamp_str), 0);
281 FSETXATTR(fd, checksum_key, checksum, strlen(checksum), 0);
282 }
283
284 if (calculated)
285 *calculated = g_strdup(checksum);
286
287 return TRUE;
288 }
289
290
291 void
lr_checksum_clear_cache(int fd)292 lr_checksum_clear_cache(int fd)
293 {
294 char *xattrs = NULL;
295 ssize_t xattrs_len;
296 ssize_t bytes_read;
297 const char *attr;
298 ssize_t prefix_len = strlen(XATTR_CHKSUM_PREFIX);
299
300 xattrs_len = FLISTXATTR(fd, NULL, 0);
301 if (xattrs_len <= 0) {
302 return;
303 }
304 xattrs = lr_malloc(xattrs_len);
305 bytes_read = FLISTXATTR(fd, xattrs, xattrs_len);
306 if (bytes_read < 0) {
307 goto cleanup;
308 }
309 attr = xattrs;
310 while (attr < xattrs + xattrs_len) {
311 if (strncmp(XATTR_CHKSUM_PREFIX, attr, prefix_len) == 0) {
312 FREMOVEXATTR(fd, attr);
313 }
314 attr += strlen(attr) + 1;
315 }
316 cleanup:
317 lr_free(xattrs);
318 }
319