Example #1
0
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;
}
Example #2
0
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);
}
Example #4
0
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
}
Example #5
0
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);
}
Example #6
0
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));
}
Example #7
0
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));
}
Example #8
0
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);
}
Example #9
0
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);
}
Example #10
0
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;
}
Example #11
0
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);
}
Example #12
0
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;
}
Example #13
0
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);
}
Example #14
0
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);
}
Example #15
0
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));
}
Example #16
0
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));
}
Example #17
0
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));
}
Example #18
0
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;
}
Example #19
0
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);
}
Example #20
0
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();
}
Example #22
0
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;
}
Example #23
0
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
};
Example #24
0
/* 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;
}
Example #25
0
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;
}
Example #26
0
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);
	}
}
Example #27
0
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;
}
Example #28
0
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;
}
Example #29
0
void auth_token_deinit(void)
{
	/* not very useful, but we do it anyway */
	safe_memset(auth_token_secret, 0, sizeof(auth_token_secret));
}
Example #30
0
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;
}