Skip to content

Commit

Permalink
Merge pull request #787 from openziti/misc-oidc-flow-fixes
Browse files Browse the repository at this point in the history
Misc OIDC flow fixes
  • Loading branch information
ekoby authored Dec 10, 2024
2 parents d2cc1aa + de4bab8 commit 4365c76
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 65 deletions.
2 changes: 0 additions & 2 deletions inc_internal/zt_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,6 @@ void ziti_re_auth_with_cb(ziti_context ztx, void(*cb)(ziti_api_session *, const

void ziti_queue_work(ziti_context ztx, ztx_work_f w, void *data);

void ziti_set_unauthenticated(ziti_context ztx);

void ziti_force_service_update(ziti_context ztx, const char *service_id);

void ziti_services_refresh(ziti_context ztx, bool now);
Expand Down
10 changes: 9 additions & 1 deletion library/external_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,23 @@ static void internal_link_cb(oidc_client_t *oidc, const char *url, void *ctx) {
ztx->ext_launch_ctx = NULL;
}

static void ext_token_cb(oidc_client_t *oidc, int status, const char *token) {
static void ext_token_cb(oidc_client_t *oidc, int status, const char *data) {
ziti_context ztx = oidc->data;
if (status == ZITI_OK) {
const char *token = data;
ZITI_LOG(DEBUG, "received access token: %.*s...", 20, token);
ztx->auth_method->set_ext_jwt(ztx->auth_method, token);
ztx->auth_method->start(ztx->auth_method, ztx_auth_state_cb, ztx);
} else {
const char *message = data;
ZITI_LOG(WARN, "failed to get external authentication token: %d/%s",
status, ziti_errorstr(status));
char err[256];
snprintf(err, sizeof(err), "failed to get external auth token: %s", message);
ztx_auth_state_cb(ztx, ZitiAuthImpossibleToAuthenticate, &(ziti_error){
.err = status,
.message = message,
});
}
}

Expand Down
4 changes: 3 additions & 1 deletion library/ha_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ static void token_cb(oidc_client_t *oidc, int status, const char *token) {
} else {
char err[128];
snprintf(err, sizeof(err), "failed to auth: %d", status);
auth->cb(auth->cb_ctx, ZitiAuthStateUnauthenticated, err);
auth->cb(auth->cb_ctx, ZitiAuthStateUnauthenticated, &(ziti_error){
.err = status,
.message = err});
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions library/legacy_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ static void mfa_cb(void * UNUSED(empty), const ziti_error *err, void *ctx) {
if (err->http_code == HTTP_STATUS_UNAUTHORIZED) {
free_ziti_api_session_ptr(auth->session);
auth->session = NULL;
auth->cb(auth->ctx, ZitiAuthStateUnauthenticated, (void*)err);
auth->cb(auth->ctx, ZitiAuthStateUnauthenticated, err);
uv_timer_start(&auth->timer, auth_timer_cb, 0, 0);
} else {
ZITI_LOG(ERROR, "failed to submit MFA code: %d/%s", (int)err->err, err->message);
Expand Down Expand Up @@ -179,7 +179,7 @@ static void login_cb(ziti_api_session *session, const ziti_error *err, void *ctx
ZITI_LOG(WARN, "failed to login to ctrl[%s] %s[%d] %s", auth->ctrl->url, err->code, errCode, err->message);

if (errCode == ZITI_AUTHENTICATION_FAILED) {
auth->cb(auth->ctx, ZitiAuthImpossibleToAuthenticate, err->message);
auth->cb(auth->ctx, ZitiAuthImpossibleToAuthenticate, err);
} else {
uint64_t delay = next_backoff(&auth->backoff, 5, 5000);
ZITI_LOG(DEBUG, "failed to login [%d/%s] setting retry in %" PRIu64 "ms",
Expand Down Expand Up @@ -213,7 +213,7 @@ static void refresh_cb(ziti_api_session *session, const ziti_error *err, void *c
switch (err->err) {
case ZITI_AUTHENTICATION_FAILED:
// session expired or was deleted, try to re-auth
auth->cb(auth->ctx, ZitiAuthStateUnauthenticated, err->message);
auth->cb(auth->ctx, ZitiAuthStateUnauthenticated, err);
free_ziti_api_session_ptr(auth->session);
auth->session = NULL;
uv_timer_start(&auth->timer, auth_timer_cb, 0, 0);
Expand Down
148 changes: 105 additions & 43 deletions library/oidc.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@

#define TOKEN_EXCHANGE_GRANT "urn:ietf:params:oauth:grant-type:token-exchange"

#if _WIN32
#define close_socket(s) closesocket(s)
#define sock_error WSAGetLastError()
#else
#define close_socket(s) close(s)
#define sock_error errno
#endif
#define OIDC_ACCEPT_TIMEOUT 30
#define OIDC_REQ_TIMEOUT 5

typedef struct oidc_req oidc_req;

typedef void (*oidc_cb)(oidc_req *, int status, json_object *resp);
Expand All @@ -58,6 +68,14 @@ struct oidc_req {
void *ctx;
};

struct ext_link_req {
uv_work_t wr;
uv_os_sock_t sock;
struct auth_req *req;
char *code;
int err;
};

typedef struct auth_req {
oidc_client_t *clt;
char code_verifier[code_verifier_len];
Expand All @@ -66,6 +84,7 @@ typedef struct auth_req {
json_tokener *json_parser;
char *id;
bool totp;
struct ext_link_req *elr;
} auth_req;

static oidc_req *new_oidc_req(oidc_client_t *clt, oidc_cb cb, void *ctx) {
Expand Down Expand Up @@ -111,9 +130,7 @@ static void unhandled_body_cb(tlsuv_http_req_t *r, char *data, ssize_t len) {
if (len > 0) {
ZITI_LOG(WARN, "%.*s", (int)len, data);
} else {
oidc_req *req = r->data;
ZITI_LOG(WARN, "status = %zd\n", len);
complete_oidc_req(req, UV_EINVAL, NULL);
}
}

Expand Down Expand Up @@ -146,6 +163,8 @@ static void parse_cb(tlsuv_http_resp_t *resp, void *ctx) {
}

ZITI_LOG(ERROR, "unexpected content-type[%s]: %s", resp->req->path, ct);
complete_oidc_req(req, UV_EINVAL, NULL);
resp->req->data = NULL;
handle_unexpected_resp(resp);
}

Expand Down Expand Up @@ -270,23 +289,37 @@ static void free_auth_req(auth_req *req) {
}

static void failed_auth_req(auth_req *req, const char *error) {
if (req->clt->token_cb) {
oidc_client_t *clt = req->clt;
if (clt && clt->token_cb) {
ZITI_LOG(WARN, "OIDC authorization failed: %s", error);
req->clt->token_cb(req->clt, ZITI_AUTHENTICATION_FAILED, NULL);
clt->token_cb(clt, ZITI_AUTHENTICATION_FAILED, error);
clt->request = NULL;
clt = NULL;
}

if (req->elr) {
req->elr->err = ECANCELED;
if (uv_cancel((uv_req_t *) &req->elr->wr) == 0) {
free(req->elr);
}
}

free_auth_req(req);
}

static void parse_token_cb(tlsuv_http_req_t *r, char *body, ssize_t len) {
auth_req *req = r->data;
assert(req);
if (req == NULL) return;

oidc_client_t *clt = req->clt;
clt->request = NULL;
int err;
if (len > 0) {
if (req->json_parser) {
json_object *j = json_tokener_parse_ex(req->json_parser, body, (int) len);

if (j != NULL) {
oidc_client_set_tokens(req->clt, j);
oidc_client_set_tokens(clt, j);
r->data = NULL;
r->resp.body_cb = NULL;
free_auth_req(req);
Expand All @@ -302,9 +335,9 @@ static void parse_token_cb(tlsuv_http_req_t *r, char *body, ssize_t len) {
}

// error before req is complete
if (req) {
failed_auth_req(req, uv_strerror((int) len));
}
r->data = NULL;
r->resp.body_cb = NULL;
failed_auth_req(req, uv_strerror((int)len));
}

static void token_cb(tlsuv_http_resp_t *http_resp, void *ctx) {
Expand All @@ -314,8 +347,10 @@ static void token_cb(tlsuv_http_resp_t *http_resp, void *ctx) {
req->json_parser = json_tokener_new();
http_resp->body_cb = parse_token_cb;
} else {
handle_unexpected_resp(http_resp);
failed_auth_req(req, http_resp->status);

http_resp->req->data = NULL;
handle_unexpected_resp(http_resp);
}
}

Expand Down Expand Up @@ -395,14 +430,6 @@ static void auth_cb(tlsuv_http_resp_t *http_resp, void *ctx) {
}
}

struct ext_link_req {
uv_work_t wr;
uv_os_sock_t sock;
auth_req *req;
char *code;
int err;
};

static int set_blocking(uv_os_sock_t sock) {
int yes = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
Expand All @@ -425,34 +452,44 @@ static int set_blocking(uv_os_sock_t sock) {
return 0;
}

#if _WIN32
#define close_socket(s) closesocket(s)
#define sock_error WSAGetLastError()
#else
#define close_socket(s) close(s)
#define sock_error errno
#endif
#define OIDC_ACCEPT_TIMEOUT 30
#define OIDC_REQ_TIMEOUT 5

static void ext_accept(uv_work_t *wr) {
struct ext_link_req *elr = (struct ext_link_req *) wr;


int rc;
fd_set fds;
FD_ZERO(&fds);
FD_SET(elr->sock, &fds);
int rc = select(elr->sock + 1, &fds, NULL, NULL, &(struct timeval){
.tv_sec = OIDC_ACCEPT_TIMEOUT,
});
uint64_t timeout = OIDC_ACCEPT_TIMEOUT;

while(timeout > 0) {
FD_ZERO(&fds);
FD_SET(elr->sock, &fds);
rc = select(elr->sock + 1, &fds, NULL, NULL, &(struct timeval) {
.tv_sec = 1,
});
if (elr->err == ECANCELED) {
close_socket(elr->sock);
elr->sock = (uv_os_sock_t)-1;
return;
}
if (rc == 0) {
timeout--;
continue;
}

if (rc < 0) {
elr->err = sock_error;
return;
}

break;
}

if (rc == 0) {
elr->err = ETIMEDOUT;
ZITI_LOG(WARN, "redirect_uri was not called in time");
return;
} else if (rc < 0) {
elr->err = sock_error;
return;
}

uv_os_sock_t clt = accept(elr->sock, NULL, NULL);
FD_ZERO(&fds);
FD_SET(clt, &fds);
Expand Down Expand Up @@ -579,11 +616,22 @@ static void ext_accept(uv_work_t *wr) {
static void ext_done(uv_work_t *wr, int status) {
struct ext_link_req *elr = (struct ext_link_req *) wr;
close_socket(elr->sock);
elr->sock = (uv_os_sock_t)-1;

if (elr->code) {
request_token(elr->req, elr->code);
if (elr->err) {
ZITI_LOG(ERROR, "accept failed: %s", strerror(elr->err));
}

if (status != UV_ECANCELED && elr->err != ECANCELED) {
struct auth_req *req = elr->req;
req->elr = NULL;
if (elr->code) {
request_token(req, elr->code);
} else {
failed_auth_req(req, elr->err ? strerror(elr->err) : "code not received");
}
} else {
failed_auth_req(elr->req, elr->err ? strerror(elr->err) : "code not received");

}

free(elr->code);
Expand All @@ -598,13 +646,18 @@ static void start_ext_auth(auth_req *req, const char *ep, int qc, tlsuv_http_pai
.sin6_port = htons(auth_cb_port),
};
int sock = socket(AF_INET6, SOCK_STREAM, 0);
#if defined(IPV6_V6ONLY)
int off = 0;
int on = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
#if defined(SO_REUSEPORT)
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
#endif
#if defined(IPV6_V6ONLY)
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
#endif
if (bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) || listen(sock, 1)) {
failed_auth_req(req, strerror(errno));
close(sock);
close_socket(sock);
return;
}
set_blocking(sock);
Expand All @@ -615,7 +668,7 @@ static void start_ext_auth(auth_req *req, const char *ep, int qc, tlsuv_http_pai
int rc = uv_queue_work(loop, &elr->wr, ext_accept, ext_done);
if (rc != 0) {
free(elr);
close(sock);
close_socket(sock);
failed_auth_req(req, uv_strerror(rc));
return;
}
Expand All @@ -630,6 +683,7 @@ static void start_ext_auth(auth_req *req, const char *ep, int qc, tlsuv_http_pai
}
char *url = string_buf_to_string(buf, NULL);

req->elr = elr;
req->clt->link_cb(req->clt, url, req->clt->link_ctx);

free(url);
Expand All @@ -643,6 +697,7 @@ int oidc_client_start(oidc_client_t *clt, oidc_token_cb cb) {
}
ZITI_LOG(DEBUG, "requesting authentication code");
auth_req *req = new_auth_req(clt);
clt->request = req;

string_buf_t *scopes_buf = new_string_buf();
string_buf_append(scopes_buf, default_scope);
Expand Down Expand Up @@ -745,12 +800,19 @@ int oidc_client_close(oidc_client_t *clt, oidc_close_cb cb) {
if (clt->close_cb) {
return UV_EALREADY;
}

clt->token_cb = NULL;
clt->close_cb = cb;
tlsuv_http_close(&clt->http, http_close_cb);
uv_close((uv_handle_t *) clt->timer, (uv_close_cb) free);
clt->timer = NULL;
free_ziti_jwt_signer(&clt->signer_cfg);

if (clt->request) {
clt->request->clt = NULL;
failed_auth_req(clt->request, strerror(ECANCELED));
}

return 0;
}

Expand Down
Loading

0 comments on commit 4365c76

Please sign in to comment.