bool_t check_tokyo_tank(check_tank_opt_t *opts) { inx_t *check_collection = NULL; if (!(check_collection = inx_alloc(M_INX_LINKED, &mm_free))) { return false; } else if (!check_tokyo_tank_load(tank_check_data_path, check_collection, opts)) { inx_free(check_collection); return false; } else if (!check_tokyo_tank_verify(check_collection)) { inx_free(check_collection); return false; } else if (TANK_CHECK_DATA_CLEANUP && !check_tokyo_tank_cleanup(check_collection)) { inx_free(check_collection); return false; } inx_free(check_collection); return true; }
bool_t check_inx_append_mthread(MAGMA_INDEX inx_type, stringer_t *errmsg) { void *result; inx_t *inx = NULL; bool_t outcome = true; pthread_t *threads = NULL; if (status() && (!(inx = inx_alloc(inx_type | M_INX_LOCK_MANUAL, &ns_free)))) { st_sprint(errmsg, "An error occured during initial allocation in the inx check append multi-threaded test."); outcome = false; } else { if (!INX_CHECK_MTHREADS || !(threads = mm_alloc(sizeof(pthread_t) * INX_CHECK_MTHREADS))) { outcome = false; } else { for (uint64_t counter = 0; counter < INX_CHECK_MTHREADS; counter++) { if (thread_launch(threads + counter, &check_inx_append_mthread_test, inx)) { st_sprint(errmsg, "An error occured when launching a thread."); outcome = false; } } for (uint64_t counter = 0; counter < INX_CHECK_MTHREADS; counter++) { if (thread_result(*(threads + counter), &result) || !result || !*(bool_t *)result) { if (st_empty(errmsg)) st_sprint(errmsg, "One of the append check threads returned false."); outcome = false; } mm_cleanup(result); } mm_free(threads); } if (inx_count(inx) != 0 && st_empty(errmsg)) { st_sprint(errmsg, "The index was not properly cleared."); outcome = false; } } if (inx) { inx_cleanup(inx); } return outcome; }
bool_t check_inx_append_sthread(MAGMA_INDEX inx_type, stringer_t *errmsg) { inx_t *inx = NULL; bool_t outcome = true; if (status() && (!(inx = inx_alloc(inx_type | M_INX_LOCK_MANUAL, &ns_free)))) { st_sprint(errmsg, "An error occured during initial allocation in the inx check append single-threaded test."); outcome = false; } else if(!check_inx_append_helper(inx)) { st_sprint(errmsg, "An error occured inside append test helper."); outcome = false; } if (outcome && inx_count(inx) != 0) { st_sprint(errmsg, "The index was not properly cleared."); outcome = false; } inx_cleanup(inx); return outcome; }
/** * @brief Create a new session for a given web connection. * @note The session stores the following data points: remote IP address, request path, application name, the specified http hostname, * the remote client's user agent string, the server's host number, a unique session id, the server's current timestamp, a randomly- * generated session key for authentication, and an encrypted token for the session returned to the user as a cookie. * @param con a pointer to the connection underlying the web session. * @param path a pointer to a managed string containing the pathname of the generating request (should be "/portal/camel"). * @param application a pointer to a managed string containing the name of the parent application of the session (should be "portal"). * @return NULL on failure or a pointer to a newly allocated session object for the specified connection. */ session_t *sess_create(connection_t *con, stringer_t *path, stringer_t *application) { session_t *output; multi_t key = { .type = M_TYPE_UINT64, .val.u64 = 0 }; if (!(output = mm_alloc(sizeof(session_t)))) { log_pedantic("Unable to allocate %zu bytes for a session context.", sizeof(session_t)); return NULL; } else if (pthread_mutex_init(&(output->lock), NULL) != 0) { log_pedantic("Unable to initialize reference lock for new user session."); mm_free(output); return NULL; } else if (!(output->compositions = inx_alloc(M_INX_LINKED, &sess_release_composition))) { log_pedantic("Unable to allocate space for user session's compositions."); mm_free(output); return NULL; } if (!(ip_copy(&(output->warden.ip), con_addr(con, MEMORYBUF(64)))) || (path && !(output->request.path = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, path))) || (application && !(output->request.application = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, application))) || (con->http.host && !(output->request.host = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, con->http.host))) || (con->http.agent && !(output->warden.agent = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, con->http.agent))) || !(output->warden.host = magma.host.number) || !(key.val.u64 = output->warden.number = sess_number()) || !(output->warden.stamp = time(NULL)) || !(output->warden.key = sess_key()) || !(output->warden.token = sess_token(output))) { log_pedantic("Unable to initialize the session warden context."); sess_destroy(output); return NULL; } output->request.httponly = true; output->request.secure = (con_secure(con) == 1 ? true : false); sess_ref_add(output); if (inx_insert(objects.sessions, key, output) != 1) { log_pedantic("Unable to insert the session into the global context."); sess_ref_dec(output); sess_destroy(output); return NULL; } return output; } /** * @brief Try to retrieve the session associated with a client connection's supplied cookie. * @param con a pointer to the connection object sending the cookie. * @param application a managed string containing the application associated with the session. * @param path a managed string containing the path associated with the session. * @param token the encrypted user token retrieved from the supplied http cookie. * @return 1 if the cookie was found and valid, or one of the following values on failure: * 0 = Session not found. * -1 = Server error. * -2 = Invalid token. * -3 = Security violation / incorrect user-agent. * -4 = Security violation / incorrect session key. * -5 = Security violation / incorrect source address. * -6 = Session terminated by logout. * -7 = Session timed out. */ int_t sess_get(connection_t *con, stringer_t *application, stringer_t *path, stringer_t *token) { uint64_t *numbers; scramble_t *scramble; stringer_t *binary, *encrypted; multi_t key = { .type = M_TYPE_UINT64, .val.u64 = 0 }; int_t result = 1; /// Most session attributes need simple equality comparison, except for timeout checking. Make sure not to validate against a stale session that should have already timed out (which will have to be determined dynamically). encrypted = zbase32_decode(token); scramble = scramble_import(encrypted); binary = scramble_decrypt(magma.secure.sessions, scramble); st_cleanup(encrypted); if (!binary) { return 0; } numbers = st_data_get(binary); // QUESTION: Is this necessary? doesn't inx_find() lock the inx? inx_lock_read(objects.sessions); key.val.u64 = *(numbers + 2); if ((con->http.session = inx_find(objects.sessions, key))) { sess_ref_add(con->http.session); } inx_unlock(objects.sessions); st_free(binary); // Return if we didn't find the session or user. if (!con->http.session || !con->http.session->user) { return 0; } // We need to do full validation against the cookie and associated session. // First, the cookie. if ((*numbers != con->http.session->warden.host) || (*(numbers + 1) != con->http.session->warden.stamp) || (*(numbers + 2) != con->http.session->warden.number)) { log_error("Received mismatched cookie for authenticated session { user = %s }", st_char_get(con->http.session->user->username)); result = -2; } else if (*(numbers + 3) != con->http.session->warden.key) { log_error("Cookie contained an incorrect session key { user = %s }", st_char_get(con->http.session->user->username)); result = -4; } else if (st_cmp_cs_eq(application, con->http.session->request.application)) { log_error("Cookie did not match session's application { user = %s }", st_char_get(con->http.session->user->username)); result = -2; } else if (st_cmp_cs_eq(path, con->http.session->request.path)) { log_error("Cookie did not match session's path { user = %s }", st_char_get(con->http.session->user->username)); result = -2; } else if (st_cmp_cs_eq(con->http.agent, con->http.session->warden.agent)) { log_error("Cookie contained a mismatched user agent { user = %s }", st_char_get(con->http.session->user->username)); result = -3; } else if (con->http.session->request.secure != (con_secure(con) ? 1 : 0)) { log_error("Cookie was submitted from a mismatched transport layer { user = %s }", st_char_get(con->http.session->user->username)); result = -5; } else if (!ip_address_equal(&(con->http.session->warden.ip), (ip_t *)con_addr(con, MEMORYBUF(64)))) { log_error("Cookie was submitted from a mismatched IP address { user = %s }", st_char_get(con->http.session->user->username)); result = -5; } // Finally, do comparisons to see that we haven't timed out. /* Did we expire? */ if (magma.http.session_timeout <= (time(NULL) - con->http.session->warden.stamp)) { log_pedantic("User submitted expired or invalidated cookie; marking for deletion { user = %s }", st_char_get(con->http.session->user->username)); result = -7; } // QUESTION: This destruction needs a second look. if (result < 0) { if (!inx_delete(objects.sessions, key)) { log_pedantic("Unexpected error occurred attempting to delete expired cookie { user = %s }", st_char_get(con->http.session->user->username)); } sess_ref_dec(con->http.session); //sess_destroy(con->http.session); con->http.session = NULL; } // Otherwise, if the last session status update is more than 10 minutes ago, check now to see if things are current. // QUESTION: Why is it 600 here and 120 elsewhere? else if ((time(NULL) - sess_refresh_stamp(con->http.session)) > 600) { sess_update(con->http.session); } return result; }
/** * @brief Parse a json string array into a linked list of managed strings. * @param json a json object containing an array of strings. * @param nout if not NULL, an optional pointer to a size_t to receive the item count of the json string array. * @return NULL on failure, or a pointer to an inx holder containing the specified json array contents as a collection of managed strings. */ inx_t * portal_parse_json_str_array (json_t *json, size_t *nout) { inx_t *result; stringer_t *istr; const chr_t *str; size_t count; multi_t key = { .type = M_TYPE_UINT64, .val.u64 = 0 }; if (!json) { log_pedantic("Portal cannot parse null json array."); return NULL; // The json object must be an array. } else if (!json_is_array(json)) { log_pedantic("Portal cannot parse mistyped json array."); return NULL; } else if (!(result = inx_alloc(M_INX_LINKED, st_free))) { log_error("Portal could not allocate space to parse json array."); return NULL; } // If it's an empty array, return right away. if (!(count = json_array_size_d(json))) { if (nout) { *nout = 0; } return result; } // Before starting a SQL transaction check that all of the array values are positive integer values. for (size_t i = 0; i < count; i++) { if (!json_is_string(json_array_get_d(json, i))) { log_pedantic("Portal cannot parse json string array with non-string element."); inx_free(result); return NULL; } if (!(str = json_string_value_d(json_array_get_d(json, i)))) { log_pedantic("Portal encountered json string array with NULL element."); inx_free(result); return NULL; } if (!(istr = st_import(str, ns_length_get(str)))) { log_error("Portal could not import string from json array."); inx_free(result); return NULL; } key.val.u64 = i+1; if (!inx_insert(result, key, istr)) { log_error("Portal could not prepare data from json array."); inx_free(result); return NULL; } } if (nout) { *nout = count; } return result; } /** * @brief Parse the context of a requested folder. * @note If no context is specified, the "mail" context is assumed (PORTAL_ENDPOINT_CONTEXT_MAIL). * @param context a managed string containing the context (supported values are "mail", "contacts", "settings", and "help"). * @return PORTAL_ENDPOINT_CONTEXT_INVALID on failure, or the flag of the determined context on success. */ int_t portal_parse_context(stringer_t *context) { int_t result = PORTAL_ENDPOINT_CONTEXT_MAIL; if (!context) { return result; } // Parse the context. if (!st_cmp_ci_eq(context, PLACER("mail", 4))) { result = PORTAL_ENDPOINT_CONTEXT_MAIL; } else if (!st_cmp_ci_eq(context, PLACER("contacts", 8))) { result = PORTAL_ENDPOINT_CONTEXT_CONTACTS; } else if (!st_cmp_ci_eq(context, PLACER("settings", 8))) { result = PORTAL_ENDPOINT_CONTEXT_SETTINGS; } else if (!st_cmp_ci_eq(context, PLACER("help", 4))) { result = PORTAL_ENDPOINT_CONTEXT_HELP; } else { result = PORTAL_ENDPOINT_CONTEXT_INVALID; } return result; }