IoT_Error_t iot_tls_connect(Network *pNetwork, TLSConnectParams *params) { if (pNetwork == NULL) return NULL_VALUE_ERROR; IoT_Error_t ret_val = AWS_FAILURE; if (params != NULL) { Populate_ConnectParams(pNetwork, params->pRootCALocation, params->pDeviceCertLocation, params->pDevicePrivateKeyLocation, params->pDestinationURL, params->DestinationPort, params->timeout_ms, params->ServerVerificationFlag); } pNetwork->my_socket = Create_TCPSocket(); if (-1 == pNetwork->my_socket) { ret_val = TCP_CONNECTION_ERROR; return ret_val; } ret_val = Connect_TCPSocket(pNetwork->my_socket, pNetwork->tlsConnectParams.pDestinationURL, pNetwork->tlsConnectParams.DestinationPort); if (AWS_SUCCESS != ret_val) { Close_TCPSocket(&pNetwork->my_socket); return ret_val; } tls_cfg.flags = TLS_USE_CLIENT_CERT; tls_cfg.tls.client.client_cert = (unsigned char *) pNetwork->tlsConnectParams.pDeviceCertLocation; tls_cfg.tls.client.client_cert_size = strlen(pNetwork->tlsConnectParams.pDeviceCertLocation); tls_cfg.tls.client.client_key = (unsigned char *) pNetwork->tlsConnectParams.pDevicePrivateKeyLocation; tls_cfg.tls.client.client_key_size = strlen(pNetwork->tlsConnectParams.pDevicePrivateKeyLocation); tls_cfg.tls.client.ca_cert = (unsigned char *)pNetwork->tlsConnectParams.pRootCALocation; tls_cfg.tls.client.ca_cert_size = strlen(pNetwork->tlsConnectParams.pRootCALocation); ret_val = tls_session_init(&tls_handle, pNetwork->my_socket, &tls_cfg); if (AWS_SUCCESS != ret_val) Close_TCPSocket(&pNetwork->my_socket); return ret_val; }
int tls_server_start(uschar *require_ciphers, uschar *require_mac, uschar *require_kx, uschar *require_proto) { int rc; const char *error; uschar *expciphers = NULL; uschar *expmac = NULL; uschar *expkx = NULL; uschar *expproto = NULL; /* Check for previous activation */ if (tls_active >= 0) { tls_error("STARTTLS received after TLS started", NULL, ""); smtp_printf("554 Already in TLS\r\n"); return FAIL; } /* Initialize the library. If it fails, it will already have logged the error and sent an SMTP response. */ DEBUG(D_tls) debug_printf("initializing GnuTLS as a server\n"); rc = tls_init(NULL, tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl); if (rc != OK) return rc; if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || !expand_check(require_mac, US"gnutls_require_mac", &expmac) || !expand_check(require_kx, US"gnutls_require_kx", &expkx) || !expand_check(require_proto, US"gnutls_require_proto", &expproto)) return FAIL; /* If this is a host for which certificate verification is mandatory or optional, set up appropriately. */ tls_certificate_verified = FALSE; verify_requirement = VERIFY_NONE; if (verify_check_host(&tls_verify_hosts) == OK) verify_requirement = VERIFY_REQUIRED; else if (verify_check_host(&tls_try_verify_hosts) == OK) verify_requirement = VERIFY_OPTIONAL; /* Prepare for new connection */ tls_session = tls_session_init(GNUTLS_SERVER, expciphers, expmac, expkx, expproto); if (tls_session == NULL) return tls_error(US"tls_session_init", NULL, gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); /* Set context and tell client to go ahead, except in the case of TLS startup on connection, where outputting anything now upsets the clients and tends to make them disconnect. We need to have an explicit fflush() here, to force out the response. Other smtp_printf() calls do not need it, because in non-TLS mode, the fflush() happens when smtp_getc() is called. */ if (!tls_on_connect) { smtp_printf("220 TLS go ahead\r\n"); fflush(smtp_out); } /* Now negotiate the TLS session. We put our own timer on it, since it seems that the GnuTLS library doesn't. */ gnutls_transport_set_ptr2(tls_session, (gnutls_transport_ptr)fileno(smtp_in), (gnutls_transport_ptr)fileno(smtp_out)); sigalrm_seen = FALSE; if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); rc = gnutls_handshake(tls_session); alarm(0); if (rc < 0) { tls_error(US"gnutls_handshake", NULL, sigalrm_seen ? "timed out" : gnutls_strerror(rc)); /* It seems that, except in the case of a timeout, we have to close the connection right here; otherwise if the other end is running OpenSSL it hangs until the server times out. */ if (!sigalrm_seen) { (void)fclose(smtp_out); (void)fclose(smtp_in); } return FAIL; } DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); if (verify_requirement != VERIFY_NONE && !verify_certificate(tls_session, &error)) { tls_error(US"certificate verification failed", NULL, error); return FAIL; } construct_cipher_name(tls_session); /* TLS has been set up. Adjust the input functions to read via TLS, and initialize appropriately. */ ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size); ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0; ssl_xfer_eof = ssl_xfer_error = 0; receive_getc = tls_getc; receive_ungetc = tls_ungetc; receive_feof = tls_feof; receive_ferror = tls_ferror; receive_smtp_buffered = tls_smtp_buffered; tls_active = fileno(smtp_out); return OK; }
int tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam, uschar *certificate, uschar *privatekey, uschar *verify_certs, uschar *verify_crl, uschar *require_ciphers, uschar *require_mac, uschar *require_kx, uschar *require_proto, int timeout) { const gnutls_datum *server_certs; uschar *expciphers = NULL; uschar *expmac = NULL; uschar *expkx = NULL; uschar *expproto = NULL; const char *error; unsigned int server_certs_size; int rc; DEBUG(D_tls) debug_printf("initializing GnuTLS as a client\n"); verify_requirement = (verify_certs == NULL)? VERIFY_NONE : VERIFY_REQUIRED; rc = tls_init(host, certificate, privatekey, verify_certs, verify_crl); if (rc != OK) return rc; if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || !expand_check(require_mac, US"gnutls_require_mac", &expmac) || !expand_check(require_kx, US"gnutls_require_kx", &expkx) || !expand_check(require_proto, US"gnutls_require_proto", &expproto)) return FAIL; tls_session = tls_session_init(GNUTLS_CLIENT, expciphers, expmac, expkx, expproto); if (tls_session == NULL) return tls_error(US "tls_session_init", host, gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)fd); /* There doesn't seem to be a built-in timeout on connection. */ sigalrm_seen = FALSE; alarm(timeout); rc = gnutls_handshake(tls_session); alarm(0); if (rc < 0) return tls_error(US "gnutls_handshake", host, sigalrm_seen ? "timed out" : gnutls_strerror(rc)); server_certs = gnutls_certificate_get_peers(tls_session, &server_certs_size); if (server_certs != NULL) { uschar buff[1024]; gnutls_x509_crt gcert; gnutls_x509_crt_init(&gcert); tls_peerdn = US"unknown"; if (gnutls_x509_crt_import(gcert, server_certs, GNUTLS_X509_FMT_DER) == 0) { size_t bufsize = sizeof(buff); if (gnutls_x509_crt_get_dn(gcert, CS buff, &bufsize) >= 0) tls_peerdn = string_copy_malloc(buff); } } /* Should we also verify the hostname here? */ if (verify_requirement != VERIFY_NONE && !verify_certificate(tls_session, &error)) return tls_error(US"certificate verification failed", host, error); construct_cipher_name(tls_session); /* Sets tls_cipher */ tls_active = fd; return OK; }