diff --git a/libhttp/http.h b/libhttp/http.h index 07e235d..c754a29 100644 --- a/libhttp/http.h +++ b/libhttp/http.h @@ -127,6 +127,7 @@ Server *httpGetServer(const HttpConnection *http); ServerConnection *httpGetServerConnection(const HttpConnection *); int httpGetFd(const HttpConnection *http); const char *httpGetPeerName(const HttpConnection *http); +const char *httpGetRealIP(const HttpConnection *http); const char *httpGetMethod(const HttpConnection *http); const char *httpGetVersion(const HttpConnection *http); const HashMap *httpGetHeaders(const HttpConnection *http); diff --git a/libhttp/httpconnection.c b/libhttp/httpconnection.c index 1bdc376..043a712 100644 --- a/libhttp/httpconnection.c +++ b/libhttp/httpconnection.c @@ -1889,6 +1889,10 @@ const char *httpGetPeerName(const struct HttpConnection *http) { return http->peerName; } +const char *httpGetRealIP(const struct HttpConnection *http) { + return getFromHashMap(&http->header, "x-real-ip"); +} + const char *httpGetMethod(const struct HttpConnection *http) { return http->method; } diff --git a/libhttp/httpconnection.h b/libhttp/httpconnection.h index eb503aa..f28513a 100644 --- a/libhttp/httpconnection.h +++ b/libhttp/httpconnection.h @@ -148,6 +148,7 @@ struct Server *httpGetServer(const struct HttpConnection *http); struct ServerConnection *httpGetServerConnection(const struct HttpConnection*); int httpGetFd(const HttpConnection *http); const char *httpGetPeerName(const struct HttpConnection *http); +const char *httpGetRealIP(const struct HttpConnection *http); const char *httpGetMethod(const struct HttpConnection *http); const char *httpGetProtocol(const struct HttpConnection *http); const char *httpGetHost(const struct HttpConnection *http); diff --git a/shellinabox/launcher.c b/shellinabox/launcher.c index 587fa0e..f5eba21 100644 --- a/shellinabox/launcher.c +++ b/shellinabox/launcher.c @@ -523,8 +523,12 @@ int launchChild(int service, struct Session *session, const char *url) { request->terminate = -1; request->width = session->width; request->height = session->height; - strncat(request->peerName, httpGetPeerName(session->http), - sizeof(request->peerName) - 1); + const char *peerName = httpGetPeerName(session->http); + strncat(request->peerName, peerName, sizeof(request->peerName) - 1); + const char *realIP = httpGetRealIP(session->http); + if (realIP && *realIP) { + strncat(request->realIP, realIP, sizeof(request->realIP) - 1); + } request->urlLength = strlen(u); memcpy(&request->url, u, request->urlLength); free(u); @@ -597,7 +601,7 @@ struct Utmp { static HashMap *childProcesses; void initUtmp(struct Utmp *utmp, int useLogin, const char *ptyPath, - const char *peerName) { + const char *peerName, const char *realIP) { memset(utmp, 0, sizeof(struct Utmp)); utmp->pty = -1; utmp->useLogin = useLogin; @@ -609,7 +613,11 @@ void initUtmp(struct Utmp *utmp, int useLogin, const char *ptyPath, strncat(&utmp->utmpx.ut_line[0], ptyPath + 5, sizeof(utmp->utmpx.ut_line) - 1); strncat(&utmp->utmpx.ut_id[0], ptyPath + 8, sizeof(utmp->utmpx.ut_id) - 1); strncat(&utmp->utmpx.ut_user[0], "SHELLINABOX", sizeof(utmp->utmpx.ut_user) - 1); - strncat(&utmp->utmpx.ut_host[0], peerName, sizeof(utmp->utmpx.ut_host) - 1); + char remoteHost[256]; + snprintf(remoteHost, 256, + (*realIP) ? "%s, %s" : "%s%s", peerName, + (*realIP) ? realIP : ""); + strncat(&utmp->utmpx.ut_host[0], remoteHost, sizeof(utmp->utmpx.ut_host) - 1); struct timeval tv; check(!gettimeofday(&tv, NULL)); utmp->utmpx.ut_tv.tv_sec = tv.tv_sec; @@ -618,10 +626,10 @@ void initUtmp(struct Utmp *utmp, int useLogin, const char *ptyPath, } struct Utmp *newUtmp(int useLogin, const char *ptyPath, - const char *peerName) { + const char *peerName, const char *realIP) { struct Utmp *utmp; check(utmp = malloc(sizeof(struct Utmp))); - initUtmp(utmp, useLogin, ptyPath, peerName); + initUtmp(utmp, useLogin, ptyPath, peerName, realIP); return utmp; } @@ -776,7 +784,7 @@ static int ptsname_r(int fd, char *buf, size_t buflen) { #endif static int forkPty(int *pty, int useLogin, struct Utmp **utmp, - const char *peerName) { + const char *peerName, const char *realIP) { int slave; #ifdef HAVE_OPENPTY char* ptyPath = NULL; @@ -857,7 +865,7 @@ static int forkPty(int *pty, int useLogin, struct Utmp **utmp, #endif // Fill in utmp entry - *utmp = newUtmp(useLogin, ptyPath, peerName); + *utmp = newUtmp(useLogin, ptyPath, peerName, realIP); // Now, fork off the child process pid_t pid; @@ -1250,7 +1258,8 @@ static void destroyVariableHashEntry(void *arg ATTR_UNUSED, char *key, static void execService(int width ATTR_UNUSED, int height ATTR_UNUSED, struct Service *service, const char *peerName, - char **environment, const char *url) { + const char *realIP, char **environment, + const char *url) { UNUSED(width); UNUSED(height); @@ -1287,6 +1296,9 @@ static void execService(int width ATTR_UNUSED, int height ATTR_UNUSED, check(key = strdup("peer")); check(value = strdup(peerName)); addToHashMap(vars, key, value); + check(key = strdup("realip")); + check(value = strdup(realIP)); + addToHashMap(vars, key, value); check(key = strdup("uid")); addToHashMap(vars, key, stringPrintf(NULL, "%d", service->uid)); check(key = strdup("url")); @@ -1476,7 +1488,7 @@ void setWindowSize(int pty, int width, int height) { } static void childProcess(struct Service *service, int width, int height, - struct Utmp *utmp, const char *peerName, + struct Utmp *utmp, const char *peerName, const char *realIP, const char *url) { // Set initial window size setWindowSize(0, width, height); @@ -1504,6 +1516,18 @@ static void childProcess(struct Service *service, int width, int height, legalEnv[i], value); } } + + // Add useful environment variables that can be used in custom client scripts + // or programs. + numEnvVars += 3; + check(environment = realloc(environment, + (numEnvVars + 1)*sizeof(char *))); + environment[numEnvVars-3] = stringPrintf(NULL, "SHELLINABOX_URL=%s", + url); + environment[numEnvVars-2] = stringPrintf(NULL, "SHELLINABOX_PEERNAME=%s", + peerName); + environment[numEnvVars-1] = stringPrintf(NULL, "SHELLINABOX_REALIP=%s", + realIP); environment[numEnvVars] = NULL; // Set initial terminal settings @@ -1603,12 +1627,20 @@ static void childProcess(struct Service *service, int width, int height, // Finally, launch the child process. if (service->useLogin == 1) { - execle("/bin/login", "login", "-p", "-h", peerName, + // At login service launch, we try to pass real IP in '-h' parameter. Real + // IP is provided in HTTP header field 'X-Real-IP', if ShellInABox is used + // behind properly configured HTTP proxy. + char remoteHost[256]; + snprintf(remoteHost, 256, + (*realIP) ? "%s, %s" : "%s%s", peerName, + (*realIP) ? realIP : ""); + execle("/bin/login", "login", "-p", "-h", remoteHost, (void *)0, environment); - execle("/usr/bin/login", "login", "-p", "-h", peerName, + execle("/usr/bin/login", "login", "-p", "-h", remoteHost, (void *)0, environment); } else { - execService(width, height, service, peerName, environment, url); + // Launch user provied service + execService(width, height, service, peerName, realIP, environment, url); } _exit(1); } @@ -1666,9 +1698,10 @@ static void launcherDaemon(int fd) { if (kill(request.terminate, SIGTERM) == 0) { debug("Terminating child %d (kill)", request.terminate); } else { - debug("Terminating child failed [%s]", strerror(errno)); + debug("Terminating child %d failed [%s]", request.terminate, + strerror(errno)); } - } + } continue; } @@ -1696,8 +1729,8 @@ static void launcherDaemon(int fd) { check(request.service >= 0); check(request.service < numServices); - // Sanitize the host name, so that we do not pass any unexpected characters - // to our child process. + // Sanitize peer name and real IP, so that we do not pass any unexpected + // characters to our child process. request.peerName[sizeof(request.peerName)-1] = '\000'; for (char *s = request.peerName; *s; s++) { if (!((*s >= '0' && *s <= '9') || @@ -1708,14 +1741,26 @@ static void launcherDaemon(int fd) { } } + request.realIP[sizeof(request.realIP)-1] = '\000'; + for (char *s = request.realIP; *s; s++) { + if (!((*s >= '0' && *s <= '9') || + (*s >= 'A' && *s <= 'Z') || + (*s >= 'a' && *s <= 'z') || + *s == '.' || *s == '-')) { + *s = '-'; + } + } + // Fork and exec the child process. int pty; struct Utmp *utmp; if ((pid = forkPty(&pty, services[request.service]->useLogin, - &utmp, request.peerName)) == 0) { + &utmp, + request.peerName, + request.realIP)) == 0) { childProcess(services[request.service], request.width, request.height, - utmp, request.peerName, url); + utmp, request.peerName, request.realIP, url); free(url); _exit(1); } else { diff --git a/shellinabox/launcher.h b/shellinabox/launcher.h index 2d7d145..64b7519 100644 --- a/shellinabox/launcher.h +++ b/shellinabox/launcher.h @@ -56,6 +56,7 @@ struct LaunchRequest { int width, height; pid_t terminate; char peerName[128]; + char realIP[128]; int urlLength; char url[0]; }; diff --git a/shellinabox/shellinaboxd.c b/shellinabox/shellinaboxd.c index bf44d2e..0df94bf 100644 --- a/shellinabox/shellinaboxd.c +++ b/shellinabox/shellinaboxd.c @@ -800,6 +800,7 @@ static void usage(void) { " ${home} - home directory\n" " ${lines} - number of rows\n" " ${peer} - name of remote peer\n" + " ${realip} - value of HTTP header field 'X-Real-IP'\n" " ${uid} - user id\n" " ${url} - the URL that serves the terminal session\n" " ${user} - user name\n" diff --git a/shellinabox/shellinaboxd.man.in b/shellinabox/shellinaboxd.man.in index 0e401a6..082b37e 100644 --- a/shellinabox/shellinaboxd.man.in +++ b/shellinabox/shellinaboxd.man.in @@ -449,6 +449,9 @@ number of rows. .B ${peer} name of remote peer. .TP +.B ${realip} +value of HTTP header field 'X-Real-IP'. +.TP .B ${uid} numeric user id. .TP @@ -458,11 +461,14 @@ the URL that serves the terminal session. .B ${user} user name. .P -Other than the default environment variables of +Other than the environment variables of .BR $TERM , -.B $COLUMNS +.B $COLUMNS, +.B $LINES, +.B $SHELLINABOX_PEERNAME, +.B $SHELLINABOX_REALIP and -.BR $LINES , +.BR $SHELLINABOX_URL, services can have environment variables passed to them, by preceding the with space separated variable assignments of the form .IR KEY = VALUE .