コード例 #1
0
ファイル: askpass.c プロジェクト: Raffprta/core
void askpass(const char *prompt, char *buf, size_t buf_size)
{
	buffer_t str;

	buffer_create_from_data(&str, buf, buf_size);
	askpass_str(prompt, &str);
	buffer_append_c(&str, '\0');
}
コード例 #2
0
static bool verify_b(const char *str, unsigned int i, bool starts_with_a)
{
	unsigned int line_start = i, start, j, char_count = 0;
	char bufdata[1000];
	buffer_t buf;

	buffer_create_from_data(&buf, bufdata, sizeof(bufdata));
	if (strncmp(str+i, "\n\t", 2) == 0) {
		i += 2;
		line_start = i - 1;
	}

	for (;;) {
		if (strncmp(str+i, "=?utf-8?b?", 10) != 0)
			return FALSE;
		i += 10;

		start = i;
		for (; str[i] != '?'; i++) {
			if (str[i] == '\0')
				return FALSE;
		}
		buffer_set_used_size(&buf, 0);
		if (base64_decode(str+start, i-start, NULL, &buf) < 0)
			return FALSE;
		i++;

		if (!starts_with_a)
			j = 0;
		else {
			if (bufdata[0] != 'a')
				return FALSE;
			starts_with_a = FALSE;
			j = 1;
		}
		for (; j < buf.used; j += 2) {
			if (bufdata[j] != '\xc3' || bufdata[j+1] != '\xa4')
				return FALSE;
			char_count++;
		}
		if (j != buf.used)
			return FALSE;

		if (str[i++] != '=')
			return FALSE;

		if (i - line_start > 76)
			return FALSE;

		if (str[i] == '\0')
			break;
		if (strncmp(str+i, "\n\t", 2) != 0)
			return FALSE;
		i += 2;
		line_start = i - 1;
	}
	return char_count == 40;
}
コード例 #3
0
ファイル: mech-digest-md5.c プロジェクト: bsmr-dovecot/core
static string_t *get_digest_challenge(struct digest_auth_request *request)
{
	const struct auth_settings *set = request->auth_request.set;
	buffer_t buf;
	string_t *str;
	const char *const *tmp;
	unsigned char nonce[16];
	unsigned char nonce_base64[MAX_BASE64_ENCODED_SIZE(sizeof(nonce))+1];
	int i;
	bool first_qop;

	/*
	   realm="hostname" (multiple allowed)
	   nonce="randomized data, at least 64bit"
	   qop="auth,auth-int,auth-conf"
	   maxbuf=number (with auth-int, auth-conf, defaults to 64k)
	   charset="utf-8" (iso-8859-1 if it doesn't exist)
	   algorithm="md5-sess"
	   cipher="3des,des,rc4-40,rc4,rc4-56" (with auth-conf)
	*/

	/* get 128bit of random data as nonce */
	random_fill(nonce, sizeof(nonce));

	buffer_create_from_data(&buf, nonce_base64, sizeof(nonce_base64));
	base64_encode(nonce, sizeof(nonce), &buf);
	buffer_append_c(&buf, '\0');
	request->nonce = p_strdup(request->pool, buf.data);

	str = t_str_new(256);
	if (*set->realms_arr == NULL) {
		/* If no realms are given, at least Cyrus SASL client defaults
		   to destination host name */
		str_append(str, "realm=\"\",");
	} else {
		for (tmp = set->realms_arr; *tmp != NULL; tmp++)
			str_printfa(str, "realm=\"%s\",", *tmp);
	}

	str_printfa(str, "nonce=\"%s\",", request->nonce);

	str_append(str, "qop=\""); first_qop = TRUE;
	for (i = 0; i < QOP_COUNT; i++) {
		if (request->qop & (1 << i)) {
			if (first_qop)
				first_qop = FALSE;
			else
				str_append_c(str, ',');
			str_append(str, qop_names[i]);
		}
	}
	str_append(str, "\",");

	str_append(str, "charset=\"utf-8\","
		   "algorithm=\"md5-sess\"");
	return str;
}
コード例 #4
0
static int
i_stream_base64_try_encode_line(struct base64_encoder_istream *bstream)
{
	struct istream_private *stream = &bstream->istream;
	const unsigned char *data;
	size_t size, avail, buffer_avail;
	buffer_t buf;

	data = i_stream_get_data(stream->parent, &size);
	if (size == 0 || (size < 3 && !stream->parent->eof))
		return 0;

	if (bstream->cur_line_len == bstream->chars_per_line) {
		/* @UNSAFE: end of line, add newline */
		if (!i_stream_try_alloc(stream, bstream->crlf ? 2 : 1, &avail))
			return -2;

		if (bstream->crlf)
			stream->w_buffer[stream->pos++] = '\r';
		stream->w_buffer[stream->pos++] = '\n';
		bstream->cur_line_len = 0;
	}

	i_stream_try_alloc(stream, (size+2)/3*4, &avail);
	buffer_avail = stream->buffer_size - stream->pos;

	if ((size + 2) / 3 * 4 > buffer_avail) {
		/* can't fit everything to destination buffer.
		   write as much as we can. */
		size = (buffer_avail / 4) * 3;
		if (size == 0)
			return -2;
	} else if (!stream->parent->eof && size % 3 != 0) {
		/* encode 3 chars at a time, so base64_encode() doesn't
		   add '=' characters in the middle of the stream */
		size -= (size % 3);
	}
	i_assert(size != 0);

	if (bstream->cur_line_len + (size+2)/3*4 > bstream->chars_per_line) {
		size = (bstream->chars_per_line - bstream->cur_line_len)/4 * 3;
		i_assert(size != 0);
	}

	buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
				buffer_avail);
	base64_encode(data, size, &buf);
	i_assert(buf.used > 0);

	bstream->cur_line_len += buf.used;
	i_assert(bstream->cur_line_len <= bstream->chars_per_line);
	stream->pos += buf.used;
	i_stream_skip(stream->parent, size);
	return 1;
}
コード例 #5
0
static void
pbkdf_run(const char *plaintext, const char *salt,
	  unsigned int rounds, unsigned char key_r[PBKDF2_KEY_SIZE_SHA1])
{
	memset(key_r, 0, PBKDF2_KEY_SIZE_SHA1);
	buffer_t buf;
	buffer_create_from_data(&buf, key_r, PBKDF2_KEY_SIZE_SHA1);

	pkcs5_pbkdf(PKCS5_PBKDF2, hash_method_lookup("sha1"),
		(const unsigned char *)plaintext, strlen(plaintext),
		(const unsigned char *)salt, strlen(salt),
		rounds, PBKDF2_KEY_SIZE_SHA1, &buf);
}
コード例 #6
0
static bool
master_input_request(struct auth_master_connection *conn, const char *args)
{
	struct auth_client_connection *client_conn;
	const char *const *list, *const *params;
	unsigned int id, client_pid, client_id;
	uint8_t cookie[MASTER_AUTH_COOKIE_SIZE];
	buffer_t buf;

	/* <id> <client-pid> <client-id> <cookie> [<parameters>] */
	list = t_strsplit_tab(args);
	if (str_array_length(list) < 4 ||
	    str_to_uint(list[0], &id) < 0 ||
	    str_to_uint(list[1], &client_pid) < 0 ||
	    str_to_uint(list[2], &client_id) < 0) {
		i_error("BUG: Master sent broken REQUEST");
		return FALSE;
	}

	buffer_create_from_data(&buf, cookie, sizeof(cookie));
	if (hex_to_binary(list[3], &buf) < 0) {
		i_error("BUG: Master sent broken REQUEST cookie");
		return FALSE;
	}
	params = list + 4;

	client_conn = auth_client_connection_lookup(client_pid);
	if (client_conn == NULL) {
		i_error("Master requested auth for nonexistent client %u",
			client_pid);
		o_stream_nsend_str(conn->output,
				   t_strdup_printf("FAIL\t%u\n", id));
	} else if (memcmp(client_conn->cookie, cookie, sizeof(cookie)) != 0) {
		i_error("Master requested auth for client %u with invalid cookie",
			client_pid);
		o_stream_nsend_str(conn->output,
				   t_strdup_printf("FAIL\t%u\n", id));
	} else if (!auth_request_handler_master_request(
			client_conn->request_handler, conn, id, client_id, params)) {
		i_error("Master requested auth for non-login client %u",
			client_pid);
		o_stream_nsend_str(conn->output,
				   t_strdup_printf("FAIL\t%u\n", id));
	}
	return TRUE;
}
コード例 #7
0
ファイル: mail.c プロジェクト: zatsepin/core
void mail_generate_guid_128_hash(const char *guid, guid_128_t guid_128_r)
{
	unsigned char sha1_sum[SHA1_RESULTLEN];
	buffer_t buf;

	if (guid_128_from_string(guid, guid_128_r) < 0) {
		/* not 128bit hex. use a hash of it instead. */
		buffer_create_from_data(&buf, guid_128_r, GUID_128_SIZE);
		buffer_set_used_size(&buf, 0);
		sha1_get_digest(guid, strlen(guid), sha1_sum);
#if SHA1_RESULTLEN < GUID_128_SIZE
#  error not possible
#endif
		buffer_append(&buf,
			      sha1_sum + SHA1_RESULTLEN - GUID_128_SIZE,
			      GUID_128_SIZE);
	}
}
コード例 #8
0
static int
i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof)
{
    struct istream_private *stream = &bstream->istream;
    const unsigned char *data;
    size_t size, avail, buffer_avail, pos;
    buffer_t buf;
    int ret;

    data = i_stream_get_data(stream->parent, &size);
    if (size == 0)
        return 0;

    /* normally the decoded quoted-printable content can't be larger than
       the encoded content, but because we always use CRLFs, it may use
       twice as much space by only converting LFs to CRLFs. */
    i_stream_try_alloc(stream, size, &avail);
    buffer_avail = stream->buffer_size - stream->pos;

    if (size > buffer_avail/2) {
        /* can't fit everything to destination buffer.
           write as much as we can. */
        size = buffer_avail/2;
        if (size == 0)
            return -2;
    }

    buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
                            buffer_avail);
    ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) :
          quoted_printable_decode_final(data, size, &pos, &buf);
    if (ret < 0) {
        io_stream_set_error(&stream->iostream,
                            "Invalid quoted-printable data: 0x%s",
                            binary_to_hex(data+pos, I_MAX(size-pos, 8)));
        stream->istream.stream_errno = EINVAL;
        return -1;
    }

    stream->pos += buf.used;
    i_stream_skip(stream->parent, pos);
    return pos > 0 ? 1 : 0;
}
コード例 #9
0
ファイル: istream-qp-decoder.c プロジェクト: bdraco/dovecot
static int
i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof)
{
	struct istream_private *stream = &bstream->istream;
	const unsigned char *data;
	size_t size, avail, buffer_avail, pos;
	buffer_t buf;
	int ret;

	data = i_stream_get_data(stream->parent, &size);
	if (size == 0)
		return 0;

	/* the decoded quoted-printable content can never be larger than the
	   encoded content. at worst they are equal. */
	i_stream_try_alloc(stream, size, &avail);
	buffer_avail = stream->buffer_size - stream->pos;

	if (size > buffer_avail) {
		/* can't fit everything to destination buffer.
		   write as much as we can. */
		size = buffer_avail;
		if (size == 0)
			return -2;
	}

	buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
				buffer_avail);
	ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) :
		quoted_printable_decode_final(data, size, &pos, &buf);
	if (ret < 0) {
		stream->istream.stream_errno = EINVAL;
		return -1;
	}

	stream->pos += buf.used;
	i_stream_skip(stream->parent, pos);
	return pos > 0 ? 1 : 0;
}
コード例 #10
0
static void keywords_ext_register(struct mail_index_sync_map_ctx *ctx,
				  uint32_t ext_map_idx, uint32_t reset_id,
				  uint32_t hdr_size, uint32_t keywords_count)
{
	buffer_t ext_intro_buf;
	struct mail_transaction_ext_intro *u;
	unsigned char ext_intro_data[sizeof(*u) +
				     sizeof(MAIL_INDEX_EXT_KEYWORDS)-1];

	i_assert(keywords_count > 0);

	buffer_create_from_data(&ext_intro_buf, ext_intro_data,
				sizeof(ext_intro_data));

	u = buffer_append_space_unsafe(&ext_intro_buf, sizeof(*u));
	u->ext_id = ext_map_idx;
	u->reset_id = reset_id;
	u->hdr_size = hdr_size;
	u->record_size = (keywords_count + CHAR_BIT - 1) / CHAR_BIT;
	if ((u->record_size % 4) != 0) {
		/* since we aren't properly aligned anyway,
		   reserve one extra byte for future */
		u->record_size++;
	}
	u->record_align = 1;

	if (ext_map_idx == (uint32_t)-1) {
		u->name_size = strlen(MAIL_INDEX_EXT_KEYWORDS);
		buffer_append(&ext_intro_buf, MAIL_INDEX_EXT_KEYWORDS,
			      u->name_size);
	}

	ctx->internal_update = TRUE;
	if (mail_index_sync_ext_intro(ctx, u) < 0)
		i_panic("Keyword extension growing failed");
	ctx->internal_update = FALSE;
}
コード例 #11
0
static int
i_stream_base64_try_decode_block(struct base64_decoder_istream *bstream)
{
	struct istream_private *stream = &bstream->istream;
	const unsigned char *data;
	size_t size, avail, buffer_avail, pos;
	buffer_t buf;

	data = i_stream_get_data(stream->parent, &size);
	if (size == 0)
		return 0;

	i_stream_try_alloc(stream, (size+3)/4*3, &avail);
	buffer_avail = stream->buffer_size - stream->pos;

	if ((size + 3) / 4 * 3 > buffer_avail) {
		/* can't fit everything to destination buffer.
		   write as much as we can. */
		size = (buffer_avail / 3) * 4;
		if (size == 0)
			return -2;
	}

	buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
				buffer_avail);
	if (base64_decode(data, size, &pos, &buf) < 0) {
		io_stream_set_error(&stream->iostream,
			"Invalid base64 data: 0x%s",
			binary_to_hex(data+pos, I_MIN(size-pos, 8)));
		stream->istream.stream_errno = EINVAL;
		return -1;
	}

	stream->pos += buf.used;
	i_stream_skip(stream->parent, pos);
	return pos > 0 ? 1 : 0;
}
コード例 #12
0
ファイル: istream-decrypt.c プロジェクト: nikwrt/dovecot-core
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);
	}
}
コード例 #13
0
ファイル: istream-decrypt.c プロジェクト: nikwrt/dovecot-core
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;
}
コード例 #14
0
ファイル: ostream-encrypt.c プロジェクト: manuelm/dovecot
static
int o_stream_encrypt_keydata_create_v1(struct encrypt_ostream *stream)
{
	buffer_t *encrypted_key, *ephemeral_key, *secret, *res, buf;
	const char *error = NULL;
	const struct hash_method *hash = &hash_method_sha256;

	/* various temporary buffers */
	unsigned char seed[IO_STREAM_ENCRYPT_SEED_SIZE];
	unsigned char pkhash[hash->digest_size];
	unsigned char ekhash[hash->digest_size];
	unsigned char hres[hash->digest_size];

	unsigned char hctx[hash->context_size];

	/* hash the public key first */
	buffer_create_from_data(&buf, pkhash, sizeof(pkhash));
	if (!dcrypt_key_id_public_old(stream->pub, &buf, &error)) {
		io_stream_set_error(&stream->ostream.iostream, "Key hash failed: %s", error);
		return -1;
	}

	/* hash the key base */
	hash->init(hctx);
	hash->loop(hctx, seed, sizeof(seed));
	hash->result(hctx, ekhash);

	ephemeral_key = buffer_create_dynamic(pool_datastack_create(), 256);
	encrypted_key = buffer_create_dynamic(pool_datastack_create(), 256);
	secret = buffer_create_dynamic(pool_datastack_create(), 256);

	if (!dcrypt_ecdh_derive_secret_peer(stream->pub, ephemeral_key, secret, &error)) {
		io_stream_set_error(&stream->ostream.iostream, "Cannot perform ECDH: %s", error);
		return -1;
	}

	/* hash the secret data */
	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);

	/* use it to encrypt the actual encryption key */
	struct dcrypt_context_symmetric *dctx;
	if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_ENCRYPT, &dctx, &error)) {
		io_stream_set_error(&stream->ostream.iostream, "Key encryption error: %s", error);
		return -1;
	}

	random_fill(seed, sizeof(seed));
	hash->init(hctx);
	hash->loop(hctx, seed, sizeof(seed));
	hash->result(hctx, ekhash);

	int ec = 0;

	/* 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 */
	dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
	dcrypt_ctx_sym_set_key(dctx, hres, sizeof(hres));

	if (!dcrypt_ctx_sym_init(dctx, &error) ||
	    !dcrypt_ctx_sym_update(dctx, seed, sizeof(seed), encrypted_key, &error) ||
	    !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) {
		ec = -1;
	}
	dcrypt_ctx_sym_destroy(&dctx);

	if (ec != 0) {
		safe_memset(seed, 0, sizeof(seed));
		io_stream_set_error(&stream->ostream.iostream, "Key encryption error: %s", error);
		return -1;
	}

	/* same as above */
	dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
	dcrypt_ctx_sym_set_key(stream->ctx_sym, seed, sizeof(seed));
	safe_memset(seed, 0, sizeof(seed));

	if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) {
		io_stream_set_error(&stream->ostream.iostream, "Encryption init error: %s", error);
		return -1;
	}

	res = buffer_create_dynamic(default_pool, 256);

	/* ephemeral key */
	unsigned short s;
	s = htons(ephemeral_key->used);
	buffer_append(res, &s, 2);
	buffer_append(res, ephemeral_key->data, ephemeral_key->used);
	/* public key hash */
	s = htons(sizeof(pkhash));
	buffer_append(res, &s, 2);
	buffer_append(res, pkhash, sizeof(pkhash));
	/* encrypted key hash */
	s = htons(sizeof(ekhash));
	buffer_append(res, &s, 2);
	buffer_append(res, ekhash, sizeof(ekhash));
	/* encrypted key */
	s = htons(encrypted_key->used);
	buffer_append(res, &s, 2);
	buffer_append(res, encrypted_key->data, encrypted_key->used);

	stream->key_data_len = res->used;
	stream->key_data = buffer_free_without_data(&res);

	return 0;
}
コード例 #15
0
static void
mail_transaction_log_append_ext_intros(struct mail_index_export_context *ctx)
{
	struct mail_index_transaction *t = ctx->trans;
        const struct mail_transaction_ext_intro *resize;
	const struct mail_index_transaction_ext_hdr_update *hdrs;
	struct mail_transaction_ext_reset ext_reset;
	unsigned int resize_count, ext_count = 0;
	unsigned int hdrs_count, reset_id_count, reset_count;
	uint32_t ext_id, reset_id;
	const struct mail_transaction_ext_reset *reset;
	const uint32_t *reset_ids;
	buffer_t reset_buf;

	if (!array_is_created(&t->ext_resizes)) {
		resize = NULL;
		resize_count = 0;
	} else {
		resize = array_get(&t->ext_resizes, &resize_count);
		if (ext_count < resize_count)
			ext_count = resize_count;
	}

	if (!array_is_created(&t->ext_reset_ids)) {
		reset_ids = NULL;
		reset_id_count = 0;
	} else {
		reset_ids = array_get(&t->ext_reset_ids, &reset_id_count);
	}

	if (!array_is_created(&t->ext_resets)) {
		reset = NULL;
		reset_count = 0;
	} else {
		reset = array_get(&t->ext_resets, &reset_count);
		if (ext_count < reset_count)
			ext_count = reset_count;
	}

	if (!array_is_created(&t->ext_hdr_updates)) {
		hdrs = NULL;
		hdrs_count = 0;
	} else {
		hdrs = array_get(&t->ext_hdr_updates, &hdrs_count);
		if (ext_count < hdrs_count)
			ext_count = hdrs_count;
	}

	memset(&ext_reset, 0, sizeof(ext_reset));
	buffer_create_from_data(&reset_buf, &ext_reset, sizeof(ext_reset));
	buffer_set_used_size(&reset_buf, sizeof(ext_reset));

	for (ext_id = 0; ext_id < ext_count; ext_id++) {
		if (ext_id < reset_count)
			ext_reset = reset[ext_id];
		else
			ext_reset.new_reset_id = 0;
		if ((ext_id < resize_count && resize[ext_id].name_size) ||
		    ext_reset.new_reset_id != 0 ||
		    (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0)) {
			if (ext_reset.new_reset_id != 0) {
				/* we're going to reset this extension
				   immediately after the intro */
				reset_id = 0;
			} else {
				reset_id = ext_id < reset_id_count ?
					reset_ids[ext_id] : 0;
			}
			log_append_ext_intro(ctx, ext_id, reset_id);
		}
		if (ext_reset.new_reset_id != 0) {
			i_assert(ext_id < reset_id_count &&
				 ext_reset.new_reset_id == reset_ids[ext_id]);
			log_append_buffer(ctx, &reset_buf,
					  MAIL_TRANSACTION_EXT_RESET);
		}
		if (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0) {
			T_BEGIN {
				log_append_ext_hdr_update(ctx, &hdrs[ext_id]);
			} T_END;
		}
	}