bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi/* RFC5801 based unescaping */
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomistatic bool oauth2_unescape_username(const char *in, const char **username_r)
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomistatic void oauth2_verify_callback(enum passdb_result result,
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi const char *error,
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi i_assert(result == PASSDB_RESULT_OK || error != NULL);
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* we could get new token after this */
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi auth_request_handler_reply_continue(request, error, strlen(error));
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomixoauth2_verify_callback(enum passdb_result result, struct auth_request *request)
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi "{\"status\":\"401\",\"schemes\":\"bearer\",\"scope\":\"mail\"}";
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomioauthbearer_verify_callback(enum passdb_result result, struct auth_request *request)
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi "{\"status\":\"invalid_token\"}";
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi/* Input syntax:
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi user=Username^Aauth=Bearer token^A^A
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomimech_xoauth2_auth_continue(struct auth_request *request,
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi const unsigned char *data,
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* Specification says that client is sent "invalid token" challenge
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi which the client is supposed to ack with empty response */
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* split the data from ^A */
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* xoauth2 does not require unescaping because the data
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi format does not contain anything to escape */
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi if (!auth_request_set_username(request, username, &error)) {
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi "Invalid continued data");
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* do not fail on unexpected fields */
aee9f241f3c2b2afab2262969e59df5ac8c6ccc2Aki Tuomi auth_request_log_info(request, AUTH_SUBSYS_MECH, "Username or token missing");
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi/* Input syntax for data:
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi gs2flag,a=username,^Afield=...^Afield=...^Aauth=Bearer token^A^A
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomimech_oauthbearer_auth_continue(struct auth_request *request,
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi const unsigned char *data,
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* split the data from ^A */
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* ensure initial field is OK */
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi "Invalid continued data");
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* the first field is specified by RFC5801 as gs2-header */
8054b019bdca47df5676e40e19cbde7e68827640Aki Tuomi for(ptr = t_strsplit_spaces(fields[0], ","); *ptr != NULL; ptr++) {
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi switch(*ptr[0]) {
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi "Client requested non-standard mechanism");
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* channel binding is not supported */
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi "Client requested and used channel-binding");
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* we don't need to use channel-binding */
aee9f241f3c2b2afab2262969e59df5ac8c6ccc2Aki Tuomi "Invalid username escaping");
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi } else if (!auth_request_set_username(request, username, &error)) {
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi "Invalid gs2-header in request");
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi "Invalid continued data");
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* do not fail on unexpected fields */
aee9f241f3c2b2afab2262969e59df5ac8c6ccc2Aki Tuomi auth_request_log_info(request, AUTH_SUBSYS_MECH, "Missing username or token");
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomistatic struct auth_request *mech_oauth2_auth_new(void)
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi pool = pool_alloconly_create(MEMPOOL_GROWING"oauth2_auth_request", 2048);
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi request = p_new(pool, struct oauth2_auth_request, 1);
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi "OAUTHBEARER",
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi /* while this does not transfer plaintext password,
d1c565179ff8b17fe9ff21a3f1cb2f9c4d4f5836Aki Tuomi the token is still considered as password */