Example #1
0
static int client_run_url(struct client *client)
{
	const unsigned char *data;
	size_t size;
	ssize_t ret = 0;

	while (i_stream_read_data(client->msg_part_input, &data, &size, 0) > 0) {
		if ((ret = o_stream_send(client->output, data, size)) < 0)
			break;
		i_stream_skip(client->msg_part_input, ret);

		if (o_stream_get_buffer_used_size(client->output) >= 4096) {
			if ((ret = o_stream_flush(client->output)) < 0)
				break;
			if (ret == 0)
				return 0;
		}
	}

	if (client->output->closed || ret < 0) {
		imap_msgpart_url_free(&client->url);
		return -1;
	}

	if (client->msg_part_input->eof) {
		o_stream_send(client->output, "\n", 1);
		imap_msgpart_url_free(&client->url);
		return 1;
	}
	return 0;
}
Example #2
0
static int
cmd_append_catenate_url(struct client_command_context *cmd, const char *caturl)
{
	struct cmd_append_context *ctx = cmd->context;
	struct imap_msgpart_url *mpurl;
	const char *error;
	int ret;

	if (ctx->failed)
		return -1;

	ret = imap_msgpart_url_parse(cmd->client->user, cmd->client->mailbox,
				     caturl, &mpurl, &error);
	if (ret < 0) {
		client_send_box_error(cmd, ctx->box);
		return -1;
	}
	if (ret == 0) {
		/* invalid url, abort */
		client_send_tagline(cmd,
			t_strdup_printf("NO [BADURL %s] %s.", caturl, error));
		return -1;
	}
	ret = cmd_append_catenate_mpurl(cmd, caturl, mpurl);
	imap_msgpart_url_free(&mpurl);
	return ret;
}
static void imap_urlauth_fetch_abort_local(struct imap_urlauth_fetch *ufetch)
{
	struct imap_urlauth_fetch_url *url, *url_next;

	if (ufetch->local_url != NULL) {
		ufetch->pending_requests--;
		imap_msgpart_url_free(&ufetch->local_url);
	}

	i_free_and_null(ufetch->pending_reply.url);
	i_free_and_null(ufetch->pending_reply.bodypartstruct);
	i_free_and_null(ufetch->pending_reply.error);
	if (ufetch->pending_reply.input != NULL)
		i_stream_unref(&ufetch->pending_reply.input);

	url = ufetch->local_urls_head;
	for (; url != NULL; url = url_next) {
		url_next = url->next;
		i_free(url->url);
		i_free(url);
		ufetch->pending_requests--;
	}
	ufetch->local_urls_head = ufetch->local_urls_tail = NULL;
}
Example #4
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 #5
0
int imap_urlauth_generate(struct imap_urlauth_context *uctx,
			  const char *mechanism, const char *rumpurl,
			  const char **urlauth_r, const char **error_r)
{
	struct mail_user *user = uctx->user;
	enum imap_url_parse_flags url_flags =
		IMAP_URL_PARSE_ALLOW_URLAUTH;
	struct imap_url *url;
	struct imap_msgpart_url *mpurl = NULL;
	struct mailbox *box;
	const char *error;
	enum mail_error error_code;
	unsigned char mailbox_key[IMAP_URLAUTH_KEY_LEN];
	const unsigned char *token;
	size_t token_len;
	int ret;

	/* validate mechanism */
	if (strcasecmp(mechanism, "INTERNAL") != 0) {
		*error_r = t_strdup_printf("Unsupported URLAUTH mechanism: %s", mechanism);
		return 0;
	}

	/* validate URL */
	if (imap_url_parse(rumpurl, NULL, url_flags, &url, &error) < 0) {
		*error_r = t_strdup_printf("Invalid URL: %s", error);
		return 0;
	}

	if (url->mailbox == NULL || url->uid == 0 || url->search_program != NULL ||
		url->uauth_rumpurl == NULL || url->uauth_mechanism != NULL) {
		*error_r = "Invalid URL: Must be an URLAUTH rump URL";
		return 0;
	}

	/* validate expiry time */
	if (url->uauth_expire != (time_t)-1) {
		time_t now = time(NULL);

		if (now > url->uauth_expire) {
			*error_r = t_strdup_printf("URLAUTH has already expired");
			return 0;
		}
	}

	/* validate user */
	if (url->userid == NULL) {
		*error_r = "Invalid URL: Missing user name";
		return 0;
	}
	if (user->anonymous || strcmp(url->userid, user->username) != 0) {
		*error_r = t_strdup_printf(
			"Not permitted to generate URLAUTH for user %s",
			url->userid);
		return 0;
	}

	/* validate host:port */
	if (!imap_urlauth_check_hostport(uctx, url, error_r))
		return 0;

	/* validate mailbox */
	if ((ret = imap_msgpart_url_create(user, url, &mpurl, &error)) < 0 ||
	    imap_msgpart_url_verify(mpurl, &error) <= 0) {
		*error_r = t_strdup_printf("Invalid URL: %s", error);
		if (mpurl != NULL)
			imap_msgpart_url_free(&mpurl);
		return ret;
	}
	box = imap_msgpart_url_get_mailbox(mpurl);

	/* obtain mailbox key */
	ret = imap_urlauth_backend_get_mailbox_key(box, TRUE, mailbox_key,
						   error_r, &error_code);
	if (ret < 0) {
		imap_msgpart_url_free(&mpurl);
		return ret;
	}

	token = imap_urlauth_internal_generate(rumpurl, mailbox_key, &token_len);
	imap_msgpart_url_free(&mpurl);

	*urlauth_r = imap_url_add_urlauth(rumpurl, mechanism, token, token_len);
	return 1;
}
Example #6
0
static int client_fetch_url(struct client *client, const char *url,
			    enum imap_urlauth_fetch_flags url_flags)
{
	string_t *response;
	const char *bpstruct, *errormsg;
	bool binary_with_nuls;
	int ret;

	i_assert(client->url == NULL);

	client->msg_part_size = 0;
	client->msg_part_input = NULL;

	if (client->debug)
		i_debug("Fetching URLAUTH %s", url);

	/* fetch URL */
	ret = client_fetch_urlpart(client, url, url_flags, &bpstruct,
				   &binary_with_nuls, &errormsg);
	if (ret <= 0) {
		/* fetch failed */
		if (client->url != NULL)
			imap_msgpart_url_free(&client->url);
		/* don't send error details to anonymous users: just to be sure
		   that no information about the target user account is unduly
		   leaked. */
		if (client->access_anonymous || errormsg == NULL)
			client_send_line(client, "NO");
		else {
			client_send_line(client, "NO\terror=%s",
					 str_tabescape(errormsg));
		}
		if (ret < 0) {
			/* fetch failed badly */
			client_abort(client, "Session aborted: Fatal failure while fetching URL");
		}
		return 0;
	}

	response = t_str_new(256);
	str_append(response, "OK");
	if (binary_with_nuls)
		str_append(response, "\thasnuls");
	if (bpstruct != NULL) {
		str_append(response, "\tbpstruct=");
		str_append(response, str_tabescape(bpstruct));
		if (client->debug) {
			i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)",
				bpstruct);
		}
	}

	/* return content */
	o_stream_cork(client->output);
	if (client->msg_part_size == 0 || client->msg_part_input == NULL) {
		/* empty */
		str_append(response, "\t0");
		client_send_line(client, "%s", str_c(response));

		imap_msgpart_url_free(&client->url);
		client->url = NULL;
		if (client->debug)
			i_debug("Fetched URLAUTH yielded empty result");
	} else {

		/* actual content */
		str_printfa(response, "\t%"PRIuUOFF_T, client->msg_part_size);
		client_send_line(client, "%s", str_c(response));

		if (client->debug) {
			i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes "
				"of %smessage data", client->msg_part_size,
				(binary_with_nuls ? "binary " : ""));
		}
		if (client_run_url(client) < 0) {
			client_abort(client,
				"Session aborted: Fatal failure while transfering URL");
			return 0;
		}		
	}

	if (client->url != NULL) {
		/* URL not finished */
		o_stream_set_flush_pending(client->output, TRUE);
		client->waiting_input = TRUE;
	}
	o_stream_uncork(client->output);
	return client->url != NULL ? 0 : 1;
}
static void
imap_urlauth_fetch_local(struct imap_urlauth_fetch *ufetch, const char *url,
			 enum imap_urlauth_fetch_flags url_flags,
			 struct imap_url *imap_url)
{
	struct imap_urlauth_fetch_reply reply;
	struct imap_msgpart_open_result mpresult;
	const char *error, *errormsg = NULL, *bpstruct = NULL;
	bool debug = ufetch->uctx->user->mail_debug, success;
	enum mail_error error_code;
	struct imap_msgpart_url *mpurl = NULL;
	int ret;

	success = TRUE;

	if (debug)
		i_debug("Fetching local URLAUTH %s", url);

	if (url_flags == 0)
		url_flags = IMAP_URLAUTH_FETCH_FLAG_BODY;

	/* fetch URL */
	if (imap_url == NULL) {
		ret = imap_urlauth_fetch(ufetch->uctx, url,
					 &mpurl, &error_code, &error);
	} else {
		ret = imap_urlauth_fetch_parsed(ufetch->uctx, imap_url,
						&mpurl, &error_code, &error);
	}
	if (ret <= 0) {
		if (ret == 0) {
			errormsg = t_strdup_printf("Failed to fetch URLAUTH \"%s\": %s",
						   url, error);
			if (debug)
				i_debug("%s", errormsg);
		}
		success = FALSE;
	}

	/* fetch metadata */
	if (success && (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)
		imap_msgpart_url_set_decode_to_binary(mpurl);
	if (success &&
	    (url_flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) {
		ret = imap_msgpart_url_get_bodypartstructure(mpurl, &bpstruct, &error);
		if (ret <= 0) {
			if (ret == 0) {
				errormsg = t_strdup_printf
					("Failed to read URLAUTH \"%s\": %s",	url, error);
				if (debug)
					i_debug("%s", errormsg);
			}
			success = FALSE;
		}
	}

	/* if requested, read the message part the URL points to */
	memset(&mpresult, 0, sizeof(mpresult));
	if (success && ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 ||
			(url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)) {
		ret = imap_msgpart_url_read_part(mpurl, &mpresult, &error);
		if (ret <= 0) {
			if (ret == 0) {
				errormsg = t_strdup_printf
					("Failed to read URLAUTH \"%s\": %s",	url, error);
				if (debug)
					i_debug("%s", errormsg);
			}
			success = FALSE;
		}
	}

	if (debug && success) {
		if (bpstruct != NULL)
			i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)", bpstruct);
		if (mpresult.size == 0 || mpresult.input == NULL)
			i_debug("Fetched URLAUTH yielded empty result");
		else {
			i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes "
				"of %smessage data", mpresult.size,
				(mpresult.binary_decoded_input_has_nuls ? "binary " : ""));
		}
	}

	ufetch->pending_requests--;

	if (!success && ret < 0) {
		if (mpurl != NULL)
			imap_msgpart_url_free(&mpurl);
		(void)ufetch->callback(NULL, TRUE, ufetch->context);
		imap_urlauth_fetch_fail(ufetch);
		return;
	}

	memset(&reply, 0, sizeof(reply));
	reply.url = url;
	reply.flags = url_flags;
	reply.error = errormsg;
	reply.succeeded = success;

	reply.bodypartstruct = bpstruct;
	reply.binary_has_nuls = mpresult.binary_decoded_input_has_nuls;
	reply.size = mpresult.size;
	reply.input = mpresult.input;

	ret = ufetch->callback(&reply, ufetch->pending_requests == 0,
			       ufetch->context);
	if (ret == 0) {
		ufetch->local_url = mpurl;
		ufetch->waiting_local = TRUE;
		ufetch->pending_requests++;
	} else {

		if (mpurl != NULL)
			imap_msgpart_url_free(&mpurl);
		if (ret < 0)
			imap_urlauth_fetch_fail(ufetch);
	}
}