Use new xreadlink interface.
[shishi.git] / src / starttls.c
blob6ac8bff486bfa0eb1132a38b9713d508294f9304
1 /* starttls.c --- Handle extended TCP connections (for TLS).
2 * Copyright (C) 2002, 2003, 2006 Simon Josefsson
4 * This file is part of Shishi.
6 * Shishi is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Shishi is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Shishi; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 /* Note: only use syslog to report errors in this file. */
24 /* Get Shishid stuff. */
25 #include "kdc.h"
27 /* This function will print information about this session's peer
28 * certificate. */
29 static void
30 logcertinfo (gnutls_session session)
32 time_t now = time (NULL);
33 const gnutls_datum *cert_list;
34 unsigned cert_list_size = 0;
35 gnutls_x509_crt cert;
36 size_t i;
37 int rc;
39 cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
40 if (!cert_list)
41 return;
43 rc = gnutls_x509_crt_init (&cert);
44 if (rc < 0)
46 syslog (LOG_ERR, "TLS xci failed (%d): %s", rc, gnutls_strerror (rc));
47 return;
50 if (gnutls_certificate_type_get (session) == GNUTLS_CRT_X509)
51 for (i = 0; i < cert_list_size; i++)
53 time_t expiration_time, activation_time;
54 char *expiration_time_str = NULL, *activation_time_str = NULL;
55 unsigned char *serial = NULL, *serialhex = NULL;
56 char *issuer = NULL, *subject = NULL;
57 size_t seriallen, issuerlen, subjectlen;
58 unsigned char md5fingerprint[16], md5fingerprinthex[3 * 16 + 1];
59 size_t md5fingerprintlen;
60 int algo;
61 unsigned bits;
62 const char *keytype, *validity;
64 rc = gnutls_x509_crt_import (cert, &cert_list[i],
65 GNUTLS_X509_FMT_DER);
66 if (rc < 0)
68 syslog (LOG_ERR, "TLS xci[%d] failed (%d): %s", i,
69 rc, gnutls_strerror (rc));
70 goto cleanup;
73 md5fingerprintlen = sizeof (md5fingerprint);
74 rc = gnutls_fingerprint (GNUTLS_DIG_MD5, &cert_list[i],
75 md5fingerprint, &md5fingerprintlen);
76 if (rc != GNUTLS_E_SUCCESS)
78 syslog (LOG_ERR, "TLS f[%d] failed (%d): %s", i,
79 rc, gnutls_strerror (rc));
80 goto cleanup;
83 for (i = 0; i < md5fingerprintlen; i++)
84 sprintf (&md5fingerprinthex[3 * i], "%.2x:", md5fingerprint[i]);
86 expiration_time = gnutls_x509_crt_get_expiration_time (cert);
87 if (expiration_time == (time_t) - 1)
89 syslog (LOG_ERR, "TLS xcget[%d] failed (%d): %s", i,
90 rc, gnutls_strerror (rc));
91 goto cleanup;
94 activation_time = gnutls_x509_crt_get_activation_time (cert);
95 if (expiration_time == (time_t) - 1)
97 syslog (LOG_ERR, "TLS xcgat[%d] failed (%d): %s", i,
98 rc, gnutls_strerror (rc));
99 goto cleanup;
102 expiration_time_str = xstrdup (ctime (&expiration_time));
103 if (expiration_time_str[strlen (expiration_time_str) - 1] == '\n')
104 expiration_time_str[strlen (expiration_time_str) - 1] = '\0';
106 activation_time_str = xstrdup (ctime (&activation_time));
107 if (activation_time_str[strlen (activation_time_str) - 1] == '\n')
108 activation_time_str[strlen (activation_time_str) - 1] = '\0';
110 rc = gnutls_x509_crt_get_dn (cert, NULL, &subjectlen);
111 if (rc != GNUTLS_E_SUCCESS && rc != GNUTLS_E_SHORT_MEMORY_BUFFER)
113 syslog (LOG_ERR, "TLS xcgd[%d] failed (%d): %s", i,
114 rc, gnutls_strerror (rc));
115 goto cleanup;
117 subject = xmalloc (++subjectlen);
118 rc = gnutls_x509_crt_get_dn (cert, subject, &subjectlen);
119 if (rc != GNUTLS_E_SUCCESS)
121 syslog (LOG_ERR, "TLS xcgd2[%d] failed (%d): %s", i,
122 rc, gnutls_strerror (rc));
123 goto cleanup;
126 rc = gnutls_x509_crt_get_issuer_dn (cert, NULL, &issuerlen);
127 if (rc != GNUTLS_E_SUCCESS && rc != GNUTLS_E_SHORT_MEMORY_BUFFER)
129 syslog (LOG_ERR, "TLS xcgid[%d] failed (%d): %s", i,
130 rc, gnutls_strerror (rc));
131 goto cleanup;
133 issuer = xmalloc (++issuerlen);
134 rc = gnutls_x509_crt_get_issuer_dn (cert, issuer, &issuerlen);
135 if (rc != GNUTLS_E_SUCCESS)
137 syslog (LOG_ERR, "TLS xcgid2[%d] failed (%d): %s", i,
138 rc, gnutls_strerror (rc));
139 goto cleanup;
142 seriallen = 0;
143 rc = gnutls_x509_crt_get_serial (cert, NULL, &seriallen);
144 if (rc != GNUTLS_E_SUCCESS && rc != GNUTLS_E_SHORT_MEMORY_BUFFER)
146 syslog (LOG_ERR, "TLS xcgs[%d] failed (%d): %s", i,
147 rc, gnutls_strerror (rc));
148 goto cleanup;
150 serial = xmalloc (seriallen);
151 rc = gnutls_x509_crt_get_serial (cert, serial, &seriallen);
152 if (rc != GNUTLS_E_SUCCESS)
154 syslog (LOG_ERR, "TLS xcgs2[%d] failed (%d): %s", i,
155 rc, gnutls_strerror (rc));
156 goto cleanup;
159 serialhex = xmalloc (2 * seriallen + 1);
160 for (i = 0; i < seriallen; i++)
161 sprintf (&serialhex[2 * i], "%.2x", serial[i]);
164 algo = gnutls_x509_crt_get_pk_algorithm (cert, &bits);
165 if (algo == GNUTLS_PK_RSA)
166 keytype = "RSA modulus";
167 else if (algo == GNUTLS_PK_DSA)
168 keytype = "DSA exponent";
169 else
170 keytype = "UNKNOWN";
172 if (expiration_time < now)
173 validity = "EXPIRED";
174 else if (activation_time > now)
175 validity = "NOT YET ACTIVATED";
176 else
177 validity = "valid";
179 syslog (LOG_INFO, "TLS client certificate `%s', issued by `%s', "
180 "serial number `%s', MD5 fingerprint `%s', activated `%s', "
181 "expires `%s', version #%d, key %s %d bits, currently %s",
182 subject, issuer, serialhex, md5fingerprinthex,
183 activation_time_str, expiration_time_str,
184 gnutls_x509_crt_get_version (cert), keytype, bits, validity);
186 cleanup:
187 if (serialhex)
188 free (serialhex);
189 if (serial)
190 free (serial);
191 if (expiration_time_str)
192 free (expiration_time_str);
193 if (activation_time_str)
194 free (activation_time_str);
195 if (issuer)
196 free (issuer);
197 if (subject)
198 free (subject);
201 gnutls_x509_crt_deinit (cert);
203 rc = gnutls_certificate_verify_peers (session);
204 if (rc != GNUTLS_E_SUCCESS)
205 syslog (LOG_ERR, "TLS client certificate verify failed (%d): %s",
206 rc, gnutls_strerror (rc));
209 /* This function will log some details of the given session. */
210 static void
211 logtlsinfo (gnutls_session session)
213 gnutls_credentials_type cred;
214 const char *protocol =
215 gnutls_protocol_get_name (gnutls_protocol_get_version (session));
216 gnutls_kx_algorithm kx = gnutls_kx_get (session);
217 const char *keyexchange = gnutls_kx_get_name (kx);
218 const char *certtype =
219 gnutls_certificate_type_get_name (gnutls_certificate_type_get (session));
220 const char *cipher = gnutls_cipher_get_name (gnutls_cipher_get (session));
221 const char *mac = gnutls_mac_get_name (gnutls_mac_get (session));
222 const char *compression =
223 gnutls_compression_get_name (gnutls_compression_get (session));
224 int resumedp = gnutls_session_is_resumed (session);
226 syslog (LOG_INFO, "TLS handshake negotiated protocol `%s', "
227 "key exchange `%s', certficate type `%s', cipher `%s', "
228 "mac `%s', compression `%s', %s",
229 protocol ? protocol : "N/A",
230 keyexchange ? keyexchange : "N/A",
231 certtype ? certtype : "N/A",
232 cipher ? cipher : "N/A",
233 mac ? mac : "N/A", compression ? compression : "N/A",
234 resumedp ? "resumed session" : "session not resumed");
236 cred = gnutls_auth_get_type (session);
237 switch (cred)
239 case GNUTLS_CRD_ANON:
240 syslog (LOG_INFO,
241 "TLS anonymous authentication with %d bit Diffie-Hellman",
242 gnutls_dh_get_prime_bits (session));
243 break;
245 case GNUTLS_CRD_CERTIFICATE:
246 if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS)
247 syslog (LOG_INFO, "TLS certificate authentication with %d bit "
248 "ephemeral Diffie-Hellman",
249 gnutls_dh_get_prime_bits (session));
250 logcertinfo (session);
251 break;
253 default:
254 syslog (LOG_ERR, "Unknown TLS authentication (%d)", cred);
255 break;
259 #define STARTTLS_CLIENT_REQUEST "\x70\x00\x00\x01"
260 #define STARTTLS_SERVER_ACCEPT "\x00\x00\x00\x00"
261 #define STARTTLS_LEN 4
263 /* Handle the high TCP length bit, currently only used for STARTTLS. */
265 kdc_extension (struct listenspec *ls)
267 int rc;
269 if (!ls->usetls && ls->type == SOCK_STREAM && ls->bufpos == 4 &&
270 memcmp (ls->buf, STARTTLS_CLIENT_REQUEST, STARTTLS_LEN) == 0)
272 const int kx_prio[] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS,
273 GNUTLS_KX_DHE_RSA, GNUTLS_KX_ANON_DH, 0
276 syslog (LOG_INFO, "Trying STARTTLS");
278 memcpy (ls->buf, STARTTLS_SERVER_ACCEPT, STARTTLS_LEN);
279 ls->bufpos = STARTTLS_LEN;
281 kdc_send1 (ls);
283 rc = gnutls_init (&ls->session, GNUTLS_SERVER);
284 if (rc != GNUTLS_E_SUCCESS)
286 syslog (LOG_ERR, "TLS initialization failed (%d): %s", rc,
287 gnutls_strerror (rc));
288 return -1;
291 rc = gnutls_set_default_priority (ls->session);
292 if (rc != GNUTLS_E_SUCCESS)
294 syslog (LOG_ERR, "TLS failed, gnutls_sdp %d: %s", rc,
295 gnutls_strerror (rc));
296 return -1;
299 rc = gnutls_kx_set_priority (ls->session, kx_prio);
300 if (rc != GNUTLS_E_SUCCESS)
302 syslog (LOG_ERR, "TLS failed, gnutls_ksp %d: %s", rc,
303 gnutls_strerror (rc));
304 return -1;
307 rc = gnutls_credentials_set (ls->session, GNUTLS_CRD_ANON, anoncred);
308 if (rc != GNUTLS_E_SUCCESS)
310 syslog (LOG_ERR, "TLS failed, gnutls_cs %d: %s", rc,
311 gnutls_strerror (rc));
312 return -1;
315 rc = gnutls_credentials_set (ls->session, GNUTLS_CRD_CERTIFICATE,
316 x509cred);
317 if (rc != GNUTLS_E_SUCCESS)
319 syslog (LOG_ERR, "TLS failed, gnutls_cs X.509 %d: %s", rc,
320 gnutls_strerror (rc));
321 return -1;
324 gnutls_certificate_server_set_request (ls->session,
325 GNUTLS_CERT_REQUEST);
327 gnutls_dh_set_prime_bits (ls->session, DH_BITS);
328 gnutls_transport_set_ptr (ls->session,
329 (gnutls_transport_ptr) ls->sockfd);
331 gnutls_db_set_retrieve_function (ls->session, resume_db_fetch);
332 gnutls_db_set_store_function (ls->session, resume_db_store);
333 gnutls_db_set_remove_function (ls->session, resume_db_delete);
335 rc = gnutls_handshake (ls->session);
336 if (rc < 0)
338 syslog (LOG_ERR, "TLS handshake failed (%d): %s\n",
339 rc, gnutls_strerror (rc));
340 return -1;
343 logtlsinfo (ls->session);
345 ls->bufpos = 0;
346 ls->usetls = 1;
349 return 0;