Пример #1
0
stringer_t * imap_build_array_isliteral(placer_t data) {

	size_t length;
	chr_t *holder, buffer[32];

	if (pl_empty(data)) {
		return NULL;
	}

	length = pl_length_get(data);
	holder = pl_data_get(data);

	// Look for illegal characters.
	for (size_t i = 0; i < length; i++) {
		if ((*holder >= '*' && *holder <= '~') || *holder == ' ' || *holder == '_'	|| *holder == '!' || (*holder >= '#' && *holder <= '&')) {
			holder++;
		}
		else {
			snprintf(buffer, 32, "{%zu}\r\n", length);
			return st_merge("ns", buffer, &data);

		}
	}

	return NULL;
}
Пример #2
0
/**
 * @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;
}
Пример #3
0
/**
 * @brief	Merge a list of smtp message headers into a single string, preceded by the leading text and followed by the trailing text.
 * @param	headers		an inx holder containing a collection of header string data to be merged together.
 * @param	leading		a managed string containing text that will lead each header line.
 * @param	trailing	a managed string containing text that will trail each header line.
 * @return	NULL on failure or a managed string containing the merged headers on success.
 */
stringer_t * portal_smtp_merge_headers(inx_t *headers, stringer_t *leading, stringer_t *trailing) {

	stringer_t *result = NULL, *tmp, *current;
	inx_cursor_t *cursor;

	if (!headers || !leading || !trailing) {
		return NULL;
	}

	if (!(cursor = inx_cursor_alloc(headers))) {
		return NULL;
	}

	while ((current = inx_cursor_value_next(cursor))) {

		if (!(tmp = st_merge("ssss", result, leading, current, trailing))) {
			inx_cursor_free(cursor);
			st_cleanup(result);
			return NULL;
		}

		result = tmp;
	}

	inx_cursor_free(cursor);

	// We should at least return an empty managed string if we have a valid inx holder but no data in it.
	if (!result) {
		result = st_alloc(0);
	}

	return result;
}
Пример #4
0
/**
 * @brief	Encode a MIME part for a provided block of data (file attachment) with the specified filename.
 * @note	This function will look up the media type based on the supplied filename, and use that media type as a determination
 * 			of whether the content is to be encoded as either quoted-printable or base64.
 * @param	data		a pointer to a managed string containing the body of the data to be encoded.
 * @param	filename	a pointer to a managed string containing the filename of the attachment for which the data was provided.
 * @param	boundary	a pointer to a managed string containing the boundary that will be used to separate the individual MIME parts.
 * @return	NULL on failure, or a pointer to a managed string containing the file attachment encoded as a MIME part on success.
 */
stringer_t * mail_mime_encode_part(stringer_t *data, stringer_t *filename, stringer_t *boundary) {

	stringer_t *result, *encoded;
	media_type_t *mtype;
	chr_t *fstart, *extptr = NULL, *ctype;
	size_t flen;

	if (!data) {
		return NULL;
	}

	// First get the extension of the filename so we can look up the media type.
	if (!st_empty_out(filename, (uchr_t **)&fstart, &flen)) {
		extptr = fstart + flen + 1;

		while (extptr >= fstart) {

			if (*extptr == '.') {
				break;
			}

			extptr--;
		}

		if (extptr < fstart) {
			extptr = NULL;
		}

	}

	mtype = mail_mime_get_media_type (extptr);

	if (mtype->bin) {
		encoded = base64_encode(data, NULL);
		ctype = "base64";
	} else {
		encoded = qp_encode(data);
		ctype = "quoted-printable";
	}

	if (!encoded) {
		log_pedantic("Unable to encode MIME part data.");
		return NULL;
	}

	// What we return is: boundary/CRLF, Content-Type/CRLF, Content-Transfer-Encoding/CRLF, Content-Disposition/CRLF, data/CRLF
	if (!(result = st_merge("nsnnnnnnnsns", "--------------", boundary, "\r\n", "Content-Type: ", mtype->name, ";\r\n", "Content-Transfer-Encoding: ", ctype,
			"\r\nContent-Disposition: attachment; filename=\"", filename, "\"\r\n\r\n", encoded))) {
		log_pedantic("Unable to generate MIME part data.");
		return NULL;
	}

	st_free(encoded);

	return result;
}
Пример #5
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;
}
Пример #6
0
stringer_t * imap_parse_address(stringer_t *address) {

	placer_t token;
	uint32_t part = 0;
	stringer_t *holder, *value, *output = NULL;

	if (address == NULL) {
		return NULL;
	}

	while (!pl_empty((token = imap_parse_address_breaker(address, part++)))) {

		holder = imap_parse_address_part(token);

		if (output != NULL) {
			if ((value = st_merge("sns", output, " ", holder)) != NULL) {
				st_free(holder);
				st_free(output);
				output = value;
			}
			else {
				st_free(holder);
			}
		}
		else {
			output = holder;
		}
	}

	if (output != NULL && (value = imap_build_array("a", output)) != NULL) {
		st_free(output);
		output = value;
	}

	return output;
}
Пример #7
0
/**
 * @brief	Get the boundary from a MIME header.
 * @note	This function first scans the value of Content-Type for the boundary, and then the rest of the MIME header.
 * @param	header	a placer pointing to the MIME header to be parsed.
 * @return	NULL on failure, or a pointer to a managed string containing the MIME boundary string on success.
 */
stringer_t * mail_mime_boundary(placer_t header) {

	chr_t *stream;
	int_t quote = 0;
	size_t length, bounder;
	stringer_t *holder, *haystack, *boundary, *content;

	// Get the content type line from the header.
	if ((content = mail_header_fetch_all(&header, PLACER("Content-Type", 12)))) {
		haystack = PLACER(st_char_get(content), st_length_get(content));
	}
	// If there is no content line, search the entire header.
	else {
		haystack = &header;
	}

	// Find the boundary.
	if (!st_search_ci(haystack, PLACER("boundary", 8), &bounder)) {
		log_pedantic("We couldn't find the MIME boundary.");
		st_cleanup(content);
		return NULL;
	}

	// Get setup.
	length = st_length_get(haystack) - bounder;
	stream = st_char_get(haystack) + bounder + 8;

	// Skip the garbage.
	while (length != 0 && quote == 0 && (*stream == '"' || *stream == ' ' || *stream == '\r' || *stream == '\n' || *stream == '\t' || *stream == '=')) {

		if (*stream == '"') {
			quote = 1;
		}

		stream++;
		length--;
	}

	// How long is the boundary.
	bounder = 0;

	while (length != 0 && *stream != '"' && *stream != '\r' && *stream != '\n' && *stream != '\t' && *stream != ';' && (quote == 1 || *stream != ' ')) {
		stream++;
		bounder++;
		length--;
	}

	// Is there something to extract.
	if (!length) {
		log_pedantic("We couldn't find the MIME boundary.");
		st_cleanup(content);
		return NULL;
	}

	// Setup a placer with the boundary.
	holder = PLACER(stream - bounder, bounder);

	// Create the boundary search string.
	if (!(boundary = st_merge("ns", "--", holder))) {
		log_error("Failed to allocate space for boundary string.");
		st_cleanup(content);
		return NULL;
	}

	// Release the stringer, if it was used.
	st_cleanup(content);

	return boundary;
}
Пример #8
0
/**
 * @brief	Get smtp envelope data for an outbound message sent by a webmail client.
 * @param	from		a pointer to a managed string containing the sender's address.
 * @param	tos			a pointer to an inx holder containing a collection of managed strings with the email addresses specified in the To: header.
 * @param	ccs			a pointer to an inx holder containing a collection of managed strings with the email addresses specified in the CC: header.
 * @param	bccs		a pointer to an inx holder containing a collection of managed strings with the email addresses specified in the BCC: header.
 * @param	subject		a pointer to a managed string containing the text of the subject line.
 * @param	boundary	a pointer to a managed string containing a boundary string to be used in multipart messages.
 * @param	attached	if true, the envelope is to be created for a mail with attachments (type "multipart/mixed");
 * 						if false, only a single part will be sent for the main email body.
 * @return	NULL on failure or a pointer to a managed string containing the smtp envelope data that will be supplied to an smtp
 * 			relay server at the beginning of the DATA command.
 */
stringer_t * mail_mime_get_smtp_envelope(stringer_t *from, inx_t *tos, inx_t *ccs, inx_t *bccs, stringer_t *subject, stringer_t *boundary, bool_t attached) {

	stringer_t *result, *firsthead = NULL;
	stringer_t *str_to, *str_cc, *str_bcc;
	struct tm ltime;
	static const chr_t *date_format = "Date: %a, %d %b %Y %H:%M:%S %z";
	chr_t date_buffer[1024];
	time_t utime;

	if (!from || !tos || !ccs | !bccs || !subject) {
		log_pedantic("Could not build smtp envelope with incomplete information.");
		return NULL;
	}

	// Serialize the To, CC, and BCC data into separate strings.
	if (!(str_to = portal_smtp_merge_headers(tos, NULLER("To: "), NULLER("\r\n")))) {
		log_error("Could not construct To: header for smtp envelope.");
		return NULL;
	} else if (!(str_cc = portal_smtp_merge_headers(ccs, NULLER("CC: "), NULLER("\r\n")))) {
		log_error("Could not construct CC: header for smtp envelope.");
		st_free(str_to);
		return NULL;
	} else if (!(str_bcc = portal_smtp_merge_headers(bccs, NULLER("BCC: "), NULLER("\r\n")))) {
		log_error("Could not construct BCC: header for smtp envelope.");
		st_free(str_to);
		st_free(str_cc);
		return NULL;
	}

	// Add the current date/time to the outbound message.
	if (((utime = time(&utime)) == -1) || (localtime_r(&utime, &ltime) == NULL) || (strftime(date_buffer, sizeof(date_buffer), date_format, &ltime) <= 0)) {
		log_error("Could not build smtp envelope without current time.");
		st_free(str_to);
		st_free(str_cc);
		st_free(str_bcc);
		return NULL;
	}

	// If there are attachments, we are going to make a multipart message.
	if (attached) {

		if (!(firsthead = st_merge("nsn", "Content-Type: multipart/mixed; boundary=\"------------", boundary, "\"\r\n\r\nThis is a multi-part message in MIME format.\r\n"))) {
			log_error("Could not build multipart header for smtp envelope.");
			st_free(str_to);
			st_free(str_cc);
			st_free(str_bcc);
			return NULL;
		}

	}

	/*if (!(result = st_merge("nnsnsnsns", date_buffer, "\r\nFrom: ", from, "\r\nUser-Agent: lavaweb 1.0\r\nMIME-Version: 1.0\r\nTo: ",
			to, "\r\nSubject: ", subject, "\r\n", firsthead))) { */
	if (!(result = st_merge("nnsnsssnsns", date_buffer, "\r\nFrom: ", from, "\r\nUser-Agent: lavaweb 1.0\r\nMIME-Version: 1.0\r\n",
				str_to, str_cc, str_bcc, "Subject: ", subject, "\r\n", firsthead))) {
		log_error("Unable to allocate space for smtp envelope.");
		st_free(str_to);
		st_free(str_cc);
		st_free(str_bcc);
		st_cleanup(firsthead);
		return NULL;
	}

	st_free(str_to);
	st_free(str_cc);
	st_free(str_bcc);
	st_cleanup(firsthead);

	return result;
}
Пример #9
0
/**
 * @brief	Send the contents of a user-submitted contact or abuse form to the magma-configured contact email address.
 * @param	con		the connection of the web client making the request.
 * @param	branch	a null-terminated string containing the destination of the contact form: either "Abuse" or "Contact".
 * @return	This function returns no value.
 */
void contact_business(connection_t *con, chr_t *branch) {

	stringer_t *composed, *ip, *daddr;
	http_data_t *name, *email, *message;
	bool_t is_contact;

	// A contact form submission by default
	if (!st_cmp_cs_eq(NULLER(branch), PLACER("Abuse", 5))) {
		is_contact = false;
		daddr = magma.admin.abuse;
	} else {
		is_contact = true;
		daddr = magma.admin.contact;
	}

	// One last sanity check, just in case.
	if (is_contact && !daddr) {
		contact_print_message(con, branch, "An unexpected error occurred while trying to process your request. This server does not have a contact address configured.");
		log_error("Contact form routine was reached but no contact address was configured.");
		return;
	}
	else if (!is_contact && !daddr) {
		contact_print_message(con, branch, "An unexpected error occurred while trying to process your request. This server does not have an abuse address configured.");
		log_error("Abuse form routine was reached but no abuse address was configured.");
		return;
	}

	// If either the name, email, or message fields are omitted from the post, spit out an error message.
	if (!(name = http_data_get(con, HTTP_DATA_POST, "your_name")) || !name->value) {
		contact_print_form(con, branch, contact_business_add_error(branch, (uchr_t *)"//xhtml:input[@id='your_name']", (uchr_t *)"your_name_msg", (uchr_t *)"Please enter your name."));
		return;
	}
	else if (!(email = http_data_get(con, HTTP_DATA_POST, "your_email")) || !email->value || !contact_business_valid_email(email->value)) {
		contact_print_form(con, branch, contact_business_add_error(branch,  (uchr_t *)"//xhtml:input[@id='your_email']",  (uchr_t *)"your_email_msg",  (uchr_t *)"A valid e-mail address is required."));
		return;
	}
	else if (!(message = http_data_get(con, HTTP_DATA_POST, "your_message")) || !message->value) {
		contact_print_form(con, branch, contact_business_add_error(branch,  (uchr_t *)"//xhtml:textarea[@id='your_message']",  (uchr_t *)"your_message_msg",  (uchr_t *)"Please enter your message."));
		return;
	}

	// Build the IP string.
	ip = con_addr_presentation(con, MANAGEDBUF(64));

	if ((composed = st_merge("nnnsnsnsnsnsnsnsn", "Subject: ", (is_contact ? "* Contact Form Submission *" : "*Abuse Form Submission*"),
		"\r\nFrom: ", magma.admin.contact, "\r\nTo: ", magma.admin.contact, "\r\nReply-To: ",
		email->value, "\r\n\r\nip = ", ip, "\r\nname = ", name->value, "\r\nemail = ", email->value, "\r\n------------\r\n\r\n ", message->value, "\r\n"))) {

		// Dotstuff the message. Just to prevent SMTP injection attacks.
		st_replace(&composed, PLACER("\n.",2), PLACER("\n..", 3));

		// Send the message.
		if (smtp_send_message(daddr, daddr, composed) != 1) {
			contact_print_message(con, branch, "An error occurred while trying to process your request. Please try again in a few minutes.");
			st_free(composed);
			return;
		}

		// Increment our abuse counters.
		contact_abuse_increment_history(con);
		st_free(composed);
	}

	contact_print_message(con, branch, "Your message has been submitted. We'll review your message-typically within one business day-and get back to you. However "
		"during certain busy periods our team can take up to three business days to respond.");

	return;
}
Пример #10
0
/**
 * @brief	Insert a spam signature training link into a base64-encoded message part.
 * @see		mail_discover_insertion_point()
 * @note	This is similar to mail_insert_chunk_text() except the part has to be decoded and then re-encoded after the training link is inserted.
 * @param	server		the server object of the web server where the teacher application is hosted.
 * @param	message		a managed string containing the base64-encoded 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 base64-encoded message with the inserted signature training link on success.
 */
stringer_t * mail_insert_chunk_base64(server_t *server, stringer_t *message, stringer_t *part, uint64_t signum, uint64_t sigkey, int_t disposition, int_t type, int_t encoding) {

	chr_t *end;
	chr_t *start;
	chr_t *stream;
	size_t length;
	stringer_t *posttext;
	stringer_t *portion;
	int_t headpart = 0;
	stringer_t *result;
	stringer_t *decoded;
	stringer_t *encoded;
	stringer_t *signature;

	// If this is the first time through, and were looking at the message header, start the headpart count at 1.
	if (st_char_get(message) == st_data_get(part)) {
		headpart = 1;
	}

	// Discover the length of the header.
	length = st_length_get(part);
	stream = st_data_get(part);

	while (length != 0 && headpart != 3) {

		// Logic for detecting the end of the header.
		if (headpart == 0 && *stream == '\n') {
			headpart++;
		}
		else if (headpart == 1 && *stream == '\n') {
			headpart += 2;
		}
		else if (headpart == 1 && *stream == '\r') {
			headpart++;
		}
		else if (headpart == 2 && *stream == '\n') {
			headpart++;
		}
		else if (headpart != 0) {
			headpart = 0;
		}
		stream++;
		length--;
	}

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

	// This is the insertion starting point.
	start = stream;

	// Figure out how long the base64 amount is.
	while (length != 0 && ((*stream >= 'A' && *stream <= 'Z') || (*stream >= 'a' && *stream <= 'z') || (*stream >= '0' && *stream <= '9') || *stream == '+' || *stream == '/' || *stream == '\n' || *stream == '\r' || *stream == '\t' || *stream == ' ')) {
		stream++;
		length--;
	}

	// Record the ending point.
	end = stream;

	// If its an empty base64 chunk, just encode a signature and insert.
	if (start == end) {

		if (!(signature = mail_build_signature(server, type, encoding, signum, sigkey, disposition))) {
			return NULL;
		}

		encoded = base64_encode(signature,NULL);
		st_free(signature);

		portion = PLACER(st_char_get(message), start - (chr_t *)st_char_get(message));
		posttext = PLACER(end, st_length_get(message) - (end - (chr_t *)st_char_get(message)));
		result = st_merge("sss", portion, encoded, posttext);
		st_cleanup(encoded);
		return result;
	}

	// Decode.
	if (!(decoded = base64_decode(PLACER(start, end - start),NULL))) {
		log_pedantic("Nothing was returned from the base64 decoder.");
		return NULL;
	}

	// Find the insertion point.
	portion = PLACER(st_char_get(decoded), st_length_get(decoded));
	length = mail_discover_insertion_point(decoded, portion, type);

	// Get our place holders setup.
	portion = PLACER(st_char_get(decoded), length);
	posttext = PLACER(st_char_get(decoded) + length, st_length_get(decoded) - length);

	// Get the signature.
	if (!(signature = mail_build_signature(server, type, encoding, signum, sigkey, disposition))) {
		st_free(decoded);
		return NULL;
	}

	// Merge and clean.
	result = st_merge("sss", portion, signature, posttext);
	st_free(decoded);
	st_free(signature);

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

	// Make it base64 again.
	encoded = base64_encode(result,NULL);
	st_free(result);

	// Now rejoin the message again.
	portion = PLACER(st_char_get(message), start - (chr_t *)st_char_get(message));
	posttext = PLACER(end, st_length_get(message) - (end - (chr_t *)st_char_get(message)));
	result = st_merge("sss", portion, encoded, posttext);

	st_free(encoded);

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

	return result;
}
Пример #11
0
/**
 * @brief	Build a spam text signature for insertion into a message body.
 * @param	server				the server object of the web server where the teacher application is hosted.
 * @param	type				MESSAGE_TYPE_HTML to generate an html-based signature, or any other value for plain text.
 * @param	content_encoding	if MESSAGE_ENCODING_QUOTED_PRINTABLE, set the encoding type to qp.
 * @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	NULL on failure, or a newly allocated managed string containing the desired mail signature on success.
 */
stringer_t * mail_build_signature(server_t *server, int_t content_type, int_t content_encoding, uint64_t signum, uint64_t sigkey, int_t disposition) {

	size_t length;
	stringer_t *holder;
	stringer_t *result = NULL;
	stringer_t *spam_text = NULL;
	static const chr_t *line = "\r\n____________________________________________________________________________________\r\n";

	// Build the spam signature.
	if (signum && sigkey) {
		// This is the format for spam signatures.
		if (content_type == MESSAGE_TYPE_HTML) {
			holder = st_merge("nnnnnsnsn", "\r\n<div style='display: block; text-transform: none; text-indent: 0px; letter-spacing: normal; "
					"line-height: normal; text-align: left; white-space: normal; height: auto; visibility: visible; border-bottom: 1px solid black; border-collapse: collapse; "
					"width: 50em; font-size: 12px; color: black; font-family: sans-serif; padding: 0px 0px 4px 0px; clear: both; font-weight: normal;"
					"text-decoration: none; background: white; ", "margin: 20px 2px 10px 2px; border-top: 1px solid black;",
					"'>\r\n\tUse the link below to report this message as ", !disposition ? "spam" : "innocent",
					".<br />\r\n\t<a style='font-size: 12px; font-family: sans-serif; color: blue; background: white; text-decoration: underline; font-weight: normal;' href='https://",
					server->domain, "/apps/teacher?sig=%lu&amp;key=%lu'>https://", server->domain, "/apps/teacher?sig=%lu&amp;key=%lu</a>\r\n</div>\r\n");
		}
		else {
			holder = st_merge("nnsn", "Use the link below to report this message as ", !disposition ? "spam.\r\nhttps://" : "innocent.\r\nhttps://",
				server->domain, "/apps/teacher?sig=%lu&amp;key=%lu");
		}

		if (!holder) {
			log_pedantic("Unable to build the spam signature.");
			return NULL;
		}

		// How big is the signature with the numbers inserted.
		length = st_length_get(holder) + uint64_digits(signum) + uint64_digits(sigkey) + uint64_digits(signum) + uint64_digits(sigkey);

		if (!(spam_text = st_alloc(length))) {
			log_pedantic("Unable to build the spam signature.");
			st_free(holder);
			return NULL;
		}

		// Print the spam signature text.
		if (content_type == MESSAGE_TYPE_HTML) {
			length = st_sprint(spam_text, st_char_get(holder), signum, sigkey, signum, sigkey);
		}
		else {
			length = st_sprint(spam_text, st_char_get(holder), signum, sigkey);
		}

		st_free(holder);

		if (!length) {
			log_pedantic("Unable to build the spam signature.");
			st_free(spam_text);
			return NULL;
		}

	}

	// Error check.
	if (!spam_text) {
		log_pedantic("Could not build the signature.");
		return NULL;
	}

	// Build the output signature.
	if (content_type == MESSAGE_TYPE_HTML) {
		result = spam_text;
		spam_text = NULL;
	}
	else {
		result = st_merge("nnsn", line, (spam_text == NULL) ? "" : line, spam_text, line);
	}

	if (!result) {
		log_pedantic("An error occurred while attempting to merge the signature.");
	}

	// Cleanup.
	st_cleanup(spam_text);

	// Check for quoted printable encodings.
	if (content_encoding == MESSAGE_ENCODING_QUOTED_PRINTABLE) {
		holder = qp_encode(result);

		if (holder && result) {
			st_free(result);
			result = holder;
		}

	}

	return result;
}
Пример #12
0
/**
 * @brief	Calculates the legacy symmetric encryption key and authentication token.
 * @param	username	a managed string holding the sanitzed username.
 * @param	password	a managed string holding the plain text user password.
 * @return	an auth_legacy_t structure is returned upon success, and NULL upon failure.
 **/
auth_legacy_t * auth_legacy(stringer_t *username, stringer_t *password) {

	auth_legacy_t *legacy = NULL;
	stringer_t *input = NULL, *intermediate = MANAGEDBUF(64);

	// Make sure all three required inputs are valid pointers and hold at least one character.
	if (st_empty(username) || st_empty(password) || st_empty(magma.secure.salt)) {
		log_error("A variable needed to calculate the legacy authentication and encryption values was invalid.");
		return NULL;
	}
	else if (!(legacy = auth_legacy_alloc())) {
		log_error("We were unable to allocate a buffer to hold the legacy hash values.");
		return NULL;
	}

	// Combine the three inputs into a single buffer.
	else if (!(input = st_merge("sss", username, magma.secure.salt, password))) {
		log_error("Unable to combine the three input values needed to calculate the legacy authentication and encryption values.");
		auth_legacy_free(legacy);
		return NULL;
	}

	// Hash the inputs together and we'll get the legacy symmetric encryption key.
	else if (!(legacy->key = st_alloc_opts(MANAGED_T | CONTIGUOUS | SECURE, 64)) || !(hash_sha512(input, legacy->key))) {
		log_error("Unable to calculate the legacy hash values.");
		auth_legacy_free(legacy);
		st_free(input);
		return NULL;
	}

	// Free and reuse the holder variable.
	st_free(input);

	// Prepare the inputs for the intermediary hash.
	if (!(input = st_merge("ss", password, legacy->key))) {
		log_error("Failed to merge the legacy authentication inputs for the intermediate hash round.");
		auth_legacy_free(legacy);
		return NULL;
	}

	// Hash the password with the output of the first round. Note that if the hash function returns NULL and overwrites
	// the intermediate string pointer, the buffer will be freed automatically because it was allocated off the stack.
	else if (!(hash_sha512(input, intermediate))) {
		log_error("Unable to calculate the legacy hash values.");
		auth_legacy_free(legacy);
		st_free(input);
		return NULL;
	}

	// Free and reuse the holder variable.
	st_free(input);

	// Prepare the inputs for the intermediary hash.
	if (!(input = st_merge("ss", password, intermediate))) {
		log_error("Failed to merge the legacy authentication inputs for the final hash round.");
		auth_legacy_free(legacy);
		return NULL;
	}

	// Hash the inputs together and we'll get the legacy authentication token.
	if (!(legacy->token = st_alloc(64)) || !(hash_sha512(input, legacy->token))) {
		log_error("Failed to merge the legacy authentication inputs for the final hash round.");
		auth_legacy_free(legacy);
		st_free(input);
		return NULL;
	}

	// Free the inputs.
	st_free(input);

	// And return success.
	return legacy;
}
Пример #13
0
/**
 * @brief	Create the data of an outbound smtp message that will be specified with the smtp DATA command.
 * @param	attachments		an optional inx holder containing a list of attachments to be included in the message.
 * @param	from			a managed string containing the email address sending the message.
 * @param	to				an inx holder containing a list of To: email recipients as managed strings.
 * @param	cc				an inx holder containing a list of 0 or more CC: recipients as managed strings.
 * @param	bcc				an inx holder containing a list of 0 or more BCC: recipients as managed strings.
 * @param	subject			a managed string containing the subject of the message.
 * @param	body_plain		an optional managed string containing the plain text body of the message.
 * @param	body_html		an optional managed string containing the html-formatted body of the message.
 * @return	NULL on failure or a managed string containing the packaged outbound smtp message data on success.
 */
stringer_t * portal_smtp_create_data(inx_t *attachments, stringer_t *from, inx_t *to, inx_t *cc, inx_t *bcc, stringer_t *subject, stringer_t *body_plain, stringer_t *body_html) {

		inx_cursor_t *cursor = NULL;
		array_t *all_attachments = NULL;
		attachment_t *attachment;
		stringer_t *result = NULL, *boundary = NULL, *mime_data, *tmp;
		size_t nattached = 0;

		if (attachments && (!(cursor = inx_cursor_alloc(attachments)))) {
			log_pedantic("Unable to read message attachments.");
			return NULL;
		} else if (attachments) {

			while ((attachment = inx_cursor_value_next(cursor))) {

					// We are creating an array of all the attachment data. It needs to be ARRAY_TYPE_POINTER so deallocating the array doesn't free the underlying data.
					if (attachment->filedata && !ar_append(&all_attachments, ARRAY_TYPE_POINTER, attachment->filedata)) {
						log_pedantic("Unable to parse message attachments.");
						inx_cursor_free(cursor);
						if (all_attachments) ar_free(all_attachments);
						return NULL;
					}

			}

			if (all_attachments) {
				nattached = ar_length_get(all_attachments);
			}

		}

		if (!nattached) {

			if (cursor) {
				inx_cursor_free(cursor);
			}

			if (!(result = mail_mime_get_smtp_envelope(from, to, cc, bcc, subject, boundary, false))) {
						log_pedantic("Unable to generate smtp envelope for outbound mail.");
						return NULL;
			}

		} else {

			// TODO: This should really happen after encoding is performed, but the chances of a collision are still infinitesimal.
			// Now that we have all the attachment data in an array, we can generate a unique boundary string.
			if (!(boundary = mail_mime_generate_boundary(all_attachments))) {
				log_pedantic("Unable to generate boundary for MIME attachments.");
				inx_cursor_free(cursor);
				ar_free(all_attachments);
				return NULL;
			}

			ar_free(all_attachments);

			// Get the envelope for a message that has attachments.
			if (!(result = mail_mime_get_smtp_envelope(from, to, cc, bcc, subject, boundary, true))) {
				log_pedantic("Unable to generate smtp envelope for outbound mail.");
				inx_cursor_free(cursor);
				st_free(boundary);
				return NULL;
			}

			// Now that we have the envelope, the first content is the actual email body.
			if (!(tmp = st_merge("snsnsn", result, "--------------", boundary, "\r\nContent-Type: text-plain\r\nContent-Transfer-Encoding: 7bit\r\n\r\n", body_plain, "\r\n"))) {
				log_pedantic("Unable to pack message body into outbound message.");
				inx_cursor_free(cursor);
				st_free(boundary);
				return NULL;
			}

			st_free(result);
			result = tmp;

			// Now go through each attachment, encode it, and append it to the envelope.
			inx_cursor_reset(cursor);

			while ((attachment = inx_cursor_value_next(cursor))) {

				if (!(mime_data = mail_mime_encode_part(attachment->filedata, attachment->filename, boundary))) {
					log_pedantic("Unable to mime encode part for message attachment.");
					inx_cursor_free(cursor);
					st_free(boundary);
					return NULL;
				}

				tmp = st_merge("ss", result, mime_data);
				st_free(mime_data);
				st_free(result);

				if (!tmp) {
					log_pedantic("Unable to allocate space for portal smtp message.");
					inx_cursor_free(cursor);
					return NULL;
				}

				result = tmp;
			}

			inx_cursor_free(cursor);

			// One final boundary at the end.
			tmp = st_merge("snsn", result, "\r\n--------------", boundary, "--");
			st_free(result);

			if (!tmp) {
				log_pedantic("Unable to allocate space for portal smtp message.");
				return NULL;
			}

			result = tmp;
		}

		return result;
}
Пример #14
0
bool_t check_string_merge(void) {

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

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

	strings[0] = st_alloc(PLACER_T | JOINTED | HEAP, 0);
	st_placer_set(strings[0], st_data_get(constant), st_length_get(constant));

	strings[1] = st_merge(NULLER_T | CONTIGUOUS | HEAP, "s", strings[0]);
	strings[2] = st_merge(NULLER_T | JOINTED | HEAP, "s", strings[1]);
	strings[3] = st_merge(BLOCK_T | CONTIGUOUS | HEAP, "s", strings[2]);
	strings[4] = st_merge(BLOCK_T | JOINTED | HEAP, "s", strings[3]);
	strings[5] = st_merge(MANAGED_T | CONTIGUOUS | HEAP, "s", strings[4]);
	strings[6] = st_merge(MANAGED_T | JOINTED | HEAP, "s", strings[5]);
	strings[7] = st_merge(MAPPED_T | JOINTED | HEAP, "s", strings[6]);

	strings[8] = st_merge(NULLER_T | CONTIGUOUS | SECURE, "s", strings[7]);
	strings[9] = st_merge(NULLER_T | JOINTED | SECURE, "s", strings[8]);
	strings[10] = st_merge(BLOCK_T | CONTIGUOUS | SECURE, "s", strings[9]);
	strings[11] = st_merge(BLOCK_T | JOINTED | SECURE, "s", strings[10]);
	strings[12] = st_merge(MANAGED_T | CONTIGUOUS | SECURE, "s", strings[11]);
	strings[13] = st_merge(MANAGED_T | JOINTED | SECURE, "s", strings[12]);
	strings[14] = st_merge(MAPPED_T | JOINTED | SECURE, "s", strings[13]);

	for (int i = 0; i < 15 && 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;
		}
	}

	if (result) {

		strings[15] = st_merge(MANAGED_T | JOINTED | HEAP, "snsnsnsnsnsnsnsnsnsnsnsnsnsnsnsn", constant, "\n", strings[0], "\n", strings[1], "\n", strings[2], "\n",
				strings[3], "\n", strings[4], "\n", strings[5], "\n", strings[6], "\n", strings[7], "\n", strings[8], "\n", strings[9], "\n", strings[10], "\n", strings[11],
				"\n", strings[12], "\n", strings[13], "\n", strings[14], "\n");

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

		if (total != (5376lu * 16lu)) {
			result = false;
		}

	}

	log_print("%28.28s = %s", "merged", result ? "passed" : "failed");

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

	return result;

}