SLNSessionRef SLNSessionCreateInternal(SLNSessionCacheRef const cache, uint64_t const sessionID, byte_t const *const sessionKeyRaw, byte_t const *const sessionKeyEnc, uint64_t const userID, SLNMode const mode_trusted, strarg_t const username) { assert(cache); if(!mode_trusted) return NULL; SLNSessionRef session = calloc(1, sizeof(struct SLNSession)); if(!session) return NULL; session->cache = cache; session->sessionID = sessionID; session->sessionKeyRaw = malloc(SESSION_KEY_LEN); session->sessionKeyEnc = malloc(SESSION_KEY_LEN); if(!session->sessionKeyRaw || !session->sessionKeyEnc) { SLNSessionRelease(&session); return NULL; } if(sessionKeyRaw) { memcpy(session->sessionKeyRaw, sessionKeyRaw, SESSION_KEY_LEN); } else { FREE(&session->sessionKeyRaw); } if(sessionKeyEnc) { memcpy(session->sessionKeyEnc, sessionKeyEnc, SESSION_KEY_LEN); } else if(sessionKeyRaw) { byte_t buf[SHA256_DIGEST_LENGTH]; SHA256(sessionKeyRaw, SESSION_KEY_LEN, buf); memcpy(session->sessionKeyEnc, buf, SESSION_KEY_LEN); } else { FREE(&session->sessionKeyEnc); } session->userID = userID; session->mode = mode_trusted; session->username = username ? strdup(username) : NULL; session->refcount = 1; return session; }
static int listener0(void *ctx, HTTPServerRef const server, HTTPConnectionRef const conn) { HTTPMethod method; str_t URI[URI_MAX]; ssize_t len = HTTPConnectionReadRequest(conn, &method, URI, sizeof(URI)); if(UV_EOF == len) { // HACK: Force the connection to realize it's dead. // Otherwise there is a timeout period of like 15-20 seconds // and we can run out of file descriptors. I suspect this // is a bug with libuv, but I'm not sure. HTTPConnectionWrite(conn, (byte_t const *)STR_LEN("x")); HTTPConnectionFlush(conn); return 0; } if(UV_EMSGSIZE == len) return 414; // Request-URI Too Large if(len < 0) { fprintf(stderr, "Request error: %s\n", uv_strerror(len)); return 500; } HTTPHeadersRef headers; int rc = HTTPHeadersCreateFromConnection(conn, &headers); if(UV_EMSGSIZE == rc) return 431; // Request Header Fields Too Large if(rc < 0) return 500; strarg_t const host = HTTPHeadersGet(headers, "host"); str_t domain[1023+1]; domain[0] = '\0'; if(host) sscanf(host, "%1023[^:]", domain); // TODO: Verify Host header to prevent DNS rebinding. if(SERVER_PORT_TLS && server == server_raw) { // Redirect from HTTP to HTTPS if('\0' == domain[0]) return 400; strarg_t const port = SERVER_PORT_TLS; str_t loc[URI_MAX]; rc = snprintf(loc, sizeof(loc), "https://%s:%s%s", domain, port, URI); if(rc >= sizeof(loc)) 414; // Request-URI Too Large if(rc < 0) return 500; HTTPConnectionSendRedirect(conn, 301, loc); return 0; } strarg_t const cookie = HTTPHeadersGet(headers, "cookie"); SLNSessionCacheRef const cache = SLNRepoGetSessionCache(repo); SLNSessionRef session = NULL; rc = SLNSessionCacheCopyActiveSession(cache, cookie, &session); if(rc < 0) return 500; // Note: null session is valid (zero permissions). rc = -1; rc = rc >= 0 ? rc : SLNServerDispatch(repo, session, conn, method, URI, headers); rc = rc >= 0 ? rc : BlogDispatch(blog, session, conn, method, URI, headers); SLNSessionRelease(&session); HTTPHeadersFree(&headers); return rc; }
static int POST_auth(BlogRef const blog, SLNSessionRef const session, HTTPConnectionRef const conn, HTTPMethod const method, strarg_t const URI, HTTPHeadersRef const headers) { if(HTTP_POST != method) return -1; if(0 != uripathcmp("/auth", URI, NULL)) return -1; // TODO: Check that Content-Type is application/x-www-form-urlencoded. str_t formdata[AUTH_FORM_MAX]; ssize_t len = HTTPConnectionReadBodyStatic(conn, (byte_t *)formdata, sizeof(formdata)-1); if(UV_EMSGSIZE == len) return 413; // Request Entity Too Large if(len < 0) return 500; formdata[len] = '\0'; SLNSessionCacheRef const cache = SLNRepoGetSessionCache(blog->repo); static strarg_t const fields[] = { "action-login", "action-register", "user", "pass", "token", // TODO: CSRF protection }; str_t *values[numberof(fields)] = {}; QSValuesParse(formdata, values, fields, numberof(fields)); if(values[1]) { QSValuesCleanup(values, numberof(values)); return 501; // TODO: Not Implemented } if(!values[0]) { QSValuesCleanup(values, numberof(values)); return 400; // Not login? } SLNSessionRef s; int rc = SLNSessionCacheCreateSession(cache, values[2], values[3], &s); // TODO QSValuesCleanup(values, numberof(values)); if(rc < 0) { HTTPConnectionSendRedirect(conn, 303, "/account?err=1"); return 0; } str_t *cookie = SLNSessionCopyCookie(s); SLNSessionRelease(&s); if(!cookie) return 500; HTTPConnectionWriteResponse(conn, 303, "See Other"); HTTPConnectionWriteHeader(conn, "Location", "/"); HTTPConnectionWriteSetCookie(conn, cookie, "/", 60 * 60 * 24 * 365); HTTPConnectionWriteContentLength(conn, 0); HTTPConnectionBeginBody(conn); HTTPConnectionEnd(conn); FREE(&cookie); return 0; }
int SLNSessionCreateSession(SLNSessionRef const session, SLNSessionRef *const out) { assert(out); if(!session) return DB_EACCES; SLNSessionCacheRef const cache = session->cache; uint64_t const userID = session->userID; SLNMode const mode = session->mode; strarg_t const username = session->username; DB_env *db = NULL; DB_txn *txn = NULL; SLNSessionRef alt = NULL; byte_t key_raw[SESSION_KEY_LEN]; int rc = async_random(key_raw, sizeof(key_raw)); if(rc < 0) goto cleanup; byte_t key_enc[SHA256_DIGEST_LENGTH]; SHA256(key_raw, sizeof(key_raw), key_enc); str_t key_str[SESSION_KEY_HEX+1]; tohex(key_str, key_enc, SESSION_KEY_LEN); key_str[SESSION_KEY_HEX] = '\0'; rc = SLNSessionDBOpen(session, SLN_RDWR, &db); // TODO: Custom permission? if(rc < 0) goto cleanup; rc = db_txn_begin(db, NULL, DB_RDWR, &txn); if(rc < 0) goto cleanup; uint64_t const sessionID = db_next_id(SLNSessionByID, txn); DB_val key[1], val[1]; SLNSessionByIDKeyPack(key, txn, sessionID); SLNSessionByIDValPack(val, txn, userID, key_str); rc = db_put(txn, key, val, DB_NOOVERWRITE_FAST); if(rc < 0) goto cleanup; rc = db_txn_commit(txn); txn = NULL; SLNSessionDBClose(session, &db); if(rc < 0) goto cleanup; rc = SLNSessionCreateInternal(cache, sessionID, key_raw, key_enc, userID, mode, username, &alt); if(rc < 0) goto cleanup; *out = alt; alt = NULL; cleanup: db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); SLNSessionRelease(&alt); return rc; }
static int listener0(void *ctx, HTTPServerRef const server, HTTPConnectionRef const conn) { HTTPMethod method; str_t URI[URI_MAX]; ssize_t len = HTTPConnectionReadRequest(conn, &method, URI, sizeof(URI)); if(UV_EOF == len) return 0; if(UV_ECONNRESET == len) return 0; if(UV_EMSGSIZE == len) return 414; // Request-URI Too Large if(len < 0) { alogf("Request error: %s\n", uv_strerror(len)); return 500; } HTTPHeadersRef headers; int rc = HTTPHeadersCreateFromConnection(conn, &headers); if(UV_EMSGSIZE == rc) return 431; // Request Header Fields Too Large if(rc < 0) return 500; strarg_t const host = HTTPHeadersGet(headers, "host"); str_t domain[1023+1]; domain[0] = '\0'; if(host) sscanf(host, "%1023[^:]", domain); // TODO: Verify Host header to prevent DNS rebinding. if(SERVER_PORT_TLS && server == server_raw) { // Redirect from HTTP to HTTPS if('\0' == domain[0]) return 400; strarg_t const port = SERVER_PORT_TLS; str_t loc[URI_MAX]; rc = snprintf(loc, sizeof(loc), "https://%s:%s%s", domain, port, URI); if(rc >= sizeof(loc)) 414; // Request-URI Too Large if(rc < 0) return 500; HTTPConnectionSendRedirect(conn, 301, loc); return 0; } strarg_t const cookie = HTTPHeadersGet(headers, "cookie"); SLNSessionCacheRef const cache = SLNRepoGetSessionCache(repo); SLNSessionRef session = NULL; rc = SLNSessionCacheCopyActiveSession(cache, cookie, &session); if(rc < 0) return 500; // Note: null session is valid (zero permissions). rc = -1; rc = rc >= 0 ? rc : SLNServerDispatch(repo, session, conn, method, URI, headers); rc = rc >= 0 ? rc : BlogDispatch(blog, session, conn, method, URI, headers); SLNSessionRelease(&session); HTTPHeadersFree(&headers); return rc; }
static void listener(void *ctx, HTTPServerRef const server, HTTPConnectionRef const conn) { assert(server); assert(conn); str_t URI[URI_MAX]; URI[0] = '\0'; HTTPHeadersRef headers = NULL; SLNSessionRef session = NULL; int rc = listener0(server, conn, URI, sizeof(URI), &headers, &session); if(rc < 0) rc = 404; if(rc > 0) HTTPConnectionSendStatus(conn, rc); strarg_t const username = SLNSessionGetUsername(session); HTTPConnectionLog(conn, URI, username, headers, SERVER_LOG_FILE); SLNSessionRelease(&session); HTTPHeadersFree(&headers); }
int SLNSessionCreateInternal(SLNSessionCacheRef const cache, uint64_t const sessionID, byte_t const *const sessionKeyRaw, byte_t const *const sessionKeyEnc, uint64_t const userID, SLNMode const mode_trusted, strarg_t const username, SLNSessionRef *const out) { assert(cache); assert(out); // 0 == sessionID is valid for "temporary" sessions. if(!mode_trusted) return UV_EACCES; // Even trusted code can't create a session without perms. SLNSessionRef session = calloc(1, sizeof(struct SLNSession)); if(!session) return UV_ENOMEM; int rc = 0; session->refcount = 1; session->cache = cache; session->sessionID = sessionID; session->sessionKeyRaw = malloc(SESSION_KEY_LEN); session->sessionKeyEnc = malloc(SESSION_KEY_LEN); if(!session->sessionKeyRaw || !session->sessionKeyEnc) rc = UV_ENOMEM; if(rc < 0) goto cleanup; if(sessionKeyRaw) { memcpy(session->sessionKeyRaw, sessionKeyRaw, SESSION_KEY_LEN); } else { FREE(&session->sessionKeyRaw); } if(sessionKeyEnc) { memcpy(session->sessionKeyEnc, sessionKeyEnc, SESSION_KEY_LEN); } else if(sessionKeyRaw) { byte_t buf[SHA256_DIGEST_LENGTH]; SHA256(sessionKeyRaw, SESSION_KEY_LEN, buf); memcpy(session->sessionKeyEnc, buf, SESSION_KEY_LEN); } else { FREE(&session->sessionKeyEnc); } session->userID = userID; session->mode = mode_trusted; session->username = username ? strdup(username) : NULL; *out = session; session = NULL; cleanup: SLNSessionRelease(&session); return rc; }
void SLNPullFree(SLNPullRef *const pullptr) { SLNPullRef pull = *pullptr; if(!pull) return; SLNPullStop(pull); pull->pullID = 0; SLNSessionRelease(&pull->session); FREE(&pull->host); FREE(&pull->cookie); // FREE(&pull->query); async_mutex_destroy(pull->connlock); async_mutex_destroy(pull->mutex); async_cond_destroy(pull->cond); pull->stop = false; assert_zeroed(pull, 1); FREE(pullptr); pull = NULL; }