Beispiel #1
0
bool_t check_encoding_qp(void) {

	stringer_t *qp, *binary;
	byte_t buffer[QP_CHECK_SIZE];

	for (uint64_t i = 0; status() && i < QP_CHECK_ITERATIONS; i++) {

		// Fill the buffer with random data and convert the buffer to hex.
		if (rand_write(PLACER(buffer, QP_CHECK_SIZE)) != QP_CHECK_SIZE) {
			return false;
		}
		else if (!(qp = qp_encode(PLACER(buffer, QP_CHECK_SIZE)))) {
			return false;
		}

		//log_pedantic("qp = %.*s", st_length_int(qp), st_char_get(qp));

		// Convert the buffer back to binary and compare it with the original array.
		if (!(binary = qp_decode(qp))) {
			st_free(qp);
			return false;
		}
		else if (st_cmp_cs_eq(binary, PLACER(buffer, QP_CHECK_SIZE))) {
			st_free(binary);
			st_free(qp);
			return false;
		}

		st_free(binary);
		st_free(qp);
	}

	return true;
}
/**
 * @brief	Count the number of "Received:" lines in a mail message.
 * @param	message		a managed string containing the mail message to be scanned.
 * @return	the number of matching lines found, or 0 on failure.
 */
uint32_t mail_count_received(stringer_t *message) {

	size_t len;
	uchr_t *ptr;
	uint32_t result = 0;

	if (st_empty_out(message, &ptr, &len) || len <= 9) {
		log_pedantic("Message is too short to hold any valid received lines.");
		return result;
	}

	// Loop through and check every new line.
	// LOW: This loop could be improved using the new string interfaces.
	for (size_t i = 0; i <= (len - 9); i++) {

		// Make sure we detect the end of a header on messages without headers.
		if (*ptr == '\n' || i == 0) {

			// Detect the end of a header.
			if (*(ptr + 1) == '\r' && *(ptr + 2) == '\n') {
				i = len;
			} else if (*(ptr + 1) == '\n') {
				i = len;
			} else if (st_cmp_ci_starts(PLACER(ptr + 1, len - i - 1), PLACER("Received:", 9)) == 0) {
				result++;
			}

		}
		ptr++;
	}

	return result;
}
/**
 * @brief	Insert a spam signature training link into a plain text message part.
 * @see		mail_discover_insertion_point()
 * @param	server		the server object of the web server where the teacher application is hosted.
 * @param	message		a managed string containing the message body to be parsed.
 * @param	part		a managed string (placer) containing the part of the message where the signature should be inserted.
 * @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".
 * @param	type		the encoding type of the message (MESSAGE_TYPE_HTML or other).
 * @param	encoding	if MESSAGE_ENCODING_QUOTED_PRINTABLE, set the encoding type to qp.
 * @return	NULL on failure or a managed string containing the message with the inserted signature training link on success.
 */
stringer_t * mail_insert_chunk_text(server_t *server, stringer_t *message, stringer_t *part, uint64_t signum, uint64_t sigkey, int_t disposition, int_t type, int_t encoding) {

	size_t point;
	stringer_t *pretext;
	stringer_t *posttext;
	stringer_t *result;
	stringer_t *signature;

	if (st_empty(part)) {
		return NULL;
	}

	// Start at the end of the chunk and search for where to insert the signature.
	if (!(signature = mail_build_signature(server, type, encoding, signum, sigkey, disposition))) {
		return NULL;
	}

	// Figure out the insertion point_t and create the new message.
	point = mail_discover_insertion_point(message, part, type);
	pretext = PLACER(st_char_get(message), point);
	posttext = PLACER(st_char_get(message) + point, st_length_get(message) - point);
	result = st_merge("sss", pretext, signature, posttext);

	st_free(signature);

	if (!result) {
		log_pedantic("Unable to merge the strings together.");
	}

	return result;
}
Beispiel #4
0
/**
 * @note	This function is currently not called anywhere in the code and may be subject to removal.
 */
int_t portal_parse_action(stringer_t *action) {

	int_t result = PORTAL_ENDPOINT_ACTION_INVALID;

	if (!action) {
		return result;
	}

	// Parse the action.
	if (!st_cmp_ci_eq(action, PLACER("add", 3))) {
		result = PORTAL_ENDPOINT_ACTION_ADD;
	}
	else if (!st_cmp_ci_eq(action, PLACER("remove", 6))) {
		result = PORTAL_ENDPOINT_ACTION_REMOVE;
	}
	else if (!st_cmp_ci_eq(action, PLACER("replace", 7))) {
		result = PORTAL_ENDPOINT_ACTION_REPLACE;
	}
	else if (!st_cmp_ci_eq(action, PLACER("list", 4))) {
		result = PORTAL_ENDPOINT_ACTION_LIST;
	}
	else {
		result = PORTAL_ENDPOINT_ACTION_INVALID;
	}

	return result;
}
bool_t check_encoding_zbase32(void) {

	stringer_t *zb32, *binary;
	byte_t buffer[ZBASE32_CHECK_SIZE];

	for (uint64_t i = 0; status() && i < ZBASE32_CHECK_ITERATIONS; i++) {

		// Fill the buffer with random data and convert the buffer to hex.
		if (rand_write(PLACER(buffer, ZBASE32_CHECK_SIZE)) != ZBASE32_CHECK_SIZE) {
			return false;
		}
		else if (!(zb32 = zbase32_encode(PLACER(buffer, ZBASE32_CHECK_SIZE)))) {
			return false;
		}

		//log_pedantic("zb32 = %.*s", st_length_int(zb32), st_char_get(zb32));

		// Convert the buffer back to binary and compare it with the original array.
		if (!(binary = zbase32_decode(zb32))) {
			st_free(zb32);
			return false;
		}
		else if (st_cmp_cs_eq(binary, PLACER(buffer, ZBASE32_CHECK_SIZE))) {
			st_free(binary);
			st_free(zb32);
			return false;
		}

		st_free(binary);
		st_free(zb32);
	}

	return true;
}
/**
 * @brief	Get a specified chunk (mime part) of a multipart mime message.
 * @param	message		a managed string containing the mime message to be parsed.
 * @param	boundary	a managed string containing the boundary used to split the multipart mime message.
 * @param	chunk		the one-index based chunk to be retrieved from the multipart message
 * @return	NULL on failure or a placer containing the specified chunk on success.
 */
stringer_t * mail_get_chunk(stringer_t *message, stringer_t *boundary, int_t chunk) {

	int_t found = 0;
	stringer_t *result;
	size_t start = 0, length = 0, input = 0;

	while (chunk != 0) {

		// So on repeats we don't have to start all over again.
		if (length != 0) {
			start += length - 1;
		}

		found = 0;

		while (found == 0) {

			// Get the start of the MIME message part.
			if (!st_search_cs(PLACER(st_char_get(message) + start, st_length_get(message) - start), boundary, &input)) {
				log_pedantic("The boundary doesn't appear to be part of this message.");
				return NULL;
			}

			// Skip the boundary before searching again.
			start += input + st_length_get(boundary);

			// This will detect the section ending.
			if (st_length_get(message) - start >= 2 && mm_cmp_cs_eq(st_char_get(message) + start, "--", 2) == 1) {
				return NULL;
			}
			// Some broken implementations use similar boundaries. This should detect those.
			else if (st_length_get(message) - start > 0 && (*(st_char_get(message) + start) < '!' || *(st_char_get(message) + start) > '~')) {
				found = 1;
			}
		}

		found = 0;

		while (found == 0) {

			// Get the end.
			if (!st_search_cs(PLACER(st_char_get(message) + start, st_length_get(message) - start), boundary, &length)) {
				length = st_length_get(message) - start;
				found = 1;
			}
			else if (st_length_get(message) - start - length > 0 && (*(st_char_get(message) + start) < '!' || *(st_char_get(message) + start) > '~')) {
				found = 1;
			}

		}

		chunk--;
	}

	// Setup a placer with the chunk.
	result = PLACER(st_char_get(message) + start, length);

	return result;
}
Beispiel #7
0
bool_t check_bitwise_determinism(void) {

	stringer_t *a = NULL, *b = NULL, *res1 = NULL, *res2 = NULL;

	unsigned char a_buf[] = {0x00, 0x33, 0x80, 0x04, 0x20};
	unsigned char b_buf[] = {0x11, 0xCC, 0x78, 0x4B, 0x1E};

	a = PLACER(a_buf, 5);
	b = PLACER(b_buf, 5);

	if(!(res1 = st_xor(a, b, NULL)) || !(res2 = st_xor(a, b, NULL))) {
		st_cleanup(res1, res2);
		return false;
	}
	else if(st_cmp_cs_eq(res1, res2)) {
		st_cleanup(res1, res2);
		return false;
	}

	st_cleanup(res1, res2);
	res1 = res2 = NULL;

	if(!(res1 = st_and(a, b, NULL)) || !(res2 = st_and(a, b, NULL))) {
		st_cleanup(res1, res2);
		return false;
	}
	else if(st_cmp_cs_eq(res1, res2)) {
		st_cleanup(res1, res2);
		return false;
	}

	st_cleanup(res1, res2);
	res1 = res2 = NULL;

	if(!(res1 = st_or(a, b, NULL)) || !(res2 = st_or(a, b, NULL))) {
		st_cleanup(res1, res2);
		return false;
	}
	else if(st_cmp_cs_eq(res1, res2)) {
		st_cleanup(res1, res2);
		return false;
	}

	st_cleanup(res1, res2);
	res1 = res2 = NULL;

	if(!(res1 = st_not(a, NULL)) || !(res2 = st_not(a, NULL))) {
		st_cleanup(res1, res2);
		return false;
	}
	else if(st_cmp_cs_eq(res1, res2)) {
		st_cleanup(res1, res2);
		return false;
	}

	st_cleanup(res1, res2);
	return true;
}
Beispiel #8
0
/**
 * @brief	Compare the names of two commands.
 * @note	This is an internal function used to sort imap commands and search for them.
 * @param	compare		a pointer to the first command to be compared.
 * @param	command		a pointer to the second command to be compared.
 * @return	-1 if compare < command, 1 if command < compare, or 0 if the two commands are equal.
 */
int_t imap_compare(const void *compare, const void *command) {

	int_t result;
	command_t *cmd = (command_t *)command, *cmp = (command_t *)compare;

	if (!cmp->function)	result = st_cmp_ci_starts(PLACER(cmp->string, cmp->length), PLACER(cmd->string, cmd->length));
	else result = st_cmp_ci_eq(PLACER(cmp->string, cmp->length), PLACER(cmd->string, cmd->length));

	return result;
}
Beispiel #9
0
/**
 * @brief	Select a random truetype font from the directory specified in magma.http.fonts.
 * @return	NULL on failure, or a managed string containing the pathname of the randomly selected font file on success.
 */
stringer_t * register_captcha_random_font(void) {

	DIR *directory;
	stringer_t *path;
	struct dirent64 *dp;
	size_t count = 0, selection;

	// Open the current working directory.
	if (!(directory = opendir(magma.http.fonts))) {
		log_pedantic("Unable to open the font directory. { directory = %s }", magma.http.fonts);
		return NULL;
	}

	// Count the number of fonts.
	while ((dp = readdir64(directory))) {

		if (!st_cmp_ci_ends(NULLER(dp->d_name), PLACER(".ttf", 4))) {
			count++;
		}

	};

	// No fonts were found.
	if (!count) {
		log_pedantic("The web fonts directory is empty. { directory = %s }", magma.http.fonts);
		closedir(directory);
		return NULL;
	}

	// Pick a random font.
	selection = (rand_get_uint32() % count) + 1;

	// Reset the directory stream.
	rewinddir(directory);

	// Do the loop again.
	while (selection && (dp = readdir64(directory))) {

		if (!st_cmp_ci_ends(NULLER(dp->d_name), PLACER(".ttf", 4))) {
			selection--;
		}

	}

	// Build the path.
	if (selection || !dp || !(path = st_aprint("%s/%s", magma.http.fonts, dp->d_name))) {
		log_pedantic("Could not build the font file path.");
		closedir(directory);
		return NULL;
	}

	closedir(directory);
	return path;
}
Beispiel #10
0
/**
 * @brief	Encode a data buffer as a valid URL component.
 * @param	s       a managed string containing the data to be encoded.
 * @return	NULL on failure, or a freshly allocated managed string containing the fully-escaped string suitable for use in a URL.
 */
stringer_t *url_encode(stringer_t *s) {

	chr_t hex[4];
	uchr_t *p;
	stringer_t *output, *r;
	size_t len, expected = 0, written = 0;

	if (st_empty_out(s, &p, &len)) {
		log_pedantic("An empty string was passed in for encoding.");
		return NULL;
	}

	// Increment through the stringer and count the characters that need to be encoded.
	for (size_t i = 0; i < len; i++) {
		if (url_valid_chr(*p)) {
			expected++;
		} else {
			expected += 3;
		}

		p++;
	}

	// Allocate one byte for printable characters and three bytes for non-printable characters.
	if (!(output = st_alloc_opts(MANAGED_T | JOINTED | HEAP, expected))) {
		log_pedantic("Could not allocate a buffer large enough to hold encoded result. {requested = %zu}", expected);
		return NULL;
	}

	// Get setup.
	p = st_data_get(s);

	// Increment through the stringer and copy the data into the new stringer.
	for (size_t i = 0; i < len; i++) {

		// Escape the invalid characters.
		if (url_valid_chr(*p)) {
			if ((r = st_append(output, PLACER(p, 1)))) {
				output = r;
				written++;
			}
		} else if (snprintf(hex, 4, "%%%02X", *p) == 3 && (r = st_append(output, PLACER(&hex[0], 3)))) {
			output = r;
			written += 3;
		}

		// We always advance the input pointer.
		p++;
	}

	st_length_set(output, written);
	return output;
}
// QUESTION: Why are we using managed strings here? It makes no sense, especially since we have expectations as to where they point.
// QUESTION: And why even have message? It seems like we could make do with just having part.
size_t mail_discover_insertion_point(stringer_t *message, stringer_t *part, int_t type) {

	chr_t *stream;
	size_t length;
	stringer_t *tag;

	// If the message is not HTML, return the end of the part as the insertion point.
	if (type != MESSAGE_TYPE_HTML) {
		return st_char_get(part) - st_char_get(message) + st_length_get(part);
	}

	// Get setup.
	length = st_length_get(part);
	stream = st_data_get(part) + length - 1;

	while (length) {

		// Reverse until we find a character that is not whitespace.
		while (length && (*stream == ' ' || *stream == '\r' || *stream == '\n' || *stream == '\t')) {
			length--;
			stream--;
		}

		if (!(tag = mail_extract_tag(stream, length))) {
			return (st_char_get(part) - st_char_get(message)) + length;
		}

		// What tag is it?
		if (st_cmp_ci_eq(tag, PLACER("</body>", 7)) != 0 && st_cmp_ci_eq(tag, PLACER("</html>", 7)) != 0) {
			st_free(tag);
			return (st_char_get(part) - st_char_get(message)) + length;
		}

		st_free(tag);

		// Advance past this tag.
		while (length && *stream != '<') {
			length--;
			stream--;
		}

		if (length && *stream == '<') {
			length--;
			stream--;
		}

	}

	return (st_char_get(part) - st_char_get(message)) + length;
}
Beispiel #12
0
/**
 * @brief	Fetch a user's config collection from the database.
 * @param	collection	a pointer to the specified user config collection object (with usernum field set by the caller) to be populated from the database.
 * @return	true on success or false on failure.
 */
bool_t user_config_fetch(user_config_t *collection) {

	row_t *row;
	table_t *result;
	MYSQL_BIND parameters[1];
	user_config_entry_t *record;
	multi_t key = { .type = M_TYPE_STRINGER, .val.st = NULL };

	mm_wipe(parameters, sizeof(parameters));

	if (!collection || !collection->usernum) {
		log_pedantic("Invalid data passed to user config fetch.");
		return false;
	}

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

	if (!(result = stmt_get_result(stmts.select_user_config, parameters))) {
		log_pedantic("Unable to fetch the user config entries.");
		return false;
	}

	// Loop through each row and create a user config entry.
	while ((row = res_row_next(result))) {

		if (!(record = user_config_entry_alloc(PLACER(res_field_block(row, 0), res_field_length(row, 0)),
			PLACER(res_field_block(row, 1), res_field_length(row, 1)), res_field_uint64(row, 2))) ||
			!(key.val.st = record->key) || !inx_insert(collection->entries, key, record)) {
			log_info("The index refused to accept a user config entry. { user  = %lu }", res_field_uint64(row, 0));

			if (record) {
				user_config_entry_free(record);
			}

			res_table_free(result);
			return false;
		}

	}

	res_table_free(result);

	return true;
}
/* modeled closely after args_parse() */
void check_args_parse(int argc, char *argv[]) {

	int_t i = 1;

	while (i < argc) {

		if (!st_cmp_cs_eq(NULLER(argv[i]), PLACER("-v", 2))) {

			if (!(virus_check_data_path = check_next_opt(argv, &i, argc))) {
				do_virus_check = false;
			}

		}
		else if (!st_cmp_cs_eq(NULLER(argv[i]), PLACER("-t", 2))) {

			if (!(tank_check_data_path = check_next_opt(argv, &i, argc))) {
				do_tank_check = false;
			}

		}
		else if (!st_cmp_cs_eq(NULLER(argv[i]), PLACER("-d", 2))) {

			if (!(dspam_check_data_path = check_next_opt(argv, &i, argc))) {
				do_dspam_check = false;
			}

		}
		else if (!st_cmp_cs_eq(NULLER(argv[i]), PLACER("-s", 2))) {
			do_spf_check = false;
			i++;
		}
		// See if it's an illegal parameter beginning with "-"
		else if (!mm_cmp_cs_eq(argv[i], "-", 1)) {
			check_display_usage(argv[0]);
		}
		// Otherwise it's the config file
		else if (i == (argc-1)) {
			snprintf(magma.config.file, MAGMA_FILEPATH_MAX, "%s", argv[i]);
			i++;
		}
		else {
			check_display_usage(argv[0]);
		}

	}

	return;
}
Beispiel #14
0
/**
 * @brief
 *  Load an EC private key from a file.
 * @param filename
 *  the name of the filename from which the key should be loaded
 * @return
 *  a pointer to the deserialized private key from the the file.
 */
EC_KEY * _load_ec_privkey(char const *filename) {
	char *filedata;
	unsigned char *bin;
	size_t binsize;
	EC_KEY *result;

	if (!filename) {
		RET_ERROR_PTR(ERR_BAD_PARAM, NULL);
	}

	if (!(filedata = _read_pem_data(filename, "EC PRIVATE KEY", 1))) {
		RET_ERROR_PTR(ERR_UNSPEC, "could not read ec pubkey pem file");
	}

	bin = _b64decode(filedata, strlen(filedata), &binsize);
	_secure_wipe(filedata, strlen(filedata));
	free(filedata);
	if (!bin) {
		RET_ERROR_PTR(ERR_UNSPEC, "could not decode b64 data");
	}

	//result = _deserialize_ec_privkey(bin, binsize, 0);
	result = secp256k1_private_set(PLACER(bin, binsize));
	_secure_wipe(bin, binsize);
	free(bin);
	if (!result) {
		RET_ERROR_PTR(ERR_UNSPEC, "could not deserialize binary ec pubkey");
	}

	return result;
}
Beispiel #15
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;
}
Beispiel #16
0
/**
 * @brief	Get the subtype of the Content-Type header value from a mime header.
 * @note	For example in the case of a Content-Type of 'text/plain', "plain" would be the subtype.
 * @param	header	a placer pointing to the mime header to be parsed.
 * @return	a managed string containing the content subtype of the header, with "plain" as the default.
 */
stringer_t * mail_mime_type_sub(placer_t header) {

	chr_t *stream;
	stringer_t *line, *result;
	size_t remaining, characters = 0;

	if (!(line = mail_header_fetch_cleaned(&header, PLACER("Content-Type", 12)))) {
		return st_import("plain", 5);
	}

	stream = st_char_get(line);
	remaining = st_length_get(line);

	// Advance past any garbage.
	while (remaining && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t' || *stream == '"')) {
		stream++;
		remaining--;
	}

	// Count how many characters are in the group. Based on RFC4288, section 4.2.
	while (remaining && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') ||
		*stream == '!' || *stream == '#' || *stream == '$' || *stream == '&' || *stream == '.' || *stream == '+' || *stream == '-' ||
		*stream == '^' || *stream == '_')) {
		stream++;
		remaining--;
	}

	// Make sure we got something back. Use a default value of plain.
	if (!remaining || *stream != '/') {
		st_free(line);
		return st_import("plain", 5);
	}

	// Advance past the separator.
	stream++;
	remaining--;

	// Count how many characters are in the subtype. Based on RFC4288, section 4.2.
	while (remaining != 0 && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') ||
		*stream == '!' || *stream == '#' || *stream == '$' || *stream == '&' || *stream == '.' || *stream == '+' || *stream == '-' ||
		*stream == '^' || *stream == '_')) {
		stream++;
		remaining--;
		characters++;
	}

	// Make sure we got something back. Use a default value of text.
	if (!characters) {
		st_free(line);
		return st_import("plain", 5);
	}

	result = st_import(stream - characters, characters);
	st_free(line);

	return result;
}
Beispiel #17
0
 /**
  * @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;
}
Beispiel #18
0
/**
 * @brief	Provide the SSL/TLS/DTLS version as a string constant.
 * @see		SSL_get_version()
 */
chr_t * tls_version(TLS *tls) {

	chr_t *version = NULL;
	SSL_CIPHER *cipher = NULL;

	if (!tls || !(cipher = SSL_get_current_cipher_d(tls)) || !(version = (chr_t *)SSL_get_version_d(tls))) {
		return NULL ;
	}

	return (st_cmp_cs_eq(NULLER(version), PLACER("TLSv1", 5)) ? version : "TLSv1.0");
 }
Beispiel #19
0
/**
 * @brief	Securely generate a unique zbase32-encoded token for a session.
 * @param	sess	a pointer to the input web session.
 * @return	a managed string containing the generated web session token.
 */
stringer_t * sess_token(session_t *sess) {

	scramble_t *encrypted = NULL;
	stringer_t *binary = NULL, *encoded = NULL;

	if (!(binary = st_merge("ssss", PLACER(&(sess->warden.host), sizeof(uint64_t)), PLACER(&(sess->warden.stamp), sizeof(uint64_t)),
		PLACER(&(sess->warden.number),	sizeof(uint64_t)), PLACER(&(sess->warden.key), sizeof(uint64_t)))) ||
		!(encrypted = scramble_encrypt(magma.secure.sessions, binary)) ||
		!(encoded = zbase32_encode(PLACER(encrypted, scramble_total_length(encrypted))))) {
		log_pedantic("An error occurred while trying to generate the session token.");
	}

	if (encrypted) {
		scramble_free(encrypted);
	}

	st_cleanup(binary);

	return encoded;
}
/**
 * @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;
}
Beispiel #21
0
bool_t check_hash_sthread(chr_t *name) {

	stringer_t *hash;
	digest_t *digest;
	byte_t buffer[DIGEST_CHECK_SIZE];

	for (uint64_t i = 0; status() && i < DIGEST_CHECK_ITERATIONS; i++) {

		// Fill the buffer with random data and convert the buffer to encrypted.
		if (rand_write(PLACER(buffer, DIGEST_CHECK_SIZE)) != DIGEST_CHECK_SIZE) {
			return false;
		}
		//else if (!(digest = digest_name(NULLER(name))) || !(hash = hash_digest(digest, PLACER(buffer, DIGEST_CHECK_SIZE), NULL))) {
		else if (!(digest = digest_name(NULLER(name))) || !(hash = hash_digest(digest, PLACER(" ", 1), NULL))) {
			return false;
		}

		st_free(hash);
	}

	return true;
}
Beispiel #22
0
bool_t check_encoding_url(void) {

	bool_t result = true;
	stringer_t *url, *binary;
	byte_t buffer[URL_CHECK_SIZE];

	for (uint64_t i = 0; status() && result && i < URL_CHECK_ITERATIONS; i++) {

		// Fill the buffer with random data and convert the buffer to hex.
		if (rand_write(PLACER(buffer, URL_CHECK_SIZE)) != URL_CHECK_SIZE) {
			return false;
		}
		else if (!(url = url_encode(PLACER(buffer, URL_CHECK_SIZE)))) {
			return false;
		}
		else if (!url_valid_st(url)) {
			return false;
		}

		// Convert the buffer back to binary and compare it with the original array.
		if (!(binary = url_decode(url))) {
			st_free(url);
			return false;
		}
		else if (st_cmp_cs_eq(binary, PLACER(buffer, URL_CHECK_SIZE))) {
			result = false;
		}

		//log_pedantic("%-15.15s = %.*s", "plain", URL_CHECK_SIZE, buffer);
		//log_pedantic("%-15.15s = %.*s", "url", st_length_int(url), st_char_get(url));
		//log_pedantic("%-15.15s = %.*s", "decoded", st_length_int(binary), st_char_get(binary));

		st_free(binary);
		st_free(url);
	}

	return result;
}
Beispiel #23
0
/**
 * @brief	Get the content type from a MIME header.
 * @note	If no content type is specified in the header via Content-Type, "text/plain" is assumed.
 * @param	header	a placer containing the MIME header to be examined.
 * @return	the MIME content type specified by the header, or MESSAGE_TYPE_UNKNOWN on failure.
 */
int_t mail_mime_type(placer_t header) {

	chr_t *stream;
	int_t result;
	size_t remaining;
	stringer_t *holder;

	if ((holder = mail_header_fetch_cleaned(&header, PLACER("Content-Type", 12))) == NULL) {
		return MESSAGE_TYPE_PLAIN;
	}

	remaining = st_length_get(holder);
	stream = st_char_get(holder);

	// Advance past any garbage.
	while (remaining != 0 && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t' || *stream == '"')) {
		stream++;
		remaining--;
	}

	if (remaining >= 21 && mm_cmp_ci_eq(stream, "multipart/alternative", 21) == 0) {
		result = MESSAGE_TYPE_MULTI_ALTERNATIVE;
	}
	else if (remaining >= 17 && mm_cmp_ci_eq(stream, "multipart/related", 17) == 0) {
		result = MESSAGE_TYPE_MULTI_RELATED;
	}
	else if (remaining >= 15 && mm_cmp_ci_eq(stream, "multipart/rfc822", 16) == 0) {
		result = MESSAGE_TYPE_MULTI_RFC822;
	}
	else if (remaining >= 15 && mm_cmp_ci_eq(stream, "multipart/mixed", 15) == 0) {
		result = MESSAGE_TYPE_MULTI_MIXED;
	}
	else if (remaining >= 9 && mm_cmp_ci_eq(stream, "text/html", 9) == 0) {
		result = MESSAGE_TYPE_HTML;
	}
	else if (remaining >= 9 && mm_cmp_ci_eq(stream, "text/plain", 9) == 0) {
		result = MESSAGE_TYPE_PLAIN;
	}
	else if (remaining >= 15 && mm_cmp_ci_eq(stream, "multipart", 9) == 0) {
		result = MESSAGE_TYPE_MULTI_UNKOWN;
	}
	else {
		result = MESSAGE_TYPE_UNKNOWN;
	}

	st_free(holder);

	return result;
}
stringer_t * check_rand_sthread(void) {

	size_t len;
	uint64_t num = 0;
	stringer_t *buffer;

	if (!(buffer = st_alloc(RAND_CHECK_SIZE_MAX))) {
		return st_dupe(NULLER("Buffer allocation error."));
	}

	for (int_t i = 0; status() && i < RAND_CHECK_ITERATIONS; i++) {

		num |= rand_get_int8();
		num |= rand_get_int16();
		num |= rand_get_int32();
		num |= rand_get_int64();

		num |= rand_get_uint8();
		num |= rand_get_uint16();
		num |= rand_get_uint32();
		num |= rand_get_uint64();

		// Pick a random length.
		len = (rand() % (RAND_CHECK_SIZE_MAX - RAND_CHECK_SIZE_MIN)) + RAND_CHECK_SIZE_MIN;

		if (rand_write(PLACER(st_char_get(buffer), len)) != len) {
			st_cleanup(buffer);
			return st_dupe(NULLER("Unable to fill the buffer with random data."));
		}
	}

	st_cleanup(buffer);

	// This time through we use the choices function since it will allocate its own output buffer.
	for (int_t i = 0; status() && i < RAND_CHECK_ITERATIONS; i++) {

		// Pick a random length.
		len = (rand() % (RAND_CHECK_SIZE_MAX - RAND_CHECK_SIZE_MIN)) + RAND_CHECK_SIZE_MIN;

		if (!(buffer = rand_choices("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", len))) {
			return st_dupe(NULLER("Unable to fill the buffer with random data."));
		}
		st_free(buffer);
	}

	return NULL;
}
Beispiel #25
0
/**
 * @brief	Free all loaded magma configuration options.
 * @note	First all magma config keys will be freed, then the cache servers, relay servers, and magma servers.
 * @return	This function returns no value.
 */
void config_free(void) {

	for (uint64_t i = 0; i < sizeof(magma_keys) / sizeof(magma_keys_t); i++) {
		switch (magma_keys[i].norm.type) {

		case (M_TYPE_BLOCK):
			if (*((void **)(magma_keys[i].store))) {
				mm_free(*((void **)(magma_keys[i].store)));
			}
			break;
		case (M_TYPE_NULLER):
			if (*((char **)(magma_keys[i].store))) {
				ns_free(*((char **)(magma_keys[i].store)));
			}
			break;
		case (M_TYPE_STRINGER):

			// Intercept the blacklist config key.
			if (!st_cmp_cs_eq(NULLER(magma_keys[i].name), PLACER("magma.smtp.blacklist", 20))) {
				for (uint32_t j = 0; j < magma.smtp.blacklists.count; j++) {
					st_free(magma.smtp.blacklists.domain[j]);
				}
			}
			else if (*((stringer_t **)(magma_keys[i].store))) {
				st_free(*((stringer_t **)(magma_keys[i].store)));
			}
			break;
		default:
#ifdef MAGMA_PEDANTIC
			if (magma_keys[i].norm.type != M_TYPE_BOOLEAN && magma_keys[i].norm.type != M_TYPE_DOUBLE && magma_keys[i].norm.type != M_TYPE_FLOAT &&
					magma_keys[i].norm.type != M_TYPE_INT16 && magma_keys[i].norm.type != M_TYPE_INT32 && magma_keys[i].norm.type != M_TYPE_INT64 &&
					magma_keys[i].norm.type != M_TYPE_INT8 && magma_keys[i].norm.type != M_TYPE_UINT8 && magma_keys[i].norm.type != M_TYPE_UINT16 &&
					magma_keys[i].norm.type != M_TYPE_UINT32 && magma_keys[i].norm.type != M_TYPE_UINT64 && magma_keys[i].norm.type != M_TYPE_ENUM) {
				log_pedantic("Unexpected type. {type = %s = %u}", type(magma_keys[i].norm.type), magma_keys[i].norm.type);
			}
#endif
			break;
		}
	}

	cache_free();
	relay_free();
	servers_free();
	return;
}
/**
 * @brief	Get the value of the Content-Type header in a mail message header.
 * @note	Possible return values include MESSAGE_TYPE_MULTI_ALTERNATIVE, MESSAGE_TYPE_MULTI_RELATED, MESSAGE_TYPE_MULTI_MIXED,
 * 			MESSAGE_TYPE_HTML, MESSAGE_TYPE_MULTI_UNKOWN, and MESSAGE_TYPE_PLAIN.
 * @param	header	a managed string containing the mail message header to be parsed.
 * @return	the MESSAGE_TYPE code of the mail content type, or MESSAGE_TYPE_PLAIN by default.
 */
int_t mail_discover_type(stringer_t *header) {

	chr_t *stream;
	size_t remaining;
	stringer_t *content;
	int_t result = MESSAGE_TYPE_PLAIN;

	// Get the content encoding line from the header.
	if (!(content = mail_header_fetch_all(header, PLACER("Content-Type", 12)))) {
		return MESSAGE_TYPE_PLAIN;
	}

	remaining = st_length_get(content) - 13;
	stream = st_char_get(content) + 13;

	// Advance past any garbage.
	while (remaining && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t')) {
		stream++;
		remaining--;
	}

	if (remaining >= 21 && !mm_cmp_ci_eq(stream, "multipart/alternative", 21)) {
		result = MESSAGE_TYPE_MULTI_ALTERNATIVE;
	}
	else if (remaining >= 17 && !mm_cmp_ci_eq(stream, "multipart/related", 17)) {
		result = MESSAGE_TYPE_MULTI_RELATED;
	}
	else if (remaining >= 15 && !mm_cmp_ci_eq(stream, "multipart/mixed", 15)) {
		result = MESSAGE_TYPE_MULTI_MIXED;
	}
	else if (remaining >= 9 && !mm_cmp_ci_eq(stream, "text/html", 9)) {
		result = MESSAGE_TYPE_HTML;
	}
	else if (remaining >= 9 && !mm_cmp_ci_eq(stream, "text/plain", 9)) {
		result = MESSAGE_TYPE_PLAIN;
	}
	else if (remaining >= 15 && !mm_cmp_ci_eq(stream, "multipart", 9)) {
		result = MESSAGE_TYPE_MULTI_UNKOWN;
	}

	st_free(content);

	return result;
}
Beispiel #27
0
/**
 * @brief
 *  Deserialize an EC private key stored in binary format.
 * @param buf
 *  a pointer to the buffer holding the EC private key in binary format.
 * @param blen
 *  the length, in bytes, of the buffer holding the EC private key.
 * @return
 *  a pointer to the deserialized EC private key on success, or NULL on
 *  failure.
 */
EC_KEY * _deserialize_ec_privkey(unsigned char const *buf, size_t blen) {
	EC_KEY *result;
	const unsigned char *bufptr = buf;

	if (!buf || !blen) {
		RET_ERROR_PTR(ERR_BAD_PARAM, NULL);
	}

	result = secp256k1_private_set(PLACER((unsigned char *)bufptr, blen));

	/*
	 * At this point, in most cases bufptr == buf + blen. There may be cases
	 * though where the private key is shorter than the provided buffer. This
	 * is because DER is a variable-length encoding. Parsing any field behind
	 * the privkey must take this into account.
	 */

	return result;
}
Beispiel #28
0
/**
 * @brief	Get an array of the key/value pairs of parameters passed to the value of the mime Content-Type header.
 * @note	The parameters of the Content-Type header value will be examined, and each found parameter will result in the addition of
 * 			TWO managed strings to the returned array: the first containing the parameter key name, and the second with the parameter value.
 * @param	header	a placer pointing to the mime header to be parsed.
 * @return	NULL on failure, or on success, an array of managed strings structured as the key name followed by the value of each parameter
 * 			passed in the Content-Type header.
 */
array_t * mail_mime_type_parameters(placer_t header) {

	array_t *output;
	stringer_t *key, *holder;
	placer_t parameter;
	unsigned increment, tokens;

	if (!(holder = mail_header_fetch_cleaned(&header, PLACER("Content-Type", 12)))) {
		return NULL;
	}

	if ((tokens = tok_get_count_st(holder, ';')) <= 1) {
		st_free(holder);
		return NULL;
	}

	// Allocate an array.
	if (!(output = ar_alloc((tokens - 1) * 2))) {
		st_free(holder);
		return NULL;
	}

	for (increment = 1; increment < tokens; increment++) {
		tok_get_st(holder, ';', increment, &parameter);

		if ((key = mail_mime_type_parameters_key(&parameter))) {
			upper_st(key);
			ar_append(&output, ARRAY_TYPE_STRINGER, key);
			ar_append(&output, ARRAY_TYPE_STRINGER, mail_mime_type_parameters_value(&parameter));
		}

	}

	st_free(holder);

	if (!ar_length_get(output)) {
		ar_free(output);
		return NULL;
	}

	return output;
}
Beispiel #29
0
/**
 * @brief
 *  Serialize an EC private key into a data buffer.
 * @param key
 *  a pointer to the EC key pair to have its private key serialized.
 * @param outsize
 *  a pointer to a variable that will receive the length of the serialized key
 *  on success.
 * @return
 *  a pointer to the serialized EC private key on success, or NULL on failure.
 */
unsigned char * _serialize_ec_privkey(EC_KEY *key, size_t *outsize) {
	unsigned char *buf = NULL;

	if (!key || !outsize) {
		RET_ERROR_PTR(ERR_BAD_PARAM, NULL);
	}

	stringer_t *priv;
	buf = mm_alloc(32);

	if (!(priv = secp256k1_private_get(key, PLACER(buf, 32)))) {
		//  if ((bsize = i2d_ECPrivateKey_d(key, &buf)) < 0) {
		PUSH_ERROR_OPENSSL();
		RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC private key");
	}

	*outsize = 32;

	return buf;
}
Beispiel #30
0
/**
 * @brief	Get the encoding type from a MIME header.
 * @note	If no encoding type is specified in the header via Content-Transfer-Encoding, 7bit encoding is assumed.
 * @param	header	a placer containing the MIME header to be examined.
 * @return	the MIME encoding type specified by the header, or MESSAGE_ENCODING_UNKNOWN on failure.
 */
int_t mail_mime_encoding(placer_t header) {

	chr_t *stream;
	int_t result;
	size_t remaining;
	stringer_t *holder;

	if ((holder = mail_header_fetch_cleaned(&header, PLACER("Content-Transfer-Encoding", 25))) == NULL) {
		return MESSAGE_ENCODING_7BIT;
	}

	remaining = st_length_get(holder);
	stream = st_char_get(holder);

	// Advance past any garbage.
	while (remaining != 0 && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t')) {
		stream++;
		remaining--;
	}

	if (remaining >= 16 && mm_cmp_ci_eq(stream, "quoted-printable", 16) == 0) {
		result = MESSAGE_ENCODING_QUOTED_PRINTABLE;
	}
	else if (remaining >= 6 && mm_cmp_ci_eq(stream, "base64", 6) == 0) {
		result = MESSAGE_ENCODING_BASE64;
	}
	else if (remaining >= 4 && mm_cmp_ci_eq(stream, "8bit", 4) == 0) {
		result = MESSAGE_ENCODING_8BIT;
	}
	else if (remaining >= 4 && mm_cmp_ci_eq(stream, "7bit", 4) == 0) {
		result = MESSAGE_ENCODING_7BIT;
	}
	else {
		result = MESSAGE_ENCODING_UNKNOWN;
	}

	st_free(holder);

	return result;
}