tpm2_certifyX509certutil.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <fcntl.h>
  3. #include <stdbool.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <unistd.h>
  9. #include <openssl/x509.h>
  10. #include <openssl/x509v3.h>
  11. #include <openssl/bio.h>
  12. #include "log.h"
  13. #include "tpm2_tool.h"
  14. struct tpm_gen_partial_cert {
  15. const char *out_path;
  16. const char *valid_str;
  17. const char *subject;
  18. const char *issuer;
  19. };
  20. #define CERT_FILE "partial_cert.der"
  21. #define VALID_DAYS "3560"
  22. #define SUBJ "C=US;O=CA org;OU=CA unit;CN=example"
  23. #define ISSUER "C=US;O=CA org;OU=CA unit;CN=example"
  24. static struct tpm_gen_partial_cert ctx = {
  25. .out_path = CERT_FILE,
  26. .valid_str = VALID_DAYS,
  27. .subject = SUBJ,
  28. .issuer = ISSUER
  29. };
  30. static bool on_option(char key, char *value) {
  31. switch (key) {
  32. case 'o':
  33. ctx.out_path = value;
  34. break;
  35. case 'd':
  36. ctx.valid_str = value;
  37. break;
  38. case 's':
  39. ctx.subject = value;
  40. break;
  41. case 'i':
  42. ctx.issuer = value;
  43. break;
  44. }
  45. return true;
  46. }
  47. static bool tpm2_tool_onstart(tpm2_options **opts) {
  48. const struct option topts[] = {
  49. { "outcert", optional_argument, NULL, 'o' },
  50. { "days", optional_argument, NULL, 'd' },
  51. { "subject", optional_argument, NULL, 's' },
  52. { "issuer", optional_argument, NULL, 'i' }
  53. };
  54. *opts = tpm2_options_new("o:d:s:i:", ARRAY_LEN(topts), topts, on_option,
  55. NULL, TPM2_OPTIONS_NO_SAPI);
  56. return *opts != NULL;
  57. }
  58. struct name_fields {
  59. const char *field;
  60. const char *del;
  61. const char *def;
  62. int maxlen;
  63. };
  64. static struct name_fields names[] = {
  65. { .field = "CN",
  66. .del = "CN=",
  67. .maxlen = 8,
  68. .def = "default" },
  69. { .field = "C",
  70. .del = "C=",
  71. .maxlen = 2,
  72. .def = "US" },
  73. { .field = "O",
  74. .del = "O=",
  75. .maxlen = 8,
  76. .def = "CA Org" },
  77. { .field = "OU",
  78. .del = "OU=",
  79. .maxlen = 8,
  80. .def = "CA Unit" },
  81. };
  82. static tool_rc fixup_cert(const char *cert) {
  83. int fd = open(cert, O_RDONLY);
  84. if (fd < 0) {
  85. LOG_ERR("open failed");
  86. return tool_rc_general_error;
  87. }
  88. struct stat fs;
  89. int ret = fstat(fd, &fs);
  90. if (ret < 0) {
  91. close(fd);
  92. return tool_rc_general_error;
  93. }
  94. ssize_t size = fs.st_size;
  95. if (size < 100 || size > 255) {
  96. LOG_ERR("Wrong cert size %zd", size);
  97. close(fd);
  98. return tool_rc_general_error; /* there is something wrong with this cert */
  99. }
  100. char* buf = calloc(1, size);
  101. if (!buf) {
  102. LOG_ERR("Alloc failed");
  103. close(fd);
  104. return tool_rc_general_error;
  105. }
  106. tool_rc rc = tool_rc_success;
  107. ret = read(fd, buf, size);
  108. close(fd);
  109. if (ret != size) {
  110. LOG_ERR("read failed");
  111. rc = tool_rc_general_error;
  112. goto out;
  113. }
  114. fd = open(cert, O_WRONLY | O_TRUNC);
  115. if (fd < 0) {
  116. LOG_ERR("second open failed");
  117. rc = tool_rc_general_error;
  118. goto out;
  119. }
  120. /* We need to skip one wrapping sequence (8 bytes) and one
  121. * sequence with one empty byte field at the end (5 bytes).
  122. * Fix the size here */
  123. buf[2] = size - 16;
  124. /* Write the external sequence with the fixed size */
  125. ret = write(fd, buf, 3);
  126. if (ret != 3) {
  127. LOG_ERR("write failed");
  128. rc = tool_rc_general_error;
  129. close(fd);
  130. goto out;
  131. }
  132. /* skip the wrapping sequence the write the rest
  133. * without the 5 bytes at the end */
  134. ret = write(fd, buf + 11, size - 16);
  135. close(fd);
  136. if (ret != size - 16) {
  137. LOG_ERR("second write failed");
  138. rc = tool_rc_general_error;
  139. }
  140. out:
  141. free(buf);
  142. return rc;
  143. }
  144. static int populate_fields(X509_NAME *name, const char *opt) {
  145. char *name_opt = strdup(opt);
  146. if (!name_opt) {
  147. LOG_ERR("Alloc failed");
  148. return -1;
  149. }
  150. const char *tok = strtok(name_opt, ";");
  151. unsigned i = 0;
  152. int fields_added = 0;
  153. while (tok != NULL) {
  154. LOG_INFO("Parsing token %s", tok);
  155. /* Loop through supported fields and add them if found */
  156. for (i = 0; i < ARRAY_LEN(names); i++) {
  157. const char *del = names[i].del;
  158. const char *fld = names[i].field;
  159. unsigned int maxlen = names[i].maxlen;
  160. size_t len = strlen(del);
  161. const char *ptr;
  162. if (strncmp(tok, del, len) == 0) {
  163. if (strlen(tok + len) > maxlen || strlen(tok + len) == 0) {
  164. LOG_WARN("Field %s too long or empty. Using default", fld);
  165. ptr = names[i].def;
  166. } else {
  167. ptr = tok + len;
  168. }
  169. LOG_INFO("Adding name field %s%s", del, ptr);
  170. int ret = X509_NAME_add_entry_by_txt(name, fld, MBSTRING_ASC,
  171. (const unsigned char *) ptr,
  172. -1, -1, 0);
  173. if (ret != 1) {
  174. free(name_opt);
  175. LOG_ERR("X509_NAME_add_entry_by_txt");
  176. return -1;
  177. }
  178. fields_added++;
  179. }
  180. }
  181. tok = strtok(NULL, ";");
  182. }
  183. free(name_opt);
  184. return fields_added;
  185. }
  186. static tool_rc generate_partial_X509() {
  187. BIO *cert_out = BIO_new_file(ctx.out_path, "wb");
  188. if (!cert_out) {
  189. LOG_ERR("Can not create file %s", ctx.out_path);
  190. return -1;
  191. }
  192. X509_EXTENSION *extv3 = NULL;
  193. X509 *cert = X509_new();
  194. if (!cert) {
  195. LOG_ERR("X509_new");
  196. goto out_err;
  197. }
  198. X509_NAME *issuer = X509_get_issuer_name(cert);
  199. if (!issuer) {
  200. LOG_ERR("X509_get_issuer_name");
  201. goto out_err;
  202. }
  203. int fields_added = populate_fields(issuer, ctx.issuer);
  204. if (fields_added <= 0) {
  205. LOG_ERR("Could not parse any issuer fields");
  206. goto out_err;
  207. } else {
  208. LOG_INFO("Added %d issuer fields", fields_added);
  209. }
  210. int ret = X509_set_issuer_name(cert, issuer); // add issuer
  211. if (ret != 1) {
  212. LOG_ERR("X509_set_issuer_name");
  213. goto out_err;
  214. }
  215. unsigned int valid_days;
  216. if (!tpm2_util_string_to_uint32(ctx.valid_str, &valid_days)) {
  217. LOG_ERR("string_to_uint32");
  218. goto out_err;
  219. }
  220. X509_gmtime_adj(X509_get_notBefore(cert), 0); // add valid not before
  221. X509_gmtime_adj(X509_get_notAfter(cert), valid_days * 86400); // add valid not after
  222. X509_NAME *subject = X509_get_subject_name(cert);
  223. if (!subject) {
  224. LOG_ERR("X509_get_subject_name");
  225. goto out_err;
  226. }
  227. fields_added = populate_fields(subject, ctx.subject);
  228. if (fields_added <= 0) {
  229. LOG_ERR("Could not parse any subject fields");
  230. goto out_err;
  231. } else {
  232. LOG_INFO("Added %d subject fields", fields_added);
  233. }
  234. ret = X509_set_subject_name(cert, subject); // add subject
  235. if (ret != 1) {
  236. LOG_ERR("X509_NAME_add_entry_by_txt");
  237. goto out_err;
  238. }
  239. extv3 = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
  240. "critical,digitalSignature,keyCertSign,cRLSign");
  241. if (!extv3) {
  242. LOG_ERR("X509V3_EXT_conf_nid");
  243. goto out_err;
  244. }
  245. ret = X509_add_ext(cert, extv3, -1); // add required v3 extention: key usage
  246. if (ret != 1) {
  247. LOG_ERR("X509_add_ext");
  248. goto out_err;
  249. }
  250. ret = i2d_X509_bio(cert_out, cert); // print cert in DER format
  251. if (ret != 1) {
  252. LOG_ERR("i2d_X509_bio");
  253. goto out_err;
  254. }
  255. X509_EXTENSION_free(extv3);
  256. X509_free(cert);
  257. BIO_free_all(cert_out);
  258. ret = fixup_cert(ctx.out_path);
  259. if (ret) {
  260. LOG_ERR("fixup_cert");
  261. return tool_rc_general_error;
  262. }
  263. return tool_rc_success;
  264. out_err:
  265. BIO_free_all(cert_out);
  266. if (cert) {
  267. X509_free(cert);
  268. }
  269. if (extv3) {
  270. X509_EXTENSION_free(extv3);
  271. }
  272. return tool_rc_general_error;
  273. }
  274. static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
  275. UNUSED(flags);
  276. UNUSED(ectx);
  277. return generate_partial_X509();
  278. }
  279. TPM2_TOOL_REGISTER("certifyX509certutil", tpm2_tool_onstart, tpm2_tool_onrun, NULL, NULL)