example_httpsrv_tls_cert_auth.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /* _
  2. * ___ __ _ __ _ _ _(_)
  3. * / __|/ _` |/ _` | | | | |
  4. * \__ \ (_| | (_| | |_| | |
  5. * |___/\__,_|\__, |\__,_|_|
  6. * |___/
  7. *
  8. * Cross-platform library which helps to develop web servers or frameworks.
  9. *
  10. * Copyright (C) 2016-2019 Silvio Clecio <[email protected]>
  11. *
  12. * Sagui library is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU Lesser General Public
  14. * License as published by the Free Software Foundation; either
  15. * version 2.1 of the License, or (at your option) any later version.
  16. *
  17. * Sagui library is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. * Lesser General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Lesser General Public
  23. * License along with Sagui library; if not, write to the Free Software
  24. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  25. */
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include <stdint.h>
  29. #include <errno.h>
  30. #include <gnutls/gnutls.h>
  31. #include <gnutls/x509.h>
  32. #include <sagui.h>
  33. /* NOTE: Error checking has been omitted to make it clear. */
  34. /*
  35. * Simple example using TLS client authentication.
  36. *
  37. * Client example using cURL:
  38. *
  39. * curl -k --cert certs/client.p12 --pass abc123 --cert-type p12 https://localhost:<PORT>
  40. *
  41. * Certificate generation:
  42. *
  43. * ## CA
  44. * certtool --generate-privkey --outfile ca.key
  45. * echo 'cn = GnuTLS test CA' > ca.tmpl
  46. * echo 'ca' >> ca.tmpl
  47. * echo 'cert_signing_key' >> ca.tmpl
  48. * echo 'expiration_days = 3650' >> ca.tmpl
  49. * certtool --generate-self-signed --load-privkey ca.key --template ca.tmpl --outfile ca.pem
  50. *
  51. * ## Server
  52. * certtool --generate-privkey --outfile server.key
  53. * echo 'organization = GnuTLS test server' > server.tmpl
  54. * echo 'cn = test.gnutls.org' >> server.tmpl
  55. * echo 'tls_www_server' >> server.tmpl
  56. * echo 'expiration_days = 3650' >> server.tmpl
  57. * certtool --generate-certificate --load-ca-privkey ca.key --load-ca-certificate ca.pem --load-privkey server.key --template server.tmpl --outfile server.pem
  58. *
  59. * ## Client
  60. * certtool --generate-privkey --outfile client.key
  61. * echo 'cn = GnuTLS test client' > client.tmpl
  62. * echo 'tls_www_client' >> client.tmpl
  63. * echo 'expiration_days = 3650' >> client.tmpl
  64. * certtool --generate-certificate --load-ca-certificate ca.pem --load-ca-privkey ca.key --load-privkey client.key --template client.tmpl --outfile client.pem
  65. * certtool --to-p12 --p12-name=MyKey --password=abc123 --load-ca-certificate ca.pem --load-privkey client.key --load-certificate client.pem --outder --outfile client.p12
  66. */
  67. #define KEY_FILE SG_EXAMPLES_CERTS_DIR "/server.key"
  68. #define CERT_FILE SG_EXAMPLES_CERTS_DIR "/server.pem"
  69. #define CA_FILE SG_EXAMPLES_CERTS_DIR "/ca.pem"
  70. #define ERR_SIZE 256
  71. #define PAGE_FMT \
  72. "<html><head><title>Hello world</title></head><body><font " \
  73. "color=\"%s\">%s</font></font></body></html>"
  74. #define SECRET_MSG "Secret"
  75. static void concat(char *s1, ...) {
  76. va_list ap;
  77. const char *s;
  78. va_start(ap, s1);
  79. while ((s = va_arg(ap, const char *)))
  80. strcat(s1, s);
  81. va_end(ap);
  82. }
  83. static bool sess_verify_cert(gnutls_session_t tls_session,
  84. const char *line_break, char *err) {
  85. gnutls_x509_crt_t cert = NULL;
  86. const gnutls_datum_t *certs;
  87. size_t len;
  88. unsigned int status, certs_size;
  89. int ret;
  90. if (!tls_session || !line_break || !err) {
  91. sg_strerror(EINVAL, err, ERR_SIZE);
  92. return false;
  93. }
  94. if ((ret = gnutls_certificate_verify_peers2(tls_session, &status)) !=
  95. GNUTLS_E_SUCCESS) {
  96. concat(err, "Error verifying peers: ", gnutls_strerror(ret), line_break,
  97. NULL);
  98. goto error;
  99. }
  100. if (status & GNUTLS_CERT_INVALID)
  101. concat(err, "The certificate is not trusted", line_break, NULL);
  102. if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
  103. concat(err, "The certificate has not got a known issuer", line_break, NULL);
  104. if (status & GNUTLS_CERT_REVOKED)
  105. concat(err, "The certificate has been revoked", line_break, NULL);
  106. if (gnutls_certificate_type_get(tls_session) != GNUTLS_CRT_X509) {
  107. concat(err, "The certificate type is not X.509", line_break, NULL);
  108. goto error;
  109. }
  110. if ((ret = gnutls_x509_crt_init(&cert)) != GNUTLS_E_SUCCESS) {
  111. concat(err,
  112. "Error in the certificate initialization: ", gnutls_strerror(ret),
  113. line_break, NULL);
  114. goto error;
  115. }
  116. if (!(certs = gnutls_certificate_get_peers(tls_session, &certs_size))) {
  117. concat(err, "No certificate was found", line_break, NULL);
  118. goto error;
  119. }
  120. if ((ret = gnutls_x509_crt_import(cert, &certs[0], GNUTLS_X509_FMT_DER)) !=
  121. GNUTLS_E_SUCCESS) {
  122. concat(err, "Error parsing certificate: ", gnutls_strerror(ret), line_break,
  123. NULL);
  124. goto error;
  125. }
  126. if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL)) {
  127. concat(err, "The certificate has expired", line_break, NULL);
  128. goto error;
  129. }
  130. if (gnutls_x509_crt_get_activation_time(cert) > time(NULL)) {
  131. concat(err, "The certificate has not been activated yet", line_break, NULL);
  132. goto error;
  133. }
  134. error:
  135. len = strlen(err);
  136. err[len - strlen("<br>")] = '\0';
  137. gnutls_x509_crt_deinit(cert);
  138. return len == 0;
  139. }
  140. static void req_cb(__SG_UNUSED void *cls, struct sg_httpreq *req,
  141. struct sg_httpres *res) {
  142. char msg[ERR_SIZE];
  143. char *color, *page;
  144. size_t page_size;
  145. unsigned int status;
  146. if (sess_verify_cert(sg_httpreq_tls_session(req), "<br>", msg)) {
  147. strcpy(msg, SECRET_MSG);
  148. color = "green";
  149. status = 200;
  150. } else {
  151. color = "red";
  152. status = 500;
  153. }
  154. page_size = (size_t) snprintf(NULL, 0, PAGE_FMT, color, msg);
  155. page = sg_alloc(page_size);
  156. snprintf(page, page_size, PAGE_FMT, color, msg);
  157. sg_httpres_send(res, page, "text/html; charset=utf-8", status);
  158. sg_free(page);
  159. }
  160. int main(int argc, const char *argv[]) {
  161. struct sg_httpsrv *srv;
  162. gnutls_datum_t key_file, cert_file, ca_file;
  163. int ret, status;
  164. uint16_t port;
  165. if (argc != 2) {
  166. printf("%s <PORT>\n", argv[0]);
  167. return EXIT_FAILURE;
  168. }
  169. port = strtol(argv[1], NULL, 10);
  170. status = EXIT_FAILURE;
  171. srv = sg_httpsrv_new(req_cb, NULL);
  172. memset(&key_file, 0, sizeof(gnutls_datum_t));
  173. memset(&cert_file, 0, sizeof(gnutls_datum_t));
  174. memset(&ca_file, 0, sizeof(gnutls_datum_t));
  175. if ((ret = gnutls_load_file(KEY_FILE, &key_file)) != GNUTLS_E_SUCCESS) {
  176. fprintf(stderr, "Error loading the private key \"%s\": %s\n", KEY_FILE,
  177. gnutls_strerror(ret));
  178. fflush(stdout);
  179. goto error;
  180. }
  181. if ((ret = gnutls_load_file(CERT_FILE, &cert_file)) != GNUTLS_E_SUCCESS) {
  182. fprintf(stderr, "Error loading the certificate \"%s\": %s\n", CERT_FILE,
  183. gnutls_strerror(ret));
  184. fflush(stdout);
  185. goto error;
  186. }
  187. if ((ret = gnutls_load_file(CA_FILE, &ca_file)) != GNUTLS_E_SUCCESS) {
  188. fprintf(stderr, "Error loading the CA \"%s\": %s\n", CA_FILE,
  189. gnutls_strerror(ret));
  190. fflush(stdout);
  191. goto error;
  192. }
  193. if (sg_httpsrv_tls_listen2(srv, (const char *) key_file.data, NULL,
  194. (const char *) cert_file.data,
  195. (const char *) ca_file.data, NULL, port, false)) {
  196. status = EXIT_SUCCESS;
  197. fprintf(stdout, "Server running at https://localhost:%d\n",
  198. sg_httpsrv_port(srv));
  199. fflush(stdout);
  200. getchar();
  201. }
  202. error:
  203. sg_httpsrv_free(srv);
  204. if (key_file.size > 0)
  205. gnutls_free(key_file.data);
  206. if (cert_file.size > 0)
  207. gnutls_free(cert_file.data);
  208. if (ca_file.size > 0)
  209. gnutls_free(ca_file.data);
  210. return status;
  211. }