static mod_context *mod_gnutls_context_new(liServer *srv) { mod_context *ctx = g_slice_new0(mod_context); int r; if (GNUTLS_E_SUCCESS > (r = gnutls_certificate_allocate_credentials(&ctx->server_cert))) { ERROR(srv, "gnutls_certificate_allocate_credentials failed(%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); goto error0; } if (GNUTLS_E_SUCCESS > (r = gnutls_priority_init(&ctx->server_priority, "NORMAL", NULL))) { ERROR(srv, "gnutls_priority_init('NORMAL') failed(%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); goto error1; } if (GNUTLS_E_SUCCESS > (r = gnutls_priority_init(&ctx->server_priority_beast, "NORMAL:-CIPHER-ALL:+ARCFOUR-128", NULL))) { int r1; if (GNUTLS_E_SUCCESS > (r1 = gnutls_priority_init(&ctx->server_priority_beast, "NORMAL", NULL))) { ERROR(srv, "gnutls_priority_init('NORMAL') failed(%s): %s", gnutls_strerror_name(r1), gnutls_strerror(r1)); goto error2; } else { ERROR(srv, "gnutls_priority_init('NORMAL:-CIPHER-ALL:+ARCFOUR-128') failed(%s): %s. Using 'NORMAL' instead (BEAST mitigation not available)", gnutls_strerror_name(r), gnutls_strerror(r)); } } #ifdef HAVE_SESSION_TICKET if (GNUTLS_E_SUCCESS > (r = gnutls_session_ticket_key_generate(&ctx->ticket_key))) { ERROR(srv, "gnutls_session_ticket_key_generate failed(%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); goto error3; } #endif ctx->srv = srv; ctx->ocsp = li_gnutls_ocsp_new(); ctx->refcount = 1; ctx->protect_against_beast = 1; return ctx; error3: gnutls_priority_deinit(ctx->server_priority_beast); error2: gnutls_priority_deinit(ctx->server_priority); error1: gnutls_certificate_free_credentials(ctx->server_cert); error0: g_slice_free(mod_context, ctx); return NULL; }
static int main_texinfo(void) { int i, j; const char *desc; const char *_name; char buffer[500]; error_name names_to_sort[MAX_CODES]; /* up to MAX_CODES names */ printf("@multitable @columnfractions .15 .40 .37\n"); memset(names_to_sort, 0, sizeof(names_to_sort)); j = 0; for (i = 0; i > -MAX_CODES; i--) { _name = gnutls_strerror_name(i); if (_name == NULL) continue; desc = gnutls_strerror(i); printf("@item %d @tab %s @tab %s\n", i, escape_texi_string(_name, buffer, sizeof(buffer)), desc); strcpy(names_to_sort[j].name, _name); names_to_sort[j].error_index = i; j++; } printf("@end multitable\n"); return 0; }
gboolean li_gnutls_ocsp_search_datum(liServer *srv, liGnuTLSOCSP *ocsp, gnutls_datum_t const* file) { int r; gnutls_datum_t decoded = { NULL, 0 }; gboolean result = FALSE; r = gnutls_pem_base64_decode_alloc("OCSP RESPONSE", file, &decoded); if (GNUTLS_E_SUCCESS <= r) { result = add_response(srv, ocsp, &decoded); if (!result) { ERROR(srv, "%s", "Failed loading OCSP response from PEM block"); goto error; } } else if (GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR == r) { /* ignore GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR */ } else { ERROR(srv, "gnutls_pem_base64_decode_alloc failed to decode OCSP RESPONSE from PEM block (%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); /* continue anyway */ } result = TRUE; error: gnutls_free(decoded.data); return result; }
static void do_handle_error(liGnuTLSFilter *f, const char *gnutlsfunc, int r, gboolean writing) { switch (r) { case GNUTLS_E_AGAIN: if (writing) f->write_wants_read = TRUE; break; case GNUTLS_E_REHANDSHAKE: #ifdef HAVE_SAVE_RENEGOTIATION if (f->initial_handshaked_finished && !gnutls_safe_renegotiation_status(f->session)) { _ERROR(f->srv, f->wrk, f->log_context, "%s: client initiated unsafe renegotitation, closing connection", gnutlsfunc); f_close_with_alert(f, r); } else { _DEBUG(f->srv, f->wrk, f->log_context, "%s: client initiated renegotitation", gnutlsfunc); } #else if (f->initial_handshaked_finished) { _ERROR(f->srv, f->wrk, f->log_context, "%s: client initiated renegotitation, closing connection", gnutlsfunc); f_close_with_alert(f, r); } #endif break; case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: f_close_with_alert(f, r); break; case GNUTLS_E_UNKNOWN_CIPHER_SUITE: case GNUTLS_E_UNSUPPORTED_VERSION_PACKET: _DEBUG(f->srv, f->wrk, f->log_context, "%s (%s): %s", gnutlsfunc, gnutls_strerror_name(r), gnutls_strerror(r)); f_close_with_alert(f, r); break; default: if (gnutls_error_is_fatal(r)) { _ERROR(f->srv, f->wrk, f->log_context, "%s (%s): %s", gnutlsfunc, gnutls_strerror_name(r), gnutls_strerror(r)); if (f->initial_handshaked_finished) { f_close_with_alert(f, r); } else { f_abort_gnutls(f); } } else { _ERROR(f->srv, f->wrk, f->log_context, "%s non fatal (%s): %s", gnutlsfunc, gnutls_strerror_name(r), gnutls_strerror(r)); } } }
/* pass ownership of der_data */ static gboolean add_response(liServer *srv, liGnuTLSOCSP *ocsp, gnutls_datum_t* der_data) { ocsp_response response; int r; guint i; memset(&response, 0, sizeof(response)); response.resp_data = *der_data; der_data->data = NULL; der_data->size = 0; if (GNUTLS_E_SUCCESS > (r = gnutls_ocsp_resp_init(&response.resp))) { ERROR(srv, "gnutls_ocsp_resp_init (%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); goto error; } if (GNUTLS_E_SUCCESS > (r = gnutls_ocsp_resp_import(response.resp, &response.resp_data))) { ERROR(srv, "gnutls_ocsp_resp_import (%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); goto error; } response.certificates = g_array_new(FALSE, TRUE, sizeof(ocsp_response_cert_entry)); for (i = 0; ; ++i) { ocsp_response_cert_entry entry; r = get_entry(srv, &entry, response.resp, i); if (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE == r) break; /* got them all */ if (GNUTLS_E_SUCCESS > r) goto error; g_array_append_vals(response.certificates, &entry, 1); if (entry.serial.size > ocsp->max_serial_length) ocsp->max_serial_length = entry.serial.size; if (entry.issuer_name_hash.size > ocsp->max_hash_length) ocsp->max_hash_length = entry.issuer_name_hash.size; } g_array_append_vals(ocsp->responses, &response, 1); return TRUE; error: clear_response(&response); return FALSE; }
gboolean li_gnutls_ocsp_add(liServer *srv, liGnuTLSOCSP *ocsp, const char* filename) { int r; gnutls_datum_t file = { NULL, 0 }; gnutls_datum_t decoded = { NULL, 0 }; gnutls_datum_t* der_data; gboolean result = FALSE; if (GNUTLS_E_SUCCESS > (r = gnutls_load_file(filename, &file))) { ERROR(srv, "Failed to load OCSP file '%s' (%s): %s", filename, gnutls_strerror_name(r), gnutls_strerror(r)); goto error; } /* decode pem "-----BEGIN OCSP RESPONSE-----", otherwise expect DER */ if (file.size > 20 && 0 == memcmp(file.data, CONST_STR_LEN("-----BEGIN "))) { r = gnutls_pem_base64_decode_alloc("OCSP RESPONSE", &file, &decoded); if (GNUTLS_E_SUCCESS > r) { ERROR(srv, "gnutls_pem_base64_decode_alloc failed to decode OCSP RESPONSE from '%s' (%s): %s", filename, gnutls_strerror_name(r), gnutls_strerror(r)); goto error; } der_data = &decoded; } else { der_data = &file; } result = add_response(srv, ocsp, der_data); if (!result) { ERROR(srv, "Failed loading OCSP response from '%s'", filename); } error: gnutls_free(file.data); gnutls_free(decoded.data); return result; }
static Eina_Bool _process_data(gnutls_session_t client, Ecore_Fd_Handler * fd_handler) { static int ret, lastret; static unsigned int count = 0; if (!done) { lastret = ret; ret = gnutls_handshake(client); count++; if (gnutls_record_get_direction(client)) ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_WRITE); else ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_READ); /* avoid printing messages infinity times */ if (lastret != ret && ret != 0 && ret != GNUTLS_E_AGAIN) { print("gnutls returned with: %s - %s", gnutls_strerror_name(ret), gnutls_strerror(ret)); if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) || (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)) print("Also received alert: %s", gnutls_alert_get_name (gnutls_alert_get(client))); print("last out: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS (gnutls_handshake_get_last_out(client))); print("last in: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS (gnutls_handshake_get_last_in(client))); } if (gnutls_error_is_fatal(ret)) { print("yarrr this be an error!"); exit(1); } } if (ret == GNUTLS_E_SUCCESS) { done = 1; //print("Handshake successful in %u handshake calls!", count); ecore_main_loop_quit(); } return ECORE_CALLBACK_RENEW; }
static void main_latex(void) { int i, j; static char buffer1[500]; static char buffer2[500]; const char *desc; const char *_name; error_name names_to_sort[MAX_CODES]; /* up to MAX_CODES names */ puts(headers); printf ("\\begin{supertabular}{|p{.05\\linewidth}|p{.40\\linewidth}|p{.45\\linewidth}|}\n"); memset(names_to_sort, 0, sizeof(names_to_sort)); j = 0; for (i = 0; i > -MAX_CODES; i--) { _name = gnutls_strerror_name(i); if (_name == NULL) continue; strcpy(names_to_sort[j].name, _name); names_to_sort[j].error_index = i; j++; } //qsort( names_to_sort, j, sizeof(error_name), compar); for (i = 0; i < j; i++) { _name = names_to_sort[i].name; desc = gnutls_strerror(names_to_sort[i].error_index); if (desc == NULL || _name == NULL) continue; printf("%d & {\\scriptsize{%s}} & %s", names_to_sort[i].error_index, escape_string(_name, buffer1, sizeof (buffer1)), escape_string(desc, buffer2, sizeof(buffer2))); printf("\\\\\n"); } printf("\\end{supertabular}\n\n"); return; }
int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "usage: %s <error_code>\n", argv[0]); return 1; } char *end = NULL; long error = strtol(argv[1], &end, 10); if (*end != '\0' || error < INT_MIN || error > INT_MAX) { fprintf(stderr, "Invalid error code.\n"); return 1; } printf("%s (%ld): %s\n", gnutls_strerror_name(error), error, gnutls_strerror(error)); return 0; }
int main (int argc, char *argv[]) { int i, j; const char *desc; const char *_name; error_name names_to_sort[400]; /* up to 400 names */ printf ("@table @code\n"); memset (names_to_sort, 0, sizeof (names_to_sort)); j = 0; for (i = 0; i > -400; i--) { _name = gnutls_strerror_name (i); if (_name == NULL) continue; strcpy (names_to_sort[j].name, _name); names_to_sort[j].error_index = i; j++; } qsort (names_to_sort, j, sizeof (error_name), compar); for (i = 0; i < j; i++) { _name = names_to_sort[i].name; desc = gnutls_strerror (names_to_sort[i].error_index); if (desc == NULL || _name == NULL) continue; printf ("@item %s:\n%s\n\n", _name, desc); } printf ("@end table\n"); return 0; }
static int get_entry(liServer *srv, ocsp_response_cert_entry* entry, gnutls_ocsp_resp_t resp, unsigned int ndx) { int r; memset(entry, 0, sizeof(*entry)); r = gnutls_ocsp_resp_get_single( resp, ndx, &entry->digest, &entry->issuer_name_hash, NULL, &entry->serial, NULL, NULL, NULL, NULL, NULL); if (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE == r) return r; if (GNUTLS_E_SUCCESS > r) { ERROR(srv, "Couldn't retrieve OCSP response information for entry %u (%s): %s", ndx, gnutls_strerror_name(r), gnutls_strerror(r)); return r; } if (0 == entry->serial.size || GNUTLS_DIG_UNKNOWN == entry->digest || entry->issuer_name_hash.size != gnutls_hash_get_len(entry->digest)) { ERROR(srv, "Invalid OCSP response data in entry %u", ndx); return GNUTLS_E_OCSP_RESPONSE_ERROR; } return GNUTLS_E_SUCCESS; }
static void do_handle_error(liGnuTLSFilter *f, const char *gnutlsfunc, int r, gboolean writing) { switch (r) { case GNUTLS_E_AGAIN: if (writing) f->write_wants_read = TRUE; return; case GNUTLS_E_REHANDSHAKE: #ifdef HAVE_SAVE_RENEGOTIATION if (f->initial_handshaked_finished && !gnutls_safe_renegotiation_status(f->session)) { _ERROR(f->srv, f->wrk, f->log_context, "%s: client initiated unsafe renegotitation, closing connection", gnutlsfunc); f_close_with_alert(f, r); } else { _DEBUG(f->srv, f->wrk, f->log_context, "%s: client initiated renegotitation", gnutlsfunc); } #else if (f->initial_handshaked_finished) { _ERROR(f->srv, f->wrk, f->log_context, "%s: client initiated renegotitation, closing connection", gnutlsfunc); f_close_with_alert(f, r); } #endif return; case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: f_close_with_alert(f, r); return; case GNUTLS_E_UNKNOWN_CIPHER_SUITE: case GNUTLS_E_UNSUPPORTED_VERSION_PACKET: _DEBUG(f->srv, f->wrk, f->log_context, "%s (%s): %s", gnutlsfunc, gnutls_strerror_name(r), gnutls_strerror(r)); f_close_with_alert(f, r); return; case GNUTLS_E_FATAL_ALERT_RECEIVED: case GNUTLS_E_WARNING_ALERT_RECEIVED: { gnutls_alert_description_t alert_desc = gnutls_alert_get(f->session); const char* alert_desc_name = gnutls_alert_get_name(alert_desc); _INFO(f->srv, f->wrk, f->log_context, "%s (%s): %s %s (%u)", gnutlsfunc, gnutls_strerror_name(r), gnutls_strerror(r), (NULL != alert_desc_name) ? alert_desc_name : "unknown alert", (unsigned int) alert_desc); } /* error not handled yet: break instead of return */ break; default: if (gnutls_error_is_fatal(r)) { _ERROR(f->srv, f->wrk, f->log_context, "%s (%s): %s", gnutlsfunc, gnutls_strerror_name(r), gnutls_strerror(r)); } else { _WARNING(f->srv, f->wrk, f->log_context, "%s non fatal (%s): %s", gnutlsfunc, gnutls_strerror_name(r), gnutls_strerror(r)); } /* error not handled yet: break instead of return */ break; } /* generic error handling */ if (gnutls_error_is_fatal(r)) { if (f->initial_handshaked_finished) { f_close_with_alert(f, r); } else { f_abort_gnutls(f); } } }
/* * Helper function converts a data blob to a gnutls_datum_t. * Note that this does not copy the data. * So the returned value should be treated as read only. * And that changes to the length of the underlying DATA_BLOB * will not be reflected in the returned object. * */ static const gnutls_datum_t convert_from_data_blob(DATA_BLOB blob) { const gnutls_datum_t datum = { .size = blob.length, .data = blob.data, }; return datum; } /* * @brief Get the gnutls algorithm needed to decrypt the EncryptedSecret * * @param ldb ldb context, to allow logging. * @param es the encrypted secret * * @return The gnutls algoritm number, or 0 if there is no match. * */ static int gnutls_get_algorithm(struct ldb_context *ldb, struct EncryptedSecret *es) { switch (es->header.algorithm) { case ENC_SECRET_AES_128_AEAD: return GNUTLS_CIPHER_AES_128_GCM; default: ldb_asprintf_errstring(ldb, "Unsupported encryption algorithm %d\n", es->header.algorithm); return 0; } } /* * * @param err Pointer to an error code, set to: * LDB_SUCESS If the value was successfully encrypted * LDB_ERR_OPERATIONS_ERROR If there was an error. * * @param ctx Talloc memory context the will own the memory allocated * @param ldb ldb context, to allow logging. * @param val The ldb value to encrypt, not altered or freed * @param data The context data for this module. * * @return The encrypted ldb_val, or data_blob_null if there was an error. */ static struct ldb_val gnutls_encrypt_aead(int *err, TALLOC_CTX *ctx, struct ldb_context *ldb, const struct ldb_val val, const struct es_data *data) { struct EncryptedSecret *es = NULL; struct ldb_val enc = data_blob_null; DATA_BLOB pt = data_blob_null; gnutls_aead_cipher_hd_t cipher_hnd; int rc; TALLOC_CTX *frame = talloc_stackframe(); es = makeEncryptedSecret(ldb, frame); if (es == NULL) { goto error_exit; } pt = makePlainText(frame, ldb, val); if (pt.length == 0) { goto error_exit; } /* * Set the encryption key and initialize the encryption handle. */ { const size_t key_size = gnutls_cipher_get_key_size( data->encryption_algorithm); gnutls_datum_t cipher_key; DATA_BLOB key_blob = get_key(data); if (key_blob.length != key_size) { ldb_asprintf_errstring(ldb, "Invalid EncryptedSecrets key " "size, expected %zu bytes and " "it is %zu bytes\n", key_size, key_blob.length); goto error_exit; } cipher_key = convert_from_data_blob(key_blob); rc = gnutls_aead_cipher_init(&cipher_hnd, data->encryption_algorithm, &cipher_key); if (rc !=0) { ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_init failed " "%s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit; } } /* * Set the initialisation vector */ { unsigned iv_size = gnutls_cipher_get_iv_size( data->encryption_algorithm); uint8_t *iv; iv = talloc_zero_size(frame, iv_size); if (iv == NULL) { ldb_set_errstring(ldb, "Out of memory allocating IV\n"); goto error_exit_handle; } rc = gnutls_rnd(GNUTLS_RND_NONCE, iv, iv_size); if (rc !=0) { ldb_asprintf_errstring(ldb, "gnutls_rnd failed %s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit_handle; } es->iv.length = iv_size; es->iv.data = iv; } /* * Encrypt the value. */ { const unsigned block_size = gnutls_cipher_get_block_size( data->encryption_algorithm); const unsigned tag_size = gnutls_cipher_get_tag_size( data->encryption_algorithm); const size_t ed_size = round_to_block_size( block_size, sizeof(struct PlaintextSecret) + val.length); const size_t en_size = ed_size + tag_size; uint8_t *ct = talloc_zero_size(frame, en_size); size_t el = en_size; if (ct == NULL) { ldb_set_errstring(ldb, "Out of memory allocation cipher " "text\n"); goto error_exit_handle; } rc = gnutls_aead_cipher_encrypt( cipher_hnd, es->iv.data, es->iv.length, &es->header, sizeof(struct EncryptedSecretHeader), tag_size, pt.data, pt.length, ct, &el); if (rc !=0) { ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_encrypt '" "failed %s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); *err = LDB_ERR_OPERATIONS_ERROR; return data_blob_null; } es->encrypted.length = el; es->encrypted.data = ct; gnutls_aead_cipher_deinit(cipher_hnd); } rc = ndr_push_struct_blob(&enc, ctx, es, (ndr_push_flags_fn_t) ndr_push_EncryptedSecret); if (!NDR_ERR_CODE_IS_SUCCESS(rc)) { ldb_set_errstring(ldb, "Unable to ndr push EncryptedSecret\n"); goto error_exit; } TALLOC_FREE(frame); return enc; error_exit_handle: gnutls_aead_cipher_deinit(cipher_hnd); error_exit: *err = LDB_ERR_OPERATIONS_ERROR; TALLOC_FREE(frame); return data_blob_null; } /* * @brief Decrypt data encrypted using an aead algorithm. * * Decrypt the data in ed and insert it into ev. The data was encrypted * with one of the gnutls aead compatable algorithms. * * @param err Pointer to an error code, set to: * LDB_SUCESS If the value was successfully decrypted * LDB_ERR_OPERATIONS_ERROR If there was an error. * * @param ctx The talloc context that will own the PlaintextSecret * @param ldb ldb context, to allow logging. * @param ev The value to be updated with the decrypted data. * @param ed The data to decrypt. * @param data The context data for this module. * * @return ev is updated with the unencrypted data. */ static void gnutls_decrypt_aead(int *err, TALLOC_CTX *ctx, struct ldb_context *ldb, struct EncryptedSecret *es, struct PlaintextSecret *ps, const struct es_data *data) { gnutls_aead_cipher_hd_t cipher_hnd; DATA_BLOB pt = data_blob_null; const unsigned tag_size = gnutls_cipher_get_tag_size(es->header.algorithm); int rc; /* * Get the encryption key and initialise the encryption handle */ { gnutls_datum_t cipher_key; DATA_BLOB key_blob; const int algorithm = gnutls_get_algorithm(ldb, es); const size_t key_size = gnutls_cipher_get_key_size(algorithm); key_blob = get_key(data); if (algorithm == 0) { goto error_exit; } if (key_blob.length != key_size) { ldb_asprintf_errstring(ldb, "Invalid EncryptedSecrets key " "size, expected %zu bytes and " "it is %zu bytes\n", key_size, key_blob.length); goto error_exit; } cipher_key = convert_from_data_blob(key_blob); rc = gnutls_aead_cipher_init( &cipher_hnd, algorithm, &cipher_key); if (rc != 0) { ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_init failed " "%s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit; } } /* * Decrypt and validate the encrypted value */ pt.length = es->encrypted.length; pt.data = talloc_zero_size(ctx, es->encrypted.length); if (pt.data == NULL) { ldb_set_errstring(ldb, "Out of memory allocating plain text\n"); goto error_exit_handle; } rc = gnutls_aead_cipher_decrypt(cipher_hnd, es->iv.data, es->iv.length, &es->header, sizeof(struct EncryptedSecretHeader), tag_size, es->encrypted.data, es->encrypted.length, pt.data, &pt.length); if (rc != 0) { /* * Typically this will indicate that the data has been * corrupted i.e. the tag comparison has failed. * At the moment gnutls does not provide a separate * error code to indicate this */ ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_decrypt failed " "%s - %s. Data possibly corrupted or " "altered\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit_handle; } gnutls_aead_cipher_deinit(cipher_hnd); rc = ndr_pull_struct_blob(&pt, ctx, ps, (ndr_pull_flags_fn_t) ndr_pull_PlaintextSecret); if(!NDR_ERR_CODE_IS_SUCCESS(rc)) { ldb_asprintf_errstring(ldb, "Error(%d) unpacking decrypted data, " "data possibly corrupted or altered\n", rc); goto error_exit; } return; error_exit_handle: gnutls_aead_cipher_deinit(cipher_hnd); error_exit: *err = LDB_ERR_OPERATIONS_ERROR; return; }
static Ecore_Con_Ssl_Error _ecore_con_ssl_server_init_gnutls(Ecore_Con_Server *svr) { const gnutls_datum_t *cert_list; unsigned int iter, cert_list_size; gnutls_x509_crt_t cert = NULL; const char *priority = "NONE:%VERIFY_ALLOW_X509_V1_CA_CRT:+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+COMP-DEFLATE:+COMP-NULL:+CTYPE-X509:+SHA1:+SHA256:+SHA384:+SHA512:+AES-256-CBC:+AES-128-CBC:+3DES-CBC:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0:+VERS-SSL3.0"; int ret = 0; switch (svr->ssl_state) { case ECORE_CON_SSL_STATE_DONE: return ECORE_CON_SSL_ERROR_NONE; case ECORE_CON_SSL_STATE_INIT: if (svr->type & ECORE_CON_USE_SSL2) /* not supported because of security issues */ return ECORE_CON_SSL_ERROR_SSL2_NOT_SUPPORTED; switch (svr->type & ECORE_CON_SSL) { case ECORE_CON_USE_SSL3: case ECORE_CON_USE_SSL3 | ECORE_CON_LOAD_CERT: priority = "NONE:%VERIFY_ALLOW_X509_V1_CA_CRT:+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+COMP-DEFLATE:+COMP-NULL:+CTYPE-X509:+SHA1:+SHA256:+SHA384:+SHA512:+AES-256-CBC:+AES-128-CBC:+3DES-CBC:!VERS-TLS1.0:!VERS-TLS1.1"; break; case ECORE_CON_USE_TLS: case ECORE_CON_USE_TLS | ECORE_CON_LOAD_CERT: priority = "NONE:%VERIFY_ALLOW_X509_V1_CA_CRT:+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+COMP-DEFLATE:+COMP-NULL:+CTYPE-X509:+SHA1:+SHA256:+SHA384:+SHA512:+AES-256-CBC:+AES-128-CBC:+3DES-CBC:!VERS-SSL3.0"; break; case ECORE_CON_USE_MIXED: case ECORE_CON_USE_MIXED | ECORE_CON_LOAD_CERT: break; default: return ECORE_CON_SSL_ERROR_NONE; } SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_init(&svr->session, GNUTLS_CLIENT)); SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_session_ticket_enable_client(svr->session)); SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_server_name_set(svr->session, GNUTLS_NAME_DNS, svr->name, strlen(svr->name))); INF("Applying priority string: %s", priority); SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_priority_set_direct(svr->session, priority, NULL)); SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_credentials_set(svr->session, GNUTLS_CRD_CERTIFICATE, svr->cert)); // SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_credentials_set(svr->session, GNUTLS_CRD_PSK, svr->pskcred_c)); if (!svr->use_cert) SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_credentials_set(svr->session, GNUTLS_CRD_ANON, svr->anoncred_c)); gnutls_dh_set_prime_bits(svr->session, 512); gnutls_transport_set_ptr(svr->session, (gnutls_transport_ptr_t)((intptr_t)svr->fd)); svr->ssl_state = ECORE_CON_SSL_STATE_HANDSHAKING; case ECORE_CON_SSL_STATE_HANDSHAKING: if (!svr->session) { DBG("Server was previously lost, going to error condition"); goto error; } ret = gnutls_handshake(svr->session); DBG("calling gnutls_handshake(): returned with '%s'", gnutls_strerror_name(ret)); SSL_ERROR_CHECK_GOTO_ERROR(gnutls_error_is_fatal(ret)); if (!ret) { svr->handshaking = EINA_FALSE; svr->ssl_state = ECORE_CON_SSL_STATE_DONE; } else { if (gnutls_record_get_direction(svr->session)) ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE); else ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ); return ECORE_CON_SSL_ERROR_NONE; } default: break; } if (!svr->verify) /* not verifying certificates, so we're done! */ return ECORE_CON_SSL_ERROR_NONE; ret = 0; /* use CRL/CA lists to verify */ SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_certificate_verify_peers2(svr->session, &iter)); if (iter & GNUTLS_CERT_INVALID) ERR("The certificate is not trusted."); else if (iter & GNUTLS_CERT_SIGNER_NOT_FOUND) ERR("The certificate hasn't got a known issuer."); else if (iter & GNUTLS_CERT_REVOKED) ERR("The certificate has been revoked."); else if (iter & GNUTLS_CERT_EXPIRED) ERR("The certificate has expired"); else if (iter & GNUTLS_CERT_NOT_ACTIVATED) ERR("The certificate is not yet activated"); if (iter) goto error; if (gnutls_certificate_type_get(svr->session) != GNUTLS_CRT_X509) { ERR("Warning: PGP certificates are not yet supported!"); goto error; } SSL_ERROR_CHECK_GOTO_ERROR(!(cert_list = gnutls_certificate_get_peers(svr->session, &cert_list_size))); SSL_ERROR_CHECK_GOTO_ERROR(!cert_list_size); SSL_ERROR_CHECK_GOTO_ERROR(gnutls_x509_crt_init(&cert)); SSL_ERROR_CHECK_GOTO_ERROR(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)); SSL_ERROR_CHECK_GOTO_ERROR(!gnutls_x509_crt_check_hostname(cert, svr->name)); gnutls_x509_crt_deinit(cert); DBG("SSL certificate verification succeeded!"); return ECORE_CON_SSL_ERROR_NONE; error: _gnutls_print_errors(ret); if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) || (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)) ERR("Also received alert: %s", gnutls_alert_get_name(gnutls_alert_get(svr->session))); if (svr->session && (svr->ssl_state != ECORE_CON_SSL_STATE_DONE)) { ERR("last out: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_out(svr->session))); ERR("last in: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_in(svr->session))); } if (cert) gnutls_x509_crt_deinit(cert); _ecore_con_ssl_server_shutdown_gnutls(svr); return ECORE_CON_SSL_ERROR_SERVER_INIT_FAILED; }
static void _gnutls_print_errors(int ret) { if (ret) ERR("gnutls returned with error: %s - %s", gnutls_strerror_name(ret), gnutls_strerror(ret)); }
char * crypto_encrypt (const char *cipher, const GByteArray *data, const char *iv, const gsize iv_len, const char *key, gsize key_len, gsize *out_len, GError **error) { gnutls_cipher_hd_t ctx; gnutls_datum_t key_dt, iv_dt; int err; int cipher_mech; char *output = NULL; gboolean success = FALSE; gsize padded_buf_len, pad_len, output_len; char *padded_buf = NULL; guint32 i; gsize salt_len; if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { cipher_mech = GNUTLS_CIPHER_3DES_CBC; salt_len = SALT_LEN; } else if (!strcmp (cipher, CIPHER_AES_CBC)) { cipher_mech = GNUTLS_CIPHER_AES_128_CBC; salt_len = iv_len; } else { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_UNKNOWN_CIPHER, _("Private key cipher '%s' was unknown."), cipher); return NULL; } /* If data->len % ivlen == 0, then we add another complete block * onto the end so that the decrypter knows there's padding. */ pad_len = iv_len - (data->len % iv_len); output_len = padded_buf_len = data->len + pad_len; padded_buf = g_malloc0 (padded_buf_len); memcpy (padded_buf, data->data, data->len); for (i = 0; i < pad_len; i++) padded_buf[data->len + i] = (guint8) (pad_len & 0xFF); output = g_malloc0 (output_len); key_dt.data = (unsigned char *) key; key_dt.size = key_len; iv_dt.data = (unsigned char *) iv; iv_dt.size = iv_len; err = gnutls_cipher_init (&ctx, cipher_mech, &key_dt, &iv_dt); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_INIT_FAILED, _("Failed to initialize the encryption cipher context: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); goto out; } err = gnutls_cipher_encrypt2 (ctx, padded_buf, padded_buf_len, output, output_len); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, _("Failed to encrypt the data: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); goto out; } *out_len = output_len; success = TRUE; out: if (padded_buf) { memset (padded_buf, 0, padded_buf_len); g_free (padded_buf); padded_buf = NULL; } if (!success) { if (output) { /* Don't expose key material */ memset (output, 0, output_len); g_free (output); output = NULL; } } gnutls_cipher_deinit (ctx); return output; }
gboolean crypto_md5_hash (const char *salt, const gsize salt_len, const char *password, gsize password_len, char *buffer, gsize buflen, GError **error) { gnutls_hash_hd_t ctx; int err; int nkey = buflen; const gsize digest_len = 16; int count = 0; char digest[MD5_HASH_LEN]; char *p = buffer; if (salt) g_return_val_if_fail (salt_len >= SALT_LEN, FALSE); g_return_val_if_fail (password != NULL, FALSE); g_return_val_if_fail (password_len > 0, FALSE); g_return_val_if_fail (buffer != NULL, FALSE); g_return_val_if_fail (buflen > 0, FALSE); if (gnutls_hash_get_len (GNUTLS_DIG_MD5) > MD5_HASH_LEN) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_MD5_INIT_FAILED, _("Hash length too long (%d > %d)."), gnutls_hash_get_len (GNUTLS_DIG_MD5), MD5_HASH_LEN); return FALSE; } while (nkey > 0) { int i = 0; err = gnutls_hash_init (&ctx, GNUTLS_DIG_MD5); if (err < 0) goto error; if (count++) gnutls_hash (ctx, digest, digest_len); gnutls_hash (ctx, password, password_len); if (salt) gnutls_hash (ctx, salt, SALT_LEN); /* Only use 8 bytes of salt */ gnutls_hash_deinit (ctx, digest); while (nkey && (i < digest_len)) { *(p++) = digest[i++]; nkey--; } } memset (digest, 0, sizeof (digest)); return TRUE; error: memset (digest, 0, sizeof (digest)); g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_MD5_INIT_FAILED, _("Failed to initialize the MD5 engine: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); return FALSE; }
static gboolean mod_gnutls_con_new(liConnection *con, int fd) { liEventLoop *loop = &con->wrk->loop; liServer *srv = con->srv; mod_context *ctx = con->srv_sock->data; mod_connection_ctx *conctx; gnutls_session_t session; int r; if (GNUTLS_E_SUCCESS > (r = gnutls_init(&session, GNUTLS_SERVER))) { ERROR(srv, "gnutls_init (%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); return FALSE; } mod_gnutls_context_acquire(ctx); if (GNUTLS_E_SUCCESS > (r = gnutls_priority_set(session, ctx->server_priority))) { ERROR(srv, "gnutls_priority_set (%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); goto fail; } if (GNUTLS_E_SUCCESS > (r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ctx->server_cert))) { ERROR(srv, "gnutls_credentials_set (%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); goto fail; } if (NULL != ctx->session_db) { gnutls_db_set_ptr(session, ctx->session_db); gnutls_db_set_remove_function(session, session_db_remove_cb); gnutls_db_set_retrieve_function(session, session_db_retrieve_cb); gnutls_db_set_store_function(session, session_db_store_cb); } #ifdef HAVE_SESSION_TICKET if (GNUTLS_E_SUCCESS > (r = gnutls_session_ticket_enable_server(session, &ctx->ticket_key))) { ERROR(srv, "gnutls_session_ticket_enable_server (%s): %s", gnutls_strerror_name(r), gnutls_strerror(r)); goto fail; } #endif #ifdef GNUTLS_ALPN_MAND { static const gnutls_datum_t proto_http1 = { (unsigned char*) CONST_STR_LEN("http/1.1") }; gnutls_alpn_set_protocols(session, &proto_http1, 1, 0); } #endif conctx = g_slice_new0(mod_connection_ctx); conctx->session = session; conctx->sock_stream = li_iostream_new(con->wrk, fd, tcp_io_cb, conctx); conctx->client_hello_stream = li_ssl_client_hello_stream(&con->wrk->loop, gnutls_client_hello_cb, conctx); #ifdef USE_SNI li_job_init(&conctx->sni_job, sni_job_cb); conctx->sni_jobref = li_job_ref(&con->wrk->loop.jobqueue, &conctx->sni_job); #endif li_stream_connect(&conctx->sock_stream->stream_in, conctx->client_hello_stream); conctx->tls_filter = li_gnutls_filter_new(srv, con->wrk, &filter_callbacks, conctx, conctx->session, conctx->client_hello_stream, &conctx->sock_stream->stream_out); conctx->con = con; conctx->ctx = ctx; con->con_sock.data = conctx; con->con_sock.callbacks = &gnutls_tcp_cbs; con->con_sock.raw_out = li_stream_plug_new(loop); con->con_sock.raw_in = li_stream_plug_new(loop); con->info.is_ssl = TRUE; return TRUE; fail: gnutls_deinit(session); mod_gnutls_context_release(ctx); return FALSE; }
char * crypto_decrypt (const char *cipher, int key_type, GByteArray *data, const char *iv, const gsize iv_len, const char *key, const gsize key_len, gsize *out_len, GError **error) { gnutls_cipher_hd_t ctx; gnutls_datum_t key_dt, iv_dt; int err; int cipher_mech, i; char *output = NULL; gboolean success = FALSE; gsize pad_len, real_iv_len; if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { cipher_mech = GNUTLS_CIPHER_3DES_CBC; real_iv_len = SALT_LEN; } else if (!strcmp (cipher, CIPHER_DES_CBC)) { cipher_mech = GNUTLS_CIPHER_DES_CBC; real_iv_len = SALT_LEN; } else if (!strcmp (cipher, CIPHER_AES_CBC)) { cipher_mech = GNUTLS_CIPHER_AES_128_CBC; real_iv_len = 16; } else { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_UNKNOWN_CIPHER, _("Private key cipher '%s' was unknown."), cipher); return NULL; } if (iv_len < real_iv_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_RAW_IV_INVALID, _("Invalid IV length (must be at least %zd)."), real_iv_len); return NULL; } output = g_malloc0 (data->len); key_dt.data = (unsigned char *) key; key_dt.size = key_len; iv_dt.data = (unsigned char *) iv; iv_dt.size = iv_len; err = gnutls_cipher_init (&ctx, cipher_mech, &key_dt, &iv_dt); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_INIT_FAILED, _("Failed to initialize the decryption cipher context: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); goto out; } err = gnutls_cipher_decrypt2 (ctx, data->data, data->len, output, data->len); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, _("Failed to decrypt the private key: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); goto out; } pad_len = output[data->len - 1]; /* Check if the padding at the end of the decrypted data is valid */ if (pad_len == 0 || pad_len > real_iv_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, _("Failed to decrypt the private key: unexpected padding length.")); goto out; } /* Validate tail padding; last byte is the padding size, and all pad bytes * should contain the padding size. */ for (i = 1; i <= pad_len; ++i) { if (output[data->len - i] != pad_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, _("Failed to decrypt the private key.")); goto out; } } *out_len = data->len - pad_len; success = TRUE; out: if (!success) { if (output) { /* Don't expose key material */ memset (output, 0, data->len); g_free (output); output = NULL; } } gnutls_cipher_deinit (ctx); return output; }