summaryrefslogtreecommitdiffstats
path: root/plugin/eurephiadb_session.c
blob: 0cbf1e5ead50090419b2e7f3d17c2446df5a3e7b (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
/* eurephiadb_session.c  --  Functions for handling sessions from eurephia-auth
 *
 *  GPLv2 only - Copyright (C) 2008 - 2012
 *               David Sommerseth <dazo@users.sourceforge.net>
 *
 *  This program 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; version 2
 *  of the License.
 *
 *  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.
 *
 */

/**
 * @file   eurephiadb_session.c
 * @author David Sommerseth <dazo@users.sourceforge.net>
 * @date   2008-08-06
 *
 * @brief  Handles creating user sessions, which is unique per openvpn connection.
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define EUREPHIA_FWINTF
#include <eurephiafw_struct.h>
#include <eurephia_context.h>
#include "eurephia_nullsafe.h"
#include "eurephia_log.h"
#include "eurephiadb_session.h"
#include <eurephiadb_session_common.h>
#include <environment.h>
#include <randstr.h>
#include <sha512.h>


// Also defined in the eurephiadb_driver.h, but not as extern.
extern char *(*eDBget_sessionkey_seed) (eurephiaCTX *ctx, sessionType type, const char *sessionseed);
extern char *(*eDBget_sessionkey_macaddr) (eurephiaCTX *ctx, const char *macaddr);

extern int (*eDBcheck_sessionkey_uniqueness) (eurephiaCTX *ctx, const char *seskey);

extern int (*eDBregister_sessionkey) (eurephiaCTX *ctx, const char *seed, const char *seskey);

extern eurephiaVALUES *(*eDBload_sessiondata) (eurephiaCTX *ctx, const char *sesskey);

/**
 * Generates a new eurephia session.  Session key will be created if session seed (input params) 
 * are not known.  If session seed is known, the already generated session key will be used.
 *
 * @param ctx       eurephiaCTX
 * @param digest    Contains the clients SHA1 fingerprint / digest
 * @param cname     Contains the clients X.509 Common Name field
 * @param username  The user name of the client
 * @param vpnipaddr The IP address of the VPN connection of the client
 * @param vpnipmask The IP address' network mask
 * @param remipaddr The public IP address the client is connecting from
 * @param remport   The remote port of the client connection
 *
 * @return returns a eurephiaSESSION pointer on success, otherwise NULL.
 */
eurephiaSESSION *eDBopen_session_seed(eurephiaCTX *ctx, const char *digest,
                                      const char *cname, const char *username,
                                      const char *vpnipaddr, const char *vpnipmask,
                                      const char *remipaddr, const char *remport)
{
        eurephiaSESSION *new_session = NULL;
        char *seeddata = NULL, *seed = NULL, *ptr = NULL;
        SHA512Context sha;
        uint8_t sha_res[SHA512_HASH_SIZE];
        size_t totlen = 0, i = 0;

        DEBUG(ctx, 12, "Function call: eDBopen_session_seed(ctx, '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
              digest, cname, username, vpnipaddr, vpnipmask, remipaddr, remport);

        new_session = (eurephiaSESSION *) malloc_nullsafe(ctx, sizeof(eurephiaSESSION) + 2);
        if( new_session == NULL ) {
                return NULL;
        }

        // Session type is stSESSION if we do have VPN address and/or netmask
        new_session->type = ((vpnipaddr == NULL) && (vpnipmask == NULL) ? stAUTHENTICATION : stSESSION);

        // Build up a string containing all elements for the session seed
        totlen = MAXLEN_TLSDIGEST + MAXLEN_CNAME + MAXLEN_USERNAME + MAXLEN_POOLIPADDR
                + MAXLEN_TRUSTEDIP + MAXLEN_TRUSTEDPORT + 5 + 15;
        // max length of: digest + cname + username + vpnipaddr + vpnipmask
        //                + remipaddr + remport + pid + extra buffer

        seeddata = (char *) malloc_nullsafe(ctx, totlen);
        if( seeddata == NULL ) {
                free_nullsafe(ctx, new_session);
                return NULL;
        }
        snprintf(seeddata, totlen, "%.60s%.64s%.34s%.34s%.34s%.6s%05i",
                 digest, cname, username, vpnipaddr, remipaddr, remport, getpid());

        // Generate a SHA512 version of session seed
        memset(&sha, 0, sizeof(SHA512Context));
        memset(&sha_res, 0, sizeof(sha_res));
        SHA512Init(&sha);
        SHA512Update(&sha, seeddata, totlen);
        SHA512Final(&sha, sha_res);

        seed = (char *) malloc_nullsafe(ctx, (SHA512_HASH_SIZE*2)+3);
        if( seed == NULL ) {
                free_nullsafe(ctx, seeddata);
                free_nullsafe(ctx, new_session);
                return NULL;
        }
        ptr = seed;

        for( i = 0; i < SHA512_HASH_SIZE; i++ ) {
                sprintf(ptr, "%02x", sha_res[i]);
                ptr += 2;
        }
        memset(&sha, 0, sizeof(SHA512Context));
        memset(&sha_res, 0, sizeof(sha_res));

        DEBUG(ctx, 13, "Using session seed '%s'", seed);

        // Try to retrieve the sessionkey from the database, based on the session seed
        new_session->sessionkey = eDBget_sessionkey_seed(ctx, new_session->type, seed);
        if( new_session->sessionkey == NULL ) {
                // ... if we do not find a sessionkey ... lets generate one
                int rndlen = 0;
                char *rndstr = NULL;
                char *skeydata = NULL;
                int loop = 0, uniqcheck = 0;

                DEBUG(ctx, 13, "Unknown session seed, creating new session key");

                // Loop until we get a unique sessionkey - don't loop more than 10 times
                skeydata = (char *) malloc_nullsafe(ctx, (totlen*2)+4);
                if( skeydata == NULL ) {
                        free_nullsafe(ctx, new_session->sessionkey);
                        free_nullsafe(ctx, new_session);
                        free_nullsafe(ctx, seeddata);
                        free_nullsafe(ctx, seed);
                        return NULL;
                }
                do {
                        memset(skeydata, 0, (totlen*2)+4);

		// FIXME:  Validate that we have enough random data for the session key

                        // Append some random data to our session seed
                        rndstr = (char *) malloc_nullsafe(ctx, (totlen * 2));
                        if( rndstr == NULL ) {
                                free_nullsafe(ctx, new_session->sessionkey);
                                free_nullsafe(ctx, new_session);
                                free_nullsafe(ctx, seeddata);
                                free_nullsafe(ctx, seed);
                                return NULL;
                        }
                        rndlen = ((totlen * 2) - strlen_nullsafe(seed) - 2);

                        if( !eurephia_randstring(ctx, rndstr, rndlen) ) {
                                eurephia_log(ctx, LOG_PANIC, 0,
                                             "Could not generate enough random data for session key");
                                free_nullsafe(ctx, new_session->sessionkey);
                                free_nullsafe(ctx, new_session);
                                free_nullsafe(ctx, seeddata);
                                free_nullsafe(ctx, seed);
                                return NULL;
                        }

                        // Generate SHA512 version of the session data
                        SHA512Init(&sha);
                        SHA512Update(&sha, rndstr, rndlen);
                        SHA512Final(&sha, sha_res);

                        free_nullsafe(ctx, new_session->sessionkey);
                        new_session->sessionkey = (char *) malloc_nullsafe(ctx, (SHA512_HASH_SIZE*2)+3);
                        if( new_session->sessionkey == NULL ) {
                                free_nullsafe(ctx, new_session);
                                free_nullsafe(ctx, seeddata);
                                free_nullsafe(ctx, seed);
                                return NULL;
                        }

                        ptr = new_session->sessionkey;
                        for( i = 0; i < SHA512_HASH_SIZE; i++ ) {
                                sprintf(ptr, "%02x", sha_res[i]);
                                ptr += 2;
                        }
                        memset(&sha, 0, sizeof(SHA512Context));
                        memset(&sha_res, 0, sizeof(sha_res));
                        free_nullsafe(ctx, rndstr);

                        loop++;
                        uniqcheck = eDBcheck_sessionkey_uniqueness(ctx, new_session->sessionkey);
                } while( (uniqcheck == 0) && loop < 11 );
                free_nullsafe(ctx, skeydata);

                // If we did not manage to create a unique session key (random data collection must have failed!)
                if( uniqcheck == 0 ) {
                        eurephia_log(ctx, LOG_FATAL, 0,
                                     "Did not manage to create a unique sessionkey after %i attempts", loop-1);
                        free_nullsafe(ctx, new_session->sessionkey);
                        free_nullsafe(ctx, new_session);
                        free_nullsafe(ctx, seeddata);
                        free_nullsafe(ctx, seed);
                        return NULL;
                }

                // Save this session key in the database and connect it to this session seed
                if( eDBregister_sessionkey(ctx, seed, new_session->sessionkey) == 0) {
                        eurephia_log(ctx, LOG_FATAL, 0, "Could not register sessionkey");
                        free_nullsafe(ctx, new_session->sessionkey);
                        free_nullsafe(ctx, new_session);
                        free_nullsafe(ctx, seeddata);
                        free_nullsafe(ctx, seed);
                        return NULL;
                };
                new_session->sessionstatus = SESSION_NEW;
        } else {
                new_session->sessionstatus = SESSION_EXISTING;
                DEBUG(ctx, 13, "Session seed found, using sessionkey '%s'", new_session->sessionkey);
        }
        free_nullsafe(ctx, seed);
        free_nullsafe(ctx, seeddata);

        // Load session values from the database
        new_session->sessvals = eDBload_sessiondata(ctx, new_session->sessionkey);

        // Return struct which contains session key and session variables
        return new_session;
}


/**
 * Load a session based on a known session key
 *
 * @param ctx      eurephiaCTX
 * @param sesskey  A string containing the session key
 * @param sesstype What kind of type the session data should be opend as (sessionType)
 *
 * @return Returns a pointer to the corresponding eurephiaSESSION struct on success, otherwise NULL.
 */

eurephiaSESSION *eDBsession_load(eurephiaCTX *ctx, const char *sesskey, sessionType sesstype) {
        eurephiaSESSION *new_session = NULL;

        DEBUG(ctx, 12, "Function call: eDBsession_load(ctx, '%s')", sesskey);

        new_session = (eurephiaSESSION *) malloc_nullsafe(ctx, sizeof(eurephiaSESSION) + 2);
        if( new_session == NULL ) {
                return NULL;
        }
        new_session->type = sesstype;

        // Get the sessionkey from the database
        new_session->sessionkey = strdup_nullsafe(sesskey);
        if( new_session->sessionkey == NULL ) {
                eurephia_log(ctx, LOG_CRITICAL, 0, "Failed to set the session key to '%s'",
                             sesskey);
                free_nullsafe(ctx, new_session);
                return NULL;
        }

        // Load session values from the database
        new_session->sessvals = eDBload_sessiondata(ctx, new_session->sessionkey);

        // Return struct which contains the current session
        return new_session;
}


/**
 * Open an existing eurephia session based on a MAC address.  This function is only used
 * when there is not enough information to generate a session seed and when the MAC address
 * is available.  Usually this only happens when the client has disconnected and the session
 * is about to be marked as closed.
 *
 * @param ctx     eurephiaCTX
 * @param macaddr MAC address of the client
 *
 * @return returns a eurephiaSESSION pointer on success, otherwise NULL.
 */
eurephiaSESSION *eDBopen_session_macaddr(eurephiaCTX *ctx, const char *macaddr) {
        char *sesskey = NULL;

        DEBUG(ctx, 12, "Function call: eDBopen_session_mac(ctx, '%s')", macaddr);

        // Get the sessionkey from the database
        sesskey = eDBget_sessionkey_macaddr(ctx, macaddr);
        if( sesskey == NULL ) {
                eurephia_log(ctx, LOG_CRITICAL, 0, "Could not find an active session for MAC address '%s'",
                             macaddr);
                return NULL;
        }
        DEBUG(ctx, 13, "Session seed found, using sessionkey '%s'", sesskey);

        // Open and load the session from the database
        return eDBsession_load(ctx, sesskey, stSESSION);
}