void expire_tls_sessions(main_server_st * s) { tls_cache_st *cache; struct htable_iter iter; time_t now, exp; now = time(0); cache = htable_first(s->tls_db.ht, &iter); while (cache != NULL) { gnutls_datum_t d; d.data = (void *)cache->session_data; d.size = cache->session_data_size; exp = gnutls_db_check_entry_time(&d); if (now - exp > TLS_SESSION_EXPIRATION_TIME(s->config)) { cache->session_id_size = 0; htable_delval(s->tls_db.ht, &iter); safe_memset(cache->session_data, 0, cache->session_data_size); talloc_free(cache); s->tls_db.entries--; } cache = htable_next(s->tls_db.ht, &iter); } return; }
static void passwd_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct passwd pw; enum passdb_result res; int ret; res = passwd_lookup(request, &pw); if (res != PASSDB_RESULT_OK) { callback(res, request); return; } /* check if the password is valid */ ret = auth_request_password_verify(request, password, pw.pw_passwd, PASSWD_PASS_SCHEME, AUTH_SUBSYS_DB); /* clear the passwords from memory */ safe_memset(pw.pw_passwd, 0, strlen(pw.pw_passwd)); if (ret <= 0) { callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; } /* make sure we're using the username exactly as it's in the database */ auth_request_set_field(request, "user", pw.pw_name, NULL); callback(PASSDB_RESULT_OK, request); }
static void proxy_free_password(struct client *client) { if (client->proxy_password == NULL) return; safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); i_free_and_null(client->proxy_password); }
void safe_memclear(void *s, size_t n) { #if defined(HAVE_MEMSET_S) memset_s(s, n, 0, n); #elif defined(HAVE_EXPLICIT_BZERO) explicit_bzero(s, n); #else safe_memset(s, 0, n); #endif }
void password_generate_rpa(const char *pw, unsigned char result[]) { unsigned char *ucs2be_pw; size_t size; ucs2be_pw = ucs2be_str(unsafe_data_stack_pool, pw, &size); md5_get_digest(ucs2be_pw, size, result); safe_memset(ucs2be_pw, 0, size); }
static void mech_plain_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { const char *authid, *authenid, *error; char *pass; size_t i, len; int count; /* authorization ID \0 authentication ID \0 pass. */ authid = (const char *) data; authenid = NULL; pass = NULL; count = 0; for (i = 0; i < data_size; i++) { if (data[i] == '\0') { if (++count == 1) authenid = (const char *) data + i+1; else { i++; len = data_size - i; pass = p_strndup(unsafe_data_stack_pool, data+i, len); break; } } } if (authenid != NULL && strcmp(authid, authenid) == 0) { /* the login username isn't different */ authid = ""; } if (count != 2) { /* invalid input */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "invalid input"); auth_request_fail(request); } else if (!auth_request_set_username(request, authenid, &error)) { /* invalid username */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(request); } else if (*authid != '\0' && !auth_request_set_login_username(request, authid, &error)) { /* invalid login username */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "login user: %s", error); auth_request_fail(request); } else { auth_request_verify_plain(request, pass, plain_verify_callback); } /* make sure it's cleared */ if (pass != NULL) safe_memset(pass, 0, strlen(pass)); }
static void mech_dovecot_token_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size) { const char *session_id, *username, *pid, *service, *error; char *auth_token; size_t i, len; int count; /* service \0 pid \0 username \0 session_id \0 auth_token */ service = (const char *) data; session_id = username = pid = auth_token = NULL; count = 0; for (i = 0; i < data_size; i++) { if (data[i] == '\0') { count++; i++; if (count == 1) pid = (const char *)data + i; else if (count == 2) username = (const char *)data + i; else if (count == 3) session_id = (const char *)data + i; else { len = data_size - i; auth_token = p_strndup(unsafe_data_stack_pool, data+i, len); break; } } } if (count != 4) { /* invalid input */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "invalid input"); auth_request_fail(request); } else if (!auth_request_set_username(request, username, &error)) { /* invalid username */ auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", error); auth_request_fail(request); } else { const char *valid_token = auth_token_get(service, pid, request->user, session_id); if (auth_token != NULL && strcmp(auth_token, valid_token) == 0) { request->passdb_success = TRUE; auth_request_success(request, NULL, 0); } else { auth_request_fail(request); } } /* make sure it's cleared */ if (auth_token != NULL) safe_memset(auth_token, 0, strlen(auth_token)); }
static void vpopmail_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { enum passdb_result result; const char *scheme, *tmp_pass; char *crypted_pass; bool cleartext = FALSE; int ret; crypted_pass = vpopmail_password_lookup(request, &cleartext, &result); if (crypted_pass == NULL) { callback(result, request); return; } tmp_pass = crypted_pass; if (cleartext) scheme = "CLEARTEXT"; else { scheme = password_get_scheme(&tmp_pass); if (scheme == NULL) scheme = request->passdb->passdb->default_pass_scheme; } ret = auth_request_password_verify(request, password, tmp_pass, scheme, AUTH_SUBSYS_DB); safe_memset(crypted_pass, 0, strlen(crypted_pass)); if (ret <= 0) { callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; } #ifdef POP_AUTH_OPEN_RELAY if (strcasecmp(request->service, "POP3") == 0 || strcasecmp(request->service, "IMAP") == 0) { const char *host = net_ip2addr(&request->remote_ip); /* vpopmail 5.4 does not understand IPv6 */ if (host[0] != '\0' && IPADDR_IS_V4(&request->remote_ip)) { /* use putenv() directly rather than env_put() which would leak memory every time we got here. use a static buffer for putenv() as SUSv2 requirements would otherwise corrupt our environment later. */ static char ip_env[256]; i_snprintf(ip_env, sizeof(ip_env), "TCPREMOTEIP=%s", host); putenv(ip_env); open_smtp_relay(); } } #endif callback(PASSDB_RESULT_OK, request); }
static void stored_length_free (void *v) { size_t n; v -= sizeof (size_t); n = *((size_t *) v); safe_memset (v, 0, n + sizeof (size_t)); free (v); }
static char * vpopmail_password_lookup(struct auth_request *auth_request, bool *cleartext, enum passdb_result *result_r) { char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT]; struct vqpasswd *vpw; char *password; vpw = vpopmail_lookup_vqp(auth_request, vpop_user, vpop_domain); if (vpw == NULL) { *result_r = PASSDB_RESULT_USER_UNKNOWN; return NULL; } if (vpopmail_is_disabled(auth_request, vpw)) { auth_request_log_info(auth_request, AUTH_SUBSYS_DB, "%s disabled in vpopmail for this user", auth_request->service); password = NULL; *result_r = PASSDB_RESULT_USER_DISABLED; } else { if (vpw->pw_clear_passwd != NULL && *vpw->pw_clear_passwd != '\0') { password = t_strdup_noconst(vpw->pw_clear_passwd); *cleartext = TRUE; } else if (!*cleartext) password = t_strdup_noconst(vpw->pw_passwd); else password = NULL; *result_r = password != NULL ? PASSDB_RESULT_OK : PASSDB_RESULT_SCHEME_NOT_AVAILABLE; } safe_memset(vpw->pw_passwd, 0, strlen(vpw->pw_passwd)); if (vpw->pw_clear_passwd != NULL) { safe_memset(vpw->pw_clear_passwd, 0, strlen(vpw->pw_clear_passwd)); } return password; }
void checkpassword_request_free(struct chkpw_auth_request *request) { checkpassword_request_close(request); if (request->input_buf != NULL) str_free(&request->input_buf); if (request->password != NULL) { safe_memset(request->password, 0, strlen(request->password)); i_free(request->password); } i_free(request); }
const unsigned char * ntlm_v1_hash(const char *passwd, unsigned char hash[NTLMSSP_HASH_SIZE]) { size_t len; void *wpwd = t_unicode_str(passwd, 0, &len); md4_get_digest(wpwd, len, hash); safe_memset(wpwd, 0, len); return hash; }
void hmac_init(struct hmac_context *_ctx, const unsigned char *key, size_t key_len, const struct hash_method *meth) { struct hmac_context_priv *ctx = &_ctx->u.priv; int i; unsigned char k_ipad[64]; unsigned char k_opad[64]; unsigned char hashedkey[meth->digest_size]; i_assert(meth->context_size <= HMAC_MAX_CONTEXT_SIZE); ctx->hash = meth; if (key_len > 64) { meth->init(ctx->ctx); meth->loop(ctx->ctx, key, key_len); meth->result(ctx->ctx, hashedkey); key = hashedkey; key_len = meth->digest_size; } memcpy(k_ipad, key, key_len); memset(k_ipad + key_len, 0, 64 - key_len); memcpy(k_opad, k_ipad, 64); for (i = 0; i < 64; i++) { k_ipad[i] ^= 0x36; k_opad[i] ^= 0x5c; } meth->init(ctx->ctx); meth->loop(ctx->ctx, k_ipad, 64); meth->init(ctx->ctxo); meth->loop(ctx->ctxo, k_opad, 64); safe_memset(k_ipad, 0, 64); safe_memset(k_opad, 0, 64); }
static void passwd_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct passwd pw; int ret; auth_request_log_debug(request, "passwd", "lookup"); switch (i_getpwnam(request->user, &pw)) { case -1: auth_request_log_error(request, "passwd", "getpwnam() failed: %m"); callback(PASSDB_RESULT_INTERNAL_FAILURE, request); return; case 0: auth_request_log_unknown_user(request, "passwd"); callback(PASSDB_RESULT_USER_UNKNOWN, request); return; } if (!IS_VALID_PASSWD(pw.pw_passwd)) { auth_request_log_info(request, "passwd", "invalid password field '%s'", pw.pw_passwd); callback(PASSDB_RESULT_USER_DISABLED, request); return; } /* save the password so cache can use it */ auth_request_set_field(request, "password", pw.pw_passwd, PASSWD_PASS_SCHEME); /* check if the password is valid */ ret = auth_request_password_verify(request, password, pw.pw_passwd, PASSWD_PASS_SCHEME, "passwd"); /* clear the passwords from memory */ safe_memset(pw.pw_passwd, 0, strlen(pw.pw_passwd)); if (ret <= 0) { callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; } /* make sure we're using the username exactly as it's in the database */ auth_request_set_field(request, "user", pw.pw_name, NULL); callback(PASSDB_RESULT_OK, request); }
static void vpopmail_lookup_credentials(struct auth_request *request, lookup_credentials_callback_t *callback) { enum passdb_result result; char *password; bool cleartext = TRUE; password = vpopmail_password_lookup(request, &cleartext, &result); if (password == NULL) { callback(result, NULL, 0, request); return; } passdb_handle_credentials(PASSDB_RESULT_OK, password, "CLEARTEXT", callback, request); safe_memset(password, 0, strlen(password)); }
void ntlmssp_v1_response(const unsigned char *hash, const unsigned char *challenge, unsigned char response[NTLMSSP_RESPONSE_SIZE]) { unsigned char des_hash[NTLMSSP_DES_KEY_LENGTH * 3]; memcpy(des_hash, hash, NTLMSSP_HASH_SIZE); memset(des_hash + NTLMSSP_HASH_SIZE, 0, sizeof(des_hash) - NTLMSSP_HASH_SIZE); deshash(response, des_hash, challenge); deshash(response + 8, des_hash + 7, challenge); deshash(response + 16, des_hash + 14, challenge); safe_memset(des_hash, 0, sizeof(des_hash)); }
void ntlmssp_v2_response(const char *user, const char *target, const unsigned char *hash_v1, const unsigned char *challenge, const unsigned char *blob, size_t blob_size, unsigned char response[NTLMSSP_V2_RESPONSE_SIZE]) { struct hmac_md5_context ctx; unsigned char hash[NTLMSSP_V2_HASH_SIZE]; ntlm_v2_hash(user, target, hash_v1, hash); hmac_md5_init(&ctx, hash, NTLMSSP_V2_HASH_SIZE); hmac_md5_update(&ctx, challenge, NTLMSSP_CHALLENGE_SIZE); hmac_md5_update(&ctx, blob, blob_size); hmac_md5_final(&ctx, response); safe_memset(hash, 0, sizeof(hash)); }
const unsigned char * lm_hash(const char *passwd, unsigned char hash[LM_HASH_SIZE]) { static const unsigned char lm_magic[8] = "KGS!@#$%"; unsigned char buffer[14]; unsigned int i; strncpy((char *)buffer, passwd, sizeof(buffer)); for (i = 0; i < sizeof(buffer); i++) buffer[i] = i_toupper(buffer[i]); deshash(hash, buffer, lm_magic); deshash(hash + 8, buffer + 7, lm_magic); safe_memset(buffer, 0, sizeof(buffer)); return hash; }
static void bsdauth_verify_plain(struct auth_request *request, const char *password, verify_plain_callback_t *callback) { struct passwd pw; const char *type; int result; auth_request_log_debug(request, "bsdauth", "lookup"); switch (i_getpwnam(request->user, &pw)) { case -1: auth_request_log_error(request, "bsdauth", "getpwnam() failed: %m"); callback(PASSDB_RESULT_INTERNAL_FAILURE, request); return; case 0: auth_request_log_unknown_user(request, "bsdauth"); callback(PASSDB_RESULT_USER_UNKNOWN, request); return; } /* check if the password is valid */ type = t_strdup_printf("auth-%s", request->service); result = auth_userokay(request->user, NULL, t_strdup_noconst(type), t_strdup_noconst(password)); /* clear the passwords from memory */ safe_memset(pw.pw_passwd, 0, strlen(pw.pw_passwd)); if (result == 0) { auth_request_log_password_mismatch(request, "bsdauth"); callback(PASSDB_RESULT_PASSWORD_MISMATCH, request); return; } /* make sure we're using the username exactly as it's in the database */ auth_request_set_field(request, "user", pw.pw_name, NULL); callback(PASSDB_RESULT_OK, request); }
void sha1_result(struct sha1_ctxt *ctxt, void *digest0) { uint8_t *digest; digest = (uint8_t *)digest0; sha1_pad(ctxt); #if WORDS_BIGENDIAN memmove(digest, &ctxt->h.b8[0], 20); #else digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2]; digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0]; digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6]; digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4]; digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10]; digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8]; digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14]; digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12]; digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18]; digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16]; #endif safe_memset(ctxt, 0, sizeof(struct sha1_ctxt)); }
void client_destroy(struct client *client, const char *reason) { if (client->destroyed) return; client->destroyed = TRUE; if (!client->login_success && reason != NULL) { reason = t_strconcat(reason, " ", client_get_extra_disconnect_reason(client), NULL); } if (reason != NULL) client_log(client, reason); if (last_client == client) last_client = client->prev; DLLIST_REMOVE(&clients, client); if (client->input != NULL) i_stream_close(client->input); if (client->output != NULL) o_stream_close(client->output); if (client->master_tag != 0) { i_assert(client->auth_request == NULL); i_assert(client->authenticating); i_assert(client->refcount > 1); client->authenticating = FALSE; master_auth_request_abort(master_auth, client->master_tag); client->refcount--; } else if (client->auth_request != NULL) { i_assert(client->authenticating); sasl_server_auth_abort(client); } else { i_assert(!client->authenticating); } if (client->io != NULL) io_remove(&client->io); if (client->to_disconnect != NULL) timeout_remove(&client->to_disconnect); if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); if (client->auth_response != NULL) str_free(&client->auth_response); if (client->fd != -1) { net_disconnect(client->fd); client->fd = -1; } if (client->proxy_password != NULL) { safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); i_free_and_null(client->proxy_password); } if (client->proxy_sasl_client != NULL) dsasl_client_free(&client->proxy_sasl_client); if (client->login_proxy != NULL) login_proxy_free(&client->login_proxy); if (client->v.destroy != NULL) client->v.destroy(client); if (client_unref(&client) && initial_service_count == 1) { /* as soon as this connection is done with proxying (or whatever), the process will die. there's no need for authentication anymore, so close the connection. do this only with initial service_count=1, in case there are other clients with pending authentications */ auth_client_disconnect(auth_client, "unnecessary connection"); } login_client_destroyed(); login_refresh_proctitle(); }
static ssize_t i_stream_decrypt_read_header_v1(struct decrypt_istream *stream, const unsigned char *data, size_t mlen) { const char *error = NULL; size_t keydata_len = 0; uint16_t len; int ec, i = 0; const unsigned char *digest_pos = NULL, *key_digest_pos = NULL, *key_ct_pos = NULL; size_t pos = sizeof(IOSTREAM_CRYPT_MAGIC); size_t digest_len = 0; size_t key_ct_len = 0; size_t key_digest_size = 0; buffer_t ephemeral_key; buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 256); buffer_t *key = buffer_create_dynamic(pool_datastack_create(), 256); if (mlen < 2) return 0; keydata_len = (data[0] << 8) | data[1]; if (mlen-2 < keydata_len) { /* try to read more */ return 0; } data+=2; mlen-=2; while (i < 4 && mlen > 2) { memcpy(&len, data, 2); len = ntohs(len); data += 2; mlen -= 2; pos += 2; if (len == 0 || len > mlen) break; switch(i++) { case 0: buffer_create_from_const_data(&ephemeral_key, data, len); break; case 1: /* public key id */ digest_pos = data; digest_len = len; break; case 2: /* encryption key digest */ key_digest_pos = data; key_digest_size = len; break; case 3: /* encrypted key data */ key_ct_pos = data; key_ct_len = len; break; } pos += len; data += len; mlen -= len; } if (i < 4) { io_stream_set_error(&stream->istream.iostream, "Invalid or corrupted header"); stream->istream.istream.stream_errno = EINVAL; return -1; } /* we don't have a private key */ if (stream->priv_key == NULL) { /* see if we can get one */ if (stream->key_callback != NULL) { const char *key_id = binary_to_hex(digest_pos, digest_len); int ret = stream->key_callback(key_id, &(stream->priv_key), &error, stream->key_context); if (ret < 0) { io_stream_set_error(&stream->istream.iostream, "Private key not available: %s", error); return -1; } if (ret == 0) { io_stream_set_error(&stream->istream.iostream, "Private key not available"); return -1; } dcrypt_key_ref_private(stream->priv_key); } else { io_stream_set_error(&stream->istream.iostream, "Private key not available"); return -1; } } buffer_t *check = buffer_create_dynamic(pool_datastack_create(), 32); if (!dcrypt_key_id_private_old(stream->priv_key, check, &error)) { io_stream_set_error(&stream->istream.iostream, "Cannot get public key hash: %s", error); return -1; } else { if (memcmp(digest_pos, check->data, I_MIN(digest_len,check->used)) != 0) { io_stream_set_error(&stream->istream.iostream, "Private key not available"); return -1; } } /* derive shared secret */ if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, &ephemeral_key, secret, &error)) { io_stream_set_error(&stream->istream.iostream, "Cannot perform ECDH: %s", error); return -1; } /* run it thru SHA256 once */ const struct hash_method *hash = &hash_method_sha256; unsigned char hctx[hash->context_size]; unsigned char hres[hash->digest_size]; hash->init(hctx); hash->loop(hctx, secret->data, secret->used); hash->result(hctx, hres); safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); /* NB! The old code was broken and used this kind of IV - it is not correct, but we need to stay compatible with old data */ /* use it to decrypt the actual encryption key */ struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, &dctx, &error)) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } ec = 0; dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*)"\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); dcrypt_ctx_sym_set_key(dctx, hres, hash->digest_size); if (!dcrypt_ctx_sym_init(dctx, &error) || !dcrypt_ctx_sym_update(dctx, key_ct_pos, key_ct_len, key, &error) || !dcrypt_ctx_sym_final(dctx, key, &error)) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); ec = -1; } dcrypt_ctx_sym_destroy(&dctx); if (ec != 0) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } /* see if we got the correct key */ hash->init(hctx); hash->loop(hctx, key->data, key->used); hash->result(hctx, hres); if (key_digest_size != sizeof(hres)) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: invalid digest length"); return -1; } if (memcmp(hres, key_digest_pos, sizeof(hres)) != 0) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: decrypted key is invalid"); return -1; } /* prime context with key */ if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, &(stream->ctx_sym), &error)) { io_stream_set_error(&stream->istream.iostream, "Decryption context create error: %s", error); return -1; } /* Again, old code used this IV, so we have to use it too */ dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*)"\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); dcrypt_ctx_sym_set_key(stream->ctx_sym, key->data, key->used); safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { io_stream_set_error(&stream->istream.iostream, "Decryption init error: %s", error); return -1; } stream->use_mac = FALSE; stream->initialized = TRUE; /* now we are ready to decrypt stream */ return sizeof(IOSTREAM_CRYPT_MAGIC) + 1 + 2 + keydata_len; }
static void plain_group_list(void *pool, void *additional, char ***groupname, unsigned *groupname_size) { FILE *fp; char line[512]; ssize_t ll; char *p, *sp; unsigned i; size_t hval; struct htable_iter iter; char *tgroup[MAX_GROUPS]; unsigned tgroup_size; struct htable hash; struct plain_cfg_st *config = additional; htable_init(&hash, rehash, NULL); pool = talloc_init("plain"); fp = fopen(config->passwd, "r"); if (fp == NULL) { syslog(LOG_AUTH, "error in plain authentication; cannot open: %s", (char*)config->passwd); return; } line[sizeof(line)-1] = 0; while ((p=fgets(line, sizeof(line)-1, fp)) != NULL) { ll = strlen(p); if (ll <= 4) continue; if (line[ll - 1] == '\n') { ll--; line[ll] = 0; } if (line[ll - 1] == '\r') { ll--; line[ll] = 0; } #ifdef HAVE_STRSEP sp = line; p = strsep(&sp, ":"); if (p != NULL) { p = strsep(&sp, ":"); #else p = strtok_r(line, ":", &sp); if (p != NULL) { p = strtok_r(NULL, ":", &sp); #endif if (p != NULL) { break_group_list(pool, p, tgroup, &tgroup_size); for (i=0; i<tgroup_size; i++) { hval = rehash(tgroup[i], NULL); if (htable_get(&hash, hval, str_cmp, tgroup[i]) == NULL) { if (strlen(tgroup[i]) > 1) htable_add(&hash, hval, tgroup[i]); } } } } } *groupname_size = 0; *groupname = talloc_size(pool, sizeof(char*)*MAX_GROUPS); if (*groupname == NULL) { goto exit; } p = htable_first(&hash, &iter); while (p != NULL && (*groupname_size) < MAX_GROUPS) { (*groupname)[(*groupname_size)] = talloc_strdup(*groupname, p); p = htable_next(&hash, &iter); (*groupname_size)++; } /* always succeed */ exit: htable_clear(&hash); safe_memset(line, 0, sizeof(line)); fclose(fp); return; } const struct auth_mod_st plain_auth_funcs = { .type = AUTH_TYPE_PLAIN | AUTH_TYPE_USERNAME_PASS, .allows_retries = 1, .global_init = plain_global_init, .auth_init = plain_auth_init, .auth_deinit = plain_auth_deinit, .auth_msg = plain_auth_msg, .auth_pass = plain_auth_pass, .auth_user = plain_auth_user, .auth_group = plain_auth_group, .group_list = plain_group_list };
/* Returns 0 if the user is successfully authenticated, and sets the appropriate group name. */ static int read_auth_pass(struct plain_ctx_st *pctx) { FILE *fp; char line[512]; ssize_t ll; char *p, *sp; int ret; if (password_file == NULL) { /* no password file is set */ return 0; } pctx->failed = 1; fp = fopen(password_file, "r"); if (fp == NULL) { syslog(LOG_AUTH, "error in plain authentication; cannot open: %s", password_file); return -1; } line[sizeof(line)-1] = 0; while ((p=fgets(line, sizeof(line)-1, fp)) != NULL) { ll = strlen(p); if (ll <= 4) continue; if (line[ll - 1] == '\n') { ll--; line[ll] = 0; } if (line[ll - 1] == '\r') { ll--; line[ll] = 0; } #ifdef HAVE_STRSEP sp = line; p = strsep(&sp, ":"); if (p != NULL && strcmp(pctx->username, p) == 0) { p = strsep(&sp, ":"); if (p != NULL) { break_group_list(pctx, p, pctx->groupnames, &pctx->groupnames_size); p = strsep(&sp, ":"); if (p != NULL) { strlcpy(pctx->cpass, p, sizeof(pctx->cpass)); pctx->failed = 0; ret = 0; goto exit; } } } #else p = strtok_r(line, ":", &sp); if (p != NULL && strcmp(pctx->username, p) == 0) { p = strtok_r(NULL, ":", &sp); if (p != NULL) { break_group_list(pctx, p, pctx->groupnames, &pctx->groupnames_size); p = strtok_r(NULL, ":", &sp); if (p != NULL) { strlcpy(pctx->cpass, p, sizeof(pctx->cpass)); pctx->failed = 0; ret = 0; goto exit; } } } #endif } /* always succeed */ ret = 0; exit: safe_memset(line, 0, sizeof(line)); fclose(fp); return ret; }
static int pam_userpass_conv(int num_msg, pam_const struct pam_message **msg, struct pam_response **resp_r, void *appdata_ptr) { /* @UNSAFE */ struct pam_conv_context *ctx = appdata_ptr; struct passdb_module *_passdb = ctx->request->passdb->passdb; struct pam_passdb_module *passdb = (struct pam_passdb_module *)_passdb; struct pam_response *resp; char *string; int i; *resp_r = NULL; resp = calloc(num_msg, sizeof(struct pam_response)); if (resp == NULL) i_fatal_status(FATAL_OUTOFMEM, "Out of memory"); for (i = 0; i < num_msg; i++) { auth_request_log_debug(ctx->request, "pam", "#%d/%d style=%d msg=%s", i+1, num_msg, msg[i]->msg_style, msg[i]->msg != NULL ? msg[i]->msg : ""); switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: /* Assume we're asking for user. We might not ever get here because PAM already knows the user. */ string = strdup(ctx->request->user); if (string == NULL) i_fatal_status(FATAL_OUTOFMEM, "Out of memory"); break; case PAM_PROMPT_ECHO_OFF: /* Assume we're asking for password */ if (passdb->failure_show_msg) ctx->failure_msg = t_strdup(msg[i]->msg); string = strdup(ctx->pass); if (string == NULL) i_fatal_status(FATAL_OUTOFMEM, "Out of memory"); break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: string = NULL; break; default: while (--i >= 0) { if (resp[i].resp != NULL) { safe_memset(resp[i].resp, 0, strlen(resp[i].resp)); free(resp[i].resp); } } free(resp); return PAM_CONV_ERR; } resp[i].resp_retcode = PAM_SUCCESS; resp[i].resp = string; } *resp_r = resp; return PAM_SUCCESS; }
static ssize_t i_stream_decrypt_read(struct istream_private *stream) { struct decrypt_istream *dstream = (struct decrypt_istream *)stream; const unsigned char *data; size_t size, decrypt_size; const char *error = NULL; int ret; bool check_mac = FALSE; /* not if it's broken */ if (stream->istream.stream_errno != 0) return -1; for (;;) { /* remove skipped data from buffer */ if (stream->skip > 0) { i_assert(stream->skip <= dstream->buf->used); buffer_delete(dstream->buf, 0, stream->skip); stream->pos -= stream->skip; stream->skip = 0; } stream->buffer = dstream->buf->data; i_assert(stream->pos <= dstream->buf->used); if (stream->pos >= dstream->istream.max_buffer_size) { /* stream buffer still at maximum */ return -2; } /* if something is already decrypted, return as much of it as we can */ if (dstream->initialized && dstream->buf->used > 0) { size_t new_pos, bytes; /* only return up to max_buffer_size bytes, even when buffer actually has more, as not to confuse the caller */ if (dstream->buf->used <= dstream->istream.max_buffer_size) { new_pos = dstream->buf->used; if (dstream->finalized) stream->istream.eof = TRUE; } else { new_pos = dstream->istream.max_buffer_size; } bytes = new_pos - stream->pos; stream->pos = new_pos; return (ssize_t)bytes; } if (dstream->finalized) { /* all data decrypted */ stream->istream.eof = TRUE; return -1; } /* need to read more input */ ret = i_stream_read(stream->parent); if (ret == 0 || ret == -2) return ret; data = i_stream_get_data(stream->parent, &size); if (ret == -1 && (size == 0 || stream->parent->stream_errno != 0)) { stream->istream.stream_errno = stream->parent->stream_errno; /* file was empty */ if (!dstream->initialized && size == 0 && stream->parent->eof) { stream->istream.eof = TRUE; return -1; } if (stream->istream.stream_errno != 0) return -1; if (!dstream->initialized) { io_stream_set_error(&stream->iostream, "Decryption error: %s", "Input truncated in decryption header"); stream->istream.stream_errno = EINVAL; return -1; } /* final block */ if (dcrypt_ctx_sym_final(dstream->ctx_sym, dstream->buf, &error)) { dstream->finalized = TRUE; continue; } io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (!dstream->initialized) { ssize_t hret; if ((hret=i_stream_decrypt_read_header(dstream, data, size)) <= 0) { if (hret < 0) { if (stream->istream.stream_errno == 0) /* assume temporary failure */ stream->istream.stream_errno = EIO; return -1; } if (hret == 0 && stream->parent->eof) { /* not encrypted by us */ stream->istream.stream_errno = EINVAL; io_stream_set_error(&stream->iostream, "Truncated header"); return -1; } } if (hret == 0) { /* see if we can get more data */ continue; } else { /* clean up buffer */ safe_memset(buffer_get_modifiable_data(dstream->buf, 0), 0, dstream->buf->used); buffer_set_used_size(dstream->buf, 0); i_stream_skip(stream->parent, hret); } data = i_stream_get_data(stream->parent, &size); } decrypt_size = size; if (dstream->use_mac) { if (stream->parent->eof) { if (decrypt_size < dstream->ftr) { io_stream_set_error(&stream->iostream, "Decryption error: footer is longer than data"); stream->istream.stream_errno = EINVAL; return -1; } check_mac = TRUE; } else { /* ignore footer's length of data until we reach EOF */ size -= dstream->ftr; } decrypt_size -= dstream->ftr; if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { if (!dcrypt_ctx_hmac_update(dstream->ctx_mac, data, decrypt_size, &error)) { io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } } } if (check_mac) { if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { unsigned char dgst[dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)]; buffer_t db; buffer_create_from_data(&db, dgst, sizeof(dgst)); if (!dcrypt_ctx_hmac_final(dstream->ctx_mac, &db, &error)) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (memcmp(dgst, data + decrypt_size, dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)) != 0) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: mismatch"); stream->istream.stream_errno = EINVAL; return -1; } } else if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_tag(dstream->ctx_sym, data + decrypt_size, dstream->ftr); } } if (!dcrypt_ctx_sym_update(dstream->ctx_sym, data, decrypt_size, dstream->buf, &error)) { io_stream_set_error(&stream->iostream, "Decryption error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } i_stream_skip(stream->parent, size); } }
int imap_urlauth_fetch_parsed(struct imap_urlauth_context *uctx, struct imap_url *url, struct imap_msgpart_url **mpurl_r, enum mail_error *error_code_r, const char **error_r) { struct mail_user *user = uctx->user; struct imap_msgpart_url *mpurl; struct mailbox *box; const char *error; unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN]; int ret; *mpurl_r = NULL; *error_r = NULL; *error_code_r = MAIL_ERROR_NONE; /* check urlauth mechanism, access, userid and authority */ if (!imap_urlauth_check(uctx, url, FALSE, error_r)) { *error_code_r = MAIL_ERROR_PARAMS; return 0; } /* validate target user */ if (user->anonymous || strcmp(url->userid, user->username) != 0) { *error_r = t_strdup_printf("Not permitted to fetch URLAUTH for user %s", url->userid); *error_code_r = MAIL_ERROR_PARAMS; return 0; } /* validate mailbox */ if ((ret = imap_msgpart_url_create(user, url, &mpurl, &error)) < 0) { *error_r = t_strdup_printf("Invalid URLAUTH: %s", error); *error_code_r = MAIL_ERROR_PARAMS; return ret; } if ((ret = imap_msgpart_url_open_mailbox(mpurl, &box, error_code_r, &error)) < 0) { *error_r = "Internal server error"; imap_msgpart_url_free(&mpurl); return -1; } if (ret == 0) { /* RFC says: `If the mailbox cannot be identified, an authorization token is calculated on the rump URL, using random "plausible" keys (selected by the server) as needed, before returning a validation failure. This prevents timing attacks aimed at identifying mailbox names.' */ random_fill(mailbox_key, sizeof(mailbox_key)); (void)imap_urlauth_internal_verify(url->uauth_rumpurl, mailbox_key, url->uauth_token, url->uauth_token_size); *error_r = t_strdup_printf("Invalid URLAUTH: %s", error); imap_msgpart_url_free(&mpurl); return 0; } /* obtain mailbox key */ ret = imap_urlauth_backend_get_mailbox_key(box, FALSE, mailbox_key, error_r, error_code_r); if (ret < 0) { imap_msgpart_url_free(&mpurl); return -1; } if (ret == 0 || !imap_urlauth_internal_verify(url->uauth_rumpurl, mailbox_key, url->uauth_token, url->uauth_token_size)) { *error_r = "URLAUTH verification failed"; *error_code_r = MAIL_ERROR_PERM; imap_msgpart_url_free(&mpurl); ret = 0; } else { ret = 1; } safe_memset(mailbox_key, 0, sizeof(mailbox_key)); *mpurl_r = mpurl; return ret; }
static ssize_t i_stream_decrypt_key(struct decrypt_istream *stream, const char *malg, unsigned int rounds, const unsigned char *data, const unsigned char *end, buffer_t *key, size_t key_len) { const char *error; enum dcrypt_key_type ktype; int keys; bool have_key = FALSE; unsigned char dgst[32]; uint32_t val; buffer_t buf; if (data == end) return 0; keys = *data++; /* if we have a key, prefab the digest */ if (stream->key_callback == NULL) { if (stream->priv_key == NULL) { io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available"); return -1; } buffer_create_from_data(&buf, dgst, sizeof(dgst)); dcrypt_key_id_private(stream->priv_key, "sha256", &buf, NULL); } /* for each key */ for(;keys>0;keys--) { if ((size_t)(end-data) < 1 + (ssize_t)sizeof(dgst)) return 0; ktype = *data++; if (stream->key_callback != NULL) { const char *hexdgst = binary_to_hex(data, sizeof(dgst)); /* digest length */ /* hope you going to give us right key.. */ int ret = stream->key_callback(hexdgst, &(stream->priv_key), &error, stream->key_context); if (ret < 0) { io_stream_set_error(&stream->istream.iostream, "Private key not available: %s", error); return -1; } if (ret > 0) { dcrypt_key_ref_private(stream->priv_key); have_key = TRUE; break; } } else { /* see if key matches to the one we have */ if (memcmp(dgst, data, sizeof(dgst)) == 0) { have_key = TRUE; break; } } data += sizeof(dgst); /* wasn't correct key, skip over some data */ if (!get_msb32(&data, end, &val) || !get_msb32(&data, end, &val)) return 0; } /* didn't find matching key */ if (!have_key) { io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available"); return -1; } data += sizeof(dgst); const unsigned char *ephemeral_key; uint32_t ep_key_len; const unsigned char *encrypted_key; uint32_t eklen; const unsigned char *ekhash; uint32_t ekhash_len; /* read ephemeral key (can be missing for RSA) */ if (!get_msb32(&data, end, &ep_key_len) || (size_t)(end-data) < ep_key_len) return 0; ephemeral_key = data; data += ep_key_len; /* read encrypted key */ if (!get_msb32(&data, end, &eklen) || (size_t)(end-data) < eklen) return 0; encrypted_key = data; data += eklen; /* read key data hash */ if (!get_msb32(&data, end, &ekhash_len) || (size_t)(end-data) < ekhash_len) return 0; ekhash = data; data += ekhash_len; /* decrypt the seed */ if (ktype == DCRYPT_KEY_RSA) { if (!dcrypt_rsa_decrypt(stream->priv_key, encrypted_key, eklen, key, &error)) { io_stream_set_error(&stream->istream.iostream, "key decryption error: %s", error); return -1; } } else if (ktype == DCRYPT_KEY_EC) { /* perform ECDHE */ buffer_t *temp_key = buffer_create_dynamic(pool_datastack_create(), 256); buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 256); buffer_t peer_key; buffer_create_from_const_data(&peer_key, ephemeral_key, ep_key_len); if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, &peer_key, secret, &error)) { io_stream_set_error(&stream->istream.iostream, "Key decryption error: corrupted header"); return -1; } /* use shared secret and peer key to generate decryption key, AES-256-CBC has 32 byte key and 16 byte IV */ if (!dcrypt_pbkdf2(secret->data, secret->used, peer_key.data, peer_key.used, malg, rounds, temp_key, 32+16, &error)) { safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); if (temp_key->used != 32+16) { safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid temporary key"); return -1; } struct dcrypt_context_symmetric *dctx; if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_DECRYPT, &dctx, &error)) { safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error); return -1; } const unsigned char *ptr = temp_key->data; /* we use ephemeral_key for IV */ dcrypt_ctx_sym_set_key(dctx, ptr, 32); dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16); safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used); int ec = 0; if (!dcrypt_ctx_sym_init(dctx, &error) || !dcrypt_ctx_sym_update(dctx, encrypted_key, eklen, key, &error) || !dcrypt_ctx_sym_final(dctx, key, &error)) { io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: %s", error); ec = -1; } if (key->used != key_len) { io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid key length"); ec = -1; } dcrypt_ctx_sym_destroy(&dctx); if (ec != 0) return ec; } else { io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported key type 0x%02x", ktype); return -1; } /* make sure we were able to decrypt the encrypted key correctly */ const struct hash_method *hash = hash_method_lookup(t_str_lcase(malg)); if (hash == NULL) { safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported hash algorithm: %s", malg); return -1; } unsigned char hctx[hash->context_size]; unsigned char hres[hash->digest_size]; hash->init(hctx); hash->loop(hctx, key->data, key->used); hash->result(hctx, hres); for(int i = 1; i < 2049; i++) { uint32_t i_msb = htonl(i); hash->init(hctx); hash->loop(hctx, hres, sizeof(hres)); hash->loop(hctx, &i_msb, sizeof(i_msb)); hash->result(hctx, hres); } /* do the comparison */ if (memcmp(ekhash, hres, I_MIN(ekhash_len, sizeof(hres))) != 0) { safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); io_stream_set_error(&stream->istream.iostream, "Decryption error: corrupted header ekhash"); return -1; } return 1; }
void auth_token_deinit(void) { /* not very useful, but we do it anyway */ safe_memset(auth_token_secret, 0, sizeof(auth_token_secret)); }
static int i_stream_decrypt_header_contents(struct decrypt_istream *stream, const unsigned char *data, size_t size) { const unsigned char *end = data + size; bool failed = FALSE; /* read cipher OID */ const char *calg; if (!i_stream_decrypt_der(&data, end, &calg)) return 0; if (calg == NULL || !dcrypt_ctx_sym_create(calg, DCRYPT_MODE_DECRYPT, &(stream->ctx_sym), NULL)) { io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported/invalid cipher: %s", calg); return -1; } /* read MAC oid (MAC is used for PBKDF2 and key data digest, too) */ const char *malg; if (!i_stream_decrypt_der(&data, end, &malg)) return 0; if (malg == NULL || !dcrypt_ctx_hmac_create(malg, &(stream->ctx_mac), NULL)) { io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported/invalid MAC algorithm: %s", malg); return -1; } /* read rounds (for PBKDF2) */ uint32_t rounds; if (!get_msb32(&data, end, &rounds)) return 0; /* read key data length */ uint32_t kdlen; if (!get_msb32(&data, end, &kdlen)) return 0; size_t tagsize; if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { tagsize = IOSTREAM_TAG_SIZE; } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { tagsize = IOSTREAM_TAG_SIZE; } else { tagsize = 0; } /* how much key data we should be getting */ size_t kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize; buffer_t *keydata = buffer_create_dynamic(pool_datastack_create(), kl); /* try to decrypt the keydata with a private key */ int ret; if ((ret = i_stream_decrypt_key(stream, malg, rounds, data, end, keydata, kl)) <= 0) return ret; /* oh, it worked! */ const unsigned char *ptr = keydata->data; if (keydata->used != kl) { /* but returned wrong amount of data */ io_stream_set_error(&stream->istream.iostream, "Key decryption error: Key data length mismatch"); return -1; } /* prime contexts */ dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_key_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym); dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); stream->iv = i_malloc(dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); memcpy(stream->iv, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym); /* based on the chosen MAC, initialize HMAC or AEAD */ if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { const char *error; dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize); if (!dcrypt_ctx_hmac_init(stream->ctx_mac, &error)) { io_stream_set_error(&stream->istream.iostream, "MAC error: %s", error); stream->istream.istream.stream_errno = EINVAL; failed = TRUE; } stream->ftr = dcrypt_ctx_hmac_get_digest_length(stream->ctx_mac); stream->use_mac = TRUE; } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize); stream->ftr = tagsize; stream->use_mac = TRUE; } else { stream->use_mac = FALSE; } /* destroy private key data */ safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used); buffer_set_used_size(keydata, 0); return failed ? -1 : 1; }