summaryrefslogtreecommitdiffstats
path: root/tinyIPSec/src/tipsec.c
blob: 2abcb413bf7d808e8609600d53cd8a314f3a7d1b (plain)
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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
229
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
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
321
322
323
324
325
326
327
328
/* Copyright (C) 2010-2014 Mamadou DIOP
* Copyright (C) 2011-2014 Doubango Telecom <http://www.doubango.org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO 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 DOUBANGO.
*/

/**@file tipsec.c
 * @brief IPSec plugin and context managers.
 *
 * @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
 */
#include "tipsec.h"

#include "tsk_plugin.h"
#include "tsk_debug.h"

/** Max number of plugins (consumer types) we can create */
#if !defined(TIPSEC_MAX_PLUGINS)
#	define TIPSEC_MAX_PLUGINS			0x04
#endif /* TIPSEC_MAX_PLUGINS */

/* pointer to all registered consumers */
static const tipsec_plugin_def_t* __tipsec_plugins[TIPSEC_MAX_PLUGINS] = { tsk_null };

/**
* Create an IPSec context to manage the SAs.
* Before calling this function at least one special implementation must be registered using @ref tipsec_plugin_register_static().
* @param ipproto IPSec internet protocol.
* @param use_ipv6 Whether to use IPv6 or not (IPv4).
* @param mode IPSec mode.
* @param ealg IPSec encryption algorithm.
* @param alg IPSec algorithm.
* @param protocol IPSec protocol.
* @param pp_ctx Pointer holding the newly created context. Valid only if the retured code is @ref tipsec_error_success. This object must be destroyed using @a TSK_OBJECT_SAFE_FREE().
* @retval @ref tipsec_error_success if no error; otherwise error code.
*/
tipsec_error_t tipsec_ctx_create(
    tipsec_ipproto_t ipproto,
    tsk_bool_t use_ipv6,
    tipsec_mode_t mode,
    tipsec_ealg_t ealg,
    tipsec_alg_t alg,
    tipsec_proto_t protocol,
    tipsec_ctx_t** pp_ctx)
{
    tipsec_error_t err = tipsec_error_success;
    tsk_size_t i = 0;
    const tipsec_plugin_def_t* pc_plugin = tsk_null;
    tipsec_ctx_t* p_ctx = tsk_null;

    if (!pp_ctx || *pp_ctx) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tipsec_error_invalid_param;
    }

    // Create the context using the plugins registry
    while ((i < TIPSEC_MAX_PLUGINS) && (pc_plugin = __tipsec_plugins[i++])) {
        if (pc_plugin->objdef) {
            if ((p_ctx = tsk_object_new(pc_plugin->objdef))) {
                /* initialize the newly IPSec context */
                p_ctx->pc_plugin = pc_plugin;
                p_ctx->initialized = 0;
                p_ctx->started = 0;
                p_ctx->state = tipsec_state_initial;
                p_ctx->use_ipv6 = use_ipv6;
                p_ctx->mode = mode;
                p_ctx->ealg = ealg;
                p_ctx->alg = alg;
                p_ctx->protocol = protocol;
                p_ctx->ipproto = ipproto;
                break;
            }
        }
    }

    if (!pc_plugin || !p_ctx) {
        TSK_DEBUG_ERROR("Failed to find/create a plugin instance");
        return tipsec_error_notfound;
    }

    // Initialize the newly created context
    err = pc_plugin->init(p_ctx);
    if (err) {
        goto bail;
    }

bail:
    if (err) {
        TSK_OBJECT_SAFE_FREE(p_ctx);
    }
    *pp_ctx = p_ctx;
    return err;
}

/**
* Ensure the SAs. This function must be called before sending/receiving any data.
* @param p_ctx Pointer to a context created using @ref tipsec_ctx_create().
* @retval @ref tipsec_error_success if no error; otherwise error code.
*/
tipsec_error_t tipsec_ctx_start(tipsec_ctx_t* p_ctx)
{
    if (!p_ctx || !p_ctx->pc_plugin) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tipsec_error_invalid_param;
    }
    if (!p_ctx->initialized || p_ctx->state != tipsec_state_full) {
        TSK_DEBUG_ERROR("Invalid state (not initialized or not in full state)");
        return tipsec_error_invalid_state;
    }
    return p_ctx->pc_plugin->start(p_ctx);
}

/**
* Set local information. On windows Vista and later this function must be called to request local SPIs.
* @param p_ctx Pointer to a context created using @ref tipsec_ctx_create().
* @param addr_local Local IP address (e.g. "192.168.0.5"). The IP version depends on whether @a use_param (0 or 1) when @ref tipsec_ctx_create() was used to create the context.
* @param addr_remote Remote IP address (e.g. "192.168.0.5"). The IP version depends on whether @a use_param (0 or 1) when @ref tipsec_ctx_create() was used to create the context.
* @param port_uc Local client port used to send data. Must be within [1024-65535].
* @param port_us Local server port used to received data. Must be within [1024-65535].
* @retval @ref tipsec_error_success if no error; otherwise error code.
*/
tipsec_error_t tipsec_ctx_set_local(tipsec_ctx_t* p_ctx, const char* addr_local, const char* addr_remote, tipsec_port_t port_uc, tipsec_port_t port_us)
{
    if (!p_ctx || !p_ctx->pc_plugin || !addr_local || !addr_remote || port_uc < 1024 || port_us < 1024) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tipsec_error_invalid_param;
    }
    if (!p_ctx->initialized || p_ctx->state != tipsec_state_initial) {
        TSK_DEBUG_ERROR("Invalid state (not initialized or not in initial state)");
        return tipsec_error_invalid_state;
    }
    return p_ctx->pc_plugin->set_local(p_ctx, addr_local, addr_remote, port_uc, port_us);
}

/**
* Set Integrity (IK) and Confidentiality (CK) Keys.
* On the UE, the IK and CK are built using the "nonce" value using the 494 from the P-CSCF which means the function must be called after the REGISTER<->494 round trip.
* @param p_ctx Pointer to a context created using @ref tipsec_ctx_create().
* @param ik The Integrity Key.
* @param ck The Confidentiality Key.
* @retval @ref tipsec_error_success if no error; otherwise error code.
*/
tipsec_error_t tipsec_ctx_set_keys(tipsec_ctx_t* p_ctx, const tipsec_key_t* ik, const tipsec_key_t* ck)
{
    if (!p_ctx || !p_ctx->pc_plugin || !ik || !ck) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tipsec_error_invalid_param;
    }
    return p_ctx->pc_plugin->set_keys(p_ctx, ik, ck);
}

/**
* Set the remote information.
* @param p_ctx Pointer to a context created using @ref tipsec_ctx_create().
* @param spi_pc Remote client SPI (Security Parameter Index) used by the remote party to send data.
* @param spi_ps Remote server SPI (Security Parameter Index) used by the remote party to receive data.
* @param port_pc Remote client port used by the remote party to send data. Must be within [1024-65535].
* @param port_ps Remote server port used by the remote party to receive data. Must be within [1024-65535].
* @param lifetime The SA lifetime (in seconds). Must not be null. Should be 2xSipRegistrationTimeout. On Windows vista and later, the maximum allowed value is @a 172799 seconds.
* @retval @ref tipsec_error_success if no error; otherwise error code.
*/
tipsec_error_t tipsec_ctx_set_remote(tipsec_ctx_t* p_ctx, tipsec_spi_t spi_pc, tipsec_spi_t spi_ps, tipsec_port_t port_pc, tipsec_port_t port_ps, tipsec_lifetime_t lifetime)
{
    if (!p_ctx || !p_ctx->pc_plugin || port_pc < 1024 || port_ps < 1024 || !lifetime) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tipsec_error_invalid_param;
    }
    if (!p_ctx->initialized || p_ctx->state != tipsec_state_inbound) {
        TSK_DEBUG_ERROR("Invalid state (not initialized or not in initial state)");
        return tipsec_error_invalid_state;
    }
    return p_ctx->pc_plugin->set_remote(p_ctx, spi_pc, spi_ps, port_pc, port_ps, lifetime);
}

/**
* Shutdown all SAs associated to this context. It's no longer allowed to send/recv data after calling this function.
* @param p_ctx Pointer to a context created using @ref tipsec_ctx_create().
* @retval @ref tipsec_error_success if no error; otherwise error code.
*/
tipsec_error_t tipsec_ctx_stop(tipsec_ctx_t* p_ctx)
{
    if (!p_ctx || !p_ctx->pc_plugin) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tipsec_error_invalid_param;
    }
    return p_ctx->pc_plugin->stop(p_ctx);
}

/**
* Register an IPSec special implementation (e.g. Windows XP, Windows Vista or Linux IPSec-Tools) using a static definition.
* @param pc_plugin pointer to the static definition.
* @retval @ref tipsec_error_success if no error; otherwise error code.
* @sa @ref tipsec_plugin_unregister_static(), @ref tipsec_plugin_unregister_file()
*/
tipsec_error_t tipsec_plugin_register_static(const tipsec_plugin_def_t* pc_plugin)
{
    tsk_size_t i;
    if (!pc_plugin) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tipsec_error_invalid_param;
    }

    /* add or replace the plugin */
    for (i = 0; i < TIPSEC_MAX_PLUGINS; ++i) {
        if (!__tipsec_plugins[i] || (__tipsec_plugins[i] == pc_plugin)) {
            __tipsec_plugins[i] = pc_plugin;
            TSK_DEBUG_INFO("Register IPSec implementation: %s", pc_plugin->desc);
            return tipsec_error_success;
        }
    }

    TSK_DEBUG_ERROR("There are already %d plugins.", TIPSEC_MAX_PLUGINS);
    return tipsec_error_outofbound;
}

/**
* UnRegister an IPSec special implementation (e.g. Windows XP, Windows Vista or Linux IPSec-Tools) using a static definition.
* @param pc_plugin pointer to the static definition.
* @retval @ref tipsec_error_success if no error; otherwise error code.
* @sa @ref tipsec_plugin_register_static(), @ref tipsec_plugin_register_file()
*/
tipsec_error_t tipsec_plugin_unregister_static(const tipsec_plugin_def_t* pc_plugin)
{
    tsk_size_t i;
    tsk_bool_t b_found = tsk_false;

    if (!pc_plugin) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tipsec_error_invalid_param;
    }

    /* find the plugin to unregister */
    for (i = 0; i < TIPSEC_MAX_PLUGINS && __tipsec_plugins[i]; ++i) {
        if (__tipsec_plugins[i] == pc_plugin) {
            __tipsec_plugins[i] = tsk_null;
            b_found = tsk_true;
            TSK_DEBUG_INFO("UnRegister IPSec implementation: %s", pc_plugin->desc);
            break;
        }
    }

    /* compact */
    if (b_found) {
        for (; i < (TIPSEC_MAX_PLUGINS - 1); ++i) {
            if (__tipsec_plugins[i+1]) {
                __tipsec_plugins[i] = __tipsec_plugins[i + 1];
            }
            else {
                break;
            }
        }
        __tipsec_plugins[i] = tsk_null;
    }
    return (b_found ? tipsec_error_success : tipsec_error_notfound);
}

/**
* Register an IPSec special implementation (e.g. Windows XP, Windows Vista or Linux IPSec-Tools) using a shared library (*.DLL, *.SO).
* @param pc_filepath Path to the plugin.
* @param pp_plugin Pointer to the newly created plugin. You must call @ref tipsec_plugin_unregister_file() when you no longer need to use the plugin.
* @retval @ref tipsec_error_success if no error; otherwise error code.
* @sa @ref tipsec_plugin_unregister_static(), @ref tipsec_plugin_unregister_file()
*/
tipsec_error_t tipsec_plugin_register_file(const char* pc_filepath, struct tsk_plugin_s** pp_plugin)
{
    struct tsk_plugin_s* p_plugin = tsk_null;
    tsk_plugin_def_ptr_const_t p_def;
    tipsec_error_t err = tipsec_error_success;
    int i = 0, count = 0;

    p_plugin = tsk_plugin_create(pc_filepath);
    if (!p_plugin) {
        return tipsec_error_notfound;
    }

    while ((p_def = tsk_plugin_get_def_2(p_plugin, tsk_plugin_def_type_ipsec, tsk_plugin_def_media_type_all, i++))) {
        if (p_def) {
            err = tipsec_plugin_register_static(p_def);
            if (!err) {
                ++count;
            }
        }
    }

    if (count <= 0) {
        TSK_DEBUG_ERROR("No plugin in %s", pc_filepath);
        TSK_OBJECT_SAFE_FREE(p_plugin);
        return tipsec_error_notfound;
    }

    *pp_plugin = p_plugin;
    return err;
}

/**
* UnRegister an IPSec special implementation (e.g. Windows XP, Windows Vista or Linux IPSec-Tools).
* @param p_plugin Pointer to the plugin previously registered using @ref tipsec_plugin_register_file().
* @retval @ref tipsec_error_success if no error; otherwise error code.
* @sa @ref tipsec_plugin_unregister_static(), @ref tipsec_plugin_unregister_file()
*/
tipsec_error_t tipsec_plugin_unregister_file(struct tsk_plugin_s* p_plugin)
{
    int i = 0;
    tsk_plugin_def_ptr_const_t p_def;
    if (!p_plugin) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tipsec_error_invalid_param;
    }
    while ((p_def = tsk_plugin_get_def_2(p_plugin, tsk_plugin_def_type_ipsec, tsk_plugin_def_media_type_all, i++))) {
        if (p_def) {
            tipsec_plugin_unregister_static(p_def);
        }
    }
    return tipsec_error_success;
}
OpenPOWER on IntegriCloud