/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to server.cpp

  • Committer: Teddy Hogeborn
  • Date: 2019-02-09 23:23:26 UTC
  • Revision ID: teddy@recompile.se-20190209232326-z1z2kzpgfixz7iaj
Add support for using raw public keys in TLS (RFC 7250)

Since GnuTLS removed support for OpenPGP keys in TLS (RFC 6091), and
no other library supports it, we have to change the protocol to use
something else.  We choose to use "raw public keys" (RFC 7250).  Since
we still use OpenPGP keys to decrypt the secret password, this means
that each client will have two keys: One OpenPGP key and one TLS
public/private key, and the key ID of the latter key is used to
identify clients instead of the fingerprint of the OpenPGP key.

Note that this code is still compatible with GnuTLS before version
3.6.0 (when OpenPGP key support was removed).  This commit merely adds
support for using raw pulic keys instead with GnuTLS 3.6.6. or later.

* DBUS-API (Signals/ClientNotFound): Change name of first parameter
                                     from "Fingerprint" to "KeyID".
  (Mandos Client Interface/Properties/KeyID): New.
* INSTALL: Document conflict with GnuTLS 3.6.0 (which removed OpenPGP
           key support) up until 3.6.6, when support for raw public
           keys was added.  Also document new dependency of client on
           "gnutls-bin" package (for certtool).
* Makefile (run-client): Depend on TLS key files, and also pass them
                         as arguments to client.
  (keydir/tls-privkey.pem, keydir/tls-pubkey.pem): New.
  (confdir/clients.conf): Add dependency on TLS public key.
  (purge-client): Add removal of TLS key files.
* clients.conf ([foo]/key_id, [bar]/key_id): New.
* debian/control (Source: mandos/Build-Depends): Also allow
                                                 libgnutls30 (>= 3.6.6)
  (Package: mandos/Depends): - '' -
  (Package: mandos/Description): Alter description to match new
                                 design.
  (Package: mandos-client/Description): - '' -
  (Package: mandos-client/Depends): Move "gnutls-bin | openssl" to
                                    here from "Recommends".
* debian/mandos-client.README.Debian: Add --tls-privkey and
                                      --tls-pubkey options to test
                                      command.
* debian/mandos-client.postinst (create_key): Renamed to "create_keys"
                                             (all callers changed),
                                             and also create TLS key.
* debian/mandos-client.postrm (purge): Also remove TLS key files.
* intro.xml (DESCRIPTION): Describe new dual-key design.
* mandos (GnuTLS): Define different functions depending on whether
                   support for raw public keys is detected.
  (Client.key_id): New attribute.
  (ClientDBus.KeyID_dbus_property): New method.
  (ProxyClient.__init__): Take new "key_id" parameter.
  (ClientHandler.handle): Use key IDs when using raw public keys and
                          use fingerprints when using OpenPGP keys.
  (ClientHandler.peer_certificate): Also handle raw public keys.
  (ClientHandler.key_id): New.
  (MandosServer.handle_ipc): Pass key ID over the pipe IPC.  Also
                             check for key ID matches when looking up
                             clients.
  (main): Default GnuTLS priority string depends on whether we are
          using raw public keys or not.  When unpickling clients, set
          key_id if not set in the pickle.
  (main/MandosDBusService.ClientNotFound): Change name of first
                                           parameter from
                                           "Fingerprint" to "KeyID".
* mandos-clients.conf.xml (OPTIONS): Document new "key_id" option.
  (OPTIONS/secret): Mention new key ID matchning.
  (EXPANSION/RUNTIME EXPANSION): Add new "key_id" option.
  (EXAMPLE): - '' -
* mandos-ctl (tablewords, main/keywords): Add new "KeyID" property.
* mandos-keygen: Create TLS key files.  New "--tls-keytype" (-T)
                 option.  Alter help text to be more clear about key
                 types.  When in password mode, also output "key_id"
                 option.
* mandos-keygen.xml (SYNOPSIS): Add new "--tls-keytype" (-T) option.
  (DESCRIPTION): Alter to match new dual-key design.
  (OVERVIEW): - '' -
  (FILES): Add TLS key files.
* mandos-options.xml (priority): Document new default priority string
                                 when using raw public keys.
* mandos.xml (NETWORK PROTOCOL): Describe new protocol using key ID.
  (BUGS): Remove issue about checking expire times of OpenPGP keys,
          since TLS public keys do not have expiration times.
  (SECURITY/CLIENT): Alter description to match new design.
  (SEE ALSO/GnuTLS): - '' -
  (SEE ALSO): Add reference to RFC 7250, and alter description of when
              RFC 6091 is used.
* overview.xml: Alter text to match new design.
* plugin-runner.xml (EXAMPLE): Add --tls-pubkey and --tls-privkey
                               options to mandos-client options.
* plugins.d/mandos-client.c: Use raw public keys when compiling with
                             supporting GnuTLS versions. Add new
                             "--tls-pubkey" and "--tls-privkey"
                             options (which do nothing if GnuTLS
                             library does not support raw public
                             keys).  Alter text throughout to reflect
                             new design.  Only generate new DH
                             parameters (based on size of OpenPGP key)
                             when using OpenPGP in TLS.  Default
                             GnuTLS priority string depends on whether
                             we are using raw public keys or not.
* plugins.d/mandos-client.xml (SYNOPSIS): Add new "--tls-privkey" (-t)
                                          and "--tls-pubkey" (-T)
                                          options.
  (DESCRIPTION): Describe new dual-key design.
  (OPTIONS): Document new "--tls-privkey" (-t) and "--tls-pubkey" (-T)
             options.
  (OPTIONS/--dh-bits): No longer necessarily depends on OpenPGP key
                       size.
  (FILES): Add default locations for TLS public and private key files.
  (EXAMPLE): Use new --tls-pubkey and --tls-privkey options.
  (SECURITY): Alter wording slightly to reflect new dual-key design.
  (SEE ALSO/GnuTLS): Alter description to match new design.
  (SEE ALSO): Add reference to RFC 7250, and alter description of when
              RFC 6091 is used.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
extern "C" {
2
 
#include <sys/types.h> //socket, setsockopt, bind, listen, accept,
3
 
  // inet_ntop,
4
 
#include <sys/socket.h> //socket, setsockopt, bind, listen, accept,
5
 
  // inet_ntop
6
 
#include <sys/ioctl.h> //ioctl, sockaddr_ll, ifreq
7
 
#include <unistd.h> //write, close
8
 
#include <netinet/ip.h>         // sockaddr_in
9
 
#include <gnutls/gnutls.h>
10
 
#include <gnutls/x509.h>        // gnutls_x509_crt_init, gnutls_x509_crt_import, gnutls_x509_crt_get_dn
11
 
#include <arpa/inet.h>          // inet_ntop, htons
12
 
#include <net/if.h> //ifreq
13
 
}
14
 
 
15
 
#include <cstdio>
16
 
#include <cstring>
17
 
#include <cerrno>
18
 
#include <algorithm>            // std::max
19
 
#include <cstdlib>              // exit()
20
 
#include <fstream>              // std::ifstream
21
 
#include <string>               // std::string
22
 
#include <map>                  // std::map
23
 
#include <iostream>             // cout
24
 
#include <ostream>              // <<
25
 
 
26
 
#define SOCKET_ERR(err,s) if(err<0) {perror(s);exit(1);}
27
 
 
28
 
#define PORT 49001
29
 
#define KEYFILE "key.pem"
30
 
#define CERTFILE "cert.pem"
31
 
#define CAFILE "ca.pem"
32
 
#define CRLFILE "crl.pem"
33
 
#define DH_BITS 1024
34
 
 
35
 
using std::string;
36
 
using std::ifstream;
37
 
using std::map;
38
 
using std::cout;
39
 
 
40
 
/* These are global */
41
 
gnutls_certificate_credentials_t x509_cred;
42
 
map<string,string> table;
43
 
 
44
 
static gnutls_dh_params_t dh_params;
45
 
 
46
 
static int
47
 
generate_dh_params ()
48
 
{
49
 
 
50
 
  /* Generate Diffie Hellman parameters - for use with DHE
51
 
   * kx algorithms. These should be discarded and regenerated
52
 
   * once a day, once a week or once a month. Depending on the
53
 
   * security requirements.
54
 
   */
55
 
  gnutls_dh_params_init (&dh_params);
56
 
  gnutls_dh_params_generate2 (dh_params, DH_BITS);
57
 
 
58
 
  return 0;
59
 
}
60
 
 
61
 
gnutls_session_t
62
 
initialize_tls_session ()
63
 
{
64
 
  gnutls_session_t session;
65
 
 
66
 
  gnutls_global_init ();
67
 
 
68
 
  gnutls_certificate_allocate_credentials (&x509_cred);
69
 
  gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE,
70
 
                                          GNUTLS_X509_FMT_PEM);
71
 
  gnutls_certificate_set_x509_crl_file (x509_cred, CRLFILE,
72
 
                                        GNUTLS_X509_FMT_PEM);
73
 
  gnutls_certificate_set_x509_key_file (x509_cred, CERTFILE, KEYFILE,
74
 
                                        GNUTLS_X509_FMT_PEM);
75
 
 
76
 
  generate_dh_params ();
77
 
  gnutls_certificate_set_dh_params (x509_cred, dh_params);
78
 
 
79
 
  gnutls_init (&session, GNUTLS_SERVER);
80
 
  gnutls_set_default_priority (session);
81
 
  gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
82
 
 
83
 
  // request client certificate if any.
84
 
 
85
 
  gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
86
 
  gnutls_dh_set_prime_bits (session, DH_BITS);
87
 
 
88
 
  return session;
89
 
}
90
 
 
91
 
 
92
 
void udpreply(int &sd){
93
 
  struct sockaddr_in6 sa_cli;
94
 
  int ret;
95
 
  char buffer[512];
96
 
 
97
 
  {
98
 
    socklen_t sa_cli_len = sizeof(sa_cli);
99
 
    ret = recvfrom(sd, buffer, 512,0,
100
 
                   reinterpret_cast<sockaddr *>(& sa_cli), & sa_cli_len);
101
 
    SOCKET_ERR (ret, "recvfrom");
102
 
  }
103
 
 
104
 
  if (strncmp(buffer,"Marco", 5) == 0){
105
 
    ret = sendto(sd, "Polo", 4, 0, reinterpret_cast<sockaddr *>(& sa_cli),
106
 
                 sizeof(sa_cli));
107
 
    SOCKET_ERR (ret, "sendto");
108
 
  }
109
 
 
110
 
}
111
 
 
112
 
void tcpreply(int sd, struct sockaddr_in6 *sa_cli, gnutls_session_t session){
113
 
 
114
 
  int ret;
115
 
  unsigned int status;
116
 
  char buffer[512];
117
 
  int exit_status = 0;
118
 
  char dn[128];
119
 
 
120
 
#define DIE(s){ exit_status = s; goto tcpreply_die; }
121
 
 
122
 
  printf ("- TCP connection from %s, port %d\n",
123
 
          inet_ntop (AF_INET6, &(sa_cli->sin6_addr), buffer,
124
 
                     sizeof (buffer)), ntohs (sa_cli->sin6_port));
125
 
 
126
 
  
127
 
  gnutls_transport_set_ptr (session, reinterpret_cast<gnutls_transport_ptr_t> (sd));
128
 
  
129
 
 
130
 
  ret = gnutls_handshake (session);
131
 
  if (ret < 0)
132
 
    {
133
 
      close (sd);
134
 
      gnutls_deinit (session);
135
 
      fprintf (stderr, "*** Handshake has failed (%s)\n\n",
136
 
               gnutls_strerror (ret));
137
 
      DIE(1);
138
 
    }
139
 
  printf ("- Handshake was completed\n");
140
 
 
141
 
  //time to validate
142
 
 
143
 
  if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509){
144
 
    printf("Recived certificate not X.509\n");
145
 
    DIE(1);
146
 
  }
147
 
  {
148
 
    const gnutls_datum_t *cert_list;
149
 
    unsigned int cert_list_size = 0;
150
 
    gnutls_x509_crt_t cert;
151
 
    size_t size;
152
 
    
153
 
    cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
154
 
    
155
 
    printf ("Peer provided %d certificates.\n", cert_list_size);
156
 
    
157
 
    if (cert_list_size == 0){
158
 
      printf("No certificates recived\n");
159
 
      DIE(1);
160
 
    }
161
 
    
162
 
    gnutls_x509_crt_init (&cert);
163
 
    
164
 
    // XXX -Checking only first cert, might want to check them all
165
 
    gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER);
166
 
    
167
 
    size = sizeof (dn);
168
 
    gnutls_x509_crt_get_dn (cert, dn, &size);
169
 
    
170
 
    printf ("DN: %s\n", dn);
171
 
  }
172
 
 
173
 
  ret = gnutls_certificate_verify_peers2 (session, &status);
174
 
 
175
 
  if (ret < 0){
176
 
      printf ("Verify failed\n");
177
 
      DIE(1);
178
 
  }
179
 
  
180
 
  if (status & (GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_REVOKED)) {
181
 
    if (status & GNUTLS_CERT_INVALID) {
182
 
      printf ("The certificate is not trusted.\n");
183
 
    }
184
 
    
185
 
    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND){
186
 
      printf ("The certificate hasn't got a known issuer.\n");
187
 
    }
188
 
    
189
 
    if (status & GNUTLS_CERT_REVOKED){
190
 
      printf ("The certificate has been revoked.\n");
191
 
    }
192
 
    DIE(1);
193
 
  }  
194
 
 
195
 
  if (table.find(dn) != table.end()){
196
 
    gnutls_record_send (session, table[dn].c_str(), table[dn].size());
197
 
    printf("Password sent to client\n");
198
 
  }
199
 
  else {
200
 
    printf("dn not in list of allowed clients\n");
201
 
  }
202
 
 
203
 
  
204
 
 tcpreply_die:
205
 
  gnutls_bye (session, GNUTLS_SHUT_WR);
206
 
  close(sd);
207
 
  gnutls_deinit (session);
208
 
  gnutls_certificate_free_credentials (x509_cred);
209
 
  gnutls_global_deinit ();
210
 
  exit(exit_status);
211
 
}
212
 
 
213
 
 
214
 
void badconfigparser(string file){
215
 
 
216
 
  string dn;
217
 
  string pw;
218
 
  string pwfile;
219
 
  ifstream infile (file.c_str());
220
 
 
221
 
  while(infile){
222
 
    getline(infile, dn, '\n');
223
 
    if(not infile){
224
 
      break;
225
 
    }
226
 
    getline(infile, pw, '\n');
227
 
    if(not infile){
228
 
      break;
229
 
    }
230
 
    getline(infile, pwfile, '\n');
231
 
    if(not infile){
232
 
      break;
233
 
    }
234
 
    if(pw.empty()){
235
 
      ifstream pwf(pwfile.c_str());
236
 
      std::string tmp;
237
 
 
238
 
      while(true){
239
 
        getline(pwf,tmp);
240
 
        if (not pwf){
241
 
          break;
242
 
        }
243
 
        pw = pw + tmp + '\n';
244
 
      }
245
 
      
246
 
    }
247
 
    table[dn]=pw;
248
 
  }
249
 
  infile.close();
250
 
}
251
 
      
252
 
 
253
 
 
254
 
int main (){
255
 
  int ret, err, udp_listen_sd, tcp_listen_sd;
256
 
  struct sockaddr_in6 sa_serv;
257
 
  struct sockaddr_in6 sa_cli;
258
 
 
259
 
  int optval = 1;
260
 
  socklen_t client_len;
261
 
 
262
 
  gnutls_session_t session;
263
 
 
264
 
  fd_set rfds_orig;
265
 
 
266
 
  badconfigparser(string("clients.conf"));
267
 
 
268
 
  session = initialize_tls_session ();
269
 
 
270
 
  //UDP IPv6 socket creation
271
 
  udp_listen_sd = socket (PF_INET6, SOCK_DGRAM, 0);
272
 
  SOCKET_ERR (udp_listen_sd, "socket");
273
 
 
274
 
  memset (&sa_serv, '\0', sizeof (sa_serv));
275
 
  sa_serv.sin6_family = AF_INET6;
276
 
  sa_serv.sin6_addr = in6addr_any; //XXX only listen to link local?
277
 
  sa_serv.sin6_port = htons (PORT);     /* Server Port number */
278
 
 
279
 
  ret = setsockopt (udp_listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (optval));
280
 
  SOCKET_ERR(ret,"setsockopt reuseaddr");
281
 
 
282
 
  ret = setsockopt(udp_listen_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5);
283
 
  SOCKET_ERR(ret,"setsockopt bindtodevice");
284
 
 
285
 
  {
286
 
    int flag = 1;
287
 
    ret = setsockopt(udp_listen_sd, SOL_SOCKET, SO_BROADCAST, & flag, sizeof(flag));
288
 
    SOCKET_ERR(ret,"setsockopt broadcast");
289
 
  }
290
 
 
291
 
  err = bind (udp_listen_sd, reinterpret_cast<const sockaddr *> (& sa_serv),
292
 
              sizeof (sa_serv));
293
 
  SOCKET_ERR (err, "bind");
294
 
 
295
 
  //UDP socket creation done
296
 
 
297
 
 
298
 
  //TCP IPv6 socket creation
299
 
 
300
 
  tcp_listen_sd = socket(PF_INET6, SOCK_STREAM, 0);
301
 
  SOCKET_ERR(tcp_listen_sd,"socket");
302
 
 
303
 
  setsockopt(tcp_listen_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5);
304
 
  SOCKET_ERR(ret,"setsockopt bindtodevice");
305
 
  
306
 
  ret = setsockopt (tcp_listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (optval));
307
 
  SOCKET_ERR(ret,"setsockopt reuseaddr");
308
 
 
309
 
  err = bind (tcp_listen_sd, reinterpret_cast<const sockaddr *> (& sa_serv),
310
 
              sizeof (sa_serv));
311
 
  SOCKET_ERR (err, "bind");
312
 
 
313
 
  err = listen (tcp_listen_sd, 1024);
314
 
  SOCKET_ERR (err, "listen");
315
 
 
316
 
  //TCP IPv6 sockets creation done
317
 
 
318
 
  FD_ZERO(&rfds_orig);
319
 
  FD_SET(udp_listen_sd, &rfds_orig);
320
 
  FD_SET(tcp_listen_sd, &rfds_orig);
321
 
 
322
 
  printf ("Server ready. Listening to port '%d' on UDP and TCP.\n\n", PORT);
323
 
 
324
 
  for(;;){
325
 
    fd_set rfds = rfds_orig;
326
 
 
327
 
    ret = select(std::max(udp_listen_sd, tcp_listen_sd)+1, &rfds, 0, 0, 0);
328
 
    SOCKET_ERR(ret,"select");
329
 
 
330
 
    if (FD_ISSET(udp_listen_sd, &rfds)){
331
 
      udpreply(udp_listen_sd);
332
 
    }
333
 
 
334
 
    if (FD_ISSET(tcp_listen_sd, &rfds)){
335
 
      client_len = sizeof(sa_cli);
336
 
      int sd = accept (tcp_listen_sd,
337
 
               reinterpret_cast<struct sockaddr *> (& sa_cli),
338
 
               &client_len);
339
 
      SOCKET_ERR(sd,"accept"); //xxx not dieing when just connection abort      
340
 
      switch(fork()){
341
 
        case 0:
342
 
          tcpreply(sd, &sa_cli, session);
343
 
          return 0;
344
 
          break;
345
 
      case -1:
346
 
        perror("fork");
347
 
        close(tcp_listen_sd);
348
 
        close(udp_listen_sd);
349
 
        return 1;
350
 
        break;
351
 
      default:
352
 
        break;
353
 
      }
354
 
    }
355
 
  }
356
 
  
357
 
  close(tcp_listen_sd);
358
 
  close(udp_listen_sd);
359
 
  return 0;
360
 
 
361
 
}