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; }
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; }
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; }
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; }
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); } }