static int setenv(const char * name, const char * val, int overwrite) { uschar * s; if (Ustrchr(name, '=')) return -1; if (overwrite || !getenv(name)) putenv(CS string_copy_malloc(string_sprintf("%s=%s", name, val))); return 0; }
static BOOL verify_certificate(gnutls_session session, const char **error) { int verify; uschar *dn_string = US""; const gnutls_datum *cert; unsigned int cert_size = 0; *error = NULL; /* Get the peer's certificate. If it sent one, extract it's DN, and then attempt to verify the certificate. If no certificate is supplied, verification is forced to fail. */ cert = gnutls_certificate_get_peers(session, &cert_size); if (cert != NULL) { uschar buff[1024]; gnutls_x509_crt gcert; gnutls_x509_crt_init(&gcert); dn_string = US"unknown"; if (gnutls_x509_crt_import(gcert, cert, GNUTLS_X509_FMT_DER) == 0) { size_t bufsize = sizeof(buff); if (gnutls_x509_crt_get_dn(gcert, CS buff, &bufsize) >= 0) dn_string = string_copy_malloc(buff); } verify = gnutls_certificate_verify_peers(session); } else { DEBUG(D_tls) debug_printf("no peer certificate supplied\n"); verify = GNUTLS_CERT_INVALID; *error = "not supplied"; } /* Handle the result of verification. INVALID seems to be set as well as REVOKED, but leave the test for both. */ if ((verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) { tls_certificate_verified = FALSE; if (*error == NULL) *error = ((verify & GNUTLS_CERT_REVOKED) != 0)? "revoked" : "invalid"; if (verify_requirement == VERIFY_REQUIRED) { DEBUG(D_tls) debug_printf("TLS certificate verification failed (%s): " "peerdn=%s\n", *error, dn_string); gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE); return FALSE; /* reject */ } DEBUG(D_tls) debug_printf("TLS certificate verify failure (%s) overridden " "(host in tls_try_verify_hosts): peerdn=%s\n", *error, dn_string); } else { tls_certificate_verified = TRUE; DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=%s\n", dn_string); } tls_peerdn = dn_string; return TRUE; /* accept */ }
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; }