summaryrefslogtreecommitdiffstats
path: root/eurephiadb_session.c
blob: 72fccdb831a8bb9894b20658ea46102d442fe71f (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
/* eurephiadb_session.c  --  Global API for handling eurephia sessions
 *
 *  GPLv2 - Copyright (C) 2008  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.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>

#include "eurephia_struct.h"
#include "eurephia_nullsafe.h"
#include "eurephia_log.h"
#include "eurephiadb_session.h"
#include "eurephia_values.h"
#include "sha512.h"


// Also defined in the eurephiadb_driver.h, but not as extern.
extern char *(*eDBget_sessionkey) (eurephiaCTX *ctx, const char *sessionseed);

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);

extern int eDBstore_session_value(eurephiaCTX *ctx, eurephiaSESSION *session, int mode, 
                            const char *key, const char *val);


// Adds or updates a key in the eurephiaVALUES stack.  Database is updated before the stack is updated.
// If database fails, the stack is not updated.
int eDBset_session_value(eurephiaCTX *ctx, eurephiaSESSION *session, const char *key, const char *val) {
        eurephiaVALUES *svals = NULL;

        if( (session == NULL) || (key == NULL) ) {
                return 0;
        }
        
        eurephia_log(ctx, LOG_DEBUG, 10, "Function call: eDBset_session_value(ctx, '%s','%s','%s')",
                     session->sessionkey, key, val);

        // Create a new session value buffer if it does not exist
        if( session->sessvals == NULL ) {
                session->sessvals = eCreate_value_space(ctx, 10);
                if( session->sessvals == NULL ) {
                        eurephia_log(ctx, LOG_FATAL, 0, "Could not allocate memory for session values");
                        return 0;
                }
        }

        // Check if the session value exists already.  If it does update it, or else add it
        svals = eGet_valuestruct(session->sessvals, key);
        if( (svals == NULL) && (val != NULL) ) {
                eurephia_log(ctx, LOG_DEBUG, 23, "eDBset_session_value ... New session value: %s = '%s'",
                             key, val);
                // Add a new session value
                if( eDBstore_session_value(ctx, session, SESSVAL_NEW, key, val) ) {
                        eurephia_log(ctx, LOG_DEBUG, 23, 
                                     "eDBset_session_value ... Adding value to value stack: %s = '%s'", key, val);
                        // Add value to the stack
                        eAdd_value(ctx, session->sessvals, key, val);

                        eurephia_log(ctx, LOG_DEBUG, 11, 
                                     "Registered session variable to session '%s': %s = %s", 
                                     session->sessionkey, key, val);
                }
        } else if( svals != NULL ) {
                if( (val != NULL) && (strcmp(svals->val, val) == 0) ) {
                        eurephia_log(ctx, LOG_DEBUG, 11, "Session value not changed('%s','%s','%s)",
                                     session->sessionkey, key, val);
                        return 1;
                }
                // Update the value in the stack if database is updated without errors
                if( eDBstore_session_value(ctx, session,(val != NULL ? SESSVAL_UPDATE : SESSVAL_DELETE), key,val)){
                        free_nullsafe(svals->val);
                        svals->val = strdup_nullsafe(val);
                        eurephia_log(ctx, LOG_DEBUG, 11, 
                                     "Session variable updated in session '%s': %s = %s", 
                                     session->sessionkey, key, val);
                }
        } else if( (svals == NULL) && (val == NULL ) ) {
                eurephia_log(ctx, LOG_DEBUG, 11, "Ignoring saving new session value '%s' == NULL", key);
        }
        return 1;
}


// Collect some random data and return a string.  The string contains bytes in the ASCII range of 32-255
char *get_randstring(const char *inseed, int len) { 
        struct timeval tval;
        char *seed = NULL, *rndstr = NULL, tmpstr[5];
        int i = 0;

        seed = strdup_nullsafe(inseed);
        gettimeofday(&tval, NULL);
        initstate((unsigned int)tval.tv_usec, seed, strlen_nullsafe(seed));
        
        rndstr = (char *) malloc(len+2);
        memset(rndstr, 0, len+2);

        for( i = 0; i < len; i++ ) {
                srandom((long int) random());
                sprintf(tmpstr, "%c%c", (int)(((random()) % 223) + 32), 0);
                strcat(rndstr, tmpstr);
        }
        free_nullsafe(seed);
        return rndstr;
}


// Generates a new session structure.  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.
eurephiaSESSION *eDBopen_session(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];
        int totlen = 0, i = 0;

        eurephia_log(ctx, LOG_DEBUG, 10, 
                     "Function call: eDBopen_session(ctx, '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
                     digest, cname, username, vpnipaddr, vpnipmask, remipaddr, remport);

        new_session = (eurephiaSESSION *) malloc(sizeof(eurephiaSESSION) + 2);
        memset(new_session, 0, sizeof(eurephiaSESSION) + 2);

        // Build up a string containing all elements for the session seed
        totlen = strlen_nullsafe(digest) + strlen_nullsafe(cname) + strlen_nullsafe(username) 
                + strlen_nullsafe(vpnipaddr) + strlen_nullsafe(vpnipmask) + strlen_nullsafe(remipaddr)
                + strlen_nullsafe(remport) + 5; // +5 == len(pid)

        seeddata = (char *) malloc((totlen * 2) + 4);
        memset(seeddata, 0, (totlen * 2) + 4);
        snprintf((char *)seeddata, totlen, 
                 "%s%s%s%s%s%s%s%i", digest, cname, username, vpnipaddr, vpnipmask, 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((SHA512_HASH_SIZE*2)+3);
        memset(seed, 0, (SHA512_HASH_SIZE*2)+2);
        ptr = seed;

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

        eurephia_log(ctx, LOG_DEBUG, 12, "Using session seed '%s'", seed);

        // Try to retrieve the sessionkey from the database, based on the session seed
        new_session->sessionkey = eDBget_sessionkey(ctx, seed);
        if( new_session->sessionkey == NULL ) {
                // ... if we do not find a sessionkey ... lets generate one
                char *rndstr = NULL;
                char *skeydata = NULL;
                int loop = 0, uniqcheck = 0;
                
                eurephia_log(ctx, LOG_DEBUG, 12, "Unknown session seed, creating new session key");

                // Loop until we get a unique sessionkey - don't loop more than 10 times
                skeydata = (char *) malloc((totlen*2)+4);
                do {
                        memset(skeydata, 0, (totlen*2)+4);

                        // Append to our session seed some random data
                        rndstr = get_randstring(seed, (totlen * 2) - strlen(seed) - 2);
                        snprintf(skeydata, (totlen*2), "%s%s", rndstr, seeddata);
                        
                        // Generate SHA512 version of the session data
                        SHA512Init(&sha);
                        SHA512Update(&sha, seeddata, totlen);
                        SHA512Final(&sha, sha_res);

                        free_nullsafe(new_session->sessionkey);
                        new_session->sessionkey = (char *) malloc((SHA512_HASH_SIZE*2)+3);
                        memset(new_session->sessionkey, 0, (SHA512_HASH_SIZE*2)+3);

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

                        loop++;
                        uniqcheck = eDBcheck_sessionkey_uniqueness(ctx, new_session->sessionkey);
                } while( (uniqcheck == 0) && loop < 11 );
                free_nullsafe(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_CRITICAL, 0,
                                     "Did not manage to create a unique sessionkey after %i attempts", loop-1);
                        free_nullsafe(new_session);
                        free_nullsafe(seeddata);
                        return NULL;
                }

                // Save this session key in the database and connect it to this session seed
                eDBregister_sessionkey(ctx, seed, new_session->sessionkey);
                new_session->sessionstatus = SESSION_NEW;
        } else {
                new_session->sessionstatus = SESSION_EXISTING;
                eurephia_log(ctx, LOG_DEBUG, 12, "Session seed found, using sessionkey '%s'", 
                             new_session->sessionkey);
        }
        free_nullsafe(seed);
        free_nullsafe(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;
}

// Free up the memory used by a session structure
void eDBfree_session_func(eurephiaCTX *ctx, eurephiaSESSION *session) {
        eurephiaVALUES *svptr, *tmp;

        if( session == NULL ) {
                return;
        }
        eurephia_log(ctx, LOG_DEBUG, 20, "Function call: eDBfree_session(ctx, '%s')", session->sessionkey);
        eFree_values(ctx, session->sessvals);
        free_nullsafe(session->sessionkey);
        free_nullsafe(session);
}