Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

479 rader
13 KiB

  1. /* Copyright (c) 2014, Google Inc.
  2. *
  3. * Permission to use, copy, modify, and/or distribute this software for any
  4. * purpose with or without fee is hereby granted, provided that the above
  5. * copyright notice and this permission notice appear in all copies.
  6. *
  7. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  10. * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  12. * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  13. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
  14. #include <openssl/base.h>
  15. #include <memory>
  16. #include <string>
  17. #include <vector>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <limits.h>
  21. #include <sys/stat.h>
  22. #include <sys/types.h>
  23. #if !defined(OPENSSL_WINDOWS)
  24. #include <string.h>
  25. #include <unistd.h>
  26. #if !defined(O_BINARY)
  27. #define O_BINARY 0
  28. #endif
  29. #else
  30. OPENSSL_MSVC_PRAGMA(warning(push, 3))
  31. #include <windows.h>
  32. OPENSSL_MSVC_PRAGMA(warning(pop))
  33. #include <io.h>
  34. #define PATH_MAX MAX_PATH
  35. typedef int ssize_t;
  36. #endif
  37. #include <openssl/digest.h>
  38. #include "internal.h"
  39. struct close_delete {
  40. void operator()(int *fd) {
  41. BORINGSSL_CLOSE(*fd);
  42. }
  43. };
  44. template<typename T, typename R, R (*func) (T*)>
  45. struct func_delete {
  46. void operator()(T* obj) {
  47. func(obj);
  48. }
  49. };
  50. // Source is an awkward expression of a union type in C++: Stdin | File filename.
  51. struct Source {
  52. enum Type {
  53. STDIN,
  54. };
  55. Source() : is_stdin_(false) {}
  56. Source(Type) : is_stdin_(true) {}
  57. Source(const std::string &name) : is_stdin_(false), filename_(name) {}
  58. bool is_stdin() const { return is_stdin_; }
  59. const std::string &filename() const { return filename_; }
  60. private:
  61. bool is_stdin_;
  62. std::string filename_;
  63. };
  64. static const char kStdinName[] = "standard input";
  65. // OpenFile opens the regular file named |filename| and sets |*out_fd| to be a
  66. // file descriptor to it. Returns true on sucess or prints an error to stderr
  67. // and returns false on error.
  68. static bool OpenFile(int *out_fd, const std::string &filename) {
  69. *out_fd = -1;
  70. int fd = BORINGSSL_OPEN(filename.c_str(), O_RDONLY | O_BINARY);
  71. if (fd < 0) {
  72. fprintf(stderr, "Failed to open input file '%s': %s\n", filename.c_str(),
  73. strerror(errno));
  74. return false;
  75. }
  76. std::unique_ptr<int, close_delete> scoped_fd(&fd);
  77. #if !defined(OPENSSL_WINDOWS)
  78. struct stat st;
  79. if (fstat(fd, &st)) {
  80. fprintf(stderr, "Failed to stat input file '%s': %s\n", filename.c_str(),
  81. strerror(errno));
  82. return false;
  83. }
  84. if (!S_ISREG(st.st_mode)) {
  85. fprintf(stderr, "%s: not a regular file\n", filename.c_str());
  86. return false;
  87. }
  88. #endif
  89. *out_fd = fd;
  90. scoped_fd.release();
  91. return true;
  92. }
  93. // SumFile hashes the contents of |source| with |md| and sets |*out_hex| to the
  94. // hex-encoded result.
  95. //
  96. // It returns true on success or prints an error to stderr and returns false on
  97. // error.
  98. static bool SumFile(std::string *out_hex, const EVP_MD *md,
  99. const Source &source) {
  100. std::unique_ptr<int, close_delete> scoped_fd;
  101. int fd;
  102. if (source.is_stdin()) {
  103. fd = 0;
  104. } else {
  105. if (!OpenFile(&fd, source.filename())) {
  106. return false;
  107. }
  108. scoped_fd.reset(&fd);
  109. }
  110. static const size_t kBufSize = 8192;
  111. std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufSize]);
  112. EVP_MD_CTX ctx;
  113. EVP_MD_CTX_init(&ctx);
  114. std::unique_ptr<EVP_MD_CTX, func_delete<EVP_MD_CTX, int, EVP_MD_CTX_cleanup>>
  115. scoped_ctx(&ctx);
  116. if (!EVP_DigestInit_ex(&ctx, md, NULL)) {
  117. fprintf(stderr, "Failed to initialize EVP_MD_CTX.\n");
  118. return false;
  119. }
  120. for (;;) {
  121. ssize_t n;
  122. do {
  123. n = BORINGSSL_READ(fd, buf.get(), kBufSize);
  124. } while (n == -1 && errno == EINTR);
  125. if (n == 0) {
  126. break;
  127. } else if (n < 0) {
  128. fprintf(stderr, "Failed to read from %s: %s\n",
  129. source.is_stdin() ? kStdinName : source.filename().c_str(),
  130. strerror(errno));
  131. return false;
  132. }
  133. if (!EVP_DigestUpdate(&ctx, buf.get(), n)) {
  134. fprintf(stderr, "Failed to update hash.\n");
  135. return false;
  136. }
  137. }
  138. uint8_t digest[EVP_MAX_MD_SIZE];
  139. unsigned digest_len;
  140. if (!EVP_DigestFinal_ex(&ctx, digest, &digest_len)) {
  141. fprintf(stderr, "Failed to finish hash.\n");
  142. return false;
  143. }
  144. char hex_digest[EVP_MAX_MD_SIZE * 2];
  145. static const char kHextable[] = "0123456789abcdef";
  146. for (unsigned i = 0; i < digest_len; i++) {
  147. const uint8_t b = digest[i];
  148. hex_digest[i * 2] = kHextable[b >> 4];
  149. hex_digest[i * 2 + 1] = kHextable[b & 0xf];
  150. }
  151. *out_hex = std::string(hex_digest, digest_len * 2);
  152. return true;
  153. }
  154. // PrintFileSum hashes |source| with |md| and prints a line to stdout in the
  155. // format of the coreutils *sum utilities. It returns true on success or prints
  156. // an error to stderr and returns false on error.
  157. static bool PrintFileSum(const EVP_MD *md, const Source &source) {
  158. std::string hex_digest;
  159. if (!SumFile(&hex_digest, md, source)) {
  160. return false;
  161. }
  162. // TODO: When given "--binary" or "-b", we should print " *" instead of " "
  163. // between the digest and the filename.
  164. //
  165. // MSYS and Cygwin md5sum default to binary mode by default, whereas other
  166. // platforms' tools default to text mode by default. We default to text mode
  167. // by default and consider text mode equivalent to binary mode (i.e. we
  168. // always use Unix semantics, even on Windows), which means that our default
  169. // output will differ from the MSYS and Cygwin tools' default output.
  170. printf("%s %s\n", hex_digest.c_str(),
  171. source.is_stdin() ? "-" : source.filename().c_str());
  172. return true;
  173. }
  174. // CheckModeArguments contains arguments for the check mode. See the
  175. // sha256sum(1) man page for details.
  176. struct CheckModeArguments {
  177. bool quiet = false;
  178. bool status = false;
  179. bool warn = false;
  180. bool strict = false;
  181. };
  182. // Check reads lines from |source| where each line is in the format of the
  183. // coreutils *sum utilities. It attempts to verify each hash by reading the
  184. // file named in the line.
  185. //
  186. // It returns true if all files were verified and, if |args.strict|, no input
  187. // lines had formatting errors. Otherwise it prints errors to stderr and
  188. // returns false.
  189. static bool Check(const CheckModeArguments &args, const EVP_MD *md,
  190. const Source &source) {
  191. std::unique_ptr<FILE, func_delete<FILE, int, fclose>> scoped_file;
  192. FILE *file;
  193. if (source.is_stdin()) {
  194. file = stdin;
  195. } else {
  196. int fd;
  197. if (!OpenFile(&fd, source.filename())) {
  198. return false;
  199. }
  200. file = BORINGSSL_FDOPEN(fd, "rb");
  201. if (!file) {
  202. perror("fdopen");
  203. BORINGSSL_CLOSE(fd);
  204. return false;
  205. }
  206. scoped_file = std::unique_ptr<FILE, func_delete<FILE, int, fclose>>(file);
  207. }
  208. const size_t hex_size = EVP_MD_size(md) * 2;
  209. char line[EVP_MAX_MD_SIZE * 2 + 2 /* spaces */ + PATH_MAX + 1 /* newline */ +
  210. 1 /* NUL */];
  211. unsigned bad_lines = 0;
  212. unsigned parsed_lines = 0;
  213. unsigned error_lines = 0;
  214. unsigned bad_hash_lines = 0;
  215. unsigned line_no = 0;
  216. bool ok = true;
  217. bool draining_overlong_line = false;
  218. for (;;) {
  219. line_no++;
  220. if (fgets(line, sizeof(line), file) == nullptr) {
  221. if (feof(file)) {
  222. break;
  223. }
  224. fprintf(stderr, "Error reading from input.\n");
  225. return false;
  226. }
  227. size_t len = strlen(line);
  228. if (draining_overlong_line) {
  229. if (line[len - 1] == '\n') {
  230. draining_overlong_line = false;
  231. }
  232. continue;
  233. }
  234. const bool overlong = line[len - 1] != '\n' && !feof(file);
  235. if (len < hex_size + 2 /* spaces */ + 1 /* filename */ ||
  236. line[hex_size] != ' ' ||
  237. line[hex_size + 1] != ' ' ||
  238. overlong) {
  239. bad_lines++;
  240. if (args.warn) {
  241. fprintf(stderr, "%s: %u: improperly formatted line\n",
  242. source.is_stdin() ? kStdinName : source.filename().c_str(), line_no);
  243. }
  244. if (args.strict) {
  245. ok = false;
  246. }
  247. if (overlong) {
  248. draining_overlong_line = true;
  249. }
  250. continue;
  251. }
  252. if (line[len - 1] == '\n') {
  253. line[len - 1] = 0;
  254. len--;
  255. }
  256. parsed_lines++;
  257. // coreutils does not attempt to restrict relative or absolute paths in the
  258. // input so nor does this code.
  259. std::string calculated_hex_digest;
  260. const std::string target_filename(&line[hex_size + 2]);
  261. Source target_source;
  262. if (target_filename == "-") {
  263. // coreutils reads from stdin if the filename is "-".
  264. target_source = Source(Source::STDIN);
  265. } else {
  266. target_source = Source(target_filename);
  267. }
  268. if (!SumFile(&calculated_hex_digest, md, target_source)) {
  269. error_lines++;
  270. ok = false;
  271. continue;
  272. }
  273. if (calculated_hex_digest != std::string(line, hex_size)) {
  274. bad_hash_lines++;
  275. if (!args.status) {
  276. printf("%s: FAILED\n", target_filename.c_str());
  277. }
  278. ok = false;
  279. continue;
  280. }
  281. if (!args.quiet) {
  282. printf("%s: OK\n", target_filename.c_str());
  283. }
  284. }
  285. if (!args.status) {
  286. if (bad_lines > 0 && parsed_lines > 0) {
  287. fprintf(stderr, "WARNING: %u line%s improperly formatted\n", bad_lines,
  288. bad_lines == 1 ? " is" : "s are");
  289. }
  290. if (error_lines > 0) {
  291. fprintf(stderr, "WARNING: %u computed checksum(s) did NOT match\n",
  292. error_lines);
  293. }
  294. }
  295. if (parsed_lines == 0) {
  296. fprintf(stderr, "%s: no properly formatted checksum lines found.\n",
  297. source.is_stdin() ? kStdinName : source.filename().c_str());
  298. ok = false;
  299. }
  300. return ok;
  301. }
  302. // DigestSum acts like the coreutils *sum utilites, with the given hash
  303. // function.
  304. static bool DigestSum(const EVP_MD *md,
  305. const std::vector<std::string> &args) {
  306. bool check_mode = false;
  307. CheckModeArguments check_args;
  308. bool check_mode_args_given = false;
  309. std::vector<Source> sources;
  310. auto it = args.begin();
  311. while (it != args.end()) {
  312. const std::string &arg = *it;
  313. if (!arg.empty() && arg[0] != '-') {
  314. break;
  315. }
  316. it++;
  317. if (arg == "--") {
  318. break;
  319. }
  320. if (arg == "-") {
  321. // "-" ends the argument list and indicates that stdin should be used.
  322. sources.push_back(Source(Source::STDIN));
  323. break;
  324. }
  325. if (arg.size() >= 2 && arg[0] == '-' && arg[1] != '-') {
  326. for (size_t i = 1; i < arg.size(); i++) {
  327. switch (arg[i]) {
  328. case 'b':
  329. case 't':
  330. // Binary/text mode – irrelevent, even on Windows.
  331. break;
  332. case 'c':
  333. check_mode = true;
  334. break;
  335. case 'w':
  336. check_mode_args_given = true;
  337. check_args.warn = true;
  338. break;
  339. default:
  340. fprintf(stderr, "Unknown option '%c'.\n", arg[i]);
  341. return false;
  342. }
  343. }
  344. } else if (arg == "--binary" || arg == "--text") {
  345. // Binary/text mode – irrelevent, even on Windows.
  346. } else if (arg == "--check") {
  347. check_mode = true;
  348. } else if (arg == "--quiet") {
  349. check_mode_args_given = true;
  350. check_args.quiet = true;
  351. } else if (arg == "--status") {
  352. check_mode_args_given = true;
  353. check_args.status = true;
  354. } else if (arg == "--warn") {
  355. check_mode_args_given = true;
  356. check_args.warn = true;
  357. } else if (arg == "--strict") {
  358. check_mode_args_given = true;
  359. check_args.strict = true;
  360. } else {
  361. fprintf(stderr, "Unknown option '%s'.\n", arg.c_str());
  362. return false;
  363. }
  364. }
  365. if (check_mode_args_given && !check_mode) {
  366. fprintf(
  367. stderr,
  368. "Check mode arguments are only meaningful when verifying checksums.\n");
  369. return false;
  370. }
  371. for (; it != args.end(); it++) {
  372. sources.push_back(Source(*it));
  373. }
  374. if (sources.empty()) {
  375. sources.push_back(Source(Source::STDIN));
  376. }
  377. bool ok = true;
  378. if (check_mode) {
  379. for (auto &source : sources) {
  380. ok &= Check(check_args, md, source);
  381. }
  382. } else {
  383. for (auto &source : sources) {
  384. ok &= PrintFileSum(md, source);
  385. }
  386. }
  387. return ok;
  388. }
  389. bool MD5Sum(const std::vector<std::string> &args) {
  390. return DigestSum(EVP_md5(), args);
  391. }
  392. bool SHA1Sum(const std::vector<std::string> &args) {
  393. return DigestSum(EVP_sha1(), args);
  394. }
  395. bool SHA224Sum(const std::vector<std::string> &args) {
  396. return DigestSum(EVP_sha224(), args);
  397. }
  398. bool SHA256Sum(const std::vector<std::string> &args) {
  399. return DigestSum(EVP_sha256(), args);
  400. }
  401. bool SHA384Sum(const std::vector<std::string> &args) {
  402. return DigestSum(EVP_sha384(), args);
  403. }
  404. bool SHA512Sum(const std::vector<std::string> &args) {
  405. return DigestSum(EVP_sha512(), args);
  406. }