mod_gnutls.inl 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #if defined(USE_GNUTLS) // USE_GNUTLS used with NO_SSL
  2. #include <gnutls/gnutls.h>
  3. #include <gnutls/x509.h>
  4. typedef struct {
  5. gnutls_session_t sess;
  6. } SSL;
  7. typedef struct {
  8. gnutls_certificate_credentials_t cred;
  9. gnutls_priority_t prio;
  10. } SSL_CTX;
  11. /* public api */
  12. CIVETWEB_API int gtls_sslctx_init(SSL_CTX *ctx, const char *crt);
  13. CIVETWEB_API void gtls_sslctx_uninit(SSL_CTX *ctx);
  14. CIVETWEB_API void gtls_ssl_close(SSL *ssl);
  15. CIVETWEB_API int gtls_ssl_accept(SSL **ssl,
  16. SSL_CTX *ssl_ctx,
  17. int sock,
  18. struct mg_context *phys_ctx);
  19. CIVETWEB_API int gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len);
  20. CIVETWEB_API int gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len);
  21. CIVETWEB_API int
  22. gtls_sslctx_init(SSL_CTX *ctx, const char *crt)
  23. {
  24. int rc;
  25. if (ctx == NULL || crt == NULL) {
  26. return -1;
  27. }
  28. DEBUG_TRACE("%s", "Initializing GnuTLS SSL");
  29. rc = gnutls_certificate_allocate_credentials(&ctx->cred);
  30. if (rc != GNUTLS_E_SUCCESS) {
  31. DEBUG_TRACE("Failed to allocate credentials (%d): %s",
  32. rc,
  33. gnutls_strerror(rc));
  34. goto failed;
  35. }
  36. rc = gnutls_priority_init(&ctx->prio, NULL, NULL);
  37. if (rc != GNUTLS_E_SUCCESS) {
  38. DEBUG_TRACE("Failed to allocate priority cache (%d): %s",
  39. rc,
  40. gnutls_strerror(rc));
  41. goto failed;
  42. }
  43. rc = gnutls_certificate_set_x509_key_file2(ctx->cred,
  44. crt,
  45. crt,
  46. GNUTLS_X509_FMT_PEM,
  47. NULL,
  48. GNUTLS_PKCS_PLAIN
  49. | GNUTLS_PKCS_NULL_PASSWORD);
  50. if (rc != GNUTLS_E_SUCCESS) {
  51. DEBUG_TRACE("TLS parse crt/key file failed (%d): %s",
  52. rc,
  53. gnutls_strerror(rc));
  54. goto failed;
  55. }
  56. return 0;
  57. failed:
  58. gtls_sslctx_uninit(ctx);
  59. return -1;
  60. }
  61. CIVETWEB_API void
  62. gtls_sslctx_uninit(SSL_CTX *ctx)
  63. {
  64. if (ctx != NULL) {
  65. gnutls_certificate_free_credentials(ctx->cred);
  66. gnutls_priority_deinit(ctx->prio);
  67. ctx->cred = NULL;
  68. ctx->prio = NULL;
  69. }
  70. }
  71. CIVETWEB_API int
  72. gtls_ssl_accept(SSL **ssl,
  73. SSL_CTX *ssl_ctx,
  74. int sock,
  75. struct mg_context *phys_ctx)
  76. {
  77. int rc;
  78. if (ssl == NULL || ssl_ctx == NULL) {
  79. return -1;
  80. }
  81. DEBUG_TRACE("TLS accept processing %p", ssl);
  82. *ssl = (SSL *)mg_calloc_ctx(1, sizeof(SSL), phys_ctx);
  83. if (*ssl == NULL) {
  84. DEBUG_TRACE("Failed to allocate memory for session %zu", sizeof(SSL));
  85. return -1;
  86. }
  87. rc = gnutls_init(&(*ssl)->sess, GNUTLS_SERVER);
  88. if (rc != GNUTLS_E_SUCCESS) {
  89. DEBUG_TRACE("Failed to initialize session (%d): %s",
  90. rc,
  91. gnutls_strerror(rc));
  92. goto failed;
  93. }
  94. rc = gnutls_priority_set((*ssl)->sess, ssl_ctx->prio);
  95. if (rc != GNUTLS_E_SUCCESS) {
  96. DEBUG_TRACE("TLS set priortities failed (%d): %s",
  97. rc,
  98. gnutls_strerror(rc));
  99. goto failed;
  100. }
  101. rc = gnutls_credentials_set((*ssl)->sess,
  102. GNUTLS_CRD_CERTIFICATE,
  103. ssl_ctx->cred);
  104. if (rc != GNUTLS_E_SUCCESS) {
  105. DEBUG_TRACE("TLS set credentials failed (%d): %s",
  106. rc,
  107. gnutls_strerror(rc));
  108. goto failed;
  109. }
  110. gnutls_certificate_send_x509_rdn_sequence((*ssl)->sess, 1);
  111. gnutls_certificate_server_set_request((*ssl)->sess, GNUTLS_CERT_IGNORE);
  112. gnutls_handshake_set_timeout((*ssl)->sess,
  113. GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
  114. gnutls_transport_set_int((*ssl)->sess, sock);
  115. while ((rc = gnutls_handshake((*ssl)->sess)) != GNUTLS_E_SUCCESS) {
  116. if (gnutls_error_is_fatal(rc)) {
  117. if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
  118. DEBUG_TRACE("TLS fatal alert received: %s",
  119. gnutls_alert_get_name(
  120. gnutls_alert_get((*ssl)->sess)));
  121. } else {
  122. DEBUG_TRACE("TLS handshake failed (%d): %s",
  123. rc,
  124. gnutls_strerror(rc));
  125. }
  126. goto failed;
  127. }
  128. }
  129. DEBUG_TRACE("TLS connection %p accepted", *ssl);
  130. return 0;
  131. failed:
  132. gnutls_deinit((*ssl)->sess);
  133. mg_free(*ssl);
  134. *ssl = NULL;
  135. return -1;
  136. }
  137. CIVETWEB_API void
  138. gtls_ssl_close(SSL *ssl)
  139. {
  140. int rc;
  141. if (ssl == NULL) {
  142. return;
  143. }
  144. while ((rc = gnutls_bye(ssl->sess, GNUTLS_SHUT_RDWR)) != GNUTLS_E_SUCCESS) {
  145. switch (rc) {
  146. case GNUTLS_E_AGAIN: /* fall through */
  147. case GNUTLS_E_INTERRUPTED:
  148. continue;
  149. default: /* should actually never happen */
  150. break;
  151. }
  152. }
  153. DEBUG_TRACE("TLS connection %p closed", ssl);
  154. gnutls_deinit(ssl->sess);
  155. mg_free(ssl);
  156. }
  157. CIVETWEB_API int
  158. gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len)
  159. {
  160. ssize_t rc;
  161. if (ssl == NULL) {
  162. return GNUTLS_E_INVALID_SESSION;
  163. }
  164. while ((rc = gnutls_record_recv(ssl->sess, buf, len)) < 0) {
  165. switch (rc) {
  166. case GNUTLS_E_AGAIN: /* fall through */
  167. case GNUTLS_E_INTERRUPTED:
  168. continue;
  169. default:
  170. break;
  171. }
  172. }
  173. /* DEBUG_TRACE("gnutls_record_recv: %d", rc); */
  174. return (int)rc;
  175. }
  176. CIVETWEB_API int
  177. gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len)
  178. {
  179. ssize_t rc;
  180. if (ssl == NULL) {
  181. return GNUTLS_E_INVALID_SESSION;
  182. }
  183. while ((rc = gnutls_record_send(ssl->sess, buf, len)) < 0) {
  184. switch (rc) {
  185. case GNUTLS_E_AGAIN: /* fall through */
  186. case GNUTLS_E_INTERRUPTED:
  187. continue;
  188. default:
  189. break;
  190. }
  191. }
  192. /* DEBUG_TRACE("gnutls_record_send: %d", rc); */
  193. return (int)rc;
  194. }
  195. #endif /* USE_GNUTLS */