/*****************************************************************************\ * Copyright (c) 2004 Pelle Johansson. * * All rights reserved. * * * * This file is part of the moftpd package. Use and distribution of * * this software is governed by the terms in the file LICENCE, which * * should have come with this package. * \*****************************************************************************/ /* $moftpd: tls_openssl.c 1186 2004-10-25 14:27:26Z morth $ */ #include "system.h" #include "tls.h" #include "utf8fs/memory.h" #include "utf8fs/file.h" static int gnutls_inited = 0; extern char *sslCertsPath; const char *tls_get_cert_dir (void) { return "/etc/certs"; } tls_t tls_open (int fd, int options, tlscert_t cert, tlskey_t key) { tls_t res; gnutls_session session; gnutls_certificate_credentials creds; if (fd < 0 || !cert || !key) return NULL; if (!gnutls_inited) { if (gnutls_global_init ()) return NULL; gnutls_inited = 1; } if (gnutls_init (&session, GNUTLS_SERVER)) return NULL; if (gnutls_certificate_allocate_credentials (&creds)) { gnutls_deinit (session); return NULL; } gnutls_certificate_set_x509_key (creds, &cert, 1, key); if (options & tlsVerifyClient) { DIR *dir; struct dirent *ent; gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); char path[4097], *fp; dir = opendir (sslCertsPath); if (dir) { strcpy (path, sslCertsPath); fp = path + strlen (path); *fp++ = '/'; while ((ent = readdir (dir))) { #ifdef HAVE_STRUCT_DIRENT_D_TYPE if (ent->d_type == DT_DIR) continue; #endif if (fp - path + ent->d_namlen >= sizeof (path)) continue; strcpy (fp, ent->d_name); gnutls_certificate_set_x509_trust_file (creds, path, GNUTLS_X509_FMT_PEM); } } } gnutls_set_default_priority (session); gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, creds); res = palloc (sizeof (*res), NULL, NULL); if (!res) { gnutls_deinit (session); gnutls_certificate_free_credentials (creds); return NULL; } res->session = session; res->creds = creds; res->options = options; gnutls_transport_set_ptr (res->session, (gnutls_transport_ptr)fd); return res; } void tls_start (tls_t tls) { /* noop */ } int tls_stop (tls_t tls) { int res = gnutls_bye (tls->session, GNUTLS_SHUT_RDWR); if (!res) return 1; if (res == GNUTLS_E_AGAIN || res == GNUTLS_E_INTERRUPTED) return 0; return res; } void tls_free (tls_t tls) { gnutls_deinit (tls->session); gnutls_certificate_free_credentials (tls->creds); pfree (tls, NULL); } int tls_accept (tls_t tls) { int res = gnutls_handshake (tls->session); if (!res) { if (tls->options & tlsVerifyClient) { unsigned int status; if ((res = gnutls_certificate_verify_peers2 (tls->session, &status)) || status) { if (!res) res = GNUTLS_E_CERTIFICATE_ERROR; return res; } } return 1; } if (res == GNUTLS_E_AGAIN || res == GNUTLS_E_INTERRUPTED) return 0; return res; } ssize_t tls_read (tls_t tls, void *buf, size_t maxlen) { return gnutls_record_recv (tls->session, buf, maxlen); } ssize_t tls_write (tls_t tls, const void *buf, size_t len) { return gnutls_record_send (tls->session, buf, len); } ssize_t tls_write_vecs (tls_t tls, struct iovec *vecs, int num) { int i, l = 0; int res = 0; for (i = 0; i < num; i++) { l = tls_write (tls, vecs[i].iov_base, vecs[i].iov_len); if (l < 0) break; res += l; } if (res) return res; return l; } tlscert_t tls_read_cert (const char *file) { gnutls_x509_crt res; gnutls_datum datum; size_t sz; if (!gnutls_inited) { if (gnutls_global_init ()) return NULL; gnutls_inited = 1; } datum.data = read_file (file, &sz); datum.size = sz; if (!datum.data) return NULL; if (gnutls_x509_crt_init (&res)) return NULL; if (gnutls_x509_crt_import (res, &datum, GNUTLS_X509_FMT_PEM)) { gnutls_x509_crt_deinit (res); return NULL; } return res; } tlscert_t tls_get_peer_cert (const tls_t tls) { const gnutls_datum *certs; int ncerts = 0; gnutls_x509_crt res; certs = gnutls_certificate_get_peers (tls->session, &ncerts); if (!certs || ncerts < 1) return NULL; if (gnutls_x509_crt_init(&res)) return NULL; if (gnutls_x509_crt_import (res, certs, GNUTLS_X509_FMT_DER)) { gnutls_x509_crt_deinit (res); return NULL; } return res; } void tls_free_cert (tlscert_t cert) { gnutls_x509_crt_deinit (cert); } const char *tls_get_cn (tlscert_t cert) { size_t sz; char *buf; gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, NULL, &sz); if (!sz) return NULL; buf = talloc (sz); if (!buf) return NULL; if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, buf, &sz)) return NULL; return buf; } int tls_compare_certs (const tlscert_t c1, const tlscert_t c2) { char buf1[100], buf2[100]; size_t sz1 = sizeof (buf1), sz2 = sizeof (buf2); if (gnutls_x509_crt_get_key_id (c1, 0, buf1, &sz1)) return -1; if (gnutls_x509_crt_get_key_id (c2, 0, buf2, &sz2)) return 1; if (sz1 != sz2) return sz1 - sz2; return memcmp (buf1, buf2, sz1); } tlskey_t tls_read_key (const char *file) { gnutls_x509_privkey res; gnutls_datum datum; size_t sz; if (!gnutls_inited) { if (gnutls_global_init ()) return NULL; gnutls_inited = 1; } datum.data = read_file (file, &sz); datum.size = sz; if (!datum.data) return NULL; if (gnutls_x509_privkey_init(&res)) return NULL; if (gnutls_x509_privkey_import (res, &datum, GNUTLS_X509_FMT_PEM)) { gnutls_x509_privkey_deinit (res); return NULL; } return res; } void tls_free_key (tlskey_t key) { gnutls_x509_privkey_deinit (key); } const char *tls_error (const tls_t tls, int res) { return gnutls_strerror (res); }