You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

validator.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /*
  2. * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. *
  19. * You can also choose to distribute this program under the terms of
  20. * the Unmodified Binary Distribution Licence (as given in the file
  21. * COPYING.UBDL), provided that you have satisfied its requirements.
  22. */
  23. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. #include <string.h>
  25. #include <stdio.h>
  26. #include <errno.h>
  27. #include <ipxe/refcnt.h>
  28. #include <ipxe/malloc.h>
  29. #include <ipxe/interface.h>
  30. #include <ipxe/xfer.h>
  31. #include <ipxe/open.h>
  32. #include <ipxe/iobuf.h>
  33. #include <ipxe/xferbuf.h>
  34. #include <ipxe/process.h>
  35. #include <ipxe/x509.h>
  36. #include <ipxe/settings.h>
  37. #include <ipxe/dhcp.h>
  38. #include <ipxe/base64.h>
  39. #include <ipxe/crc32.h>
  40. #include <ipxe/ocsp.h>
  41. #include <ipxe/validator.h>
  42. #include <config/crypto.h>
  43. /** @file
  44. *
  45. * Certificate validator
  46. *
  47. */
  48. /** A certificate validator */
  49. struct validator {
  50. /** Reference count */
  51. struct refcnt refcnt;
  52. /** Job control interface */
  53. struct interface job;
  54. /** Data transfer interface */
  55. struct interface xfer;
  56. /** Process */
  57. struct process process;
  58. /** X.509 certificate chain */
  59. struct x509_chain *chain;
  60. /** OCSP check */
  61. struct ocsp_check *ocsp;
  62. /** Data buffer */
  63. struct xfer_buffer buffer;
  64. /** Action to take upon completed transfer */
  65. int ( * done ) ( struct validator *validator, const void *data,
  66. size_t len );
  67. };
  68. /**
  69. * Get validator name (for debug messages)
  70. *
  71. * @v validator Certificate validator
  72. * @ret name Validator name
  73. */
  74. static const char * validator_name ( struct validator *validator ) {
  75. /* Use name of first certificate in chain */
  76. return x509_name ( x509_first ( validator->chain ) );
  77. }
  78. /**
  79. * Free certificate validator
  80. *
  81. * @v refcnt Reference count
  82. */
  83. static void validator_free ( struct refcnt *refcnt ) {
  84. struct validator *validator =
  85. container_of ( refcnt, struct validator, refcnt );
  86. DBGC2 ( validator, "VALIDATOR %p \"%s\" freed\n",
  87. validator, validator_name ( validator ) );
  88. x509_chain_put ( validator->chain );
  89. ocsp_put ( validator->ocsp );
  90. xferbuf_free ( &validator->buffer );
  91. free ( validator );
  92. }
  93. /**
  94. * Mark certificate validation as finished
  95. *
  96. * @v validator Certificate validator
  97. * @v rc Reason for finishing
  98. */
  99. static void validator_finished ( struct validator *validator, int rc ) {
  100. /* Remove process */
  101. process_del ( &validator->process );
  102. /* Close all interfaces */
  103. intf_shutdown ( &validator->xfer, rc );
  104. intf_shutdown ( &validator->job, rc );
  105. }
  106. /****************************************************************************
  107. *
  108. * Job control interface
  109. *
  110. */
  111. /** Certificate validator job control interface operations */
  112. static struct interface_operation validator_job_operations[] = {
  113. INTF_OP ( intf_close, struct validator *, validator_finished ),
  114. };
  115. /** Certificate validator job control interface descriptor */
  116. static struct interface_descriptor validator_job_desc =
  117. INTF_DESC ( struct validator, job, validator_job_operations );
  118. /****************************************************************************
  119. *
  120. * Cross-signing certificates
  121. *
  122. */
  123. /** Cross-signed certificate source setting */
  124. const struct setting crosscert_setting __setting ( SETTING_CRYPTO, crosscert )={
  125. .name = "crosscert",
  126. .description = "Cross-signed certificate source",
  127. .tag = DHCP_EB_CROSS_CERT,
  128. .type = &setting_type_string,
  129. };
  130. /** Default cross-signed certificate source */
  131. static const char crosscert_default[] = CROSSCERT;
  132. /**
  133. * Append cross-signing certificates to certificate chain
  134. *
  135. * @v validator Certificate validator
  136. * @v data Raw cross-signing certificate data
  137. * @v len Length of raw data
  138. * @ret rc Return status code
  139. */
  140. static int validator_append ( struct validator *validator,
  141. const void *data, size_t len ) {
  142. struct asn1_cursor cursor;
  143. struct x509_chain *certs;
  144. struct x509_certificate *cert;
  145. struct x509_certificate *last;
  146. int rc;
  147. /* Allocate certificate list */
  148. certs = x509_alloc_chain();
  149. if ( ! certs ) {
  150. rc = -ENOMEM;
  151. goto err_alloc_certs;
  152. }
  153. /* Initialise cursor */
  154. cursor.data = data;
  155. cursor.len = len;
  156. /* Enter certificateSet */
  157. if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) {
  158. DBGC ( validator, "VALIDATOR %p \"%s\" could not enter "
  159. "certificateSet: %s\n", validator,
  160. validator_name ( validator ), strerror ( rc ) );
  161. goto err_certificateset;
  162. }
  163. /* Add each certificate to list */
  164. while ( cursor.len ) {
  165. /* Add certificate to chain */
  166. if ( ( rc = x509_append_raw ( certs, cursor.data,
  167. cursor.len ) ) != 0 ) {
  168. DBGC ( validator, "VALIDATOR %p \"%s\" could not "
  169. "append certificate: %s\n", validator,
  170. validator_name ( validator ), strerror ( rc) );
  171. DBGC_HDA ( validator, 0, cursor.data, cursor.len );
  172. return rc;
  173. }
  174. cert = x509_last ( certs );
  175. DBGC ( validator, "VALIDATOR %p \"%s\" found certificate ",
  176. validator, validator_name ( validator ) );
  177. DBGC ( validator, "%s\n", x509_name ( cert ) );
  178. /* Move to next certificate */
  179. asn1_skip_any ( &cursor );
  180. }
  181. /* Append certificates to chain */
  182. last = x509_last ( validator->chain );
  183. if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) {
  184. DBGC ( validator, "VALIDATOR %p \"%s\" could not append "
  185. "certificates: %s\n", validator,
  186. validator_name ( validator ), strerror ( rc ) );
  187. goto err_auto_append;
  188. }
  189. /* Check that at least one certificate has been added */
  190. if ( last == x509_last ( validator->chain ) ) {
  191. DBGC ( validator, "VALIDATOR %p \"%s\" failed to append any "
  192. "applicable certificates\n", validator,
  193. validator_name ( validator ) );
  194. rc = -EACCES;
  195. goto err_no_progress;
  196. }
  197. /* Drop reference to certificate list */
  198. x509_chain_put ( certs );
  199. return 0;
  200. err_no_progress:
  201. err_auto_append:
  202. err_certificateset:
  203. x509_chain_put ( certs );
  204. err_alloc_certs:
  205. return rc;
  206. }
  207. /**
  208. * Start download of cross-signing certificate
  209. *
  210. * @v validator Certificate validator
  211. * @v cert X.509 certificate
  212. * @ret rc Return status code
  213. */
  214. static int validator_start_download ( struct validator *validator,
  215. struct x509_certificate *cert ) {
  216. const struct asn1_cursor *issuer = &cert->issuer.raw;
  217. const char *crosscert;
  218. char *crosscert_copy;
  219. char *uri_string;
  220. size_t uri_string_len;
  221. uint32_t crc;
  222. int len;
  223. int rc;
  224. /* Determine cross-signed certificate source */
  225. fetch_string_setting_copy ( NULL, &crosscert_setting, &crosscert_copy );
  226. crosscert = ( crosscert_copy ? crosscert_copy : crosscert_default );
  227. if ( ! crosscert[0] ) {
  228. rc = -EINVAL;
  229. goto err_check_uri_string;
  230. }
  231. /* Allocate URI string */
  232. uri_string_len = ( strlen ( crosscert ) + 22 /* "/%08x.der?subject=" */
  233. + base64_encoded_len ( issuer->len ) + 1 /* NUL */ );
  234. uri_string = zalloc ( uri_string_len );
  235. if ( ! uri_string ) {
  236. rc = -ENOMEM;
  237. goto err_alloc_uri_string;
  238. }
  239. /* Generate CRC32 */
  240. crc = crc32_le ( 0xffffffffUL, issuer->data, issuer->len );
  241. /* Generate URI string */
  242. len = snprintf ( uri_string, uri_string_len, "%s/%08x.der?subject=",
  243. crosscert, crc );
  244. base64_encode ( issuer->data, issuer->len, ( uri_string + len ),
  245. ( uri_string_len - len ) );
  246. DBGC ( validator, "VALIDATOR %p \"%s\" downloading ",
  247. validator, validator_name ( validator ) );
  248. DBGC ( validator, "\"%s\" cross-signature from %s\n",
  249. x509_name ( cert ), uri_string );
  250. /* Set completion handler */
  251. validator->done = validator_append;
  252. /* Open URI */
  253. if ( ( rc = xfer_open_uri_string ( &validator->xfer,
  254. uri_string ) ) != 0 ) {
  255. DBGC ( validator, "VALIDATOR %p \"%s\" could not open %s: "
  256. "%s\n", validator, validator_name ( validator ),
  257. uri_string, strerror ( rc ) );
  258. goto err_open_uri_string;
  259. }
  260. /* Success */
  261. rc = 0;
  262. err_open_uri_string:
  263. free ( uri_string );
  264. err_alloc_uri_string:
  265. err_check_uri_string:
  266. free ( crosscert_copy );
  267. return rc;
  268. }
  269. /****************************************************************************
  270. *
  271. * OCSP checks
  272. *
  273. */
  274. /**
  275. * Validate OCSP response
  276. *
  277. * @v validator Certificate validator
  278. * @v data Raw OCSP response
  279. * @v len Length of raw data
  280. * @ret rc Return status code
  281. */
  282. static int validator_ocsp_validate ( struct validator *validator,
  283. const void *data, size_t len ) {
  284. time_t now;
  285. int rc;
  286. /* Record OCSP response */
  287. if ( ( rc = ocsp_response ( validator->ocsp, data, len ) ) != 0 ) {
  288. DBGC ( validator, "VALIDATOR %p \"%s\" could not record OCSP "
  289. "response: %s\n", validator,
  290. validator_name ( validator ),strerror ( rc ) );
  291. return rc;
  292. }
  293. /* Validate OCSP response */
  294. now = time ( NULL );
  295. if ( ( rc = ocsp_validate ( validator->ocsp, now ) ) != 0 ) {
  296. DBGC ( validator, "VALIDATOR %p \"%s\" could not validate "
  297. "OCSP response: %s\n", validator,
  298. validator_name ( validator ), strerror ( rc ) );
  299. return rc;
  300. }
  301. /* Drop reference to OCSP check */
  302. ocsp_put ( validator->ocsp );
  303. validator->ocsp = NULL;
  304. return 0;
  305. }
  306. /**
  307. * Start OCSP check
  308. *
  309. * @v validator Certificate validator
  310. * @v cert Certificate to check
  311. * @v issuer Issuing certificate
  312. * @ret rc Return status code
  313. */
  314. static int validator_start_ocsp ( struct validator *validator,
  315. struct x509_certificate *cert,
  316. struct x509_certificate *issuer ) {
  317. const char *uri_string;
  318. int rc;
  319. /* Create OCSP check */
  320. assert ( validator->ocsp == NULL );
  321. if ( ( rc = ocsp_check ( cert, issuer, &validator->ocsp ) ) != 0 ) {
  322. DBGC ( validator, "VALIDATOR %p \"%s\" could not create OCSP "
  323. "check: %s\n", validator, validator_name ( validator ),
  324. strerror ( rc ) );
  325. return rc;
  326. }
  327. /* Set completion handler */
  328. validator->done = validator_ocsp_validate;
  329. /* Open URI */
  330. uri_string = validator->ocsp->uri_string;
  331. DBGC ( validator, "VALIDATOR %p \"%s\" checking ",
  332. validator, validator_name ( validator ) );
  333. DBGC ( validator, "\"%s\" via %s\n",
  334. x509_name ( cert ), uri_string );
  335. if ( ( rc = xfer_open_uri_string ( &validator->xfer,
  336. uri_string ) ) != 0 ) {
  337. DBGC ( validator, "VALIDATOR %p \"%s\" could not open %s: "
  338. "%s\n", validator, validator_name ( validator ),
  339. uri_string, strerror ( rc ) );
  340. return rc;
  341. }
  342. return 0;
  343. }
  344. /****************************************************************************
  345. *
  346. * Data transfer interface
  347. *
  348. */
  349. /**
  350. * Close data transfer interface
  351. *
  352. * @v validator Certificate validator
  353. * @v rc Reason for close
  354. */
  355. static void validator_xfer_close ( struct validator *validator, int rc ) {
  356. /* Close data transfer interface */
  357. intf_restart ( &validator->xfer, rc );
  358. /* Check for errors */
  359. if ( rc != 0 ) {
  360. DBGC ( validator, "VALIDATOR %p \"%s\" transfer failed: %s\n",
  361. validator, validator_name ( validator ),
  362. strerror ( rc ) );
  363. goto err_transfer;
  364. }
  365. DBGC2 ( validator, "VALIDATOR %p \"%s\" transfer complete\n",
  366. validator, validator_name ( validator ) );
  367. /* Process completed download */
  368. assert ( validator->done != NULL );
  369. if ( ( rc = validator->done ( validator, validator->buffer.data,
  370. validator->buffer.len ) ) != 0 )
  371. goto err_append;
  372. /* Free downloaded data */
  373. xferbuf_free ( &validator->buffer );
  374. /* Resume validation process */
  375. process_add ( &validator->process );
  376. return;
  377. err_append:
  378. err_transfer:
  379. validator_finished ( validator, rc );
  380. }
  381. /**
  382. * Receive data
  383. *
  384. * @v validator Certificate validator
  385. * @v iobuf I/O buffer
  386. * @v meta Data transfer metadata
  387. * @ret rc Return status code
  388. */
  389. static int validator_xfer_deliver ( struct validator *validator,
  390. struct io_buffer *iobuf,
  391. struct xfer_metadata *meta ) {
  392. int rc;
  393. /* Add data to buffer */
  394. if ( ( rc = xferbuf_deliver ( &validator->buffer, iob_disown ( iobuf ),
  395. meta ) ) != 0 ) {
  396. DBGC ( validator, "VALIDATOR %p \"%s\" could not receive "
  397. "data: %s\n", validator, validator_name ( validator ),
  398. strerror ( rc ) );
  399. validator_finished ( validator, rc );
  400. return rc;
  401. }
  402. return 0;
  403. }
  404. /** Certificate validator data transfer interface operations */
  405. static struct interface_operation validator_xfer_operations[] = {
  406. INTF_OP ( xfer_deliver, struct validator *, validator_xfer_deliver ),
  407. INTF_OP ( intf_close, struct validator *, validator_xfer_close ),
  408. };
  409. /** Certificate validator data transfer interface descriptor */
  410. static struct interface_descriptor validator_xfer_desc =
  411. INTF_DESC ( struct validator, xfer, validator_xfer_operations );
  412. /****************************************************************************
  413. *
  414. * Validation process
  415. *
  416. */
  417. /**
  418. * Certificate validation process
  419. *
  420. * @v validator Certificate validator
  421. */
  422. static void validator_step ( struct validator *validator ) {
  423. struct x509_link *link;
  424. struct x509_certificate *cert;
  425. struct x509_certificate *issuer = NULL;
  426. struct x509_certificate *last;
  427. time_t now;
  428. int rc;
  429. /* Try validating chain. Try even if the chain is incomplete,
  430. * since certificates may already have been validated
  431. * previously.
  432. */
  433. now = time ( NULL );
  434. if ( ( rc = x509_validate_chain ( validator->chain, now, NULL,
  435. NULL ) ) == 0 ) {
  436. DBGC ( validator, "VALIDATOR %p \"%s\" validated\n",
  437. validator, validator_name ( validator ) );
  438. validator_finished ( validator, 0 );
  439. return;
  440. }
  441. /* If there is a certificate that could be validated using
  442. * OCSP, try it.
  443. */
  444. list_for_each_entry ( link, &validator->chain->links, list ) {
  445. cert = issuer;
  446. issuer = link->cert;
  447. if ( ! cert )
  448. continue;
  449. if ( ! x509_is_valid ( issuer ) )
  450. continue;
  451. /* The issuer is valid, but this certificate is not
  452. * yet valid. If OCSP is applicable, start it.
  453. */
  454. if ( ocsp_required ( cert ) ) {
  455. /* Start OCSP */
  456. if ( ( rc = validator_start_ocsp ( validator, cert,
  457. issuer ) ) != 0 ) {
  458. validator_finished ( validator, rc );
  459. return;
  460. }
  461. return;
  462. }
  463. /* Otherwise, this is a permanent failure */
  464. validator_finished ( validator, rc );
  465. return;
  466. }
  467. /* If chain ends with a self-issued certificate, then there is
  468. * nothing more to do.
  469. */
  470. last = x509_last ( validator->chain );
  471. if ( asn1_compare ( &last->issuer.raw, &last->subject.raw ) == 0 ) {
  472. validator_finished ( validator, rc );
  473. return;
  474. }
  475. /* Otherwise, try to download a suitable cross-signing
  476. * certificate.
  477. */
  478. if ( ( rc = validator_start_download ( validator, last ) ) != 0 ) {
  479. validator_finished ( validator, rc );
  480. return;
  481. }
  482. }
  483. /** Certificate validator process descriptor */
  484. static struct process_descriptor validator_process_desc =
  485. PROC_DESC_ONCE ( struct validator, process, validator_step );
  486. /****************************************************************************
  487. *
  488. * Instantiator
  489. *
  490. */
  491. /**
  492. * Instantiate a certificate validator
  493. *
  494. * @v job Job control interface
  495. * @v chain X.509 certificate chain
  496. * @ret rc Return status code
  497. */
  498. int create_validator ( struct interface *job, struct x509_chain *chain ) {
  499. struct validator *validator;
  500. int rc;
  501. /* Sanity check */
  502. if ( ! chain ) {
  503. rc = -EINVAL;
  504. goto err_sanity;
  505. }
  506. /* Allocate and initialise structure */
  507. validator = zalloc ( sizeof ( *validator ) );
  508. if ( ! validator ) {
  509. rc = -ENOMEM;
  510. goto err_alloc;
  511. }
  512. ref_init ( &validator->refcnt, validator_free );
  513. intf_init ( &validator->job, &validator_job_desc,
  514. &validator->refcnt );
  515. intf_init ( &validator->xfer, &validator_xfer_desc,
  516. &validator->refcnt );
  517. process_init ( &validator->process, &validator_process_desc,
  518. &validator->refcnt );
  519. validator->chain = x509_chain_get ( chain );
  520. xferbuf_malloc_init ( &validator->buffer );
  521. /* Attach parent interface, mortalise self, and return */
  522. intf_plug_plug ( &validator->job, job );
  523. ref_put ( &validator->refcnt );
  524. DBGC2 ( validator, "VALIDATOR %p \"%s\" validating X509 chain %p\n",
  525. validator, validator_name ( validator ), validator->chain );
  526. return 0;
  527. validator_finished ( validator, rc );
  528. ref_put ( &validator->refcnt );
  529. err_alloc:
  530. err_sanity:
  531. return rc;
  532. }