示例#1
0
/**
 * @brief	Get the fully expanded name of a folder.
 * @note	The folder hierarchy will be delimited with a "." character.
 * @param	folders		the inx object holding all of a user's magma folders.
 * @param	target		a pointer to the magma folder object to have its name expanded.
 * @return	NULL on failure or a pointer to a managed string with the fully expanded name of the specified folder.
 */
stringer_t * magma_folder_name(inx_t *folders, magma_folder_t *target) {

	stringer_t *result;
	int_t recursion = 0;

	if (!folders || !target || !target->foldernum || st_empty(target->name)) {
		log_pedantic("Invalid folder context. Unable to construct the name.");
		return NULL;
	}

	else if (!(result = st_dupe_opts(MANAGED_T | JOINTED | HEAP, target->name))) {
		log_pedantic("We were unable to duplicate the folder name.");
		return NULL;
	}

	while (target && target->parent != 0 && recursion++ < FOLDER_RECURSION_LIMIT) {

		// Get the parent target.
		if ((target = magma_folder_find_number(folders, target->parent)) == NULL) {
			log_pedantic("There appears to be a folder with an invalid parent.");
			return result;
		}

		// Append the seperator and then the parent name.
		result = st_append(result, PLACER(".", 1));
		result = st_append(result, target->name);
	}

	return result;
}
示例#2
0
文件: cache.c 项目: lavabit/magma
/**
 * @brief	Set the contents of the thread's mail cache.
 * @param	messagenum	the numerical id of the message to be cached.
 * @text	a managed string containing the contents of the specified message to be cached.
 * @return	This function returns no value.
 */
void mail_cache_set(uint64_t messagenum, stringer_t *text) {

	mail_cache_t *message;

	if (!(message = pthread_getspecific(mail_cache))) {

		if (!(message = mm_alloc(sizeof(mail_cache_t)))) {
			return;
		}

		if (pthread_setspecific(mail_cache, message) != 0) {
			log_pedantic("Unable to setup thread message cache.");
			mm_free(message);
			return;
		}
	}
	else {
		st_cleanup(message->text);
	}

	message->messagenum = messagenum;
	message->text = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, text);

	return;
}
示例#3
0
/**
 * @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;
}
示例#4
0
/**
 * @brief	Set the value of a global config key.
 * @note	This function will also free the value of the global config key if it has already previously been set.
 * @param	setting		a pointer to the global key to have its value adjusted.
 * @param	value		a managed string containing the new key value, or if NULL, the key's default value will be used.
 * @return	true if the specified key's value was set successfully, or false on failure.
 */
bool_t config_value_set(magma_keys_t *setting, stringer_t *value) {

	bool_t result = true;

	/// LOW: Realtime blacklist domains are handled using custom code because we don't yet have a generic type to store lists.
	if (!st_cmp_cs_eq(NULLER(setting->name), PLACER("magma.smtp.blacklist", 20))) {

		// When the default values are assigned an empty string is passed in. Returning false then tricks the system into
		// thinking the default value is wrong, so we just return true to avoid the issue.
		if (st_empty(value)) {
			return true;
		}

		// Were using a fixed array, so if we run out of room we have to reject config.
		if (magma.smtp.blacklists.count >= MAGMA_BLACKLIST_INSTANCES) {
			log_critical("magma.smtp.blacklist is limited to %u %s and the configuration currently contains more than %u %s.",
				MAGMA_BLACKLIST_INSTANCES, MAGMA_BLACKLIST_INSTANCES == 1 ? "domain" : "domains", MAGMA_BLACKLIST_INSTANCES,
				MAGMA_BLACKLIST_INSTANCES == 1 ? "domain" : "domains");
			return false;
		}

		// Make sure the targeted array slot is empty, and the string passed to us has data in it. If so duplicate the value and
		// and store it in the array. If anything fails, return false.
		else if (magma.smtp.blacklists.domain[magma.smtp.blacklists.count] || !(magma.smtp.blacklists.domain[magma.smtp.blacklists.count] =
			st_dupe_opts(MANAGED_T | CONTIGUOUS | HEAP, value))) {
			return false;
		}

		// Track the number of blacklist domains we have.
		magma.smtp.blacklists.count++;

		// Return true so the switch statement below doesn't corrupt the array.
		return true;
	}
	else if (!st_cmp_cs_eq(NULLER(setting->name), NULLER("magma.smtp.bypass_addr"))) {

			// When the default values are assigned an empty string is passed in. Returning false then tricks the system into
			// thinking the default value is wrong, so we just return true to avoid the issue.
			if (st_empty(value)) {
				return true;
			}

			if (!smtp_add_bypass_entry(value)) {
				log_critical("Unable to add smtp bypass entry { entry = %s }", st_char_get(value));
				return false;
			}

			// Return true so the switch statement below doesn't corrupt the array.
			return true;
		}


	switch (setting->norm.type) {

	// Strings
	case (M_TYPE_NULLER):
		if (!ns_empty(*((char **)(setting->store)))) {
			ns_free(*((char **)(setting->store)));
			*((char **)(setting->store)) = NULL;
		}
		if (!st_empty(value))
			*((char **)(setting->store)) = ns_import(st_char_get(value), st_length_get(value));
		else if (!ns_empty(setting->norm.val.ns))
			*((char **)(setting->store)) = ns_dupe(setting->norm.val.ns);
		break;

	case (M_TYPE_STRINGER):
		if (!st_empty(*((stringer_t **)(setting->store)))) {
			st_free(*((stringer_t **)(setting->store)));
			*((stringer_t **)(setting->store)) = NULL;
		}
		if (!st_empty(value))
			*((stringer_t **)(setting->store)) = st_dupe_opts(MANAGED_T | CONTIGUOUS | HEAP, value);
		else if (!st_empty(setting->norm.val.st))
			*((stringer_t **)(setting->store)) = st_dupe_opts(MANAGED_T | CONTIGUOUS | HEAP, setting->norm.val.st);
		break;

		// Booleans
	case (M_TYPE_BOOLEAN):
		if (!st_empty(value)) {
			if (!st_cmp_ci_eq(value, CONSTANT("true")))
				*((bool_t *)(setting->store)) = true;
			else if (!st_cmp_ci_eq(value, CONSTANT("false")))
				*((bool_t *)(setting->store)) = false;
			else {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((bool_t *)(setting->store)) = setting->norm.val.binary;
		break;

		// Integers
	case (M_TYPE_INT8):
		if (!st_empty(value)) {
			if (!int8_conv_st(value, (int8_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((int8_t *)(setting->store)) = setting->norm.val.i8;
		break;

	case (M_TYPE_INT16):
		if (!st_empty(value)) {
			if (!uint16_conv_st(value, (uint16_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((int16_t *)(setting->store)) = setting->norm.val.u16;
		break;

	case (M_TYPE_INT32):
		if (!st_empty(value)) {
			if (!int32_conv_st(value, (int32_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((int32_t *)(setting->store)) = setting->norm.val.i32;
		break;

	case (M_TYPE_INT64):
		if (!st_empty(value)) {
			if (!int64_conv_st(value, (int64_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((int64_t *)(setting->store)) = setting->norm.val.i64;
		break;

		// Unsigned Integers
	case (M_TYPE_UINT8):
		if (!st_empty(value)) {
			if (!uint8_conv_st(value, (uint8_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((uint8_t *)(setting->store)) = setting->norm.val.u8;
		break;

	case (M_TYPE_UINT16):
		if (!st_empty(value)) {
			if (!uint16_conv_st(value, (uint16_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((uint16_t *)(setting->store)) = setting->norm.val.u16;
		break;

	case (M_TYPE_UINT32):
		if (!st_empty(value)) {
			if (!uint32_conv_st(value, (uint32_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((uint32_t *)(setting->store)) = setting->norm.val.u32;
		break;

	case (M_TYPE_UINT64):
		if (!st_empty(value)) {
			if (!uint64_conv_st(value, (uint64_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((uint64_t *)(setting->store)) = setting->norm.val.u64;
		break;

	default:
		log_critical("The %s setting definition is using an invalid type.", setting->name);
		result = false;
		break;
	}
	return result;
}