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