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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
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; }
/** * @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; }
/** * @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, <ime) == NULL) || (strftime(date_buffer, sizeof(date_buffer), date_format, <ime) <= 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; }
/** * @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; }
/** * @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; }
/** * @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&key=%lu'>https://", server->domain, "/apps/teacher?sig=%lu&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&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; }
/** * @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; }
/** * @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; }
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; }