Project

General

Profile

Download (7.93 KB) Statistics
| Branch: | Revision:
1
// session.c -- Session management for HTTP/HTTPS connections
2
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
3
//
4
// This program is free software; you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License version 2 as
6
// published by the Free Software Foundation.
7
//
8
// This program is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License along
14
// with this program; if not, write to the Free Software Foundation, Inc.,
15
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
//
17
// In addition to these license terms, the author grants the following
18
// additional rights:
19
//
20
// If you modify this program, or any covered work, by linking or
21
// combining it with the OpenSSL project's OpenSSL library (or a
22
// modified version of that library), containing parts covered by the
23
// terms of the OpenSSL or SSLeay licenses, the author
24
// grants you additional permission to convey the resulting work.
25
// Corresponding Source for a non-source form of such a combination
26
// shall include the source code for the parts of OpenSSL used as well
27
// as that of the covered work.
28
//
29
// You may at your option choose to remove this additional permission from
30
// the work, or from any part of it.
31
//
32
// It is possible to build this program in a way that it loads OpenSSL
33
// libraries at run-time. If doing so, the following notices are required
34
// by the OpenSSL and SSLeay licenses:
35
//
36
// This product includes software developed by the OpenSSL Project
37
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
38
//
39
// This product includes cryptographic software written by Eric Young
40
// (eay@cryptsoft.com)
41
//
42
//
43
// The most up-to-date version of this program is always available from
44
// http://shellinabox.com
45

    
46
#include "config.h"
47

    
48
#include <stdlib.h>
49
#include <string.h>
50
#include <time.h>
51
#include <fcntl.h>
52
#include <unistd.h>
53

    
54
#include "shellinabox/session.h"
55
#include "logging/logging.h"
56

    
57
#ifdef HAVE_UNUSED
58
#defined ATTR_UNUSED __attribute__((unused))
59
#defined UNUSED(x)   do { } while (0)
60
#else
61
#define ATTR_UNUSED
62
#define UNUSED(x)    do { (void)(x); } while (0)
63
#endif
64

    
65
static HashMap *sessions;
66

    
67

    
68
static struct Graveyard {
69
  struct Graveyard *next;
70
  time_t           timeout;
71
  const char       *sessionKey;
72
} *graveyard;
73

    
74
void addToGraveyard(struct Session *session) {
75
  // It is possible for a child process to die, but for the Session to
76
  // linger around, because the browser has also navigated away and thus
77
  // nobody ever calls completePendingRequest(). We put these Sessions into
78
  // the graveyard and reap them after a while.
79
  struct Graveyard *g;
80
  check(g       = malloc(sizeof(struct Graveyard)));
81
  g->next       = graveyard;
82
  g->timeout    = time(NULL) + AJAX_TIMEOUT;
83
  g->sessionKey = strdup(session->sessionKey);
84
  graveyard     = g;
85
}
86

    
87
static void checkGraveyardInternal(int expireAll) {
88
  if (!graveyard) {
89
    return;
90
  }
91
  time_t timeout = time(NULL) - (expireAll ? 2*AJAX_TIMEOUT : 0);
92
  for (struct Graveyard **g = &graveyard, *old = *g;
93
       old; ) {
94
    if (old->timeout < timeout) {
95
      *g         = old->next;
96
      deleteFromHashMap(sessions, old->sessionKey);
97
      free((char *)old->sessionKey);
98
      free(old);
99
    } else {
100
      g          = &old->next;
101
    }
102
    old          = *g;
103
  }
104
}
105

    
106
void checkGraveyard(void) {
107
  checkGraveyardInternal(0);
108
}
109

    
110
void initSession(struct Session *session, const char *sessionKey,
111
                 Server *server, URL *url, const char *peerName) {
112
  session->sessionKey     = sessionKey;
113
  session->server         = server;
114
  check(session->peerName = strdup(peerName));
115
  session->connection     = NULL;
116
  session->http           = NULL;
117
  session->url            = url;
118
  session->done           = 0;
119
  session->pty            = -1;
120
  session->width          = 0;
121
  session->height         = 0;
122
  session->buffered       = NULL;
123
  session->len            = 0;
124
}
125

    
126
struct Session *newSession(const char *sessionKey, Server *server, URL *url,
127
                           const char *peerName) {
128
  struct Session *session;
129
  check(session = malloc(sizeof(struct Session)));
130
  initSession(session, sessionKey, server, url, peerName);
131
  return session;
132
}
133

    
134
void destroySession(struct Session *session) {
135
  if (session) {
136
    free((char *)session->peerName);
137
    free((char *)session->sessionKey);
138
    deleteURL(session->url);
139
    if (session->pty >= 0) {
140
      NOINTR(close(session->pty));
141
    }
142
  }
143
}
144

    
145
void deleteSession(struct Session *session) {
146
  destroySession(session);
147
  free(session);
148
}
149

    
150
void abandonSession(struct Session *session) {
151
  deleteFromHashMap(sessions, session->sessionKey);
152
}
153

    
154
void finishSession(struct Session *session) {
155
  deleteFromHashMap(sessions, session->sessionKey);
156
}
157

    
158
void finishAllSessions(void) {
159
  checkGraveyardInternal(1);
160
  deleteHashMap(sessions);
161
}
162

    
163
static void destroySessionHashEntry(void *arg ATTR_UNUSED,
164
                                    char *key ATTR_UNUSED, char *value) {
165
  UNUSED(arg);
166
  UNUSED(key);
167

    
168
  deleteSession((struct Session *)value);
169
}
170

    
171
char *newSessionKey(void) {
172
  int fd;
173
  check((fd = NOINTR(open("/dev/urandom", O_RDONLY))) >= 0);
174
  unsigned char buf[16];
175
  check(NOINTR(read(fd, buf, sizeof(buf))) == sizeof(buf));
176
  NOINTR(close(fd));
177
  char *sessionKey;
178
  check(sessionKey   = malloc((8*sizeof(buf) + 5)/6 + 1));
179
  char *ptr          = sessionKey;
180
  int count          = 0;
181
  int bits           = 0;
182
  for (unsigned i = 0;;) {
183
    bits             = (bits << 8) | buf[i];
184
    count           += 8;
185
  drain:
186
    while (count >= 6) {
187
      *ptr++         = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
188
                       "ghijklmnopqrstuvwxyz0123456789-/"
189
                       [(bits >> (count -= 6)) & 0x3F];
190
    }
191
    if (++i >= sizeof(buf)) {
192
      if (count && i == sizeof(buf)) {
193
        bits       <<= 8;
194
        count       += 8;
195
        goto drain;
196
      } else {
197
        break;
198
      }
199
    }
200
  }
201
  *ptr               = '\000';
202
  check(!sessions || !getFromHashMap(sessions, sessionKey));
203
  return sessionKey;
204
}
205

    
206
struct Session *findCGISession(int *isNew, HttpConnection *http, URL *url,
207
                               const char *cgiSessionKey) {
208
  *isNew                 = 1;
209
  if (!sessions) {
210
    sessions             = newHashMap(destroySessionHashEntry, NULL);
211
  }
212
  const HashMap *args    = urlGetArgs(url);
213
  const char *sessionKey = getFromHashMap(args, "session");
214
  struct Session *session= NULL;
215
  if (cgiSessionKey &&
216
      (!sessionKey || strcmp(cgiSessionKey, sessionKey))) {
217
    // In CGI mode, we only ever allow exactly one session with a
218
    // pre-negotiated key.
219
    deleteURL(url);
220
  } else {
221
    if (sessionKey && *sessionKey) {
222
      session            = (struct Session *)getFromHashMap(sessions,
223
                                                            sessionKey);
224
    }
225
    if (session) {
226
      *isNew             = 0;
227
      deleteURL(session->url);
228
      session->url       = url;
229
    } else if (!cgiSessionKey && sessionKey && *sessionKey) {
230
      *isNew             = 0;
231
      debug("Failed to find session: %s", sessionKey);
232
      deleteURL(url);
233
    } else {
234
      // First contact. Create session, now.
235
      check(sessionKey   = cgiSessionKey ? strdup(cgiSessionKey)
236
                                         : newSessionKey());
237
      session            = newSession(sessionKey, httpGetServer(http), url,
238
                                      httpGetPeerName(http));
239
      addToHashMap(sessions, sessionKey, (const char *)session);
240
      debug("Creating a new session: %s", sessionKey);
241
    }
242
  }
243
  return session;
244
}
245

    
246
struct Session *findSession(int *isNew, HttpConnection *http, URL *url) {
247
  return findCGISession(isNew, http, url, NULL);
248
}
249

    
250
void iterateOverSessions(int (*fnc)(void *, const char *, char **), void *arg){
251
  iterateOverHashMap(sessions, fnc, arg);
252
}
253

    
254
int numSessions(void) {
255
  return getHashmapSize(sessions);
256
}
(29-29/43)