Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <stdio.h>
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <string.h>
#include <stdlib.h>
#include "openvpn-plugin.h"
#include "smart_card_handler.c"
#define UNUSED(x) (void)(x)
static char *MODULE = "OPENVPN_PLUGIN_CLIENT_KEY_WRAPPING";
/*
* Constants indicating minimum API and struct versions by the functions
* in this plugin. Consult openvpn-plugin.h, look for:
* OPENVPN_PLUGIN_VERSION and OPENVPN_PLUGINv3_STRUCTVER
*
* Strictly speaking, this sample code only requires plugin_log, a feature
* of structver version 1. However, '1' lines up with ancient versions
* of openvpn that are past end-of-support. As such, we are requiring
* structver '5' here to indicate a desire for modern openvpn, rather
* than a need for any particular feature found in structver beyond '1'.
*/
#define OPENVPN_PLUGIN_VERSION_MIN 3
#define OPENVPN_PLUGIN_STRUCTVER_MIN 5
#define TLS_CRYPT_V2_MAX_WKC_LEN 1024
#define TLS_CRYPT_V2_KEY_LEN 32
#define TLS_CRYPT_V2_TAG_LEN 32
#define TLS_CRYPT_V2_LEN_LEN 2
#define EXTENDED_APDU_SIZE_WITHOUT_DATA 9
#define APDU_LEN_WITHOUT_DATA 5
#define AES_KEY 0x01
#define HMAC_KEY 0x02
#define DO_WRAP 0x01
#define DO_UNWRAP 0x02
#define CLA 0xA0
#define CLA_OFF 0x00
#define INS_OFF 0x01
#define P1_OFF 0x02
#define P2_OFF 0x03
#define LC_OFF 0x04
/* Error handling */
#define ERROR_CHECK(cond, text) do { if (cond) { plog(PLOG_ERR, text); return EXIT_FAILURE; } } while(0)
#define RESPONSE_CHECK(response, text) ERROR_CHECK(response.buffer[response.length - 2] != 0x90 || response.buffer[response.length - 1] != 0x00, text)
/* Exported plug-in v3 API functions */
plugin_vlog_t plugin_vlog_func = NULL;
plugin_base64_decode_t ovpn_base64_decode = NULL;
plugin_base64_encode_t ovpn_base64_encode = NULL;
/*
* Our context, where we keep our state.
*/
struct plugin_context {
struct CardConnectionContext *card_ctx;
};
/* local wrapping of the log function, to add more details */
static void plog(int flags, char *fmt, ...)
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
{
char logid[129];
snprintf(logid, 128, "%s", MODULE);
va_list arglist;
va_start(arglist, fmt);
plugin_vlog_func(flags, logid, fmt, arglist);
va_end(arglist);
}
/* Require a minimum OpenVPN Plugin API */
OPENVPN_EXPORT int
openvpn_plugin_min_version_required_v1()
{
return OPENVPN_PLUGIN_VERSION_MIN;
}
OPENVPN_EXPORT int
openvpn_plugin_select_initialization_point_v1()
{
return OPENVPN_PLUGIN_INIT_PRE_CONFIG_PARSE;
}
/* use v3 functions, so we can use openvpn's logging and base64 etc. */
OPENVPN_EXPORT int
openvpn_plugin_open_v3(const int v3structver,
struct openvpn_plugin_args_open_in const *args,
struct openvpn_plugin_args_open_return *ret)
{
if (v3structver < OPENVPN_PLUGIN_STRUCTVER_MIN)
{
fprintf(stderr, "%s: this plugin is incompatible with the running version of OpenVPN\n", MODULE);
return OPENVPN_PLUGIN_FUNC_ERROR;
}
/* Save global pointers to functions exported from openvpn */
plugin_vlog_func = args->callbacks->plugin_vlog;
ovpn_base64_decode = args->callbacks->plugin_base64_decode;
ovpn_base64_encode = args->callbacks->plugin_base64_encode;
/*
* Allocate our context
*/
struct plugin_context *context = NULL;
context = (struct plugin_context *) calloc(1, sizeof(struct plugin_context));
if (!context)
{
goto error;
}
/*
* Connect to smartcard
*/
context->card_ctx = malloc(sizeof(SCARDCONTEXT));
LONG rv = connectToCard(context->card_ctx);
if (rv != EXIT_SUCCESS)
{
goto error;
}
rv = selectApp(context->card_ctx);
if (rv != EXIT_SUCCESS)
{
goto error;
}
/*
* Which callbacks to intercept.
*/
ret->type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_KEY_WRAPPING);
ret->handle = (openvpn_plugin_handle_t *) context;
return OPENVPN_PLUGIN_FUNC_SUCCESS;
error:
plog(PLOG_NOTE, "initialization failed");
if (context)
{
free(context);
}
return OPENVPN_PLUGIN_FUNC_ERROR;
}
static long
trySendAPDU(struct CardConnectionContext *card_ctx, struct Request *sendBuffer, struct Request *receiveBuffer)
{
LONG rv = sendAPDU(card_ctx, sendBuffer, receiveBuffer);
plog(PLOG_WARN, "Sending APDU failed. Trying to reconnect to card.");
disconnectFromCard(card_ctx);
rv = connectToCard(card_ctx);
ERROR_CHECK(rv != EXIT_SUCCESS, "Can't connect to card");
rv = selectApp(card_ctx);
ERROR_CHECK(rv != EXIT_SUCCESS, "Can't select applet");
rv = sendAPDU(card_ctx, sendBuffer, receiveBuffer);
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
}
return rv;
}
static int
create_wrap_apdu(const char *b64_data, BYTE unwrap_apdu[], int ins_type) {
const BYTE INS = (ins_type == DO_UNWRAP) ? 0x02 : 0x03;
const uint DATA_OFF = 0x07;
unwrap_apdu[CLA_OFF] = CLA;
unwrap_apdu[INS_OFF] = INS;
unwrap_apdu[P1_OFF] = 0x00;
unwrap_apdu[P2_OFF] = 0x00;
const uint16_t len = ovpn_base64_decode(b64_data, unwrap_apdu + DATA_OFF, (int) strlen(b64_data));
if(len < 0) return -1;
const uint16_t return_len = (ins_type == DO_UNWRAP) ? (len - TLS_CRYPT_V2_TAG_LEN - TLS_CRYPT_V2_LEN_LEN) : (len + TLS_CRYPT_V2_TAG_LEN + TLS_CRYPT_V2_LEN_LEN);
const uint LE_OFF = DATA_OFF + len;
unwrap_apdu[LC_OFF] = 0x00;
unwrap_apdu[LC_OFF + 1] = len >> 8;
unwrap_apdu[LC_OFF + 2] = len & 0xFF;
unwrap_apdu[LE_OFF] = return_len >> 8;
unwrap_apdu[LE_OFF + 1] = return_len & 0xFF;
return len;
}
static int
smartcard_process_key(struct plugin_context *context, const char *argv[], struct openvpn_plugin_args_func_return *ret,
const char op_type)
{
LONG rv;
BYTE apdu[TLS_CRYPT_V2_MAX_WKC_LEN + EXTENDED_APDU_SIZE_WITHOUT_DATA];
uint16_t data_len = create_wrap_apdu(argv[2], apdu, op_type);
ERROR_CHECK(data_len < 0, "Creating APDU failed");
uint16_t apdu_len = data_len + EXTENDED_APDU_SIZE_WITHOUT_DATA;
struct Request wrapRequest = {apdu, apdu_len};
const uint16_t return_len = (op_type == DO_UNWRAP) ? (data_len - TLS_CRYPT_V2_TAG_LEN - TLS_CRYPT_V2_LEN_LEN) : (data_len + TLS_CRYPT_V2_TAG_LEN + TLS_CRYPT_V2_LEN_LEN);
BYTE resultBuffer[return_len + 2];
struct Request result = {resultBuffer, sizeof (resultBuffer)};
rv = trySendAPDU(context->card_ctx, &wrapRequest, &result);
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
ERROR_CHECK(rv != EXIT_SUCCESS, "Transmit failed");
RESPONSE_CHECK(result, "Wrapping operation on smartcard failed");
// Prepare return for openvpn
struct openvpn_plugin_string_list *rl = calloc(1, sizeof(struct openvpn_plugin_string_list));
ERROR_CHECK(!rl, "malloc(return_list) failed");
rl->name = strdup("wrapping result");
int b64_size = ovpn_base64_encode(result.buffer, (int) result.length - 2, &rl->value);
ERROR_CHECK(b64_size < 0, "ovpn_base64_encode failed");
struct openvpn_plugin_string_list **ret_list = ret->return_list;
*ret_list = rl;
return EXIT_SUCCESS;
}
static int
create_import_apdu(const char *b64_data, BYTE *apdu, uint8_t key_type) {
// smartcard constants
const BYTE INS = 0x01;
const BYTE P1 = key_type;
// defining offsets
const uint DATA_OFF = 0x05;
apdu[CLA_OFF] = CLA;
apdu[INS_OFF] = INS;
apdu[P1_OFF] = P1;
apdu[P2_OFF] = 0x00;
apdu[LC_OFF] = TLS_CRYPT_V2_KEY_LEN;
// OpenVPN send 64 Byte key
BYTE keyBuffer[2 * TLS_CRYPT_V2_KEY_LEN];
int len = ovpn_base64_decode(b64_data, keyBuffer, sizeof(keyBuffer));
memcpy(apdu + DATA_OFF, keyBuffer, TLS_CRYPT_V2_KEY_LEN);
return len;
}
static int
smartcard_import_key(struct plugin_context *context, const char **argv) {
LONG rv;
BYTE import_apdu[APDU_LEN_WITHOUT_DATA + TLS_CRYPT_V2_KEY_LEN];
uint16_t key_len;
struct Request importKeyRequest = {import_apdu, sizeof(import_apdu)};
BYTE responseBuffer[2];
struct Request responseRequest = {responseBuffer, sizeof(responseBuffer)};
// AES Key
key_len = create_import_apdu(argv[2], import_apdu, AES_KEY);
ERROR_CHECK(key_len < 0, "Creating AES Import Key APDU failed");
rv = sendAPDU(context->card_ctx, &importKeyRequest, &responseRequest);
ERROR_CHECK(rv != EXIT_SUCCESS, "Transmit failed");
RESPONSE_CHECK(responseRequest, "AES KEY import failed");
// HMAC Key
key_len = create_import_apdu(argv[3], import_apdu, HMAC_KEY);
ERROR_CHECK(key_len < 0, "Creating AES Import Key APDU failed");
rv = sendAPDU(context->card_ctx, &importKeyRequest, &responseRequest);
ERROR_CHECK(rv != EXIT_SUCCESS, "Transmit failed");
RESPONSE_CHECK(responseRequest, "HMAC key import failed");
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
return EXIT_SUCCESS;
}
int
openvpn_plugin_func_v3(const int v3structver,
struct openvpn_plugin_args_func_in const *args,
struct openvpn_plugin_args_func_return *ret)
{
if (v3structver < OPENVPN_PLUGIN_STRUCTVER_MIN)
{
fprintf(stderr, "%s: this plugin is incompatible with the running version of OpenVPN\n", MODULE);
return OPENVPN_PLUGIN_FUNC_ERROR;
}
const char **argv = args->argv;
struct plugin_context *context = (struct plugin_context *) args->handle;
switch (args->type)
{
case OPENVPN_PLUGIN_CLIENT_KEY_WRAPPING:
if(strcmp(argv[1], "unwrap") == 0) {
return smartcard_process_key(context, argv, ret, DO_UNWRAP);
} else if(strcmp(argv[1], "wrap") == 0) {
return smartcard_process_key(context, argv, ret, DO_WRAP);
} else if(strcmp(argv[1], "import") == 0) {
return smartcard_import_key(context, argv);
} else {
return OPENVPN_PLUGIN_FUNC_ERROR;
}
default:
plog(PLOG_NOTE, "OPENVPN_PLUGIN_?");
return OPENVPN_PLUGIN_FUNC_ERROR;
}
}
OPENVPN_EXPORT void
openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
{
struct plugin_context *context = (struct plugin_context *) handle;
LONG rv = unselectApp(context->card_ctx);
if(rv != 0) {
plog(PLOG_WARN, "Couldn't unselect app on smartcard");
}
rv = disconnectFromCard(context->card_ctx);
plog(PLOG_ERR, "Couldn't disconnect from smartcard");
goto end;
}
releaseContext(context->card_ctx);
end:
free(context->card_ctx);
free(context);
}