/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 plugin-helpers/mandos-client-iprouteadddel.c

  • 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
/*  -*- coding: utf-8 -*- */
 
2
/* 
 
3
 * iprouteadddel - Add or delete direct route to a local IP address
 
4
 * 
 
5
 * Copyright © 2015-2018 Teddy Hogeborn
 
6
 * Copyright © 2015-2018 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
 
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
 * General Public License for more details.
 
19
 * 
 
20
 * You should have received a copy of the GNU General Public License
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
22
 * 
 
23
 * Contact the authors at <mandos@recompile.se>.
 
24
 */
 
25
 
 
26
#define _GNU_SOURCE             /* program_invocation_short_name */
 
27
#include <stdbool.h>            /* bool, false, true */
 
28
#include <stdio.h>              /* fprintf(), stderr, FILE, vfprintf */
 
29
#include <errno.h>              /* program_invocation_short_name,
 
30
                                   errno, perror(), EINVAL, ENOMEM */
 
31
#include <stdarg.h>             /* va_list, va_start */
 
32
#include <stdlib.h>             /* EXIT_SUCCESS */
 
33
#include <argp.h>               /* struct argp_option, error_t, struct
 
34
                                   argp_state, ARGP_KEY_ARG,
 
35
                                   argp_usage(), ARGP_KEY_END,
 
36
                                   ARGP_ERR_UNKNOWN, struct argp,
 
37
                                   argp_parse() */
 
38
#include <sysexits.h>           /* EX_USAGE, EX_OSERR */
 
39
#include <netinet/ip.h>         /* sa_family_t, AF_INET6, AF_INET */
 
40
#include <inttypes.h>           /* PRIdMAX, intmax_t */
 
41
 
 
42
#include <netlink/netlink.h>    /* struct nl_addr, nl_addr_parse(),
 
43
                                   nl_geterror(),
 
44
                                   nl_addr_get_family(),
 
45
                                   nl_addr_put() */
 
46
#include <netlink/route/route.h> /* struct rtnl_route,
 
47
                                    struct rtnl_nexthop,
 
48
                                    rtnl_route_alloc(),
 
49
                                    rtnl_route_set_family(),
 
50
                                    rtnl_route_set_protocol(),
 
51
                                    RTPROT_BOOT,
 
52
                                    rtnl_route_set_scope(),
 
53
                                    RT_SCOPE_LINK,
 
54
                                    rtnl_route_set_type(),
 
55
                                    RTN_UNICAST,
 
56
                                    rtnl_route_set_dst(),
 
57
                                    rtnl_route_set_table(),
 
58
                                    RT_TABLE_MAIN,
 
59
                                    rtnl_route_nh_alloc(),
 
60
                                    rtnl_route_nh_set_ifindex(),
 
61
                                    rtnl_route_add_nexthop(),
 
62
                                    rtnl_route_add(),
 
63
                                    rtnl_route_delete(),
 
64
                                    rtnl_route_put(),
 
65
                                    rtnl_route_nh_free() */
 
66
#include <netlink/socket.h>     /* struct nl_sock, nl_socket_alloc(),
 
67
                                   nl_connect(), nl_socket_free() */
 
68
#include <netlink/route/link.h> /* rtnl_link_get_kernel(),
 
69
                                   rtnl_link_get_ifindex(),
 
70
                                   rtnl_link_put() */
 
71
 
 
72
bool debug = false;
 
73
const char *argp_program_version = "mandos-client-iprouteadddel " VERSION;
 
74
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
75
 
 
76
/* Function to use when printing errors */
 
77
void perror_plus(const char *print_text){
 
78
  int e = errno;
 
79
  fprintf(stderr, "Mandos plugin helper %s: ",
 
80
          program_invocation_short_name);
 
81
  errno = e;
 
82
  perror(print_text);
 
83
}
 
84
 
 
85
__attribute__((format (gnu_printf, 2, 3), nonnull))
 
86
int fprintf_plus(FILE *stream, const char *format, ...){
 
87
  va_list ap;
 
88
  va_start (ap, format);
 
89
  
 
90
  fprintf(stream, "Mandos plugin helper %s: ",
 
91
          program_invocation_short_name);
 
92
  return vfprintf(stream, format, ap);
 
93
}
 
94
 
 
95
int main(int argc, char *argv[]){
 
96
  int ret;
 
97
  int exitcode = EXIT_SUCCESS;
 
98
  struct arguments {
 
99
    bool add;                   /* true: add, false: delete */
 
100
    char *address;              /* IP address as string */
 
101
    struct nl_addr *nl_addr;    /* Netlink IP address */
 
102
    char *interface;            /* interface name */
 
103
  } arguments = { .add = true, .address = NULL, .interface = NULL };
 
104
  struct argp_option options[] = {
 
105
    { .name = "debug", .key = 128,
 
106
      .doc = "Debug mode" },
 
107
    { .name = NULL }
 
108
  };
 
109
  struct rtnl_route *route = NULL;
 
110
  struct rtnl_nexthop *nexthop = NULL;
 
111
  struct nl_sock *sk = NULL;
 
112
  
 
113
  error_t parse_opt(int key, char *arg, struct argp_state *state){
 
114
    int lret;
 
115
    errno = 0;
 
116
    switch(key){
 
117
    case 128:                   /* --debug */
 
118
      debug = true;
 
119
      break;
 
120
    case ARGP_KEY_ARG:
 
121
      switch(state->arg_num){
 
122
      case 0:
 
123
        if(strcasecmp(arg, "add") == 0){
 
124
          ((struct arguments *)(state->input))->add = true;
 
125
        } else if(strcasecmp(arg, "delete") == 0){
 
126
          ((struct arguments *)(state->input))->add = false;
 
127
        } else {
 
128
          fprintf_plus(stderr, "Unrecognized command: %s\n", arg);
 
129
          argp_usage(state);
 
130
        }
 
131
        break;
 
132
      case 1:
 
133
        ((struct arguments *)(state->input))->address = arg;
 
134
        lret = nl_addr_parse(arg, AF_UNSPEC, &(((struct arguments *)
 
135
                                                (state->input))
 
136
                                               ->nl_addr));
 
137
        if(lret != 0){
 
138
          fprintf_plus(stderr, "Failed to parse address %s: %s\n",
 
139
                       arg, nl_geterror(lret));
 
140
          argp_usage(state);
 
141
        }
 
142
        break;
 
143
      case 2:
 
144
        ((struct arguments *)(state->input))->interface = arg;
 
145
        break;
 
146
      default:
 
147
        argp_usage(state);
 
148
      }
 
149
      break;
 
150
    case ARGP_KEY_END:
 
151
      if(state->arg_num < 3){
 
152
        argp_usage(state);
 
153
      }
 
154
      break;
 
155
    default:
 
156
      return ARGP_ERR_UNKNOWN;
 
157
    }
 
158
    return errno;
 
159
  }
 
160
  
 
161
  struct argp argp = { .options = options, .parser = parse_opt,
 
162
                       .args_doc = "[ add | delete ] ADDRESS INTERFACE",
 
163
                       .doc = "Mandos client helper -- Add or delete"
 
164
                       " local route to IP address on interface" };
 
165
  
 
166
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &arguments);
 
167
  switch(ret){
 
168
  case 0:
 
169
    break;
 
170
  case EINVAL:
 
171
    exit(EX_USAGE);
 
172
  case ENOMEM:
 
173
  default:
 
174
    errno = ret;
 
175
    perror_plus("argp_parse");
 
176
    exitcode = EX_OSERR;
 
177
    goto end;
 
178
  }
 
179
  /* Get netlink socket */
 
180
  sk = nl_socket_alloc();
 
181
  if(sk == NULL){
 
182
    fprintf_plus(stderr, "Failed to allocate netlink socket: %s\n",
 
183
                 nl_geterror(ret));
 
184
    exitcode = EX_OSERR;
 
185
    goto end;
 
186
  }
 
187
  /* Connect socket to netlink */
 
188
  ret = nl_connect(sk, NETLINK_ROUTE);
 
189
  if(ret < 0){
 
190
    fprintf_plus(stderr, "Failed to connect socket to netlink: %s\n",
 
191
                 nl_geterror(ret));
 
192
    exitcode = EX_OSERR;
 
193
    goto end;
 
194
  }
 
195
  /* Get link object of specified interface */
 
196
  struct rtnl_link *link = NULL;
 
197
  ret = rtnl_link_get_kernel(sk, 0, arguments.interface, &link);
 
198
  if(ret < 0){
 
199
    fprintf_plus(stderr, "Failed to use interface %s: %s\n",
 
200
                 arguments.interface, nl_geterror(ret));
 
201
    exitcode = EX_OSERR;
 
202
    goto end;
 
203
  }
 
204
  /* Get netlink route object */
 
205
  route = rtnl_route_alloc();
 
206
  if(route == NULL){
 
207
    fprintf_plus(stderr, "Failed to get netlink route:\n");
 
208
    exitcode = EX_OSERR;
 
209
    goto end;
 
210
  }
 
211
  /* Get address family of specified address */
 
212
  sa_family_t af = (sa_family_t)nl_addr_get_family(arguments.nl_addr);
 
213
  if(debug){
 
214
    fprintf_plus(stderr, "Address family of %s is %s (%" PRIdMAX
 
215
                 ")\n", arguments.address,
 
216
                 af == AF_INET6 ? "AF_INET6" :
 
217
                 ( af == AF_INET ? "AF_INET" : "UNKNOWN"),
 
218
                 (intmax_t)af);
 
219
  }
 
220
  /* Set route parameters: */
 
221
  rtnl_route_set_family(route, (uint8_t)af);   /* Address family */
 
222
  rtnl_route_set_protocol(route, RTPROT_BOOT); /* protocol - see
 
223
                                                  ip-route(8) */
 
224
  rtnl_route_set_scope(route, RT_SCOPE_LINK); /* link scope */
 
225
  rtnl_route_set_type(route, RTN_UNICAST);    /* normal unicast
 
226
                                                 address route */
 
227
  rtnl_route_set_dst(route, arguments.nl_addr); /* Destination
 
228
                                                   address */
 
229
  rtnl_route_set_table(route, RT_TABLE_MAIN); /* "main" routing
 
230
                                                 table */
 
231
  /* Create nexthop */
 
232
  nexthop = rtnl_route_nh_alloc();
 
233
  if(nexthop == NULL){
 
234
    fprintf_plus(stderr, "Failed to get netlink route nexthop\n");
 
235
    exitcode = EX_OSERR;
 
236
    goto end;
 
237
  }
 
238
  /* Get index number of specified interface */
 
239
  int ifindex = rtnl_link_get_ifindex(link);
 
240
  if(debug){
 
241
    fprintf_plus(stderr, "ifindex of %s is %d\n", arguments.interface,
 
242
                 ifindex);
 
243
  }
 
244
  /* Set interface index number on nexthop object */
 
245
  rtnl_route_nh_set_ifindex(nexthop, ifindex);
 
246
  /* Set route to use nexthop object */
 
247
  rtnl_route_add_nexthop(route, nexthop);
 
248
  /* Add or delete route? */
 
249
  if(arguments.add){
 
250
    ret = rtnl_route_add(sk, route, NLM_F_EXCL);
 
251
  } else {
 
252
    ret = rtnl_route_delete(sk, route, 0);
 
253
  }
 
254
  if(ret < 0){
 
255
     fprintf_plus(stderr, "Failed to %s route: %s\n",
 
256
                  arguments.add ? "add" : "delete",
 
257
                  nl_geterror(ret));
 
258
    exitcode = EX_OSERR;
 
259
    goto end;
 
260
  }
 
261
 end:
 
262
  /* Deallocate route */
 
263
  if(route){
 
264
    rtnl_route_put(route);
 
265
  } else if(nexthop) {
 
266
    /* Deallocate route nexthop */
 
267
    rtnl_route_nh_free(nexthop);
 
268
  }
 
269
  /* Deallocate parsed address */
 
270
  if(arguments.nl_addr){
 
271
    nl_addr_put(arguments.nl_addr);
 
272
  }
 
273
  /* Deallocate link struct */
 
274
  if(link){
 
275
    rtnl_link_put(link);
 
276
  }
 
277
  /* Deallocate netlink socket struct */
 
278
   if(sk){
 
279
    nl_socket_free(sk);
 
280
  }
 
281
  return exitcode;
 
282
}