/** * @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 Derive an organizational signet from the corresponding private key structures. */ prime_org_signet_t * org_signet_generate(prime_org_key_t *org) { prime_org_signet_t *signet = NULL; stringer_t *signing = NULL, *encryption = NULL, *cryptographic = MANAGEDBUF(69); // Ensure the org structure contains the necessary private keys. if (!org || !org->encryption || !org->signing || org->signing->type != ED25519_PRIV) { return NULL; } else if (!(signet = mm_alloc(sizeof(prime_org_signet_t)))) { return NULL; } // Store the public singing, and encryption keys. else if (!(signing = ed25519_public_get(org->signing, NULL)) || !(encryption = secp256k1_public_get(org->encryption, NULL))) { log_pedantic("PRIME organizational signet generation failed, the public keys could not be derived from the provided private keys."); org_signet_free(signet); st_cleanup(signing); return NULL; } // Generate a serialized signet with the cryptographic fields. else if (st_write(cryptographic, prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, signing, MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, encryption, MANAGEDBUF(35))) != 69) { log_pedantic("PRIME organizational signet generation failed, the serialized cryptographic signet could not be derived."); org_signet_free(signet); st_free(encryption); st_free(signing); return NULL; } // Generate a signature using the serialized cryptographic fields. else if (!(signet->signature = ed25519_sign(org->signing, cryptographic, NULL))) { log_pedantic("PRIME organizational signet generation failed, the cryptographic signet signature could not be derived."); org_signet_free(signet); st_free(encryption); st_free(signing); return NULL; } // Finally, convert the serialized public keys into usable structures. else if (!(signet->signing = ed25519_public_set(signing)) || !(signet->encryption = secp256k1_public_set(encryption))) { log_pedantic("PRIME organizational signet generation failed, the serialized public keys could not be parsed."); org_signet_free(signet); st_free(encryption); st_free(signing); return NULL; } // We no longer need the serialized public keys. st_free(encryption); st_free(signing); return signet; }
stringer_t * org_signet_fingerprint(prime_org_signet_t *org, stringer_t *output) { stringer_t *holder = MANAGEDBUF(134); if (!org || !org->signing || !org->encryption || !org->signature || st_length_get(org->signature) != 64) { return false; } else if (st_write(holder, prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, ed25519_public_get(org->signing, MANAGEDBUF(32)), MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, secp256k1_public_get(org->encryption, MANAGEDBUF(33)), MANAGEDBUF(35)), prime_field_write(PRIME_ORG_SIGNET, 4, ED25519_SIGNATURE_LEN, org->signature, MANAGEDBUF(65))) != 134) { return false; } return hash_sha512(holder, output); }
/** * @brief * Serialize an EC public key to be shared. * @param * key a pointer to the EC key pair to have its public 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 public key on success, or NULL on failure. */ unsigned char * _serialize_ec_pubkey(EC_KEY *key, size_t *outsize) { unsigned char *buf = NULL; if (!key || !outsize) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } stringer_t *pub; if (!(pub = secp256k1_public_get(key, MANAGEDBUF(33)))) { PUSH_ERROR_OPENSSL(); RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC public key"); } buf = mm_alloc(33); memmove(buf, st_data_get(pub), st_length_get(pub)); *outsize = st_length_get(pub); // EC_KEY_set_conv_form_d(key, POINT_CONVERSION_COMPRESSED); // if ((bsize = i2o_ECPublicKey_d(key, &buf)) < 0) { // PUSH_ERROR_OPENSSL(); // RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC public key"); // } // *outsize = bsize; return buf; }
bool_t check_string_write(void) { stringer_t *output = MANAGEDBUF(1024), *strings[3] = { MANAGEDBUF(32), MANAGEDBUF(64), MANAGEDBUF(128) }; for (int i = 0; i < 3; i++) { rand_write(strings[i]); } if (st_write(NULL, strings[0], strings[1], strings[2]) != 224) { return false; } if (st_write(output, strings[0], strings[1], strings[2]) != 224) { return false; } return true; }
/** * @brief Acquire a lock in the magma.user keyspace. * @param usernum the numerical id of the user for whom the lock will be acquired. * @return -1 on general failure, 0 on memcached failure, or 1 on success. */ int_t user_lock(uint64_t usernum) { stringer_t *key = MANAGEDBUF(128); if (st_sprint(key, "magma.user.%lu", usernum) <= 0) { return -1; } return lock_get(key); }
stringer_t * org_signet_get(prime_org_signet_t *org, stringer_t *output) { size_t length; int_t written = 0; stringer_t *result = NULL; if (!org || !(length = org_signet_length(org))) { log_pedantic("An invalid org signet was supplied for serialization."); return NULL; } // See if we have a valid output buffer, or if output is NULL, allocate a buffer to hold the output. else if (output && (!st_valid_destination(st_opt_get(output)) || st_avail_get(output) < length)) { log_pedantic("An output string was supplied but it does not represent a buffer capable of holding the output."); return NULL; } else if (!output && !(result = st_alloc(length))) { log_pedantic("Could not allocate a buffer large enough to hold encoded result. { requested = %zu }", length); return NULL; } else if (!output) { output = result; } st_wipe(output); // Calculate the size, by writing out all the fields (minus the header) using a NULL output. length = st_write(NULL, prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, ed25519_public_get(org->signing, MANAGEDBUF(32)), MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, secp256k1_public_get(org->encryption, MANAGEDBUF(33)), MANAGEDBUF(35)), prime_field_write(PRIME_ORG_SIGNET, 4, ED25519_SIGNATURE_LEN, org->signature, MANAGEDBUF(65))); // Then output them again into the actual output buffer, but this time include the header. This is very primitive serialization logic. if ((written = st_write(output, prime_header_org_signet_write(length, MANAGEDBUF(5)), prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, ed25519_public_get(org->signing, MANAGEDBUF(32)), MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, secp256k1_public_get(org->encryption, MANAGEDBUF(33)), MANAGEDBUF(35)), prime_field_write(PRIME_ORG_SIGNET, 4, ED25519_SIGNATURE_LEN, org->signature, MANAGEDBUF(65)))) != (length + 5)) { log_pedantic("The organizational signet didn't serialize to the expected length. { written = %i }", written); st_cleanup(result); return NULL; } return output; }
/** * @brief Increment the contact abuse history counter for an IP address. * @param con a pointer to the connection object of the remote host making the contact request. * @return This function returns no value. */ void contact_abuse_increment_history(connection_t *con) { stringer_t *key = MANAGEDBUF(64), *ip = NULL; // Build the key. if (!(ip = con_addr_presentation(con, ip)) && st_sprint(key, "magma.web.contact.history.%.*s", st_length_int(ip), st_char_get(ip)) > 0) { cache_increment(key, 1, 1, 86400); } return; }
/** * @brief Unlock a lock in the magma.user keyspace. * @param usernum the numerical id of the user for whom the lock will be unlocked. * @return This function returns no value. */ void user_unlock(uint64_t usernum) { stringer_t *key = MANAGEDBUF(128); if (st_sprint(key, "magma.user.%lu", usernum) <= 0) { return; } lock_release(key); return; }
END_TEST START_TEST (check_mail_headers_s) { log_disable(); bool_t result = true; stringer_t *errmsg = MANAGEDBUF(1024); if (status()) result = check_mail_headers_sthread(errmsg); log_test("MAIL / HEADERS / SINGLE THREADED:", errmsg); ck_assert_msg(result, st_char_get(errmsg)); }
/** * @brief Check to see that a client from a given IP address hasn't exceeded its daily quota of contact requests. * @note Each IP address will be limited to at most 2 contact requests in any 24-hour period. * @param con a pointer to the connection object of the remote host making the contact request. * @param branch a null-terminated string specifying where the contact request was directed ("Abuse" or "Contact"). * @return true if the specified connection failed the abuse check or false if it did not. */ bool_t contact_abuse_checks(connection_t *con, chr_t *branch) { stringer_t *key = MANAGEDBUF(64), *ip = NULL; // Build the key. if (!(ip = con_addr_presentation(con, ip)) && st_sprint(key, "magma.web.contact.history.%.*s", st_length_int(ip), st_char_get(ip)) > 0 && cache_get_u64(key) >= 2) { contact_print_message(con, branch, "To prevent abuse, our system only allows you to submit two letters to our team in a twenty-four hour period. If you need " "to submit another message please return in twenty-four hours and try again"); return true; } return false; }
END_TEST START_TEST (check_mail_store_s) { log_disable(); bool_t result = true; stringer_t *errmsg = MANAGEDBUF(1024); if (status()) result = check_mail_store_plaintext_sthread(errmsg); if (status() && result) result = check_mail_store_encrypted_sthread(errmsg); log_test("MAIL / STORE / SINGLE THREADED:", errmsg); ck_assert_msg(result, st_char_get(errmsg)); }
bool_t org_signet_verify(prime_org_signet_t *org) { stringer_t *holder = MANAGEDBUF(69); if (!org || !org->signing || !org->encryption || !org->signature || st_length_get(org->signature) != 64) { return false; } else if (st_write(holder, prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, ed25519_public_get(org->signing, MANAGEDBUF(32)), MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, secp256k1_public_get(org->encryption, MANAGEDBUF(33)), MANAGEDBUF(35))) != 69) { return false; } else if (ed25519_verify(org->signing, holder, org->signature)) { return false; } return true; }
bool_t check_prime_signets_user_sthread(stringer_t *errmsg) { prime_t *org = NULL, *verify = NULL, *user1 = NULL, *user2 = NULL, *request1 = NULL, *request2 = NULL, *signet1 = NULL, *signet2 = NULL, *signet3 = NULL, *signet4 = NULL; stringer_t *fingerprint = NULL, *binary = NULL, *armored = NULL; // Create an org key. if (!(org = prime_key_generate(PRIME_ORG_KEY, NONE)) || !(verify = prime_signet_generate(org))) { st_sprint(errmsg, "Organizational signet/key for user signing failed."); prime_cleanup(org); return false; } // Create a user key and then generate the corresponding signing request. else if (!(user1 = prime_key_generate(PRIME_USER_KEY, NONE)) || !(request1 = prime_request_generate(user1, NULL))) { st_sprint(errmsg, "User key/signing request creation failed."); prime_cleanup(user1); prime_free(verify); prime_free(org); return false; } // Sign the user request. else if (!(signet1 = prime_request_sign(request1, org))) { st_sprint(errmsg, "User key/signing request signing failed."); prime_free(request1); prime_free(verify); prime_free(user1); prime_free(org); return false; } // Serialize the user signet. else if (!(binary = prime_get(signet1, BINARY, MANAGEDBUF(512)))) { st_sprint(errmsg, "User signet serialization failed."); prime_free(request1); prime_free(signet1); prime_free(verify); prime_free(user1); prime_free(org); return false; } // Serialize and armor the user signet. else if (!(armored = prime_get(signet1, ARMORED, MANAGEDBUF(512)))) { st_sprint(errmsg, "User signet armoring failed."); prime_free(request1); prime_free(signet1); prime_free(verify); prime_free(user1); prime_free(org); return false; } else if (!(user2 = prime_key_generate(PRIME_USER_KEY, NONE)) || !(request2 = prime_request_generate(user2, user1)) || !(signet2 = prime_request_sign(request2, org))) { st_sprint(errmsg, "User signet/key rotation failed."); prime_cleanup(request2); prime_cleanup(user2); prime_free(request1); prime_free(signet1); prime_free(verify); prime_free(user1); prime_free(org); return false; } else if (!(signet3 = prime_set(binary, BINARY, NONE)) || !(signet4 = prime_set(armored, ARMORED, NONE))) { st_sprint(errmsg, "User signet parsing failed."); prime_cleanup(signet3); prime_free(request2); prime_free(request1); prime_free(signet2); prime_free(signet1); prime_free(verify); prime_free(user2); prime_free(user1); prime_free(org); return false; } // Ensure we can generate cryptographic fingerprints. else if (!prime_signet_validate(request1, NULL) || !prime_signet_validate(signet1, NULL) || !prime_signet_validate(request2, NULL) || !prime_signet_validate(signet2, NULL) || !prime_signet_validate(signet1, verify) || !prime_signet_validate(signet2, verify) || !prime_signet_validate(request2, signet1) || !prime_signet_validate(signet2, signet1)) { st_sprint(errmsg, "User signet validation failed."); prime_free(request2); prime_free(request1); prime_free(signet4); prime_free(signet3); prime_free(signet2); prime_free(signet1); prime_free(verify); prime_free(user2); prime_free(user1); prime_free(org); return false; } // Ensure we can generate cryptographic fingerprints. else if (!(fingerprint = prime_signet_fingerprint(signet1, MANAGEDBUF(64))) || !(fingerprint = prime_signet_fingerprint(signet2, MANAGEDBUF(64)))) { st_sprint(errmsg, "User signet fingerprinting failed."); prime_free(request2); prime_free(request1); prime_free(signet4); prime_free(signet3); prime_free(signet2); prime_free(signet1); prime_free(verify); prime_free(user2); prime_free(user1); prime_free(org); return false; } prime_free(request2); prime_free(request1); prime_free(signet4); prime_free(signet3); prime_free(signet2); prime_free(signet1); prime_free(verify); prime_free(user2); prime_free(user1); prime_free(org); return true; }
bool_t check_prime_keys_org_sthread(stringer_t *errmsg) { prime_t *holder = NULL; stringer_t *packed = NULL, *key = MANAGEDBUF(64); // Create a STACIE realm key. rand_write(key); // Allocate an org key. if (!(holder = prime_alloc(PRIME_ORG_KEY, NONE))) { st_sprint(errmsg, "Organizational key allocation failed."); return false; } prime_free(holder); // Generate an org key. if (!(holder = prime_key_generate(PRIME_ORG_KEY, NONE))) { st_sprint(errmsg, "Organizational key generation failed."); return false; } // Serialize the org key. else if (!(packed = prime_get(holder, BINARY, MANAGEDBUF(256)))) { st_sprint(errmsg, "Organizational key serialization failed."); prime_free(holder); return false; } prime_free(holder); // Unpack the serialized org key. if (!(holder = prime_set(packed, BINARY, NONE))) { st_sprint(errmsg, "Organizational key parsing failed."); return false; } // Encrypt the org key. else if (!(packed = prime_key_encrypt(key, holder, BINARY, MANAGEDBUF(256)))) { st_sprint(errmsg, "Organizational key encryption failed."); prime_free(holder); return false; } prime_free(holder); // Decrypt the org key. if (!(holder = prime_key_decrypt(key, packed, BINARY, NONE))) { st_sprint(errmsg, "Encrypted organizational key parsing failed."); return false; } prime_free(holder); // Perform the same checks, but this time make the functions // allocate memory for the output. Generate an org key. if (!(holder = prime_key_generate(PRIME_ORG_KEY, NONE))) { st_sprint(errmsg, "Organizational key generation failed."); return false; } // Serialize the org key. else if (!(packed = prime_get(holder, BINARY, NULL))) { st_sprint(errmsg, "Organizational key serialization failed."); prime_free(holder); return false; } prime_free(holder); // Unpack the serialized org key. if (!(holder = prime_set(packed, BINARY, NONE))) { st_sprint(errmsg, "Organizational key parsing failed."); st_free(packed); return false; } st_free(packed); // Encrypt the org key. if (!(packed = prime_key_encrypt(key, holder, BINARY, NULL))) { st_sprint(errmsg, "Organizational key encryption failed."); prime_free(holder); return false; } prime_free(holder); // Decrypt the org key. if (!(holder = prime_key_decrypt(key, packed, BINARY, NONE))) { st_sprint(errmsg, "Encrypted organizational key parsing failed."); st_free(packed); return false; } prime_free(holder); st_free(packed); return true; }
/** * @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 Read a line of input from a network client session. * @return -1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing new line character. */ int64_t client_read_line(client_t *client) { ssize_t bytes = 0; int_t counter = 0; stringer_t *error = NULL; bool_t blocking = true, line = false; #ifdef MAGMA_PEDANTIC int_t local = 0; stringer_t *ip = NULL, *cipher = NULL; #endif if (!client || client->sockd == -1) { if (client) client->status = 1; return -1; } // Check for data past the current line buffer. else if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line)); // Update the length. st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line)); // Check whether the data we just moved contains a complete line. if (!pl_empty((client->line = line_pl_st(client->buffer, 0)))) { client->status = 1; return pl_length_get(client->line); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(client->buffer, 0); client->line = pl_null(); } // Loop until we get a complete line, an error, or the buffer is filled. do { // Read bytes off the network. Skip past any existing data in the buffer. if (client->tls) { // If bytes is zero or below and the library isn't asking for another read, then an error occurred. bytes = tls_read(client->tls, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), blocking); // If zero bytes were read, or a negative value was returned to indicate an error, call tls_erorr(), which will return // NULL if the error can be safely ignored. Otherwise log the output for debug purposes. if (bytes <= 0 && (error = tls_error(client->tls, bytes, MANAGEDBUF(512)))) { #ifdef MAGMA_PEDANTIC cipher = tls_cipher(client->tls, MANAGEDBUF(128)); ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TLS client read operation failed. { ip = %.*s / %.*s / result = %zi%s%.*s }", st_length_int(ip), st_char_get(ip), st_length_int(cipher), st_char_get(cipher), bytes, (error ? " / " : ""), st_length_int(error), st_char_get(error)); #endif client->status = -1; return -1; } // This will occur when the read operation results in a 0, or negative value, but TLS error returns NULL to // indicate it was a transient error. For transient errors we simply set bytes equal to 0 so the read call gets retried. else if (bytes <= 0) { bytes = 0; } } else { errno = 0; bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), (blocking ? 0 : MSG_DONTWAIT)); // Check for errors on non-SSL reads in the traditional way. if (bytes <= 0 && tcp_status(client->sockd)) { #ifdef MAGMA_PEDANTIC local = errno; ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TCP client read operation failed. { ip = %.*s / result = %zi / error = %i / message = %s }", st_length_int(ip), st_char_get(ip), bytes, local, strerror_r(local, MEMORYBUF(1024), 1024)); #endif client->status = -1; return -1; } } // We actually read in data, so we need to update the buffer to reflect the amount of data it currently holds. if (bytes > 0) { st_length_set(client->buffer, st_length_get(client->buffer) + bytes); } // Check whether we have a complete line before checking whether the connection was closed. if (!st_empty(client->buffer) && !pl_empty((client->line = line_pl_st(client->buffer, 0)))) { line = true; } } while (!line && counter++ < 128 && st_length_get(client->buffer) != st_avail_get(client->buffer) && status()); if (st_length_get(client->buffer) > 0) { client->status = 1; } return pl_length_get(client->line); }
/** * @brief Accept and verify a password for POP3 authentication. * @note This command is only allowed for sessions which have not yet been authenticated, but which have already supplied a username. * If the username/password combo was validated, the account information is retrieved and checked to see if it is locked. * After successful authentication, this function will prohibit insecure connections for any user configured to use SSL only, * and enforce the existence of only one POP3 session at a time. * Finally, the database Log table for this user's POP3 access is updated, and all the user's messages are retrieved. * @param con the POP3 client connection issuing the command. * @return This function returns no value. */ void pop_pass(connection_t *con) { int_t state; credential_t *cred; stringer_t *password, *username; if (con->pop.session_state != 0) { pop_invalid(con); return; } // The user must come before the PASS command. if (st_empty(con->pop.username)) { con_write_bl(con, "-ERR You must supply a username first.\r\n", 40); return; } // If they didn't pass in a valid password. if (!(password = pop_pass_parse(con))) { con_write_bl(con, "-ERR Invalid PASS command.\r\n", 28); return; } // Hash the password. // First we need to get the regular username from the fully qualified one. if (!(username = credential_username(con->pop.username))) { con_write_bl(con, "-ERR Internal server error. Please try again later.\r\n", 53); st_wipe(password); st_free(password); } st_free(con->pop.username); con->pop.username = username; if (!(cred = credential_alloc_auth(con->pop.username, password))) { con_write_bl(con, "-ERR Internal server error. Please try again later.\r\n", 53); st_wipe(password); st_free(password); return; } st_wipe(password); st_free(password); // Pull the user info out. state = meta_get(con->pop.username, cred->auth.domain, cred->auth.password, cred->auth.key, META_PROT_POP, META_GET_MESSAGES, &(con->pop.user)); // Securely delete this information, as these are the keys to the castle. credential_free(cred); // Not found, or invalid password. if (state == 0) { con_write_bl(con, "-ERR The username and password combination is invalid.\r\n", 56); return; } // Internal error. else if (state < 0 || !con->pop.user) { con_write_bl(con, "-ERR [SYS/TEMP] Internal server error. Please try again later.\r\n", 64); return; } // Locks else if (con->pop.user->lock_status != 0) { // What type of lock is it. if (con->pop.user->lock_status == 1) { con_write_bl(con, "-ERR [SYS/PERM] This account has been administratively locked.\r\n", 64); } else if (con->pop.user->lock_status == 2) { con_write_bl(con, "-ERR [SYS/PERM] This account has been locked for inactivity.\r\n", 62); } else if (con->pop.user->lock_status == 3) { con_write_bl(con, "-ERR [SYS/PERM] This account has been locked on suspicion of abuse.\r\n", 69); } else if (con->pop.user->lock_status == 4) { con_write_bl(con, "-ERR [SYS/PERM] This account has been locked at the request of the user.\r\n", 74); } else { con_write_bl(con, "-ERR [SYS/PERM] This account has been locked.\r\n", 47); } con->pop.user = NULL; meta_remove(con->pop.username, META_PROT_POP); return; } // SSL check. else if ((con->pop.user->flags & META_USER_SSL) == META_USER_SSL && con_secure(con) != 1) { con->pop.user = NULL; meta_remove(con->pop.username, META_PROT_POP); con_write_bl(con, "-ERR [SYS/PERM] This user account is configured to require that all POP sessions be connected over SSL.\r\n", 105); return; } // Single session check. else if (con->pop.user->refs.pop != 1) { con->pop.user = NULL; meta_remove(con->pop.username, META_PROT_POP); con_write_bl(con, "-ERR [IN-USE] This account is being used by another session. Please try again in a few minutes.\r\n", 97); return; } // Debug logging. log_pedantic("User %.*s logged in from %s via POP. {poprefs = %lu, imaprefs = %lu, messages = %lu}", st_length_int(con->pop.username), st_char_get(con->pop.username), st_char_get(con_addr_presentation(con, MANAGEDBUF(256))), con->pop.user->refs.pop, con->pop.user->refs.imap, con->pop.user->messages ? inx_count(con->pop.user->messages) : 0); // Update the log and unlock the session. meta_data_update_log(con->pop.user, META_PROT_POP); meta_user_wlock(con->pop.user); meta_messages_login_update(con->pop.user, META_LOCKED); meta_user_unlock(con->pop.user); // Update session state. con->pop.session_state = 1; // Tell the client everything worked. con_write_bl(con, "+OK Password accepted.\r\n", 24); return; }
/** * @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 Read data from a network connection, and store the data in the connection context buffer. * @return -1 on general failure, -2 if the connection was reset, or the amount of data that was read. */ int64_t client_read(client_t *client) { int_t counter = 0; ssize_t bytes = 0; bool_t blocking = true; stringer_t *error = NULL; #ifdef MAGMA_PEDANTIC int_t local = 0; stringer_t *ip = NULL, *cipher = NULL; #endif if (!client || client->sockd == -1 || client_status(client) < 0) { return -1; } // Check for data past the current line buffer. else if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line)); // Update the length. st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line)); // Clear the line buffer. client->line = pl_null(); } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(client->buffer, 0); client->line = pl_null(); } // Loop until the buffer has data or we get an error. do { blocking = st_length_get(client->buffer) ? false : true; // Read bytes off the network. If data is already in the buffer this should be a non-blocking read operation so we can // return the already buffered data without delay. if (client->tls) { // If bytes is zero or below and the library isn't asking for another read, then an error occurred. bytes = tls_read(client->tls, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), blocking); // If zero bytes were read, or a negative value was returned to indicate an error, call tls_erorr(), which will return // NULL if the error can be safely ignored. Otherwise log the output for debug purposes. if (bytes <= 0 && (error = tls_error(client->tls, bytes, MANAGEDBUF(512)))) { #ifdef MAGMA_PEDANTIC cipher = tls_cipher(client->tls, MANAGEDBUF(128)); ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TLS client read operation failed. { ip = %.*s / %.*s / result = %zi%s%.*s }", st_length_int(ip), st_char_get(ip), st_length_int(cipher), st_char_get(cipher), bytes, (error ? " / " : ""), st_length_int(error), st_char_get(error)); #endif client->status = -1; return -1; } // This will occur when the read operation results in a 0, or negative value, but TLS error returns NULL to // indicate it was a transient error. For transient errors we simply set bytes equal to 0 so the read call gets retried. else if (bytes <= 0) { bytes = 0; } } else { errno = 0; bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), (blocking ? 0 : MSG_DONTWAIT)); // Check for errors on non-SSL reads in the traditional way. if (bytes <= 0 && tcp_status(client->sockd)) { #ifdef MAGMA_PEDANTIC local = errno; ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TCP client read operation failed. { ip = %.*s / result = %zi / error = %i / message = %s }", st_length_int(ip), st_char_get(ip), bytes, local, strerror_r(local, MEMORYBUF(1024), 1024)); #endif client->status = -1; return -1; } } // We actually read in data, so we need to update the buffer to reflect the amount of data it currently holds. if (bytes > 0) { st_length_set(client->buffer, st_length_get(client->buffer) + bytes); } } while (blocking && counter++ < 128 && !st_length_get(client->buffer) && status()); // If there is data in the buffer process it. Otherwise if the buffer is empty and the connection appears to be closed // (as indicated by a return value of 0), then return -1 to let the caller know the connection is dead. if (st_length_get(client->buffer)) { client->status = 1; } else if (!bytes) { client->status = 2; return -2; } return st_length_get(client->buffer); }
bool_t check_prime_signets_org_sthread(stringer_t *errmsg) { prime_t *org = NULL, *signet = NULL; stringer_t *fingerprint = NULL, *binary = NULL, *armored = NULL, *signet2 = NULL, *signet3 = NULL; // Create an org key and then generate the corresponding signet. if (!(org = prime_key_generate(PRIME_ORG_KEY, NONE)) || !(signet = prime_signet_generate(org))) { st_sprint(errmsg, "Organizational signet/key creation failed."); prime_cleanup(org); return false; } // Serialize the org signet. else if (!(binary = prime_get(signet, BINARY, MANAGEDBUF(256)))) { st_sprint(errmsg, "Organizational signet serialization failed."); prime_free(signet); prime_free(org); return false; } // Serialize and armor the org signet. else if (!(armored = prime_get(signet, ARMORED, MANAGEDBUF(512)))) { st_sprint(errmsg, "Organizational signet armoring failed."); prime_free(signet); prime_free(org); return false; } else if (!(signet2 = prime_set(binary, BINARY, NONE)) || !(signet3 = prime_set(armored, ARMORED, NONE))) { st_sprint(errmsg, "Organizational signet parsing failed."); prime_cleanup(signet2); prime_free(signet); prime_free(org); return false; } // Validate the org signet. else if (!prime_signet_validate(signet, NULL)) { st_sprint(errmsg, "Organizational signet validation failed."); prime_free(signet3); prime_free(signet2); prime_free(signet); prime_free(org); return false; } // Fingerprint the org signet. else if (!(fingerprint = prime_signet_fingerprint(signet, MANAGEDBUF(64)))) { st_sprint(errmsg, "Organizational signet fingerprinting failed."); prime_free(signet3); prime_free(signet2); prime_free(signet); prime_free(org); return false; } // Hack to easily generate a new org identity. // log_enable(); // log_pedantic("%.*s", st_length_int(armored), st_char_get(armored)); // fingerprint = prime_get(org, ARMORED, MANAGEDBUF(1024)); // log_pedantic("%.*s", st_length_int(fingerprint), st_char_get(fingerprint)); // log_disable(); prime_free(signet3); prime_free(signet2); prime_free(signet); prime_free(org); return true; }
bool_t check_prime_signets_parameters_sthread(stringer_t *errmsg) { stringer_t *holder = NULL, *rand1 = MANAGEDBUF(32), *rand2 = MANAGEDBUF(128), *rand3 = MANAGEDBUF(64), *encrypted_key = NULL; prime_t *org_key = NULL, *org_signet = NULL, *user_key = NULL, *user_request = NULL, *user_signet = NULL, *rotation_key = NULL, *rotation_request = NULL, *rotation_signet = NULL, *check = NULL; // Create various PRIME types for use below. if (rand_write(rand1) != 32 || rand_write(rand2) != 128 || rand_write(rand3) != 64 || !(org_key = prime_key_generate(PRIME_ORG_KEY, NONE)) || !(org_signet = prime_signet_generate(org_key)) || !(user_key = prime_key_generate(PRIME_USER_KEY, NONE)) || !(user_request = prime_request_generate(user_key, NULL)) || !(user_signet = prime_request_sign(user_request, org_key)) || !(rotation_key = prime_key_generate(PRIME_USER_KEY, NONE)) || !(rotation_request = prime_request_generate(rotation_key, user_key)) || !(rotation_signet = prime_request_sign(rotation_request, org_key)) || !(encrypted_key = prime_key_encrypt(rand3, user_key, ARMORED, MANAGEDBUF(512)))) { st_sprint(errmsg, "Signet/key creation for parameter testing failed."); prime_cleanup(org_key); prime_cleanup(org_signet); prime_cleanup(user_key); prime_cleanup(user_request); prime_cleanup(user_signet); prime_cleanup(rotation_key); prime_cleanup(rotation_request); prime_cleanup(rotation_signet); return false; } else if ((check = prime_signet_generate(user_key)) || (check = prime_signet_generate(user_request)) || (check = prime_signet_generate(user_signet)) || (check = prime_request_generate(org_key, NULL)) || (check = prime_request_generate(org_signet, NULL)) || (check = prime_request_generate(user_request, NULL)) || (check = prime_request_generate(user_signet, NULL)) || (check = prime_request_sign(org_key, user_key)) || (check = prime_request_sign(org_key, user_request)) || (check = prime_request_sign(org_key, user_signet)) || (check = prime_request_sign(org_key, org_signet)) || (check = prime_request_sign(user_key, org_key)) || (check = prime_request_sign(user_signet, org_key)) || prime_signet_validate(user_key, NULL) || prime_signet_validate(org_key, NULL) || prime_signet_validate(org_signet, org_key) || prime_signet_validate(rotation_request, org_key) || prime_signet_validate(rotation_request, user_key) || prime_signet_validate(org_signet, user_key) || prime_signet_validate(org_signet, user_signet) || prime_signet_validate(user_request, org_key) || prime_signet_validate(user_request, user_key) || prime_signet_validate(user_request, org_signet) || prime_signet_validate(user_request, rotation_key) || prime_signet_validate(user_signet, org_key) || prime_signet_validate(user_signet, user_key) || prime_signet_validate(user_signet, rotation_signet) || prime_signet_validate(user_signet, user_request) || (check = prime_key_generate(PRIME_USER_SIGNET, NONE)) || (check = prime_key_generate(PRIME_ORG_SIGNET, NONE)) || (check = prime_key_generate(PRIME_USER_SIGNING_REQUEST, NONE)) || (check = prime_get(NULL, BINARY, NULL)) || (check = prime_get(NULL, BINARY, MANAGEDBUF(512))) || (check = prime_set(NULL, BINARY, NONE)) || (check = prime_set(NULL, ARMORED, NONE)) || (holder = prime_signet_fingerprint(org_key, MANAGEDBUF(64))) || (holder = prime_signet_fingerprint(user_key, MANAGEDBUF(64))) || (holder = prime_signet_fingerprint(user_request, MANAGEDBUF(64))) || (holder = prime_signet_fingerprint(org_signet, CONSTANT("TEST"))) || (holder = prime_key_encrypt(NULL, org_key, BINARY, MANAGEDBUF(512))) || (holder = prime_key_encrypt(rand1, org_key, BINARY, MANAGEDBUF(512))) || (holder = prime_key_encrypt(rand2, org_key, BINARY, MANAGEDBUF(512))) || (holder = prime_key_encrypt(rand3, org_signet, BINARY, MANAGEDBUF(512))) || (holder = prime_key_encrypt(rand3, NULL, BINARY, MANAGEDBUF(512))) || (holder = prime_key_decrypt(NULL, encrypted_key, ARMORED, NONE)) || (holder = prime_key_decrypt(rand3, NULL, ARMORED, NONE)) || (holder = prime_key_decrypt(rand1, encrypted_key, ARMORED, NONE)) || (holder = prime_key_decrypt(rand2, encrypted_key, ARMORED, NONE)) || (holder = prime_key_decrypt(rand3, encrypted_key, BINARY, NONE)) || (holder = prime_key_decrypt(rand3, prime_get(org_signet, BINARY, MANAGEDBUF(512)), BINARY, NONE)) || (holder = prime_key_decrypt(rand3, prime_get(user_request, BINARY, MANAGEDBUF(512)), BINARY, NONE)) || (holder = prime_key_decrypt(rand3, prime_get(user_signet, BINARY, MANAGEDBUF(512)), BINARY, NONE))) { st_sprint(errmsg, "Signet/key parameter checks failed."); prime_cleanup(check); prime_free(org_key); prime_free(org_signet); prime_free(user_key); prime_free(user_request); prime_free(user_signet); prime_free(rotation_key); prime_free(rotation_request); prime_free(rotation_signet); } prime_free(org_key); prime_free(org_signet); prime_free(user_key); prime_free(user_request); prime_free(user_signet); prime_free(rotation_key); prime_free(rotation_request); prime_free(rotation_signet); return true; }