Use an SHA-1 fingerprint rather than MD5.
[pwmd.git] / src / tls.c
blobc3e53ffb8ae19632f185c1588d2421b62df53c2f
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2008 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
19 #include <glib.h>
20 #include <glib/gprintf.h>
21 #include <gnutls/x509.h>
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #ifndef MEM_DEBUG
28 #include "mem.h"
29 #endif
31 #include "misc.h"
32 #include "common.h"
33 #include "tls.h"
35 static gchar *tls_fingerprint(gnutls_session_t ses)
37 gnutls_x509_crt_t crt;
38 const gnutls_datum_t *cert_list;
39 guint count;
40 guchar buf[20];
41 gsize len;
43 gnutls_x509_crt_init(&crt);
45 if (!crt) {
46 log_write("%s(%i): %s(): %s", __FILE__, __LINE__, __FUNCTION__,
47 gnutls_strerror(GNUTLS_E_MEMORY_ERROR));
48 return NULL;
51 cert_list = gnutls_certificate_get_peers(ses, &count);
52 gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER);
53 gnutls_x509_crt_get_fingerprint(crt, GNUTLS_MAC_SHA1, buf, &len);
54 gnutls_x509_crt_deinit(crt);
55 return tohex(buf, len);
58 struct tls_s *tls_init(gint fd, const gchar *prio)
60 struct tls_s *tls = g_malloc0(sizeof(struct tls_s));
61 gint ret;
62 guint status;
63 const gchar *prio_error;
64 gnutls_kx_algorithm_t kx;
66 if (!tls) {
67 log_write("%s(%i): %s: %s", __FILE__, __LINE__, __FUNCTION__,
68 strerror(ENOMEM));
69 return NULL;
72 ret = gnutls_init(&tls->ses, GNUTLS_SERVER);
74 if (ret != GNUTLS_E_SUCCESS)
75 goto fail;
77 ret = gnutls_priority_set_direct(tls->ses, prio, &prio_error);
79 if (ret != GNUTLS_E_SUCCESS)
80 goto fail;
82 ret = gnutls_credentials_set(tls->ses, GNUTLS_CRD_CERTIFICATE, x509_cred);
84 if (ret != GNUTLS_E_SUCCESS)
85 goto fail;
87 gnutls_certificate_server_set_request(tls->ses, GNUTLS_CERT_REQUIRE);
88 gnutls_transport_set_ptr(tls->ses, (gnutls_transport_ptr_t)fd);
89 ret = gnutls_handshake(tls->ses);
91 if (ret != GNUTLS_E_SUCCESS)
92 goto fail;
94 ret = gnutls_certificate_verify_peers2(tls->ses, &status);
96 if (ret)
97 goto fail;
99 kx = gnutls_kx_get(tls->ses);
100 tls->fp = tls_fingerprint(tls->ses);
101 log_write("PROTO=%s CIPHER=%s MAC=%s KX=%s(%d) FP=%s",
102 gnutls_protocol_get_name(gnutls_protocol_get_version(tls->ses)),
103 gnutls_cipher_get_name(gnutls_cipher_get(tls->ses)),
104 gnutls_mac_get_name(gnutls_mac_get(tls->ses)),
105 gnutls_kx_get_name(kx),
106 gnutls_dh_get_prime_bits(tls->ses),
107 tls->fp ? tls->fp : "N/A");
108 return tls;
110 fail:
111 log_write("%s", gnutls_strerror(ret));
112 gnutls_deinit(tls->ses);
113 g_free(tls);
114 return NULL;
117 /* From the documentation. */
118 gint tls_get_params(gnutls_session_t ses, gnutls_params_type_t type,
119 gnutls_params_st *st)
121 if (type == GNUTLS_PARAMS_RSA_EXPORT)
122 st->params.rsa_export = rsa_params;
123 else if (type == GNUTLS_PARAMS_DH)
124 st->params.dh = dh_params;
125 else
126 return -1;
128 st->type = type;
129 st->deinit = 0;
130 return 0;
133 void tls_log(gint level, const char *msg)
135 log_write("TLS: %i: %s", level, msg);
138 int read_hook(assuan_context_t ctx, assuan_fd_t fd, void *data,
139 size_t len, ssize_t *ret)
141 struct client_s *cl = assuan_get_pointer(ctx);
143 if (!cl || !cl->thd->remote)
144 *ret = read((int)fd, data, len);
145 else {
146 do {
147 *ret = gnutls_record_recv(cl->thd->tls->ses, data, len);
149 if (*ret == GNUTLS_E_REHANDSHAKE) {
150 *ret = gnutls_rehandshake(cl->thd->tls->ses);
152 if (*ret == GNUTLS_E_WARNING_ALERT_RECEIVED ||
153 *ret == GNUTLS_A_NO_RENEGOTIATION) {
154 log_write("%s", gnutls_strerror(*ret));
155 continue;
158 if (*ret != GNUTLS_E_SUCCESS) {
159 log_write("%s", gnutls_strerror(*ret));
160 *ret = 0;
161 break;
164 *ret = gnutls_handshake(cl->thd->tls->ses);
166 if (*ret != GNUTLS_E_SUCCESS) {
167 log_write("%s", gnutls_strerror(*ret));
168 *ret = 0;
169 break;
172 continue;
174 } while (*ret == GNUTLS_E_INTERRUPTED || *ret == GNUTLS_E_AGAIN);
177 return *ret <= 0 ? 0 : 1;
180 int write_hook(assuan_context_t ctx, assuan_fd_t fd, const void *data,
181 size_t len, ssize_t *ret)
183 struct client_s *cl = assuan_get_pointer(ctx);
185 if (!cl || !cl->thd->remote)
186 *ret = write((int)fd, data, len);
187 else {
188 do {
189 *ret = gnutls_record_send(cl->thd->tls->ses, data, len);
190 } while (*ret == GNUTLS_E_INTERRUPTED || *ret == GNUTLS_E_AGAIN);
193 return *ret <= 0 ? 0 : 1;