Пример #1
0
/**
 * @brief	Get a message, in response to a POP3 DELE command.
 * @param	con		the POP3 client connection issuing the command.
 * @return	This function returns no value.
 */
void pop_dele(connection_t *con) {

	uint64_t number;
	meta_message_t *active = NULL;

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

	// A message number is a required argument.
	if (!pop_num_parse(con, &number, true)) {
		con_write_bl(con, "-ERR The delete command requires a numeric argument.\r\n", 54);
		return;
	}

	meta_user_wlock(con->pop.user);

	if (!(active = pop_get_message(con->pop.user->messages, number))) {
		con_write_bl(con, "-ERR Message not found.\r\n", 25);
	}
	else if ((active->status & MAIL_STATUS_HIDDEN) == MAIL_STATUS_HIDDEN) {
		con_write_bl(con, "-ERR Message already deleted.\r\n", 31);
	}
	else {
		active->status += MAIL_STATUS_HIDDEN;
		con_write_bl(con, "+OK Message marked for deletion.\r\n", 34);
	}

	meta_user_unlock(con->pop.user);

	return;
}
Пример #2
0
void imap_session_destroy(connection_t *con) {

	inx_cursor_t *cursor;
	meta_message_t *active;

	meta_user_wlock(con->imap.user);

	// If a folder was selected, clear the recent flag before closing the mailbox.
	if (con->imap.session_state == 1 && con->imap.user && con->imap.selected && !con->imap.read_only &&
		(cursor = inx_cursor_alloc(con->imap.user->messages))) {

		while ((active = inx_cursor_value_next(cursor))) {

			if (active->foldernum == con->imap.selected && (active->status & MAIL_STATUS_RECENT) == MAIL_STATUS_RECENT) {
				active->status = (active->status | MAIL_STATUS_RECENT) ^ MAIL_STATUS_RECENT;
			}

		}

		inx_cursor_free(cursor);
	}

	meta_user_unlock(con->imap.user);

	// Is there a user session.
	if (con->imap.user) {

		if (con->imap.username) {
			meta_remove(con->imap.username, META_PROT_IMAP);
		}

	}

	st_cleanup(con->imap.username);
	st_cleanup(con->imap.tag);
	st_cleanup(con->imap.command);

	if (con->imap.arguments) {
		ar_free(con->imap.arguments);
	}

	mail_cache_reset();

	return;
}
Пример #3
0
/**
 * @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;
}
Пример #4
0
/**
 * Returns 0 if the selected folder wasn't modified, or 1 if things changed and the updated status should be sent to the client. A -1
 * is used to indicate the update check encountered a problem and should be retried later.
 */
int_t imap_session_update(connection_t *con) {

	int_t result = 0;
	inx_cursor_t *cursor;
	meta_message_t *active;
	uint64_t recent = 0, exists = 0, checkpoint;

	// Check for the right state.
	if (con->imap.session_state != 1 || con->imap.user == NULL || con->imap.selected == 0) {
		return -1;
	}

	if ((checkpoint = serial_get(OBJECT_USER, con->imap.user->usernum)) != con->imap.user_checkpoint) {
		meta_user_wlock(con->imap.user);

		// Update the user preferences.
		if (checkpoint != con->imap.user->serials.user) {
			meta_user_update(con->imap.user, META_LOCKED);
		}

		// Store the new checkpoint.
		con->imap.user_checkpoint = con->imap.user->serials.user;
		meta_user_unlock(con->imap.user);
	}

	if ((checkpoint = serial_get(OBJECT_FOLDERS, con->imap.user->usernum)) != con->imap.folders_checkpoint) {
		meta_user_wlock(con->imap.user);

	   // Update the list of folders.
		if (checkpoint != con->imap.user->serials.folders) {
			meta_folders_update(con->imap.user, META_LOCKED);
		}

		// Store the new checkpoint.
		con->imap.folders_checkpoint = con->imap.user->serials.folders;
		meta_user_unlock(con->imap.user);
	}

	if ((checkpoint = serial_get(OBJECT_MESSAGES, con->imap.user->usernum)) != con->imap.messages_checkpoint) {
		meta_user_wlock(con->imap.user);

		if (checkpoint != con->imap.user->serials.messages) {
			meta_messages_update(con->imap.user, META_LOCKED);
		}

		// If there is a selected folder, scan the status.
		if ((cursor = inx_cursor_alloc(con->imap.user->messages))) {

			while ((active = inx_cursor_value_next(cursor))) {

				if (active->foldernum == con->imap.selected && (active->status & MAIL_STATUS_RECENT) == MAIL_STATUS_RECENT) {
					recent++;
					exists++;
				}
				else if (active->foldernum == con->imap.selected) {
					exists++;
				}

			}

			inx_cursor_free(cursor);
		}

		// If the folder has changed, output the current status.
		if (con->imap.messages_recent != recent || con->imap.messages_total != exists) {
			con->imap.messages_recent = recent;
			con->imap.messages_total = exists;
			result = 1;
		}

		// Store the new checkpoint.
		con->imap.messages_checkpoint = con->imap.user->serials.messages;

		meta_user_unlock(con->imap.user);
	}

	return result;
}
Пример #5
0
/**
 * @brief	Lookup user and return their meta user object.
 *
 * @note	If the user is not found in the local session cache, the session will be constructed using the database, and then cached.
 *
 * @param 	usernum			the numeric identifier for the user account.
 * @param 	username		the official username stored in the database.
 * @param	salt			the user specific salt value.
 * @param	master			the user account's master encryption key which will be used to unlock the private storage key.
 * @param 	verification	the verification token.
 * @param	protocol			a set of protocol specifying the protocol used by the calling function. Values can be META_PROT_NONE,
 * 							META_PROT_SMTP, META_PROT_POP, META_PROT_IMAP, META_PROT_WEB, or META_PROT_GENERIC.
 * @param	get				a set of protocol specifying the data to be retrieved (META_GET_NONE, META_GET_MESSAGES,
 * 							META_GET_FOLDERS, or META_GET_CONTACTS)
 * @param	output			the address of a meta user object that will store a pointer to the result of the lookup.
 *
 * @return	-1 on error, 0 on success, 1 for an authentication issue.
 */
int_t meta_get(uint64_t usernum, stringer_t *username, stringer_t *salt, stringer_t *master, stringer_t *verification, META_PROTOCOL protocol, META_GET get, meta_user_t **output) {

	int_t state;
	meta_user_t *user = NULL;

	// If the auth structure is empty, or the usernum is invalid, return an error immediately.
	if (!usernum || !st_populated(username, master, verification)) {
		log_pedantic("Invalid parameters were used to get the meta data object.");
		return -1;
	}

	// Pull the user context using the usernum, or add an empty context if it doesn't exist.
	if (!(user = meta_inx_find(usernum, protocol))) {
		log_pedantic("Could not find an existing user object, nor could we create one.");
		return -1;
	}

	meta_user_wlock(user);

	// Pull the user information.
	if ((state = meta_update_user(user, META_LOCKED)) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return state;
	}

	// The auth_t object should have checked the verification token already, but we check here just to be sure.
	else if (st_empty(user->verification) || st_cmp_cs_eq(verification, user->verification)) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return 1;
	}

	// Are we supposed to get the realm keys.
	if ((get & META_GET_KEYS) && meta_update_realms(user, salt, master, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	// Are we supposed to get the mailbox keys.
	if ((get & META_GET_KEYS) && meta_update_keys(user, META_LOCKED) < 0) {

		// If key decryption fails, then the master key is likely invalid, so we need to ensure we don't cache an invalid
		// realm key as a result.
		st_cleanup(user->realm.mail);
		user->realm.mail = NULL;
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

 	 // Are we supposed to get the mailbox aliases.
	if ((get & META_GET_ALIASES) && meta_update_aliases(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	// Are we supposed to get the messages.
	if ((get & META_GET_MESSAGES) && meta_messages_update(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	if ((get & META_GET_FOLDERS) && meta_update_message_folders(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	// Are we supposed to update the folders.
	if ((get & META_GET_FOLDERS) && meta_update_folders(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	// Are we supposed to update the folders.
	if ((get & META_GET_CONTACTS) && meta_update_contacts(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	*output = user;
	meta_user_unlock(user);

	return 0;
}