コード例 #1
0
ファイル: main.c プロジェクト: 503serviceunavailable/magma
bool_t check_string_dupe(char *type, uint32_t check) {

	size_t len;
	stringer_t *s, *d;

	if (!(s = st_alloc(check, st_length_int(constant)))) {
		return false;
	}

	len = snprintf(st_char_get(s), st_length_int(constant) + 1, "%.*s", st_length_int(constant), st_char_get(constant));
	if (check & MAPPED_T || check & MANAGED_T) {
		st_length_set(s, len);
	}

	if (!(d = st_dupe(s))) {
		st_free(s);
		return false;
	}

	log_print("%28.28s = %.*s", type, st_length_int(d), st_char_get(d));

	if (memcmp(st_char_get(d), st_char_get(s), st_length_get(s))) {
		st_free(s);
		st_free(d);
		return false;
	}

	st_free(s);
	st_free(d);

	return true;
}
コード例 #2
0
ファイル: string_check.c プロジェクト: lavabit/magma
bool_t check_string_dupe(uint32_t check) {

	size_t len;
	stringer_t *s, *d;

	if (!(s = st_alloc_opts(check, st_length_int(string_check_constant)))) {
		return false;
	}

	len = snprintf(st_char_get(s), st_length_int(string_check_constant) + 1, "%.*s", st_length_int(string_check_constant), st_char_get(string_check_constant));
	if ((check & MAPPED_T) || (check & MANAGED_T)) {
		st_length_set(s, len);
	}

	if (!(d = st_dupe(s))) {
		st_free(s);
		return false;
	}

	if (memcmp(st_char_get(d), st_char_get(s), st_length_get(s))) {
		st_free(s);
		st_free(d);
		return false;
	}

	st_free(s);
	st_free(d);

	return true;
}
コード例 #3
0
ファイル: locks.c プロジェクト: lavabit/magma
 /**
  * @brief	Acquire a named lock, with synchronization provided via memcached.
  * @see	cache_silent_add()
  * @note	The lock will be held for a maximum of 10 minutes, and failed locking attempts will be retried
  * 		periodically for a maxmimum of 1 minute before returing failure.
  * @param	key		a managed string containing the name of the lock to be acquired.
  * @return	-1 on general failure, 0 on memcached failure, or 1 on success.
  */
int_t lock_get(stringer_t *key) {

	uint64_t value;
	stringer_t *lock = MANAGEDBUF(128);
	int_t success, iterations = MAGMA_LOCK_TIMEOUT;
//	const struct timespec delay = { .tv_sec = 0, .tv_nsec = 1000000000 };
	const struct timespec delay = { .tv_sec = 1, .tv_nsec = 0 };

	// Build the key.
	if (st_empty(key) || st_sprint(lock, "%.*s.lock", st_length_int(key), st_char_get(key)) <= 0) {
		log_pedantic("Unable generate the accessor for the cluster lock.");
		return -1;
	}

	// Build the lock value.
	value = time(NULL);

	do {

		// Keep the lock for ten minutes.
		if ((success = cache_silent_add(lock, PLACER(&value, sizeof(uint64_t)), MAGMA_LOCK_EXPIRATION)) != 1) {
			nanosleep(&delay, NULL);
		}

	} while (success != 1 && iterations--);

#ifdef MAGMA_PEDANTIC
	if (success != 1) log_pedantic("Unable to obtain a cluster lock for %.*s.", st_length_int(lock), st_char_get(lock));
#endif

	return success;
}

/**
  * @brief	Release a named lock, with synchronization provided via memcached.
  * @see	cache_delete()
  * @note	The lock will be held for 10 seconds, and locking attempts will occur periodically for 60 seconds prior to failure.
  * @param	key		a managed string containing the name of the lock to be released.
  * @return	-1 on general failure, 0 on memcached failure, or 1 on success.
  */
void lock_release(stringer_t *key) {

	stringer_t *lock = MANAGEDBUF(128);

	// Build the key.
	if (st_empty(key) || st_sprint(lock, "%.*s.lock", st_length_int(key), st_char_get(key)) <= 0) {
		log_pedantic("Unable generate the accessor for the cluster lock.");
		return;
	}

	/// LOW: At some point we should add logic to check whether this cluster node even owns the lock before
	/// 	blindly deleting the lock.
	cache_delete(lock);
	return;
}
コード例 #4
0
ファイル: serials.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Increment the serial number for an object in memcached.
 * @param	type	the serial type to be queried (OBJECT_USER, OBJECT_CONFIG, OBJECT_FOLDERS, OBJECT_MESSAGES, or OBJECT_CONTACTS).
 * @param	num		the specific object identifier.
 * @return	0 on failure or the new serial number of the requested object.
 */
uint64_t serial_increment(uint64_t type, uint64_t num) {

	uint64_t result = 0;
	stringer_t *key, *prefix;

	// Build retrieval key.
	if (!(prefix = serial_prefix(type)) || !(key = st_aprint("magma.%.*s.%lu", st_length_int(prefix), st_char_get(prefix), num))) {
		log_pedantic("Unable to build %.*s serial key.", st_length_int(prefix), st_char_get(prefix));
		return 0;
	}

	// Increment the key.
	result = cache_increment(key, 1, 1, 2592000);
	st_free(key);

	return result;
}
コード例 #5
0
ファイル: serials.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Get the serial number (checkpoint value) for an object from memcached.
 * @param	type	the serial type to be queried (OBJECT_USER, OBJECT_CONFIG, OBJECT_FOLDERS, OBJECT_MESSAGES, or OBJECT_CONTACTS).
 * @param	num		the specific object identifier.
 * @return	0 on failure or the serial number of the requested object.
 */
uint64_t serial_get(uint64_t type, uint64_t num) {

	uint64_t result = 0;
	stringer_t *key, *prefix;

	// Build retrieval key.
	// QUESTION: This is used a few times and definitely should be its own function.
	if (!(prefix = serial_prefix(type)) || !(key = st_aprint("magma.%.*s.%lu", st_length_int(prefix), st_char_get(prefix), num))) {
		log_pedantic("Unable to build %.*s serial key.", st_length_int(prefix), st_char_get(prefix));
		return 0;
	}

	// Get the key value. The increment functions store the value in binary form, so we must use them to access the value, even if we aren't incrementing the value.
	result = cache_increment(key, 0, 0, 2592000);
	st_free(key);

	return result;
}
コード例 #6
0
ファイル: main.c プロジェクト: 503serviceunavailable/magma
bool_t check_string_import(void) {

	stringer_t *s;

	if (!(s = st_import(st_data_get(constant), st_length_int(constant)))) {
		return false;
	}

	log_print("%28.28s = %.*s", "duplicate", st_length_int(s), st_char_get(s));

	if (memcmp(st_char_get(s), st_char_get(constant), st_length_get(constant))) {
		st_free(s);
		return false;
	}

	st_free(s);

	return true;
}
コード例 #7
0
ファイル: serials.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Reset the serial number to 1 for an object in memcached.
 * @param	type	the serial type to be queried (OBJECT_USER, OBJECT_CONFIG, OBJECT_FOLDERS, OBJECT_MESSAGES, or OBJECT_CONTACTS).
 * @param	num		the specific object identifier.
 * @return	0 on failure or the new value of the cached serial number (1) on success.
 */
uint64_t serial_reset(uint64_t type, uint64_t num) {

	uint64_t result = 0;
	stringer_t *key, *prefix;

	// Build key.
	if (!(prefix = serial_prefix(type)) || !(key = st_aprint("magma.%.*s.%lu", st_length_int(prefix), st_char_get(prefix), num))) {
		log_pedantic("Unable to build %.*s serial key.", st_length_int(prefix), st_char_get(prefix));
		return 0;
	}

	// If were able to set the key, return the value one.
	if (cache_set(key, CONSTANT("1"), 2592000) == 1) {
		result = 1;
	}

	st_free(key);

	return result;
}
コード例 #8
0
/**
 * @brief	Insert a spam signature training link into a mail message.
 * @see		mail_modify_part()
 * @param	message			the mail message object of the message to be modified.
 * @param	server			the server object of the web server where the teacher application is hosted.
 * @param	signum			the spam signature number referenced by the teacher url.
 * @param	sigkey			the spam signature key for client verification in the teacher app.
 * @param	disposition		if 0, the message disposition is "innocent"; otherwise, the disposition specifies "spam".
 * @return	This function returns no value.
 */
void mail_signature_add(mail_message_t *message, server_t *server, uint64_t signum, uint64_t sigkey, int_t disposition) {

	stringer_t *part;

	part = PLACER(st_char_get(message->text), st_length_get(message->text));

	if ((mail_modify_part(server, message, part, signum, sigkey, disposition, 0)) == 0) {
		log_pedantic("------ MESSAGE ---------\n%.*s------------------", st_length_int(message->text), st_char_get(message->text));
	}

	return;
}
コード例 #9
0
ファイル: string_check.c プロジェクト: lavabit/magma
bool_t check_string_import(void) {

	stringer_t *s;

	if (!(s = st_import(st_data_get(string_check_constant), st_length_int(string_check_constant)))) {
		return false;
	}

	if (memcmp(st_char_get(s), st_char_get(string_check_constant), st_length_get(string_check_constant))) {
		st_free(s);
		return false;
	}

	st_free(s);

	return true;
}
コード例 #10
0
ファイル: paths.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Return the fully qualified local file path of a stored mail message for a specified message number and server.
 * @param	number		the mail message id.
 * @param	server		the hostname of the server where the message data resides or if NULL, the default server.
 * @return	NULL on failure, or a pointer to a null-terminated string containing the absolute file path of the specified message.
 */
chr_t * mail_message_path(uint64_t number, chr_t *server) {

	chr_t *result;

	if (!(result = ns_alloc(1024))) {
		log_pedantic("Unable to allocate a buffer of %i bytes for the storage path.", 1024);
		return NULL;
	}

	// The default storage server.
	if (!server) {
		server = st_char_get(magma.storage.active);
	}

	// Build the message path.
	if ((snprintf(result, 1024, "%.*s/%s/%lu/%lu/%lu/%lu/%lu", st_length_int(magma.storage.root), st_char_get(magma.storage.root),
		server, number / 32768 / 32768 / 32768 / 32768, number / 32768 / 32768 / 32768 , number / 32768 / 32768,  number / 32768, number)) <= 0) {
		log_pedantic("Unable to create the message path.");
		ns_free(result);
		return NULL;
	}

	return result;
}
コード例 #11
0
/**
 * @brief	Store a mail message, with its meta-information in the database, and the contents persisted to disk.
 * @note	The stored message is always compressed, but only encrypted if the user's public key is suppplied.
 * @param	usernum		the numerical id of the user to which the message belongs.
 * @param	pubkey		if not NULL, a public key that will be used to encrypt the message for the intended user.
 * @param	foldernum	the folder # that will contain the message.
 * @param	status		a pointer to the status flags value for the message, which will be updated if the message is to be encrypted.
 * @param	signum		the spam signature for the message.
 * @param	sigkey		the spam key for the message.
 * @param	message		a managed string containing the raw body of the message.
 * @return	0 on failure, or the newly inserted id of the message in the database on success.
 *
 */
uint64_t mail_store_message(uint64_t usernum, stringer_t *pubkey, uint64_t foldernum, uint32_t *status, uint64_t signum, uint64_t sigkey, stringer_t *message) {

	chr_t *path;
	cryptex_t *encrypted = NULL;
	compress_t *reduced;
	uint64_t messagenum;
	int64_t transaction, ret;
	size_t write_len;
	uint8_t fflags = FMESSAGE_OPT_COMPRESSED;
	uchr_t *write_data;
	bool_t store_result;

	// Compress the message.
	if (!(reduced = compress_lzo(message))) {
		log_error("An error occurred while attempting to compress a message with %zu bytes.", st_length_get(message));
		return 0;
	}

	// Next, encrypt the message if necessary.
	if (pubkey) {
		*status |= MAIL_STATUS_ENCRYPTED;

		if (!(encrypted = ecies_encrypt(pubkey, ECIES_PUBLIC_BINARY, reduced, compress_total_length(reduced)))) {
			log_pedantic("Unable to decrypt mail message.");
			compress_free(reduced);
			return 0;
		}

		compress_free(reduced);
		write_data = (uchr_t *)encrypted;
		write_len = cryptex_total_length(encrypted);
		fflags |= FMESSAGE_OPT_ENCRYPTED;
	} else {
		write_data = (uchr_t *)reduced;
		write_len = compress_total_length(reduced);
	}

	// Begin the transaction.
	if ((transaction = tran_start()) < 0) {
		log_error("Could not start a transaction. {start = %li}", transaction);

		if (encrypted) {
			cryptex_free(encrypted);
		} else {
			compress_free(reduced);
		}

		return 0;
	}

	// Insert a record into the database.
	if ((messagenum = mail_db_insert_message(usernum, foldernum, *status, st_length_int(message), signum, sigkey, transaction)) == 0) {
		log_pedantic("Could not create a record in the database. mail_db_insert_message = 0");
		tran_rollback(transaction);

		if (encrypted) {
			cryptex_free(encrypted);
		} else {
			compress_free(reduced);
		}

		return 0;
	}

	// Now attempt to save everything to disk.
	store_result = mail_store_message_data(messagenum, fflags, write_data, write_len, &path);

	if (encrypted) {
		cryptex_free(encrypted);
	} else {
		compress_free(reduced);
	}

	// If storage failed, fail out.
	if (!store_result || !path) {
		log_pedantic("Failed to store user's message to disk.");
		tran_rollback(transaction);

		if (path) {
			unlink(path);
			ns_free(path);
		}

		return 0;
	}

	// Commit the transaction.
	if ((ret = tran_commit(transaction))) {
		log_error("Could not commit the transaction. { commit = %li }", ret);
		unlink(path);
		ns_free(path);
		return 0;
	}

	ns_free(path);
	return messagenum;
}
コード例 #12
0
/**
 * @brief	Perform client command processing on an established imap session.
 * @note	This function will read the next line of user input, parse the command, and then attempt to execute it with the appropriate handler.
 * @param	con		a pointer to the connection object underlying the imap session.
 * @return	This function returns no value.
 */
void imap_process(connection_t *con) {

	int_t state;
	command_t *command, client = { .function = NULL };

	// If the connection indicates an error occurred, or the socket was closed by the client we send the connection to the logout function.
	if (((state = con_read_line(con, false)) < 0) || (state == -2)) {
		con->command = NULL;
		enqueue(&imap_logout, con);
		return;
	}
	else if (pl_empty(con->network.line) && ((con->protocol.spins++) + con->protocol.violations) > con->server->violations.cutoff) {
		con->command = NULL;
		enqueue(&imap_logout, con);
		return;
	}
	else if (pl_empty(con->network.line)) {
		con->command = NULL;
		enqueue(&imap_process, con);
		return;
	}

	// Parse the line into its tag and command elements.
	if ((state = imap_command_parser(con)) < 0) {

		// Try to be helpful about the parsing error.
		if (state == -1) {
			con_write_bl(con, "* BAD Unable to parse the command tag.\r\n", 40);
		}
		else if (state == -2) {
			con_print(con, "%.*s BAD Unable to parse the command.\r\n", st_length_int(con->imap.tag), st_char_get(con->imap.tag));
		}
		else {
			con_print(con, "%.*s BAD The command arguments were submitted using an invalid syntax.\r\n", st_length_int(con->imap.tag), st_char_get(con->imap.tag));
		}

		// If the client keeps breaking rules drop them.
		if (((con->protocol.spins++) + con->protocol.violations) > con->server->violations.cutoff) {
			con->command = NULL;
			enqueue(&imap_logout, con);
			return;
		}

		// Requeue and hope the next line of data is useful.
		con->command = NULL;
		enqueue(&imap_process, con);
		return;

	}

	client.string = st_char_get(con->imap.command);
	client.length = st_length_get(con->imap.command);

	if ((command = bsearch(&client, imap_commands, sizeof(imap_commands) / sizeof(imap_commands[0]), sizeof(command_t), imap_compare))) {

		con->command = command;
		con->protocol.spins = 0;

		if (command->function == &imap_logout) {
			enqueue(command->function, con);
		}
		else {
			requeue(command->function, &imap_requeue, con);
		}
	}
	else {
		con->command = NULL;
		requeue(&imap_invalid, &imap_requeue, con);
	}
	return;
}
コード例 #13
0
ファイル: abuse.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Check to see that a client from a given IP address hasn't exceeded its daily quota of contact requests.
 * @note	Each IP address will be limited to at most 2 contact requests in any 24-hour period.
 * @param	con		a pointer to the connection object of the remote host making the contact request.
 * @param	branch	a null-terminated string specifying where the contact request was directed ("Abuse" or "Contact").
 * @return	true if the specified connection failed the abuse check or false if it did not.
 */
bool_t contact_abuse_checks(connection_t *con, chr_t *branch) {

	stringer_t *key = MANAGEDBUF(64), *ip = NULL;

	// Build the key.
	if (!(ip = con_addr_presentation(con, ip)) && st_sprint(key, "magma.web.contact.history.%.*s", st_length_int(ip), st_char_get(ip)) > 0 && cache_get_u64(key) >= 2) {
		contact_print_message(con, branch, "To prevent abuse, our system only allows you to submit two letters to our team in a twenty-four hour period. If you need "
			"to submit another message please return in twenty-four hours and try again");
		return true;
	}

	return false;
}
コード例 #14
0
ファイル: abuse.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Increment the contact abuse history counter for an IP address.
 * @param	con		a pointer to the connection object of the remote host making the contact request.
 * @return	This function returns no value.
 */
void contact_abuse_increment_history(connection_t *con) {

	stringer_t *key = MANAGEDBUF(64), *ip = NULL;

	// Build the key.
	if (!(ip = con_addr_presentation(con, ip)) && st_sprint(key, "magma.web.contact.history.%.*s", st_length_int(ip), st_char_get(ip)) > 0) {
		cache_increment(key, 1, 1, 86400);
	}

	return;
}
コード例 #15
0
ファイル: read.c プロジェクト: lavabit/magma
/**
 * @brief	Read data from a network connection, and store the data in the connection context buffer.
 * @return	-1 on general failure, -2 if the connection was reset, or the amount of data that was read.
 */
int64_t client_read(client_t *client) {

	int_t counter = 0;
	ssize_t bytes = 0;
	bool_t blocking = true;
	stringer_t *error = NULL;

#ifdef MAGMA_PEDANTIC
	int_t local = 0;
	stringer_t *ip = NULL, *cipher = NULL;
#endif

	if (!client || client->sockd == -1 || client_status(client) < 0) {
		return -1;
	}

	// Check for data past the current line buffer.
	else if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) {

		// Move the unused data to the front of the buffer.
		mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line));

		// Update the length.
		st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line));

		// Clear the line buffer.
		client->line = pl_null();
	}
	// Otherwise reset the buffer and line lengths to zero.
	else {
		st_length_set(client->buffer, 0);
		client->line = pl_null();
	}

	// Loop until the buffer has data or we get an error.
	do {
		blocking = st_length_get(client->buffer) ? false : true;

		// Read bytes off the network. If data is already in the buffer this should be a non-blocking read operation so we can
		// return the already buffered data without delay.
		if (client->tls) {

			// If bytes is zero or below and the library isn't asking for another read, then an error occurred.
			bytes = tls_read(client->tls, st_char_get(client->buffer) + st_length_get(client->buffer),
				st_avail_get(client->buffer) - st_length_get(client->buffer), blocking);

			// If zero bytes were read, or a negative value was returned to indicate an error, call tls_erorr(), which will return
			// NULL if the error can be safely ignored. Otherwise log the output for debug purposes.
			if (bytes <= 0 && (error = tls_error(client->tls, bytes, MANAGEDBUF(512)))) {
#ifdef MAGMA_PEDANTIC
				cipher = tls_cipher(client->tls, MANAGEDBUF(128));
				ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN));

				log_pedantic("TLS client read operation failed. { ip = %.*s / %.*s / result = %zi%s%.*s }",
					st_length_int(ip), st_char_get(ip), st_length_int(cipher), st_char_get(cipher),
					bytes, (error ? " / " : ""), st_length_int(error), st_char_get(error));
#endif
				client->status = -1;
				return -1;
			}
			// This will occur when the read operation results in a 0, or negative value, but TLS error returns NULL to
			// indicate it was a transient error. For transient errors we simply set bytes equal to 0 so the read call gets retried.
			else if (bytes <= 0) {
				bytes = 0;
			}
		}
		else {

			errno = 0;

			bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer),
				st_avail_get(client->buffer) - st_length_get(client->buffer), (blocking ? 0 : MSG_DONTWAIT));

			// Check for errors on non-SSL reads in the traditional way.
			if (bytes <= 0 && tcp_status(client->sockd)) {
#ifdef MAGMA_PEDANTIC
				local = errno;
				ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN));

				log_pedantic("TCP client read operation failed. { ip = %.*s / result = %zi / error = %i / message = %s }",
					st_length_int(ip), st_char_get(ip), bytes, local, strerror_r(local, MEMORYBUF(1024), 1024));
#endif
				client->status = -1;
				return -1;
			}

		}

		// We actually read in data, so we need to update the buffer to reflect the amount of data it currently holds.
		if (bytes > 0) {
			st_length_set(client->buffer, st_length_get(client->buffer) + bytes);
		}

	} while (blocking && counter++ < 128 && !st_length_get(client->buffer) && status());

	// If there is data in the buffer process it. Otherwise if the buffer is empty and the connection appears to be closed
	// (as indicated by a return value of 0), then return -1 to let the caller know the connection is dead.
	if (st_length_get(client->buffer)) {
		client->status = 1;
	}
	else if (!bytes) {
		client->status = 2;
		return -2;
	}

	return st_length_get(client->buffer);
}
コード例 #16
0
ファイル: parse.c プロジェクト: 503serviceunavailable/magma
void imap_command_log_safe(stringer_t *line) {

	uchr_t *stream, *copy;
	size_t len, loc = 0;
	int_t i;

	if (st_empty_out(line, &stream, &len)) {
		return;
	}

	// A command string that doesn't even contain "LOGIN" is inherently "safe".
	if (!st_search_ci(line, PLACER("LOGIN", 5), &loc) || !loc) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		return;
	}


	// The LOGIN command should have been preceded by a whitespace.
	if (!chr_whitespace(stream[loc-1])) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		return;
	}

	// There should be should only be ONE more non-whitespace tag before the LOGIN command.
	for (i = loc-1; i >= 0; i--) {

		if (!chr_whitespace(stream[i])) {
			break;
		}

	}

	if (i < 0) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		return;
	}

	while (i >= 0) {

		if (chr_whitespace(stream[i])) {
			log_info("%.*s", st_length_int(line), st_char_get(line));
			return;
		}
			
		i--;
	}

	if (!(copy = mm_dupe(stream, len))) {
		return;
	}

	// Skip past "LOGIN" ...
	i = loc + 5;

	// and the trailing spaces.
	while ((i < len) && chr_whitespace(stream[i])) {
		i++;
	}

	if (i == len) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		mm_free(copy);
		return;
	}

	// The next parameter is the username. Skip past that as well.
	while ((i < len) && !chr_whitespace(stream[i])) {
		i++;
	}

	while ((i < len) && chr_whitespace(stream[i])) {
		i++;
	}

	if (i == len) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		mm_free(copy);
		return;
	}

	for(;i < len; i++) {
		copy[i] = '*';
	}

	log_info("%.*s", (int)len, copy);
	mm_free(copy);

	return;
}
コード例 #17
0
ファイル: pop.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Accept and verify a password for POP3 authentication.
 * @note	This command is only allowed for sessions which have not yet been authenticated, but which have already supplied a username.
 *			If the username/password combo was validated, the account information is retrieved and checked to see if it is locked.
 *			After successful authentication, this function will prohibit insecure connections for any user configured to use SSL only,
 *			and enforce the existence of only one POP3 session at a time.
 *			Finally, the database Log table for this user's POP3 access is updated, and all the user's messages are retrieved.
 * @param	con		the POP3 client connection issuing the command.
 * @return	This function returns no value.
 */
void pop_pass(connection_t *con) {

	int_t state;
	credential_t *cred;
	stringer_t *password, *username;

	if (con->pop.session_state != 0) {
		pop_invalid(con);
		return;
	}

	// The user must come before the PASS command.
	if (st_empty(con->pop.username)) {
		con_write_bl(con, "-ERR You must supply a username first.\r\n", 40);
		return;
	}

	// If they didn't pass in a valid password.
	if (!(password = pop_pass_parse(con))) {
		con_write_bl(con, "-ERR Invalid PASS command.\r\n", 28);
		return;
	}

	// Hash the password.
	// First we need to get the regular username from the fully qualified one.
	if (!(username = credential_username(con->pop.username))) {
		con_write_bl(con, "-ERR Internal server error. Please try again later.\r\n", 53);
		st_wipe(password);
		st_free(password);
	}

	st_free(con->pop.username);
	con->pop.username = username;

	if (!(cred = credential_alloc_auth(con->pop.username, password))) {
		con_write_bl(con, "-ERR Internal server error. Please try again later.\r\n", 53);
		st_wipe(password);
		st_free(password);
		return;
	}

	st_wipe(password);
	st_free(password);

	// Pull the user info out.
	state = meta_get(con->pop.username, cred->auth.domain, cred->auth.password, cred->auth.key, META_PROT_POP, META_GET_MESSAGES, &(con->pop.user));

	// Securely delete this information, as these are the keys to the castle.
	credential_free(cred);

	// Not found, or invalid password.
	if (state == 0) {
		con_write_bl(con,  "-ERR The username and password combination is invalid.\r\n", 56);
		return;
	}
	// Internal error.
	else if (state < 0 || !con->pop.user) {
		con_write_bl(con, "-ERR [SYS/TEMP] Internal server error. Please try again later.\r\n", 64);
		return;
	}

	// Locks
	else if (con->pop.user->lock_status != 0) {
		// What type of lock is it.
		if (con->pop.user->lock_status == 1) {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been administratively locked.\r\n", 64);
		}
		else if (con->pop.user->lock_status == 2) {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been locked for inactivity.\r\n", 62);
		}
		else if (con->pop.user->lock_status == 3) {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been locked on suspicion of abuse.\r\n", 69);
		}
		else if (con->pop.user->lock_status == 4) {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been locked at the request of the user.\r\n", 74);
		}
		else {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been locked.\r\n", 47);
		}

		con->pop.user = NULL;
		meta_remove(con->pop.username, META_PROT_POP);
		return;
	}

	// SSL check.
	else if ((con->pop.user->flags & META_USER_SSL) == META_USER_SSL && con_secure(con) != 1) {
		con->pop.user = NULL;
		meta_remove(con->pop.username, META_PROT_POP);
		con_write_bl(con, "-ERR [SYS/PERM] This user account is configured to require that all POP sessions be connected over SSL.\r\n", 105);
		return;
	}

	// Single session check.
	else if (con->pop.user->refs.pop != 1) {
		con->pop.user = NULL;
		meta_remove(con->pop.username, META_PROT_POP);
		con_write_bl(con, "-ERR [IN-USE] This account is being used by another session. Please try again in a few minutes.\r\n", 97);
		return;
	}

	// Debug logging.
	log_pedantic("User %.*s logged in from %s via POP. {poprefs = %lu, imaprefs = %lu, messages = %lu}",
		st_length_int(con->pop.username), st_char_get(con->pop.username), st_char_get(con_addr_presentation(con, MANAGEDBUF(256))),
		con->pop.user->refs.pop, con->pop.user->refs.imap, con->pop.user->messages ? inx_count(con->pop.user->messages) : 0);

	// Update the log and unlock the session.
	meta_data_update_log(con->pop.user, META_PROT_POP);

	meta_user_wlock(con->pop.user);
	meta_messages_login_update(con->pop.user, META_LOCKED);
	meta_user_unlock(con->pop.user);

	// Update session state.
	con->pop.session_state = 1;

	// Tell the client everything worked.
	con_write_bl(con, "+OK Password accepted.\r\n", 24);

	return;
}
コード例 #18
0
/**
 * @brief	Update a user config entry in the database, or insert it if it does not already exist.
 * @param	usernum		the numerical id of the user to whom the specified config entry belongs.
 * @param	key			a pointer to a managed string containing the name of the config entry to be updated or inserted.
 * @param	value		a pointer to a managed string containing the value of the specified config entry key.
 * @param	flags		a bitmask of flags for the config entry (USER_CONF_STATUS_CRITICAL is supported).
 * @return	2 if the config entry was updated, 1 if a new config key was inserted into the database, 0 if no update was necessary, or -1 on general failure.
 */
int_t user_config_upsert(uint64_t usernum, stringer_t *key, stringer_t *value, uint64_t flags) {

	uint64_t affected;
	MYSQL_BIND parameters[4];

	mm_wipe(parameters, sizeof(parameters));

	// User Number
	parameters[0].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[0].buffer_length = sizeof(uint64_t);
	parameters[0].buffer = &usernum;
	parameters[0].is_unsigned = true;

	// Key
	parameters[1].buffer_type = MYSQL_TYPE_STRING;
	parameters[1].buffer_length = st_length_get(key);
	parameters[1].buffer = st_char_get(key);

	// Value
	parameters[2].buffer_type = MYSQL_TYPE_STRING;
	parameters[2].buffer_length = st_length_get(value);
	parameters[2].buffer = st_char_get(value);

	// Flags
	parameters[3].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[3].buffer_length = sizeof(uint64_t);
	parameters[3].buffer = &flags;
	parameters[3].is_unsigned = true;

	if (!(affected = stmt_exec_affected(stmts.upsert_user_config, parameters)) == (my_ulonglong)-1) {
		log_pedantic("The user config upsert triggered an error. { user = %lu / key = %.*s }", usernum, st_length_int(key), st_char_get(key));
		return -1;
	}

	log_check(affected > 2);

	return (int_t)affected;
}
コード例 #19
0
ファイル: main.c プロジェクト: 503serviceunavailable/magma
int main(void) {

	if (!mm_sec_start()) {

		log_print("Secure memory pool did not start correctly.");
		return 1;
	}

	else if (sizeof(stringer_t) != 4) {

		log_print("The string options variable should be 4 bytes/32 bits.");
		return 1;
	}

	/*chr_t *data = "Hello world.";
	size_t len = ns_length_get(data);
	stringer_t *place =
		PLACER(data, len);

	log_print("%.*s\n%.*s\n", st_length_int(PLACER(data, len)), st_char_get(PLACER(data, len)),	st_length_int((stringer_t *)place), st_char_get((stringer_t *)place));
	if (st_cmp_cs_eq(PLACER(st_data_get(constant), st_length_get(constant)), constant) || memcmp("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", st_data_get(constant), st_length_get(constant)))
		exit(1);*/

	// Since were intentionally going to trigger errors, disable standard out while doing so.
	log_disable();
	// Check that we can't specify a length greater than the available buffer.
	if (!check_string_logic(MANAGED_T | CONTIGUOUS | HEAP) || !check_string_logic(MANAGED_T | JOINTED | HEAP)	|| !check_string_logic(MAPPED_T | JOINTED | HEAP)) {
		log_print("--------------------------------------- LOGIC -------------------------------------------\n");
		log_print("String logic checks failed.");
		return 1;
	}
	log_enable();

	log_print("--------------------------------------- CONSTANT ----------------------------------------\n%28.28s = %.*s",
			"constant[fixed+contiguous]", st_length_int(constant), st_char_get(constant));

	log_print("--------------------------------------- IMPORT ------------------------------------------");
	if (!check_string_import()) {
		log_print("Import check failed.");
		return 1;
	}

	// Begin allocation checks.
	log_print("--------------------------------------- ALLOCATION --------------------------------------");

	if (!check_string_alloc("nuller[heap+contiguous]", NULLER_T | CONTIGUOUS | HEAP) || !check_string_alloc("block[heap+contiguous]", BLOCK_T | CONTIGUOUS | HEAP)
			|| !check_string_alloc("managed[heap+contiguous]", MANAGED_T | CONTIGUOUS | HEAP)) {
		log_print("Standard allocation checks failed.");
		return 1;
	}

	if (!check_string_alloc("nuller[heap+jointed]", NULLER_T | JOINTED | HEAP) || !check_string_alloc("block[heap+jointed]", BLOCK_T | JOINTED | HEAP)
			|| !check_string_alloc("managed[heap+jointed]", MANAGED_T | JOINTED | HEAP) || !check_string_alloc("mapped[heap+jointed]", MAPPED_T | JOINTED | HEAP)) {
		log_print("Jointed allocation checks failed.");
		return 1;
	}

	if (!check_string_alloc("nuller[secure+contiguous]", NULLER_T | CONTIGUOUS | SECURE) || !check_string_alloc("block[secure+contiguous]", BLOCK_T | CONTIGUOUS | SECURE)
			|| !check_string_alloc("managed[secure+contiguous]", MANAGED_T | CONTIGUOUS | SECURE)) {
		log_print("Secure allocation of contiguous types failed.");
		return 1;
	}

	if (!check_string_alloc("nuller[secure+jointed]", NULLER_T | JOINTED | SECURE) || !check_string_alloc("block[secure+jointed]", BLOCK_T | JOINTED | SECURE)
			|| !check_string_alloc("managed[secure+jointed]", MANAGED_T | JOINTED | SECURE) || !check_string_alloc("mapped[secure+jointed]", MAPPED_T | JOINTED | SECURE)) {
		log_print("Secure allocation of jointed types failed.");
		return 1;
	}

	// Begin reallocation checks.
	log_print("-------------------------------------- REALLOCATION -------------------------------------");

	if (!check_string_realloc("nuller[heap+contiguous]", NULLER_T | CONTIGUOUS | HEAP) || !check_string_realloc("block[heap+contiguous]", BLOCK_T | CONTIGUOUS | HEAP)
			|| !check_string_realloc("managed[heap+contiguous]", MANAGED_T | CONTIGUOUS | HEAP)) {
		log_print("Standard reallocation checks failed.");
		return 1;
	}

	if (!check_string_realloc("nuller[heap+jointed]", NULLER_T | JOINTED | HEAP) || !check_string_realloc("block[heap+jointed]", BLOCK_T | JOINTED | HEAP)
			|| !check_string_realloc("managed[heap+jointed]", MANAGED_T | JOINTED | HEAP) || !check_string_realloc("mapped[heap+jointed]", MAPPED_T | JOINTED | HEAP)) {
		log_print("Jointed reallocation checks failed.");
		return 1;
	}

	if (!check_string_realloc("nuller[secure+contiguous]", NULLER_T | CONTIGUOUS | SECURE) || !check_string_realloc("block[secure+contiguous]", BLOCK_T | CONTIGUOUS
			| SECURE) || !check_string_realloc("managed[secure+contiguous]", MANAGED_T | CONTIGUOUS | SECURE)) {
		log_print("Secure reallocation of contiguous types failed.");
		return 1;
	}

	if (!check_string_realloc("nuller[secure+jointed]", NULLER_T | JOINTED | SECURE) || !check_string_realloc("block[secure+jointed]", BLOCK_T | JOINTED | SECURE)
			|| !check_string_realloc("managed[secure+jointed]", MANAGED_T | JOINTED | SECURE) || !check_string_realloc("mapped[secure+jointed]", MAPPED_T | JOINTED | SECURE)) {
		log_print("Secure reallocation of jointed types failed.");
		return 1;
	}

	// Begin duplication checks.
	log_print("-------------------------------------- DUPLICATION --------------------------------------");

	if (!check_string_dupe("nuller[heap+contiguous]", NULLER_T | CONTIGUOUS | HEAP) || !check_string_dupe("block[heap+contiguous]", BLOCK_T | CONTIGUOUS | HEAP)
			|| !check_string_dupe("managed[heap+contiguous]", MANAGED_T | CONTIGUOUS | HEAP)) {
		log_print("Standard duplication checks failed.");
		return 1;
	}

	if (!check_string_dupe("nuller[heap+jointed]", NULLER_T | JOINTED | HEAP) || !check_string_dupe("block[heap+jointed]", BLOCK_T | JOINTED | HEAP) || !check_string_dupe(
			"managed[heap+jointed]", MANAGED_T | JOINTED | HEAP) || !check_string_dupe("mapped[heap+jointed]", MAPPED_T | JOINTED | HEAP)) {
		log_print("Jointed duplication checks failed.");
		return 1;
	}

	if (!check_string_dupe("nuller[secure+contiguous]", NULLER_T | CONTIGUOUS | SECURE) || !check_string_dupe("block[secure+contiguous]", BLOCK_T | CONTIGUOUS | SECURE)
			|| !check_string_dupe("managed[secure+contiguous]", MANAGED_T | CONTIGUOUS | SECURE)) {
		log_print("Secure duplication of contiguous types failed.");
		return 1;
	}

	if (!check_string_dupe("nuller[secure+jointed]", NULLER_T | JOINTED | SECURE) || !check_string_dupe("block[secure+jointed]", BLOCK_T | JOINTED | SECURE)
			|| !check_string_dupe("managed[secure+jointed]", MANAGED_T | JOINTED | SECURE) || !check_string_dupe("mapped[secure+jointed]", MAPPED_T | JOINTED | SECURE)) {
		log_print("Secure duplication of jointed types failed.");
		return 1;
	}

	log_print("-------------------------------------- MERGE --------------------------------------------");
	if (!check_string_merge()) {
		log_print("The merge function failed.");
		return 1;
	}

	log_print("-------------------------------------- PRINT --------------------------------------------");
	if (!check_string_print()) {
		log_print("The print function failed.");
		return 1;
	}

	mm_sec_stop();
	log_print("---------------------------------------- FINISHED ---------------------------------------");
	return 0;
}
コード例 #20
0
ファイル: datatier.c プロジェクト: lavabit/magma
/**
 * @brief	Delete the specified contact detail of a contact entry from the database.
 * @param	contactnum	the numerical id of the contact entry to have the specified detail removed.
 * @param	key			a managed string containing the name of the contact detail to be removed from the entry.
 * @return	-1 on error, 0 if no matching detail was found in the database, or 1 if the delete operation was successful.
 */
int_t contact_detail_delete(uint64_t contactnum, stringer_t *key) {

	int64_t affected;
	MYSQL_BIND parameters[4];

	mm_wipe(parameters, sizeof(parameters));

	// Contact Number
	parameters[0].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[0].buffer_length = sizeof(uint64_t);
	parameters[0].buffer = &contactnum;
	parameters[0].is_unsigned = true;

	// Key
	parameters[1].buffer_type = MYSQL_TYPE_STRING;
	parameters[1].buffer_length = st_length_get(key);
	parameters[1].buffer = st_char_get(key);

	if ((affected = stmt_exec_affected(stmts.delete_contact_details, parameters)) == -1) {
		log_pedantic("The contact detail deletion request triggered an error. { contact = %lu / key = %.*s }", contactnum, st_length_int(key), st_char_get(key));
		return -1;
	}

	log_check(affected > 2);

	return (int_t)affected;
}
コード例 #21
0
ファイル: global.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Log the contents of a magma configuration option.
 * @param	key		a pointer to the magma configuration key to be dumped.
 * @return	This function returns no value.
 */
void config_output_value(magma_keys_t *key) {

	switch (key->norm.type) {
	case (M_TYPE_NULLER):
		if (ns_empty(*((char **)(key->store))))
			log_info("%s = NULL", key->name);
		else
			log_info("%s = %s", key->name, *((char **)(key->store)));
		break;

	case (M_TYPE_STRINGER):
		// Intercept the blacklist config key->
		if (!st_cmp_cs_eq(NULLER(key->name), PLACER("magma.smtp.blacklist", 20))) {

			if (!magma.smtp.blacklists.count) {
				log_info("%s = NULL",key->name);
			}

			for (uint32_t j = 0; j < magma.smtp.blacklists.count; j++) {
				log_info("%s = %.*s",key->name, st_length_int(magma.smtp.blacklists.domain[j]), st_char_get(magma.smtp.blacklists.domain[j]));
			}
		}

		else if (st_empty(*((stringer_t **)(key->store))))
			log_info("%s = NULL", key->name);
		else
			log_info("%s = %.*s", key->name, st_length_int(*((stringer_t **)(key->store))), st_char_get(*((stringer_t **)(key->store))));
		break;

	case (M_TYPE_BOOLEAN):
		log_info("%s = %s", key->name, (*((bool_t *)(key->store)) ? "true" : "false"));
		break;

	case (M_TYPE_INT8):
		log_info("%s = %hhi", key->name, *((int8_t *)(key->store)));
		break;
	case (M_TYPE_INT16):
		log_info("%s = %hi", key->name, *((int16_t *)(key->store)));
		break;

	case (M_TYPE_INT32):
		log_info("%s = %i", key->name, *((int32_t *)(key->store)));
		break;

	case (M_TYPE_INT64):
		log_info("%s = %li", key->name, *((int64_t *)(key->store)));
		break;

	case (M_TYPE_UINT8):
		log_info("%s = %hhu", key->name, *((uint8_t *)(key->store)));
		break;
	case (M_TYPE_UINT16):
		log_info("%s = %hu", key->name, *((uint16_t *)(key->store)));
		break;
	case (M_TYPE_UINT32):
		log_info("%s = %u", key->name, *((uint32_t *)(key->store)));
		break;
	case (M_TYPE_UINT64):
		log_info("%s = %lu", key->name, *((uint64_t *)(key->store)));
		break;
	default:
		log_pedantic("Unexpected type. {type = %u}", key->norm.type);
		break;
	}

	return;
}
コード例 #22
0
ファイル: global.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Output a key name and value in a generic way.
 * @param	prefix		a pointer to a null-terminated string containing an optional prefix to be printed before the supplied key name.
 * @param	name		a pointer to a null-terminated string containing the name of the key being output.
 * @param	type		an M_TYPE value specifying the multi-type of the key value.
 * @param	val			a void pointer to the value of the specified key, which will be printed in accordance with the supplied multi-type.
 * @param	required	a boolean value specifying whether the specified key is a required configuration option.
 * @return	This function returns no value.
 */
void config_output_value_generic(chr_t *prefix, chr_t *name, M_TYPE type, void *val, bool_t required) {

	chr_t *reqstr = "";

	if (!prefix) {
		prefix = "";
	}

	if (required) {
		reqstr = "*";
	}

	switch (type) {
	case (M_TYPE_NULLER):
		if (ns_empty(*((char **)(val))))
			log_info("%s%s%s = NULL", prefix, name, reqstr);
		else
			log_info("%s%s%s = %s", prefix, name, reqstr, *((char **)(val)));
		break;

	case (M_TYPE_STRINGER):
		// Intercept the blacklist config key->
		if (!st_cmp_cs_eq(NULLER(name), PLACER("magma.smtp.blacklist", 20))) {

			if (!magma.smtp.blacklists.count) {
				log_info("%s%s%s = NULL", prefix, name, reqstr);
			}

			for (uint32_t j = 0; j < magma.smtp.blacklists.count; j++) {
				log_info("%s%s%s = %.*s", prefix, name, reqstr, st_length_int(magma.smtp.blacklists.domain[j]), st_char_get(magma.smtp.blacklists.domain[j]));
			}
		}

		else if (st_empty(*((stringer_t **)(val))))
			log_info("%s%s%s = NULL", prefix, name, reqstr);
		else
			log_info("%s%s%s = %.*s", prefix, name, reqstr, st_length_int(*((stringer_t **)(val))), st_char_get(*((stringer_t **)(val))));
		break;

	case (M_TYPE_ENUM):

			if (!st_cmp_cs_eq(NULLER(name), CONSTANT(".protocol"))) {
				if (*((M_PROTOCOL *)((char *)val)) == MOLTEN)
					log_info("%s%s%s = MOLTEN", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == HTTP)
					log_info("%s%s%s = HTTP", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == POP)
					log_info("%s%s%s = POP", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == IMAP)
					log_info("%s%s%s = IMAP", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == SMTP)
					log_info("%s%s%s = SMTP", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == SUBMISSION)
					log_info("%s%s%s = SUBMISSION", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == EMPTY)
						log_info("%s%s%s = EMPTY", prefix, name, reqstr);
				else
					log_info("%s%s%s = [UNKNOWN]", prefix, name, reqstr);
			} else if (!st_cmp_cs_eq(NULLER(name), CONSTANT(".network.type"))) {
				if (*((M_PORT *)((char *)val)) == TCP_PORT)
					log_info("%s%s%s = TCP", prefix, name, reqstr);
				else if (*((M_PORT *)((char *)val)) == SSL_PORT)
					log_info("%s%s%s = SSL", prefix, name, reqstr);
				else
					log_info("%s%s%s = [UNKNOWN]", prefix, name, reqstr);
			}
		break;

	case (M_TYPE_BOOLEAN):
		log_info("%s%s%s = %s", prefix, name, reqstr, (*((bool_t *)(val)) ? "true" : "false"));
		break;

	case (M_TYPE_INT8):
		log_info("%s%s%s = %hhi", prefix, name, reqstr, *((int8_t *)(val)));
		break;
	case (M_TYPE_INT16):
		log_info("%s%s%s = %hi", prefix, name, reqstr, *((int16_t *)(val)));
		break;

	case (M_TYPE_INT32):
		log_info("%s%s%s = %i", prefix, name, reqstr, *((int32_t *)(val)));
		break;

	case (M_TYPE_INT64):
		log_info("%s%s%s = %li", prefix, name, reqstr, *((int64_t *)(val)));
		break;

	case (M_TYPE_UINT8):
		log_info("%s%s%s = %hhu", prefix, name, reqstr, *((uint8_t *)(val)));
		break;
	case (M_TYPE_UINT16):
		log_info("%s%s%s = %hu", prefix, name, reqstr, *((uint16_t *)(val)));
		break;
	case (M_TYPE_UINT32):
		log_info("%s%s%s = %u", prefix, name, reqstr, *((uint32_t *)(val)));
		break;
	case (M_TYPE_UINT64):
		log_info("%s%s%s = %lu", prefix, name, reqstr, *((uint64_t *)(val)));
		break;
	default:
		log_pedantic("Unexpected type. {type = %u}", type);
		break;
	}

	return;
}
コード例 #23
0
ファイル: global.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Load all magma configuration options present in the database.
 * @note	Each key/value pair extracted from the database is submitted to the following logic:
 *	 			If a config option was loaded from the database, the key must allow it to be configurable via the database.
 *	 			Check to see that any key that has previously been set is allowed to be overwritten.
 * 				If the key is required, it may not contain an empty value.
 * 			Finally, this function sets the appropriate magma key corresponding to the config key.
 * 			All leftover keys not matched to global magma keys will be configured via servers, relay, and cache server options.
 * @return	true if all database config options were parsed and evaluated successfully, or false on failure.
 */
bool_t config_load_database_settings(void) {

	row_t *row;
	uint64_t rows;
	magma_keys_t *key;
	table_t *database_pairs;
	stringer_t *value, *name;

	if (!(magma.host.number = config_fetch_host_number()) || !(database_pairs = config_fetch_settings())) {
		return false;
	}

	// Loop through each of the row returned.
	rows = res_row_count(database_pairs);
	for (uint64_t i = 0; i < rows && (row = res_row_get(database_pairs, i)); i++) {

			name = PLACER(res_field_block(row, 0), res_field_length(row, 0));
			value = PLACER(res_field_block(row, 1), res_field_length(row, 1));

			if ((key = config_key_lookup(name))) {
				// Make sure the setting can be provided via the database.
				if (!key->database) {
					log_critical("%s cannot be changed using the database.", key->name);
					res_table_free(database_pairs);
					return false;
				}

				// Make sure the setting can be provided via the database.
				else if (key->set && !key->overwrite) {
					log_critical("%s has already been set and cannot be overwritten.", key->name);
					res_table_free(database_pairs);
					return false;
				}

				// Make sure the required magma_keys are not set to NULL.
				else if (key->required && st_empty(value)) {
					log_critical("%s requires a legal value.", key->name);
					res_table_free(database_pairs);
					return false;
				}

				// Attempt to set the value.
				else if (!config_value_set(key, value)) {
					res_table_free(database_pairs);
					return false;
				}

				// Record that we've set this parameter.
				key->set = true;
			}

			// If we haven't had a match yet, check if its a server param.
			else if (!st_cmp_ci_starts(name, CONSTANT("magma.servers"))) {
				servers_config(name, value);
			}

			// If we haven't had a match yet, check if its a relay instance.
			else if (!st_cmp_ci_starts(name, CONSTANT("magma.relay"))) {
				relay_config(name, value);
			}

			else if (!st_cmp_ci_starts(name, CONSTANT("magma.iface.cache.host"))) {
				cache_config(name, value);
			}

			// Otherwise if we still haven't matched a value, report an error.
			else {
				log_critical("%.*s is not a valid setting.", st_length_int(name), st_char_get(name));
				res_table_free(database_pairs);
				return false;
			}

	}

	res_table_free(database_pairs);
	return true;
}
コード例 #24
0
ファイル: string_check.c プロジェクト: lavabit/magma
bool_t check_string_realloc(uint32_t check) {

	size_t len;
	stringer_t *s, *swap;

	if (!(s = st_alloc_opts(check, 1)) || !(swap = st_realloc(s, st_length_get(string_check_constant)))) {
		if (s) {
			st_free(s);
		}
		return false;
	}

	// Since mapped allocations are page aligned, we reallocate to a larger than needed size to ensure an actual reallocation occurs.
	else if ((check & MAPPED_T) && !(swap = st_realloc(s, st_length_get(string_check_constant) + (getpagesize() * 128)))) {
		if (s) {
			st_free(s);
		}
		return false;
	}

	// Jointed strings allow reallocation without changing the address of s, of if the string is jointed and s changes, error out.
	else if ((check & JOINTED) && swap != s) {
		st_free(swap);
		st_free(s);
		return false;
	}

	// Contiguous strings will require the address of s to change, so if it doesn't error out. Except for the mapped type, since the jointed flag doesn't apply to it.
	else if (!(check & JOINTED) && !(check & MAPPED_T) && swap == s) {
		st_free(s);
		return false;
	}

	// For contiguous types, free the original string and replace it with the value of swap.
	else if (swap != s) {
		st_free(s);
		s = swap;
	}

	len = snprintf(st_char_get(s), st_length_int(string_check_constant) + 1, "%.*s", st_length_int(string_check_constant), st_char_get(string_check_constant));
	if ((check & MAPPED_T) || (check & MANAGED_T)) {
		st_length_set(s, len);
	}

	else if (memcmp(st_char_get(s), st_char_get(string_check_constant), st_length_get(string_check_constant))) {
		return false;
	}

	// Enlarge a buffer by a factor of 128 and make sure the data it contained wasn't changed. For managed or mapped strings, multiply the available space.
	if ((check & (MANAGED_T | MAPPED_T)) && !(swap = st_realloc(s, st_avail_get(s) * 128))) {
		st_free(s);
		return false;
	}

	// For other string types we multiply the space used by the string since the original allocation size isn't tracked.
	else if (!(check & (MANAGED_T | MAPPED_T)) && !(swap = st_realloc(s, st_length_get(s) * 128))) {
		st_free(s);
		return false;
	}

	// Since mapped allocations are page aligned, we reallocate to a larger than needed size to ensure an actual reallocation occurs.
	else if ((check & MAPPED_T) && !(swap = st_realloc(s, st_length_get(string_check_constant) + (getpagesize() * 128)))) {
		if (s) {
			st_free(s);
		}
		return false;
	}

	// Jointed strings allow reallocation without changing the address of s, of if the string is jointed and s changes, error out.
	else if ((check & JOINTED) && swap != s) {
		st_free(swap);
		st_free(s);
		return false;
	}

	// Contiguous strings will require the address of s to change, so if it doesn't error out. Except for the mapped type, since the jointed flag doesn't apply to it.
	else if (((check & JOINTED) ^ JOINTED) && !(check & MAPPED_T) && swap == s) {
		st_free(s);
		return false;
	}

	// For contiguous types, free the original string and replace it with the value of swap.
	else if (swap != s) {
		st_free(s);
		s = swap;
	}

	else if (memcmp(st_char_get(s), st_char_get(string_check_constant), st_length_get(string_check_constant))) {
		return false;
	}

	// Now we shrink the buffer back to the bare minimum and check the data one final time.
	if (!(swap = st_realloc(s, st_length_get(string_check_constant)))) {
		if (s) {
			st_free(s);
		}
		return false;
	} else if (swap != s) {
		st_free(s);
		s = swap;
	}

	st_free(s);

	return true;
}
コード例 #25
0
ファイル: paths.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Create the on-disk directory structure necessary to hold a given message's file data.
 * @param	number		the mail message id.
 * @param	server		the hostname of the server where the message data resides or if NULL, the default server.
 * @return	true on success or false on failure.
 */
bool_t mail_create_directory(uint64_t number, chr_t *server) {

	DIR *dir;
	chr_t dirpath[1024];

	if (!number) {
		return false;
	}

	// The default storage server.
	if (!server) {
		server = st_char_get(magma.storage.active);
	}

	// Check for the base NFS directory.
	snprintf(dirpath, 1024, "%.*s/%s",  st_length_int(magma.storage.root), st_char_get(magma.storage.root), server);

	if (!(dir = opendir(dirpath))) {
		log_error("It appears that the path %s is invalid.", dirpath);
		return false;
	}
	else {
		closedir(dir);
	}

	// Check the first level.
	snprintf(dirpath, 1024, "%.*s/%s/%lu", st_length_int(magma.storage.root), st_char_get(magma.storage.root), server,
		number / 32768 / 32768 / 32768 / 32768);

	if (!(dir = opendir(dirpath))) {

		if (mkdir(dirpath, S_IRWXU) != 0) {
			log_error("An error occurred while attempting to create the directory %s.", dirpath);
			return false;
		}
	}
	else {
		closedir(dir);
	}

	// Check the second level.
	snprintf(dirpath, 1024, "%.*s/%s/%lu/%lu", st_length_int(magma.storage.root), st_char_get(magma.storage.root), server,
		number / 32768 / 32768 / 32768 / 32768, number / 32768 / 32768 / 32768);

	if (!(dir = opendir(dirpath))) {

		if (mkdir(dirpath, S_IRWXU) != 0) {
			log_error("An error occurred while attempting to create the directory %s.", dirpath);
			return false;
		}
	}
	else {
		closedir(dir);
	}

	// Check the third level.
	snprintf(dirpath, 1024, "%.*s/%s/%lu/%lu/%lu", st_length_int(magma.storage.root), st_char_get(magma.storage.root), server,
		number / 32768 / 32768 / 32768 / 32768, number / 32768 / 32768 / 32768, number / 32768 / 32768 );

	if (!(dir = opendir(dirpath))) {

		if (mkdir(dirpath, S_IRWXU) != 0) {
			log_error("An error occurred while attempting to create the directory %s.", dirpath);
			return false;
		}
	}
	else {
		closedir(dir);
	}

	// Check the fourth level.
	snprintf(dirpath, 1024, "%.*s/%s/%lu/%lu/%lu/%lu", st_length_int(magma.storage.root), st_char_get(magma.storage.root), server,
		number / 32768 / 32768 / 32768 / 32768, number / 32768 / 32768 / 32768, number / 32768 / 32768,  number / 32768 );

	if (!(dir = opendir(dirpath))) {

		if (mkdir(dirpath, S_IRWXU) != 0) {
			log_error("An error occurred while attempting to create the directory %s.", dirpath);
			return false;
		}
	}
	else {
		closedir(dir);
	}

	return true;
}
コード例 #26
0
ファイル: read.c プロジェクト: lavabit/magma
/**
 * @brief	Read a line of input from a network client session.
 * @return	-1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing new line character.
 */
int64_t client_read_line(client_t *client) {

	ssize_t bytes = 0;
	int_t counter = 0;
	stringer_t *error = NULL;
	bool_t blocking = true, line = false;

#ifdef MAGMA_PEDANTIC
	int_t local = 0;
	stringer_t *ip = NULL, *cipher = NULL;
#endif

	if (!client || client->sockd == -1) {
		if (client) client->status = 1;
		return -1;
	}

	// Check for data past the current line buffer.
	else if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) {

		// Move the unused data to the front of the buffer.
		mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line));

		// Update the length.
		st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line));

		// Check whether the data we just moved contains a complete line.
		if (!pl_empty((client->line = line_pl_st(client->buffer, 0)))) {
			client->status = 1;
			return pl_length_get(client->line);
		}
	}
	// Otherwise reset the buffer and line lengths to zero.
	else {
		st_length_set(client->buffer, 0);
		client->line = pl_null();
	}

	// Loop until we get a complete line, an error, or the buffer is filled.
	do {

		// Read bytes off the network. Skip past any existing data in the buffer.
		if (client->tls) {

			// If bytes is zero or below and the library isn't asking for another read, then an error occurred.
			bytes = tls_read(client->tls, st_char_get(client->buffer) + st_length_get(client->buffer),
				st_avail_get(client->buffer) - st_length_get(client->buffer), blocking);

			// If zero bytes were read, or a negative value was returned to indicate an error, call tls_erorr(), which will return
			// NULL if the error can be safely ignored. Otherwise log the output for debug purposes.
			if (bytes <= 0 && (error = tls_error(client->tls, bytes, MANAGEDBUF(512)))) {
#ifdef MAGMA_PEDANTIC
				cipher = tls_cipher(client->tls, MANAGEDBUF(128));
				ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN));

				log_pedantic("TLS client read operation failed. { ip = %.*s / %.*s / result = %zi%s%.*s }",
					st_length_int(ip), st_char_get(ip), st_length_int(cipher), st_char_get(cipher),
					bytes, (error ? " / " : ""), st_length_int(error), st_char_get(error));
#endif
				client->status = -1;
				return -1;
			}
			// This will occur when the read operation results in a 0, or negative value, but TLS error returns NULL to
			// indicate it was a transient error. For transient errors we simply set bytes equal to 0 so the read call gets retried.
			else if (bytes <= 0) {
				bytes = 0;
			}
		}
		else {

			errno = 0;

			bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer),
				st_avail_get(client->buffer) - st_length_get(client->buffer), (blocking ? 0 : MSG_DONTWAIT));

			// Check for errors on non-SSL reads in the traditional way.
			if (bytes <= 0 && tcp_status(client->sockd)) {
#ifdef MAGMA_PEDANTIC
				local = errno;
				ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN));

				log_pedantic("TCP client read operation failed. { ip = %.*s / result = %zi / error = %i / message = %s }",
					st_length_int(ip), st_char_get(ip), bytes, local, strerror_r(local, MEMORYBUF(1024), 1024));
#endif
				client->status = -1;
				return -1;
			}

		}

		// We actually read in data, so we need to update the buffer to reflect the amount of data it currently holds.
		if (bytes > 0) {
			st_length_set(client->buffer, st_length_get(client->buffer) + bytes);
		}

		// Check whether we have a complete line before checking whether the connection was closed.
		if (!st_empty(client->buffer) && !pl_empty((client->line = line_pl_st(client->buffer, 0)))) {
			line = true;
		}

	} while (!line && counter++ < 128 && st_length_get(client->buffer) != st_avail_get(client->buffer) && status());

	if (st_length_get(client->buffer) > 0) {
		client->status = 1;
	}

	return pl_length_get(client->line);
}
コード例 #27
0
ファイル: string_check.c プロジェクト: lavabit/magma
bool_t check_string_print(void) {

	uint64_t total;
	bool_t result = true;
	stringer_t *strings[14];

	mm_set(strings, 0, sizeof(strings));

	strings[0] = st_aprint_opts(NULLER_T | CONTIGUOUS | HEAP, "%.*s", st_length_int(string_check_constant), st_char_get(string_check_constant));
	strings[1] = st_aprint_opts(NULLER_T | JOINTED | HEAP, "%.*s", st_length_int(strings[0]), st_char_get(strings[0]));
	strings[2] = st_aprint_opts(BLOCK_T | CONTIGUOUS | HEAP, "%.*s", st_length_int(strings[1]), st_char_get(strings[1]));
	strings[3] = st_aprint_opts(BLOCK_T | JOINTED | HEAP, "%.*s", st_length_int(strings[2]), st_char_get(strings[2]));
	strings[4] = st_aprint_opts(MANAGED_T | CONTIGUOUS | HEAP, "%.*s", st_length_int(strings[3]), st_char_get(strings[3]));
	strings[5] = st_aprint_opts(MANAGED_T | JOINTED | HEAP, "%.*s", st_length_int(strings[4]), st_char_get(strings[4]));
	strings[6] = st_aprint_opts(MAPPED_T | JOINTED | HEAP, "%.*s", st_length_int(strings[5]), st_char_get(strings[5]));

	strings[7] = st_aprint_opts(NULLER_T | CONTIGUOUS | SECURE, "%.*s", st_length_int(strings[6]), st_char_get(strings[6]));
	strings[8] = st_aprint_opts(NULLER_T | JOINTED | SECURE, "%.*s", st_length_int(strings[7]), st_char_get(strings[7]));
	strings[9] = st_aprint_opts(BLOCK_T | CONTIGUOUS | SECURE, "%.*s", st_length_int(strings[8]), st_char_get(strings[8]));
	strings[10] = st_aprint_opts(BLOCK_T | JOINTED | SECURE, "%.*s", st_length_int(strings[9]), st_char_get(strings[9]));
	strings[11] = st_aprint_opts(MANAGED_T | CONTIGUOUS | SECURE, "%.*s", st_length_int(strings[10]), st_char_get(strings[10]));
	strings[12] = st_aprint_opts(MANAGED_T | JOINTED | SECURE, "%.*s", st_length_int(strings[11]), st_char_get(strings[11]));
	strings[13] = st_aprint_opts(MAPPED_T | JOINTED | SECURE, "%.*s", st_length_int(strings[12]), st_char_get(strings[12]));

	for (int i = 0; i < 14 && strings[i]; i++) {
		for (unsigned int j = total = 0; strings[i] && j < st_length_get(strings[i]); j++) {
			total += *(st_char_get(strings[i]) + j);
		}

		if (total != 5366) {
			result = false;
		}
	}

	for (int i = 0; i < 14; i++) {
		if (strings[i])
			st_free(strings[i]);
	}

	return result;

}
コード例 #28
0
ファイル: global.c プロジェクト: 503serviceunavailable/magma
/**
 * @brief	Load all magma configuration options specified by the user on the command line.
 * @note	Each key/value pair extracted from the database is submitted to the following logic:
 *	 			If a config option was loaded from the database, the key must allow it to be configurable via the database.
 *	 			Check to see that any key that has previously been set is allowed to be overwritten.
 * 				If the key is required, it may not contain an empty value.
 * 			Finally, this function sets the appropriate magma key corresponding to the config key.
 * 			All leftover keys not matched to global magma keys will be configured via servers, relay, and cache server options.
 * @return	true if all database config options were parsed and evaluated successfully, or false on failure.
 */
bool_t config_load_cmdline_settings(void) {
	multi_t name;
	magma_keys_t *key;
	inx_cursor_t *cursor;
	nvp_t *config_pairs = NULL;
	stringer_t *value;

	// If not set, then bail out.
	if (!cmdline_config_data)
		return true;

	// Load the command line options and convert them into a name/value pair structure.
	if (!(config_pairs = nvp_alloc())) {
		st_free(cmdline_config_data);
		return false;
	}
	else if (nvp_parse(config_pairs, cmdline_config_data) < 0) {
		nvp_free(config_pairs);
		st_free(cmdline_config_data);
		return false;
	}
	else if (!(cursor = inx_cursor_alloc(config_pairs->pairs))) {
		nvp_free(config_pairs);
		st_free(cmdline_config_data);
		return false;
	}

	// Our command line config data won't be necessary anymore.
	st_free(cmdline_config_data);

	// Run through all of the magma_keys and see if there is a matching name/value pair.
	while (!mt_is_empty(name = inx_cursor_key_next(cursor))) {

		value = inx_cursor_value_active(cursor);

		if ((key = config_key_lookup(name.val.st))) {

			// Make sure the setting can be provided via the configuration file.
			if (!key->file && value) {
					log_critical("%s cannot be changed using command line option.", key->name);
					inx_cursor_free(cursor);
					nvp_free(config_pairs);
					return false;
			}
			// Make sure the required magma_keys are not set to NULL.
			else if (key->required && st_empty(value)) {
				log_critical("%s requires a legal value.", key->name);
				inx_cursor_free(cursor);
				nvp_free(config_pairs);
				return false;
			}

			// Attempt to set the value.
			else if (!config_value_set(key, value)) {
				inx_cursor_free(cursor);
				nvp_free(config_pairs);
				return false;
			}

			// If a legit value was provided, then record that we've set this parameter.
			key->set = true;
		}

		// If we haven't had a match yet, check if its a server param.
		else if (name.val.st && !st_cmp_ci_starts(name.val.st, CONSTANT("magma.servers"))) {
			servers_config(name.val.st, value);
		}

		// If we haven't had a match yet, check if its a relay instance.
		else if (name.val.st && !st_cmp_ci_starts(name.val.st, CONSTANT("magma.relay"))) {
			relay_config(name.val.st, value);
		}

		else if (name.val.st && !st_cmp_ci_starts(name.val.st, CONSTANT("magma.iface.cache.host"))) {
			cache_config(name.val.st, value);
		}

		else {
			log_critical("%.*s is not a valid setting.", st_length_int(name.val.st), st_char_get(name.val.st));
			inx_cursor_free(cursor);
			nvp_free(config_pairs);
			return false;
		}
	}

	inx_cursor_free(cursor);
	nvp_free(config_pairs);

	return true;
}
コード例 #29
0
ファイル: datatier.c プロジェクト: lavabit/magma
/**
 * @brief	Update a specified contact detail in the database, or insert it if it does not exist.
 * @param	contactnum	the numerical id of the contact entry to be modified.
 * @param	key			a managed string containing the name of the contact detail to be updated.
 * @param	value		a managed string containing the new value of the specified contact detail.
 * @param	flags		a bitmask of flags to be associated with the specified contact entry detail.
 * @return	-1 on error, 0 if no update was necessary, 1 if a new contact detail was inserted into the database, or 2 if the
 * 			specified contact detail was updated successfully.
 */
int_t contact_detail_upsert(uint64_t contactnum, stringer_t *key, stringer_t *value, uint64_t flags) {

	int64_t affected;
	MYSQL_BIND parameters[4];

	mm_wipe(parameters, sizeof(parameters));

	// Contact Number
	parameters[0].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[0].buffer_length = sizeof(uint64_t);
	parameters[0].buffer = &contactnum;
	parameters[0].is_unsigned = true;

	// Key
	parameters[1].buffer_type = MYSQL_TYPE_STRING;
	parameters[1].buffer_length = st_length_get(key);
	parameters[1].buffer = st_char_get(key);

	// Value
	parameters[2].buffer_type = MYSQL_TYPE_STRING;
	parameters[2].buffer_length = st_length_get(value);
	parameters[2].buffer = st_char_get(value);

	// Flags
	parameters[3].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[3].buffer_length = sizeof(uint64_t);
	parameters[3].buffer = &flags;
	parameters[3].is_unsigned = true;

	if ((affected = stmt_exec_affected(stmts.upsert_contact_detail, parameters)) == -1) {
		log_pedantic("The contact detail upsert triggered an error. { contact = %lu / key = %.*s }", contactnum, st_length_int(key), st_char_get(key));
		return -1;
	}

	log_check(affected > 2);

	return (int_t)affected;
}
コード例 #30
0
/**
 * @brief	Return a zero-length placer pointing to NULL data.
 * @return	a zero-length placer pointing to NULL data.
 */
placer_t pl_null(void) {

	return (placer_t){ .opts = PLACER_T | JOINTED | STACK | FOREIGNDATA, .data = NULL, .length = 0 };
}

/**
 * @brief	Return a placer wrapping a data buffer of given size.
 * @param	data	a pointer to the data to be wrapped.
 * @param	len		the length, in bytes, of the data.
 * @return	a placer pointing to the specified data.
 */
placer_t pl_init(void *data, size_t len) {

	return (placer_t){ .opts = PLACER_T | JOINTED | STACK | FOREIGNDATA, .data = data, .length = len };
}

placer_t pl_clone(placer_t place) {
	return (pl_init(place.data, place.length));
}

placer_t pl_set(placer_t place, placer_t set) {

	return (placer_t){ .opts = place.opts, .data = set.data, .length = set.length };
}

/**
 * @brief	Get a pointer to the data referenced by a placer.
 * @param	place	the input placer.
 * @return	NULL on failure or a pointer to the block of data associated with the specified placer on success.
 */
void * pl_data_get(placer_t place) {

	return st_data_get((stringer_t *)&place);
}

/**
 * @brief	Get a character pointer to the data referenced by a placer.
 * @param	place	the input placer.
 * @return	NULL on failure or a a character pointer to the block of data associated with the specified placer on success.
 */
chr_t * pl_char_get(placer_t place) {

	return st_char_get((stringer_t *)&place);
}

/**
 * @brief	Get the length, in bytes, of a placer as an integer.
 * @param	place	the input placer.
 * @return	the size, in bytes, of the specified placer.
 */
int_t pl_length_int(placer_t place) {

	return st_length_int((stringer_t *)&place);
}

/**
 * @brief	Get the length, in bytes, of a placer.
 * @param	place	the input placer.
 * @return	the size, in bytes, of the specified placer.
 */
size_t pl_length_get(placer_t place) {

	return st_length_get((stringer_t *)&place);
}

/**
 * @brief	Determine whether or not the specified placer is empty.
 * @param	place	the input placer.
 * @return	true if the placer is empty or zero-length, or false otherwise.
 */
bool_t pl_empty(placer_t place) {

	return st_empty((stringer_t *)&place);
}

/**
 * @brief	Determine if a placer begins with a specified character.
 * @param	place	the input placer.
 * @param	c		the character to be compared with the first byte of the placer's data.
 * @return	true if the placer begins with the given character or false otherwise.
 */
bool_t pl_starts_with_char(placer_t place, chr_t c) {

	if (pl_empty(place)) {
		return false;
	}

	if (*(pl_char_get(place)) == c) {
		return true;
	}

	return false;
}

/**
 * @brief	Advance the placer one character forward beyond an expected character.
 * @param	place	the input placer.
 * @param	more	if true, the placer must contain more data, and vice versa.
 * @return	true if more was true and the placer contains more data, or if more was false and the placer ended; false otherwise.
 */
bool_t pl_inc(placer_t *place, bool_t more) {

	if (pl_empty(*place)) {
		return false;
	}

	place->length--;
	place->data = (chr_t *)place->data + 1;

	return (more == (place->length > 0));
}