]> mj.ucw.cz Git - eval.git/blob - submit/submitd.c
TLS handshaking work.
[eval.git] / submit / submitd.c
1 /*
2  *  The Submit Daemon
3  *
4  *  (c) 2007 Martin Mares <mj@ucw.cz>
5  */
6
7 #define LOCAL_DEBUG
8
9 #include "lib/lib.h"
10
11 #include <string.h>
12 #include <unistd.h>
13 #include <sys/socket.h>
14 #include <arpa/inet.h>
15 #include <netinet/in.h>
16 #include <gnutls/gnutls.h>
17 #include <gnutls/x509.h>
18
19 static int port = 8888;
20
21 static gnutls_certificate_credentials_t cert_cred;
22 static gnutls_dh_params_t dh_params;
23
24 #define DH_BITS 1024
25 #define TLS_CHECK(name) if (err < 0) die(#name " failed: %s", gnutls_strerror(err))
26
27 static void
28 tls_init(void)
29 {
30   int err;
31
32   log(L_INFO, "Initializing TLS");
33   gnutls_global_init();
34   err = gnutls_certificate_allocate_credentials(&cert_cred);
35   TLS_CHECK(gnutls_certificate_allocate_credentials);
36   err = gnutls_certificate_set_x509_trust_file(cert_cred, "ca-cert.pem", GNUTLS_X509_FMT_PEM);
37   if (!err)
38     die("No CA certificate found");
39   if (err < 0)
40     die("Unable to load X509 trust file: %s", gnutls_strerror(err));
41   err = gnutls_certificate_set_x509_key_file(cert_cred, "server-cert.pem", "server-key.pem", GNUTLS_X509_FMT_PEM);
42   if (err < 0)
43     die("Unable to load X509 key file: %s", gnutls_strerror(err));
44
45   log(L_INFO, "Setting up DH parameters");
46   err = gnutls_dh_params_init(&dh_params); TLS_CHECK(gnutls_dh_params_init);
47   err = gnutls_dh_params_generate2(dh_params, DH_BITS); TLS_CHECK(gnutls_dh_params_generate2);
48   gnutls_certificate_set_dh_params(cert_cred, dh_params);
49 }
50
51 static gnutls_session_t
52 tls_new_session(int sk)
53 {
54   gnutls_session_t s;
55   int err;
56
57   err = gnutls_init(&s, GNUTLS_SERVER); TLS_CHECK(gnutls_init);
58   err = gnutls_set_default_priority(s); TLS_CHECK(gnutls_set_default_priority);                 // FIXME
59   gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, cert_cred);
60   gnutls_certificate_server_set_request(s, GNUTLS_CERT_REQUEST);
61   gnutls_dh_set_prime_bits(s, DH_BITS);
62   gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t) sk);
63   return s;
64 }
65
66 static const char *
67 tls_verify_cert(gnutls_session_t s)
68 {
69   uns status, num_certs;
70   int err;
71   gnutls_x509_crt_t cert;
72   const gnutls_datum_t *certs;
73
74   DBG("Verifying peer certificates");
75   err = gnutls_certificate_verify_peers2(s, &status);
76   if (err < 0)
77     return gnutls_strerror(err);
78   DBG("Verify status: %04x", status);
79   if (status & GNUTLS_CERT_INVALID)
80     return "Certificate is invalid";
81   /* XXX: We do not handle revokation. */
82   if (gnutls_certificate_type_get(s) != GNUTLS_CRT_X509)
83     return "Certificate is not X509";
84
85   err = gnutls_x509_crt_init(&cert);
86   if (err < 0)
87     return "gnutls_x509_crt_init() failed";
88   certs = gnutls_certificate_get_peers(s, &num_certs);
89   if (!certs)
90     return "No peer certificate found";
91   DBG("Got certificate list with %d peers", num_certs);
92
93   err = gnutls_x509_crt_import(cert, &certs[0], GNUTLS_X509_FMT_DER);
94   if (err < 0)
95     return "Cannot import certificate";
96   /* XXX: We do not check expiration and activation since the keys are generated for a single contest only anyway. */
97
98   byte dn[256];
99   size_t dn_len = sizeof(dn);
100   err = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn, &dn_len);
101   if (err < 0)
102     return "Cannot retrieve common name";
103   log(L_INFO, "Cert CN: %s", dn);
104
105   /* Check certificate purpose */
106   byte purp[256];
107   int purpi = 0;
108   do
109     {
110       size_t purp_len = sizeof(purp);
111       uns crit;
112       err = gnutls_x509_crt_get_key_purpose_oid(cert, purpi++, purp, &purp_len, &crit);
113       if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
114         return "Not a client certificate";
115       TLS_CHECK(gnutls_x509_crt_get_key_purpose_oid);
116     }
117   while (strcmp(purp, GNUTLS_KP_TLS_WWW_CLIENT));
118
119   DBG("Verified OK");
120   return NULL;
121 }
122
123 static void
124 tls_log_params(gnutls_session_t s)
125 {
126   const char *proto = gnutls_protocol_get_name(gnutls_protocol_get_version(s));
127   const char *kx = gnutls_kx_get_name(gnutls_kx_get(s));
128   const char *cert = gnutls_certificate_type_get_name(gnutls_certificate_type_get(s));
129   const char *comp = gnutls_compression_get_name(gnutls_compression_get(s));
130   const char *cipher = gnutls_cipher_get_name(gnutls_cipher_get(s));
131   const char *mac = gnutls_mac_get_name(gnutls_mac_get(s));
132   log(L_DEBUG, "TLS params: proto=%s kx=%s cert=%s comp=%s cipher=%s mac=%s",
133     proto, kx, cert, comp, cipher, mac);
134 }
135
136 int main(int argc UNUSED, char **argv UNUSED)
137 {
138   tls_init();
139
140   int sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
141   if (sk < 0)
142     die("socket: %m");
143   int one = 1;
144   if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
145     die("setsockopt(SO_REUSEADDR): %m");
146
147   struct sockaddr_in sa;
148   bzero(&sa, sizeof(sa));
149   sa.sin_family = AF_INET;
150   sa.sin_addr.s_addr = INADDR_ANY;
151   sa.sin_port = htons(port);
152   if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
153     die("Cannot bind to port %d: %m", port);
154   if (listen(sk, 1024) < 0)
155     die("Cannot listen on port %d: %m", port);
156   log(L_INFO, "Listening on port %d", port);
157
158   for (;;)
159     {
160       struct sockaddr_in sa2;
161       int sa2len = sizeof(sa2);
162       int sk2 = accept(sk, (struct sockaddr *) &sa2, &sa2len);
163       if (sk2 < 0)
164         die("accept: %m");
165
166       byte ipbuf[INET_ADDRSTRLEN];
167       inet_ntop(AF_INET, &sa2.sin_addr, ipbuf, sizeof(ipbuf));
168       log(L_INFO, "Connection from %s port %d", ipbuf, ntohs(sa2.sin_port));
169
170       gnutls_session_t sess = tls_new_session(sk2);
171       int err = gnutls_handshake(sess);
172       if (err < 0)
173         {
174           log(L_ERROR_R, "Handshake failed: %s", gnutls_strerror(err));
175           goto shut;
176         }
177       tls_log_params(sess);
178
179       const char *cert_err = tls_verify_cert(sess);
180       if (cert_err)
181         {
182           log(L_ERROR_R, "Certificate verification failed: %s", cert_err);
183           goto shut;
184         }
185
186       for (;;)
187         {
188           byte buf[1024];
189           int ret = gnutls_record_recv(sess, buf, sizeof(buf));
190           if (ret < 0)
191             {
192               log(L_ERROR_R, "Connection broken: %s", gnutls_strerror(ret));
193               break;
194             }
195           if (!ret)
196             {
197               log(L_INFO, "Client closed connection");
198               break;
199             }
200           log(L_DEBUG, "Received %d bytes", ret);
201           gnutls_record_send(sess, buf, ret);
202         }
203
204       gnutls_bye(sess, GNUTLS_SHUT_WR);
205 shut:
206       close(sk2);
207       gnutls_deinit(sess);
208     }
209
210   return 0;
211 }