/* * Copyright (c) 2003-2016 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "cgi.h" #include "flock.h" #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" /* Initialize the session manager. */ int CGI_SessionMgrInit(void) { SetGlobal("CGI_USERNAME_MAX", "%d", CGI_USERNAME_MAX); SetGlobal("CGI_PASSWORD_MAX", "%d", CGI_PASSWORD_MAX); SetGlobal("CGI_EMAIL_MAX", "%d", CGI_EMAIL_MAX); if (mkdir(_PATH_SESSIONS, 0700) == -1 && errno != EEXIST) { CGI_Log(CGI_LOG_EMERG, "%s: %s", _PATH_SESSIONS, CGI_GetError()); return (-1); } return (0); } /* Initialize a session structure. */ void CGI_SessionInit(CGI_Session *sess, CGI_SessionOps *ops) { sess->ops = ops; sess->id[0] = '\0'; TAILQ_INIT(&sess->vars); if (ops->init != NULL) ops->init(sess); } /* Release a session structure. */ void CGI_SessionDestroy(CGI_Session *sess) { CGI_SessionVar *var, *nvar; if (sess->ops->destroy != NULL) { sess->ops->destroy(sess); } for (var = TAILQ_FIRST(&sess->vars); var != TAILQ_END(&sess->vars); var = nvar) { nvar = TAILQ_NEXT(var, vars); free(var); } } /* Free resources allocated by the session manager. */ void CGI_SessionMgrDestroy(void) { /* Nothing to do */ } /* Terminate a session gracefully. */ void CGI_CloseSession(CGI_Session *sess) { char path[MAXPATHLEN]; int i; if (sess->ops->sessClose != NULL) { sess->ops->sessClose(sess); } for (i = 0; i < nCgiModules; i++) { CGI_Module *mod = cgiModules[i]; if (mod->sessClose != NULL) mod->sessClose(sess); } Strlcpy(path, _PATH_SESSIONS, sizeof(path)); Strlcat(path, sess->id, sizeof(path)); unlink(path); Strlcpy(path, _PATH_SESSIONS, sizeof(path)); Strlcat(path, sess->id, sizeof(path)); Strlcat(path, ".sock", sizeof(path)); unlink(path); } /* Load session information from disk. */ int CGI_SessionLoad(void *pSess, int fd) { CGI_Session *sess = pSess; Uint32 i, count; if (SYS_ReadUint32(fd) != CGI_SESSION_DATA_MAGIC) { CGI_SetError("Bad magic"); return (-1); } if ((count = SYS_ReadUint32(fd)) == 0 || count > CGI_SESSION_VARIABLES_MAX) { CGI_SetError("Bad SV count: %u", (Uint)count); return (-1); } for (i = 0; i < count; i++) { CGI_SessionVar *sv; if (!(sv = TryMalloc(sizeof(CGI_SessionVar)))) { return (-1); } SYS_CopyString(sv->key, fd, sizeof(sv->key)); SYS_CopyString(sv->value, fd, sizeof(sv->value)); TAILQ_INSERT_TAIL(&sess->vars, sv, vars); } /* Read any additional session-manager-specific data. */ if (sess->ops->load != NULL && sess->ops->load(sess, fd) == -1) { return (-1); } return (0); } /* Save session information to disk. */ int CGI_SessionSaveToFD(void *pSess, int exFd) { CGI_Session *sess = pSess; CGI_SessionVar *sv; int fd, openedFile; Uint32 count; if (exFd != -1) { fd = exFd; openedFile = 0; } else { char path[MAXPATHLEN]; Strlcpy(path, _PATH_SESSIONS, sizeof(path)); Strlcat(path, sess->id, sizeof(path)); if ((fd = open(path, O_WRONLY|O_CREAT|O_EXLOCK, 0600)) == -1) { CGI_SetError("%s: %s", path, strerror(errno)); return (-1); } openedFile = 1; } SYS_WriteUint32(fd, CGI_SESSION_DATA_MAGIC); count = 0; TAILQ_FOREACH(sv, &sess->vars, vars) { /* XXX */ count++; } SYS_WriteUint32(fd, count); TAILQ_FOREACH(sv, &sess->vars, vars) { SYS_WriteString(fd, sv->key); SYS_WriteString(fd, sv->value); } /* Write any extra session-manager specific data */ if (sess->ops->save != NULL) { sess->ops->save(sess, fd); } if (openedFile) { close(fd); } return (0); } /* Update a variable in every session opened by a given user. */ int CGI_SetSV_ALL(CGI_SessionOps *sessOps, const char *user, const char *key, const char *val) { char path[MAXPATHLEN]; struct dirent *dent; CGI_Session *sess; const char *sessUser; DIR *dir; int fd; if ((dir = opendir(_PATH_SESSIONS)) == NULL) { CGI_SetError("%s: %s", _PATH_SESSIONS, strerror(errno)); return (-1); } while ((dent = readdir(dir)) != NULL) { if (strchr(dent->d_name, '.') != NULL) { continue; } Strlcpy(path, _PATH_SESSIONS, sizeof(path)); Strlcat(path, dent->d_name, sizeof(path)); /* Read session data */ if ((fd = open(path, O_RDONLY|O_EXLOCK)) == -1) { CGI_LogErr("%s: %s (skipping)", dent->d_name, strerror(errno)); continue; } if ((sess = TryMalloc(sessOps->size)) == NULL) { close(fd); continue; } CGI_SessionInit(sess, sessOps); CGI_SessionLoad(sess, fd); close(fd); if ((sessUser = CGI_GetSV(sess, "user")) == NULL || strcmp(sessUser, user) != 0) { free(sess); continue; } /* Update entry and write session to disk. */ if ((fd = open(path, O_WRONLY|O_EXCL)) == -1) { CGI_LogErr("%s: %s (not updating)", dent->d_name, strerror(errno)); free(sess); continue; } /* CGI_LogDebug("SetSV: %s => %s", dent->d_name, key); */ CGI_SetSV_S(sess, key, val); if (CGI_SessionSaveToFD(sess, fd) == -1) { CGI_LogErr("%s: %s", dent->d_name, CGI_GetError()); } close(fd); free(sess); } closedir(dir); return (0); } /* Set the value of a session variable. */ void CGI_SetSV(void *pSess, const char *key, const char *fmt, ...) { CGI_Session *sess = pSess; CGI_SessionVar *var; va_list ap; TAILQ_FOREACH(var, &sess->vars, vars) { if (strcmp(var->key, key) == 0) break; } if (var == NULL) { var = Malloc(sizeof(CGI_SessionVar)); Strlcpy(var->key, key, sizeof(var->key)); TAILQ_INSERT_TAIL(&sess->vars, var, vars); } va_start(ap, fmt); vsnprintf(var->value, sizeof(var->value), fmt, ap); va_end(ap); } /* Set the value of a session variable. */ void CGI_SetSV_S(void *pSess, const char *key, const char *s) { CGI_Session *sess = pSess; CGI_SessionVar *var; TAILQ_FOREACH(var, &sess->vars, vars) { if (strcmp(var->key, key) == 0) break; } if (var == NULL) { var = Malloc(sizeof(CGI_SessionVar)); Strlcpy(var->key, key, sizeof(var->key)); TAILQ_INSERT_TAIL(&sess->vars, var, vars); } Strlcpy(var->value, s, sizeof(var->value)); }