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; }
/** * @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; }
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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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"); }
/** * @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; }
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; }
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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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, ¶meter); if ((key = mail_mime_type_parameters_key(¶meter))) { upper_st(key); ar_append(&output, ARRAY_TYPE_STRINGER, key); ar_append(&output, ARRAY_TYPE_STRINGER, mail_mime_type_parameters_value(¶meter)); } } st_free(holder); if (!ar_length_get(output)) { ar_free(output); return NULL; } return output; }
/** * @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; }
/** * @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; }