/** * @brief Accept a username for POP3 authentication. * @note This command is only allowed for sessions which have not yet been authenticated. * If the username has already been supplied pre-authentication, the old value will be overwritten with the new one. * @param con the POP3 client connection issuing the command. * @brief This function returns no value. */ void pop_user(connection_t *con) { stringer_t *username, *clean; if (con->pop.session_state != 0) { pop_invalid(con); return; } // If they didn't pass in a valid username. if (!(username = pop_user_parse(con)) || !(clean = credential_address(username))) { con_write_bl(con, "-ERR Invalid USER command.\r\n", 28); st_cleanup(username); return; } // Check for a previously provided value and free it. st_cleanup(con->pop.username); st_free(username); // Store the value we were given. Until authentication, this will be the fully qualified username. con->pop.username = clean; // Tell the client everything worked. con_write_bl(con, "+OK Username accepted.\r\n", 24); return; }
stringer_t * check_rand_mthread(void) { void *outcome = NULL; stringer_t *result = NULL; pthread_t *threads = NULL; if (!RAND_CHECK_MTHREADS) { return NULL; } else if (!(threads = mm_alloc(sizeof(pthread_t) * RAND_CHECK_MTHREADS))) { return st_dupe(NULLER("Thread allocation error."));; } for (uint64_t counter = 0; counter < RAND_CHECK_MTHREADS; counter++) { if (thread_launch(threads + counter, &check_rand_mthread_wrap, NULL)) { result = false; } } for (uint64_t counter = 0; counter < RAND_CHECK_MTHREADS; counter++) { if (thread_result(*(threads + counter), &outcome)) { st_cleanup(result); result = st_dupe(NULLER("Thread join error.")); } else if (outcome) { st_cleanup(result); result = outcome; } } mm_free(threads); return result; }
/** * @brief Destroy a web session and its associated data. * @param sess a pointer to the web session to be destroyed. * @return This function returns no value. */ void sess_destroy(session_t *sess) { if (sess) { if (sess->user) { meta_remove(sess->user->username, META_PROT_WEB); } st_cleanup(sess->warden.token); st_cleanup(sess->warden.agent); // Release the credential. if (sess->warden.cred) { credential_free(sess->warden.cred); sess->warden.cred = NULL; } st_cleanup(sess->request.host); st_cleanup(sess->request.path); st_cleanup(sess->request.application); inx_cleanup(sess->compositions); mutex_destroy(&(sess->lock)); mm_free(sess); } return; }
void auth_legacy_free(auth_legacy_t *legacy) { if (legacy) { st_cleanup(legacy->key); st_cleanup(legacy->token); mm_free(legacy); } return; }
/** * @brief Free an SMTP inbound filter and its underlying data. * @param filter a pointer to the SMTP inbound filter to be destroyed. * @return This function returns no value. */ void smtp_list_free_filter(smtp_inbound_filter_t *filter) { st_cleanup(filter->label); st_cleanup(filter->field); st_cleanup(filter->expression); mm_free(filter); return; }
/** * @brief Free an attachment object. * @note This is an inx helper function. * @param attachment a pointer to the attachment object to be destroyed. * @return This function returns no value. */ void sess_release_attachment(attachment_t *attachment) { if (attachment) { st_cleanup(attachment->filename); st_cleanup(attachment->filedata); mm_free(attachment); } }
/** * @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 Set the contents of the thread's mail cache. * @param messagenum the numerical id of the message to be cached. * @text a managed string containing the contents of the specified message to be cached. * @return This function returns no value. */ void mail_cache_set(uint64_t messagenum, stringer_t *text) { mail_cache_t *message; if (!(message = pthread_getspecific(mail_cache))) { if (!(message = mm_alloc(sizeof(mail_cache_t)))) { return; } if (pthread_setspecific(mail_cache, message) != 0) { log_pedantic("Unable to setup thread message cache."); mm_free(message); return; } } else { st_cleanup(message->text); } message->messagenum = messagenum; message->text = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, text); return; }
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; }
bool_t check_bitwise_simple(void) { stringer_t *a, *b, *outcome, *check; unsigned char a_buf[] = {0x00, 0xFF, 0x88, 0x44, 0x22}; unsigned char b_buf[] = {0x11, 0x33, 0xF0, 0x0F, 0x3C}; unsigned char xor_buf[] = {0x11, 0xCC, 0x78, 0x4B, 0x1E}; unsigned char and_buf[] = {0x00, 0x33, 0x80, 0x04, 0x20}; unsigned char or_buf[] = {0x11, 0xFF, 0xF8, 0x4F, 0x3E}; unsigned char nota_buf[] = {0xFF, 0x00, 0x77, 0xBB, 0xDD}; a = PLACER(a_buf, 5); b = PLACER(b_buf, 5); check = PLACER(xor_buf, 5); if(!(outcome = st_xor(a, b, NULL)) || st_cmp_cs_eq(outcome, check)) { st_cleanup(outcome); return false; } check = PLACER(and_buf, 5); st_free(outcome); if(!(outcome = st_and(a, b, NULL)) || st_cmp_cs_eq(outcome, check)) { st_cleanup(outcome); return false; } check = PLACER(or_buf, 5); st_free(outcome); if(!(outcome = st_or(a, b, NULL)) || st_cmp_cs_eq(outcome, check)) { st_cleanup(outcome); return false; } check = PLACER(nota_buf, 5); st_free(outcome); if(!(outcome = st_not(a, NULL)) || st_cmp_cs_eq(outcome,check)) { st_cleanup(outcome); return false; } st_free(outcome); return true; }
void imap_session_destroy(connection_t *con) { inx_cursor_t *cursor; meta_message_t *active; meta_user_wlock(con->imap.user); // If a folder was selected, clear the recent flag before closing the mailbox. if (con->imap.session_state == 1 && con->imap.user && con->imap.selected && !con->imap.read_only && (cursor = inx_cursor_alloc(con->imap.user->messages))) { while ((active = inx_cursor_value_next(cursor))) { if (active->foldernum == con->imap.selected && (active->status & MAIL_STATUS_RECENT) == MAIL_STATUS_RECENT) { active->status = (active->status | MAIL_STATUS_RECENT) ^ MAIL_STATUS_RECENT; } } inx_cursor_free(cursor); } meta_user_unlock(con->imap.user); // Is there a user session. if (con->imap.user) { if (con->imap.username) { meta_remove(con->imap.username, META_PROT_IMAP); } } st_cleanup(con->imap.username); st_cleanup(con->imap.tag); st_cleanup(con->imap.command); if (con->imap.arguments) { ar_free(con->imap.arguments); } mail_cache_reset(); return; }
/** * @brief Free a list of SMTP inbound mail preferences and its underlying data. * @param inbound a pointer to the head of the SMTP inbound mail preferences list to be destroyed. * @return This function returns no value. */ void smtp_free_inbound(smtp_inbound_prefs_t *inbound) { smtp_inbound_prefs_t *holder; while (inbound) { st_cleanup(inbound->rcptto); st_cleanup(inbound->address); st_cleanup(inbound->domain); st_cleanup(inbound->forwarded); st_cleanup(inbound->spamsig); inx_cleanup(inbound->filters); holder = inbound; inbound = (smtp_inbound_prefs_t *)holder->next; mm_free(holder); } return; }
/** * @brief Destroy the data associated with an SMTP session. * @param con the SMTP client connection to be destroyed. * @return This function returns no value. */ void smtp_session_destroy(connection_t *con) { st_cleanup(con->smtp.helo); st_cleanup(con->smtp.mailfrom); if (con->smtp.message) { mail_destroy_message(con->smtp.message); } if (con->smtp.in_prefs) { smtp_free_inbound(con->smtp.in_prefs); } if (con->smtp.out_prefs) { smtp_free_outbound(con->smtp.out_prefs); } 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; }
/** * @brief Free a cached mail message. * @param holder a pointer to the cached mail message object to be freed. * @return This function returns no value. */ void mail_cache_destroy(void *holder) { mail_cache_t *message = (mail_cache_t *)holder; if (message) { st_cleanup(message->text); mm_free(message); } return; }
/** * @brief Free a set of SMTP outbound mail preferences and its underlying data. * @param outbound a pointer to the SMTP outbound mail preferences set to be destroyed. * @return This function returns no value. */ void smtp_free_outbound(smtp_outbound_prefs_t *outbound) { st_cleanup(outbound->domain); if (outbound->recipients) { smtp_free_recipients(outbound->recipients); } mm_free(outbound); return; }
/** * @brief Free a list of SMTP recipients and its underlying data. * @param recipients a pointer to the head of the recipients list to be destroyed. * @return This function returns no value. */ void smtp_free_recipients(smtp_recipients_t *recipients) { smtp_recipients_t *holder; while (recipients) { st_cleanup(recipients->address); holder = recipients; recipients = (smtp_recipients_t *)holder->next; mm_free(holder); } return; }
/** * @brief Reset the thread's mail cache and free any held message. * @return This function returns no value. */ void mail_cache_reset(void) { mail_cache_t *message; if ((message = pthread_getspecific(mail_cache))) { st_cleanup(message->text); message->text = NULL; message->messagenum = 0; mm_free(message); pthread_setspecific(mail_cache, NULL); } return; }
END_TEST START_TEST (check_credential_address_s) { stringer_t *cred; char *errmsg = NULL; log_unit("%-64.64s", "OBJECTS / USERS / CERDENTIALS / ADDRESS / SINGLE THREADED:"); if (!errmsg && (!(cred = credential_address(CONSTANT("*****@*****.**"))) || st_cmp_ci_eq(cred, CONSTANT("*****@*****.**")))) { errmsg = "Address boiler failed [1]."; //printf("r1 = %lx\n", (unsigned long) cred); if (cred) printf("xxx: [%.*s] [%u]\n", (int)st_length_get(cred), st_char_get(cred), (int)st_length_get(cred)); } st_cleanup(cred); cred = NULL; if (!errmsg && (!(cred = credential_address(CONSTANT(" TEST @ DOMAIN.COM "))) || st_cmp_ci_eq(cred, CONSTANT("*****@*****.**")))) { errmsg = "Address boiler failed [2]."; //printf("r2 = %lx\n", (unsigned long) cred); if (cred) printf("xxx: [%.*s] [%u]\n", (int)st_length_get(cred), st_char_get(cred), (int)st_length_get(cred)); } st_cleanup(cred); cred = NULL; if (!errmsg && (!(cred = credential_address(CONSTANT("*****@*****.**"))) || st_cmp_ci_eq(cred, CONSTANT("*****@*****.**")))) { errmsg = "Address boiler failed [5]."; } st_cleanup(cred); cred = NULL; if (!errmsg && (!(cred = credential_address(CONSTANT("*****@*****.**"))) || st_cmp_ci_eq(cred, CONSTANT("*****@*****.**")))) { errmsg = "Address boiler failed [2]."; //printf("rx2 = %lx\n", (unsigned long) cred); if (cred) printf("xxx: [%.*s] [%u]\n", (int)st_length_get(cred), st_char_get(cred), (int)st_length_get(cred)); } st_cleanup(cred); cred = NULL; if (!errmsg && (!(cred = credential_address(CONSTANT(" TEST @ DOMAIN.COM "))) || st_cmp_ci_eq(cred, CONSTANT("*****@*****.**")))) { errmsg = "Address boiler failed [5]."; } st_cleanup(cred); cred = NULL; if (!errmsg && (!(cred = credential_address(CONSTANT("*****@*****.**"))) || st_cmp_ci_eq(cred, CONSTANT("*****@*****.**")))) { errmsg = "Address boiler failed [11]."; } st_cleanup(cred); cred = NULL; log_unit("%10.10s\n", (!status() ? "SKIPPED" : !errmsg ? "PASSED" : "FAILED")); fail_unless(!errmsg, errmsg); } END_TEST
static bool load_scene_file(const char *name) { FILE *fp = fopen(name, "r"); if (!fp) return false; if (scn != NULL) { close_gl(scn); scene_free(scn); st_cleanup(); } lang_file(fp); if ((scn = scene_read()) == NULL) return false; fclose(fp); load_objects_gl(scn->objs); setup_programs_gl(scn, scn->objs); return true; }
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 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; }
void init_gl(Scene *s) { GLint ntexu, ntexc; if( glewInit() != GLEW_OK ) { printf( "[error] %s\n", glewGetErrorString( glewInit() ) ); return; } glGetIntegerv(GL_MAX_TEXTURE_COORDS, &ntexc); glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ntexu); max_textures_gl = MAX(ntexc, ntexu) - 1; glClearColor( 0, 0, 0, 1); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); if (render_to_fbo) init_fbo(s); st_cleanup(); }
/** * @brief Free a mail mime object and its underlying data, and recursively free its children parts. * @param mime a pointer to the mail mime object to be freed. * @return This function returns no value. */ void mail_mime_free(mail_mime_t *mime) { size_t increment, elements; if (!mime) { return; } if (mime->children) { elements = ar_length_get(mime->children); for (increment = 0; increment < elements; increment++) { mail_mime_free((mail_mime_t *)ar_field_ptr(mime->children, increment)); } ar_free(mime->children); } st_cleanup(mime->boundary); mm_free(mime); return; }
/** * @brief Reset an SMTP session to its initialized state. * @param con the SMTP client connection to be reset. * @return This function returns no value. */ void smtp_session_reset(connection_t *con) { st_cleanup(con->smtp.mailfrom); con->smtp.mailfrom = NULL; if (con->smtp.message) { mail_destroy_message(con->smtp.message); con->smtp.message = NULL; } if (con->smtp.in_prefs) { smtp_free_inbound(con->smtp.in_prefs); con->smtp.in_prefs = NULL; } if (con->smtp.out_prefs && con->smtp.out_prefs->recipients) { smtp_free_recipients(con->smtp.out_prefs->recipients); con->smtp.out_prefs->recipients = NULL; } // For unauthenticated sessions, reset the max length to zero and then adjust it when a recipient is provided. if (!con->smtp.out_prefs) { con->smtp.max_length = 0; } con->smtp.checked.rbl = 0; con->smtp.checked.spf = 0; con->smtp.checked.dkim = 0; con->smtp.checked.virus = 0; con->smtp.suggested_eight_bit = false; con->smtp.suggested_length = 0; con->smtp.num_recipients = 0; return; }
/** * @brief Free a meta user object. * * @param user a pointer to the meta user object to be destroyed. * * @return This function returns no value. */ void meta_free(meta_user_t *user) { if (user) { prime_cleanup(user->prime.key); prime_cleanup(user->prime.signet); inx_cleanup(user->aliases); inx_cleanup(user->folders); inx_cleanup(user->message_folders); inx_cleanup(user->messages); inx_cleanup(user->contacts); st_cleanup(user->username, user->verification, user->realm.mail); // When read/write locking issues have been fixed, this line can be used once again. rwlock_destroy(&(user->lock)); mutex_destroy(&(user->refs.lock)); mm_free(user); } return; }
/** * @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 Create a new session for a given web connection. * @note The session stores the following data points: remote IP address, request path, application name, the specified http hostname, * the remote client's user agent string, the server's host number, a unique session id, the server's current timestamp, a randomly- * generated session key for authentication, and an encrypted token for the session returned to the user as a cookie. * @param con a pointer to the connection underlying the web session. * @param path a pointer to a managed string containing the pathname of the generating request (should be "/portal/camel"). * @param application a pointer to a managed string containing the name of the parent application of the session (should be "portal"). * @return NULL on failure or a pointer to a newly allocated session object for the specified connection. */ session_t *sess_create(connection_t *con, stringer_t *path, stringer_t *application) { session_t *output; multi_t key = { .type = M_TYPE_UINT64, .val.u64 = 0 }; if (!(output = mm_alloc(sizeof(session_t)))) { log_pedantic("Unable to allocate %zu bytes for a session context.", sizeof(session_t)); return NULL; } else if (pthread_mutex_init(&(output->lock), NULL) != 0) { log_pedantic("Unable to initialize reference lock for new user session."); mm_free(output); return NULL; } else if (!(output->compositions = inx_alloc(M_INX_LINKED, &sess_release_composition))) { log_pedantic("Unable to allocate space for user session's compositions."); mm_free(output); return NULL; } if (!(ip_copy(&(output->warden.ip), con_addr(con, MEMORYBUF(64)))) || (path && !(output->request.path = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, path))) || (application && !(output->request.application = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, application))) || (con->http.host && !(output->request.host = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, con->http.host))) || (con->http.agent && !(output->warden.agent = st_dupe_opts(MANAGED_T | HEAP | CONTIGUOUS, con->http.agent))) || !(output->warden.host = magma.host.number) || !(key.val.u64 = output->warden.number = sess_number()) || !(output->warden.stamp = time(NULL)) || !(output->warden.key = sess_key()) || !(output->warden.token = sess_token(output))) { log_pedantic("Unable to initialize the session warden context."); sess_destroy(output); return NULL; } output->request.httponly = true; output->request.secure = (con_secure(con) == 1 ? true : false); sess_ref_add(output); if (inx_insert(objects.sessions, key, output) != 1) { log_pedantic("Unable to insert the session into the global context."); sess_ref_dec(output); sess_destroy(output); return NULL; } return output; } /** * @brief Try to retrieve the session associated with a client connection's supplied cookie. * @param con a pointer to the connection object sending the cookie. * @param application a managed string containing the application associated with the session. * @param path a managed string containing the path associated with the session. * @param token the encrypted user token retrieved from the supplied http cookie. * @return 1 if the cookie was found and valid, or one of the following values on failure: * 0 = Session not found. * -1 = Server error. * -2 = Invalid token. * -3 = Security violation / incorrect user-agent. * -4 = Security violation / incorrect session key. * -5 = Security violation / incorrect source address. * -6 = Session terminated by logout. * -7 = Session timed out. */ int_t sess_get(connection_t *con, stringer_t *application, stringer_t *path, stringer_t *token) { uint64_t *numbers; scramble_t *scramble; stringer_t *binary, *encrypted; multi_t key = { .type = M_TYPE_UINT64, .val.u64 = 0 }; int_t result = 1; /// Most session attributes need simple equality comparison, except for timeout checking. Make sure not to validate against a stale session that should have already timed out (which will have to be determined dynamically). encrypted = zbase32_decode(token); scramble = scramble_import(encrypted); binary = scramble_decrypt(magma.secure.sessions, scramble); st_cleanup(encrypted); if (!binary) { return 0; } numbers = st_data_get(binary); // QUESTION: Is this necessary? doesn't inx_find() lock the inx? inx_lock_read(objects.sessions); key.val.u64 = *(numbers + 2); if ((con->http.session = inx_find(objects.sessions, key))) { sess_ref_add(con->http.session); } inx_unlock(objects.sessions); st_free(binary); // Return if we didn't find the session or user. if (!con->http.session || !con->http.session->user) { return 0; } // We need to do full validation against the cookie and associated session. // First, the cookie. if ((*numbers != con->http.session->warden.host) || (*(numbers + 1) != con->http.session->warden.stamp) || (*(numbers + 2) != con->http.session->warden.number)) { log_error("Received mismatched cookie for authenticated session { user = %s }", st_char_get(con->http.session->user->username)); result = -2; } else if (*(numbers + 3) != con->http.session->warden.key) { log_error("Cookie contained an incorrect session key { user = %s }", st_char_get(con->http.session->user->username)); result = -4; } else if (st_cmp_cs_eq(application, con->http.session->request.application)) { log_error("Cookie did not match session's application { user = %s }", st_char_get(con->http.session->user->username)); result = -2; } else if (st_cmp_cs_eq(path, con->http.session->request.path)) { log_error("Cookie did not match session's path { user = %s }", st_char_get(con->http.session->user->username)); result = -2; } else if (st_cmp_cs_eq(con->http.agent, con->http.session->warden.agent)) { log_error("Cookie contained a mismatched user agent { user = %s }", st_char_get(con->http.session->user->username)); result = -3; } else if (con->http.session->request.secure != (con_secure(con) ? 1 : 0)) { log_error("Cookie was submitted from a mismatched transport layer { user = %s }", st_char_get(con->http.session->user->username)); result = -5; } else if (!ip_address_equal(&(con->http.session->warden.ip), (ip_t *)con_addr(con, MEMORYBUF(64)))) { log_error("Cookie was submitted from a mismatched IP address { user = %s }", st_char_get(con->http.session->user->username)); result = -5; } // Finally, do comparisons to see that we haven't timed out. /* Did we expire? */ if (magma.http.session_timeout <= (time(NULL) - con->http.session->warden.stamp)) { log_pedantic("User submitted expired or invalidated cookie; marking for deletion { user = %s }", st_char_get(con->http.session->user->username)); result = -7; } // QUESTION: This destruction needs a second look. if (result < 0) { if (!inx_delete(objects.sessions, key)) { log_pedantic("Unexpected error occurred attempting to delete expired cookie { user = %s }", st_char_get(con->http.session->user->username)); } sess_ref_dec(con->http.session); //sess_destroy(con->http.session); con->http.session = NULL; } // Otherwise, if the last session status update is more than 10 minutes ago, check now to see if things are current. // QUESTION: Why is it 600 here and 120 elsewhere? else if ((time(NULL) - sess_refresh_stamp(con->http.session)) > 600) { sess_update(con->http.session); } return result; }
/** * @brief Destroy and free a generic connection object after executing its protocol-specific destructor; update any statistics accordingly. * @param con a pointer to the connection to be destroyed. * @return This function returns no value. */ void con_destroy(connection_t *con) { if (con && !con_decrement_refs(con)) { switch (con->server->protocol) { case (POP): if (con->network.ssl) { stats_decrement_by_name("pop.connections.secure"); } stats_decrement_by_name("pop.connections.total"); pop_session_destroy(con); break; case (IMAP): if (con->network.ssl) { stats_decrement_by_name("imap.connections.secure"); } stats_decrement_by_name("imap.connections.total"); imap_session_destroy(con); break; case (HTTP): if (con->network.ssl) { stats_decrement_by_name("http.connections.secure"); } stats_decrement_by_name("http.connections.total"); http_session_destroy(con); break; case (SMTP): if (con->network.ssl) { stats_decrement_by_name("smtp.connections.secure"); } stats_decrement_by_name("smtp.connections.total"); smtp_session_destroy(con); break; case (SUBMISSION): if (con->network.ssl) { stats_decrement_by_name("smtp.connections.secure"); } stats_decrement_by_name("smtp.connections.total"); smtp_session_destroy(con); break; case (MOLTEN): if (con->network.ssl) { stats_decrement_by_name("molten.connections.secure"); } stats_decrement_by_name("molten.connections.total"); molten_session_destroy(con); break; default: break; } if (con->network.ssl) { ssl_free(con->network.ssl); } if (con->network.sockd != -1) { close(con->network.sockd); } st_cleanup(con->network.buffer); st_cleanup(con->network.reverse.domain); mutex_destroy(&(con->lock)); mm_free(con); } return; }