1 // session.c -- Session management for HTTP/HTTPS connections
2 // Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
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.
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.
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.
17 // In addition to these license terms, the author grants the following
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.
29 // You may at your option choose to remove this additional permission from
30 // the work, or from any part of it.
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:
36 // This product includes software developed by the OpenSSL Project
37 // for use in the OpenSSL Toolkit. (http://www.openssl.org/)
39 // This product includes cryptographic software written by Eric Young
40 // (eay@cryptsoft.com)
43 // The most up-to-date version of this program is always available from
44 // http://shellinabox.com
54 #include "shellinabox/session.h"
55 #include "logging/logging.h"
58 #defined ATTR_UNUSED __attribute__((unused))
59 #defined UNUSED(x) do { } while (0)
62 #define UNUSED(x) do { (void)(x); } while (0)
65 static HashMap *sessions;
68 static struct Graveyard {
69 struct Graveyard *next;
71 const char *sessionKey;
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.
80 check(g = malloc(sizeof(struct Graveyard)));
82 g->timeout = time(NULL) + AJAX_TIMEOUT;
83 g->sessionKey = strdup(session->sessionKey);
87 static void checkGraveyardInternal(int expireAll) {
91 time_t timeout = time(NULL) - (expireAll ? 2*AJAX_TIMEOUT : 0);
92 for (struct Graveyard **g = &graveyard, *old = *g;
94 if (old->timeout < timeout) {
96 deleteFromHashMap(sessions, old->sessionKey);
97 free((char *)old->sessionKey);
106 void checkGraveyard(void) {
107 checkGraveyardInternal(0);
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;
122 session->buffered = NULL;
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);
134 void destroySession(struct Session *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));
145 void deleteSession(struct Session *session) {
146 destroySession(session);
150 void abandonSession(struct Session *session) {
151 deleteFromHashMap(sessions, session->sessionKey);
154 void finishSession(struct Session *session) {
155 deleteFromHashMap(sessions, session->sessionKey);
158 void finishAllSessions(void) {
159 checkGraveyardInternal(1);
160 deleteHashMap(sessions);
163 static void destroySessionHashEntry(void *arg ATTR_UNUSED,
164 char *key ATTR_UNUSED, char *value) {
168 deleteSession((struct Session *)value);
171 char *newSessionKey(void) {
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));
178 check(sessionKey = malloc((8*sizeof(buf) + 5)/6 + 1));
179 char *ptr = sessionKey;
182 for (unsigned i = 0;;) {
183 bits = (bits << 8) | buf[i];
187 *ptr++ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
188 "ghijklmnopqrstuvwxyz0123456789-/"
189 [(bits >> (count -= 6)) & 0x3F];
191 if (++i >= sizeof(buf)) {
192 if (count && i == sizeof(buf)) {
202 check(!sessions || !getFromHashMap(sessions, sessionKey));
206 struct Session *findCGISession(int *isNew, HttpConnection *http, URL *url,
207 const char *cgiSessionKey) {
210 sessions = newHashMap(destroySessionHashEntry, NULL);
212 const HashMap *args = urlGetArgs(url);
213 const char *sessionKey = getFromHashMap(args, "session");
214 struct Session *session= NULL;
216 (!sessionKey || strcmp(cgiSessionKey, sessionKey))) {
217 // In CGI mode, we only ever allow exactly one session with a
218 // pre-negotiated key.
221 if (sessionKey && *sessionKey) {
222 session = (struct Session *)getFromHashMap(sessions,
227 deleteURL(session->url);
229 } else if (!cgiSessionKey && sessionKey && *sessionKey) {
231 debug("Failed to find session: %s", sessionKey);
234 // First contact. Create session, now.
235 check(sessionKey = cgiSessionKey ? strdup(cgiSessionKey)
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);
246 struct Session *findSession(int *isNew, HttpConnection *http, URL *url) {
247 return findCGISession(isNew, http, url, NULL);
250 void iterateOverSessions(int (*fnc)(void *, const char *, char **), void *arg){
251 iterateOverHashMap(sessions, fnc, arg);
254 int numSessions(void) {
255 return getHashmapSize(sessions);