XBPS Library API  0.19
The X Binary Package System
util_hash.c
1 /*-
2  * Copyright (c) 2008-2012 Juan Romero Pardines.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #define _BSD_SOURCE /* for madvise(2) */
27 #include <sys/mman.h>
28 #undef _BSD_SOURCE
29 
30 #include <stdio.h>
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 
38 #include <openssl/sha.h>
39 
40 #include "xbps_api_impl.h"
41 
42 /**
43  * @file lib/util.c
44  * @brief Utility routines
45  * @defgroup util Utility functions
46  */
47 static void
48 digest2string(const uint8_t *digest, char *string, size_t len)
49 {
50  while (len--) {
51  if (*digest / 16 < 10)
52  *string++ = '0' + *digest / 16;
53  else
54  *string++ = 'a' + *digest / 16 - 10;
55  if (*digest % 16 < 10)
56  *string++ = '0' + *digest % 16;
57  else
58  *string++ = 'a' + *digest % 16 - 10;
59  ++digest;
60  }
61  *string = '\0';
62 }
63 
64 char *
65 xbps_file_hash(const char *file)
66 {
67  struct stat st;
68  size_t pgsize = (size_t)sysconf(_SC_PAGESIZE);
69  size_t pgmask = pgsize - 1, mapsize;
70  char hash[SHA256_DIGEST_LENGTH * 2 + 1];
71  unsigned char *buf = NULL, digest[SHA256_DIGEST_LENGTH];
72  int fd;
73  bool need_guard = false;
74 
75  assert(file != NULL);
76 
77  if ((fd = open(file, O_RDONLY|O_CLOEXEC)) == -1) {
78  free(buf);
79  return NULL;
80  }
81  memset(&st, 0, sizeof(st));
82  if (fstat(fd, &st) == -1) {
83  (void)close(fd);
84  return NULL;
85  }
86  if (st.st_size > SSIZE_MAX - 1) {
87  (void)close(fd);
88  return NULL;
89  }
90 
91  mapsize = ((size_t)st.st_size + pgmask) & ~pgmask;
92  if (mapsize < (size_t)st.st_size) {
93  (void)close(fd);
94  return NULL;
95  }
96  /*
97  * If the file length is an integral number of pages, then we
98  * need to map a guard page at the end in order to provide the
99  * necessary NUL-termination of the buffer.
100  */
101  if ((st.st_size & pgmask) == 0)
102  need_guard = true;
103 
104  buf = mmap(NULL, need_guard ? mapsize + pgsize : mapsize,
105  PROT_READ, MAP_PRIVATE, fd, 0);
106  (void)close(fd);
107  if (buf == MAP_FAILED)
108  return NULL;
109 
110  (void)madvise(buf, mapsize, MADV_SEQUENTIAL);
111  if (SHA256(buf, st.st_size, digest) == NULL) {
112  munmap(buf, mapsize);
113  return NULL;
114  }
115  (void)madvise(buf, mapsize, MADV_DONTNEED);
116  munmap(buf, mapsize);
117 
118  digest2string(digest, hash, SHA256_DIGEST_LENGTH);
119 
120  return strdup(hash);
121 }
122 
123 int
124 xbps_file_hash_check(const char *file, const char *sha256)
125 {
126  char *res;
127 
128  assert(file != NULL);
129  assert(sha256 != NULL);
130 
131  res = xbps_file_hash(file);
132  if (res == NULL)
133  return errno;
134 
135  if (strcmp(sha256, res)) {
136  free(res);
137  return ERANGE;
138  }
139  free(res);
140 
141  return 0;
142 }
143 
144 static const char *
145 file_hash_dictionary(prop_dictionary_t d, const char *key, const char *file)
146 {
147  prop_object_t obj;
148  prop_object_iterator_t iter;
149  const char *curfile = NULL, *sha256 = NULL;
150 
151  assert(prop_object_type(d) == PROP_TYPE_DICTIONARY);
152  assert(key != NULL);
153  assert(file != NULL);
154 
155  iter = xbps_array_iter_from_dict(d, key);
156  if (iter == NULL) {
157  errno = ENOENT;
158  return NULL;
159  }
160  while ((obj = prop_object_iterator_next(iter)) != NULL) {
161  prop_dictionary_get_cstring_nocopy(obj,
162  "file", &curfile);
163  if (strcmp(file, curfile) == 0) {
164  /* file matched */
165  prop_dictionary_get_cstring_nocopy(obj,
166  "sha256", &sha256);
167  break;
168  }
169  }
170  prop_object_iterator_release(iter);
171  if (sha256 == NULL)
172  errno = ENOENT;
173 
174  return sha256;
175 }
176 
177 int HIDDEN
178 xbps_file_hash_check_dictionary(struct xbps_handle *xhp,
179  prop_dictionary_t d,
180  const char *key,
181  const char *file)
182 {
183  const char *sha256d = NULL;
184  char *buf;
185  int rv;
186 
187  assert(prop_object_type(d) == PROP_TYPE_DICTIONARY);
188  assert(key != NULL);
189  assert(file != NULL);
190 
191  if ((sha256d = file_hash_dictionary(d, key, file)) == NULL) {
192  if (errno == ENOENT)
193  return 1; /* no match, file not found */
194 
195  return -1; /* error */
196  }
197 
198  if (strcmp(xhp->rootdir, "/") == 0) {
199  rv = xbps_file_hash_check(file, sha256d);
200  } else {
201  buf = xbps_xasprintf("%s/%s", xhp->rootdir, file);
202  rv = xbps_file_hash_check(buf, sha256d);
203  free(buf);
204  }
205  if (rv == 0)
206  return 0; /* matched */
207  else if (rv == ERANGE || rv == ENOENT)
208  return 1; /* no match */
209  else
210  return -1; /* error */
211 }