diff --git a/inc_internal/zt_internal.h b/inc_internal/zt_internal.h index c2f36f6c..7c8e4a9c 100644 --- a/inc_internal/zt_internal.h +++ b/inc_internal/zt_internal.h @@ -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); diff --git a/library/external_auth.c b/library/external_auth.c index a1d08754..590b2315 100644 --- a/library/external_auth.c +++ b/library/external_auth.c @@ -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, + }); } } diff --git a/library/ha_auth.c b/library/ha_auth.c index 6ab4b1a9..7f6d3fb0 100644 --- a/library/ha_auth.c +++ b/library/ha_auth.c @@ -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}); } } } diff --git a/library/legacy_auth.c b/library/legacy_auth.c index 3cfa4118..abc75fee 100644 --- a/library/legacy_auth.c +++ b/library/legacy_auth.c @@ -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); @@ -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", @@ -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); diff --git a/library/oidc.c b/library/oidc.c index 11752d29..6392cf9f 100644 --- a/library/oidc.c +++ b/library/oidc.c @@ -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); @@ -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]; @@ -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) { @@ -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); } } @@ -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); } @@ -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); @@ -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) { @@ -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); } } @@ -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)); @@ -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); @@ -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); @@ -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); @@ -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; } @@ -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); @@ -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); @@ -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; } diff --git a/library/ziti.c b/library/ziti.c index b13409dd..e5d3b7f7 100644 --- a/library/ziti.c +++ b/library/ziti.c @@ -207,7 +207,11 @@ void ziti_set_auth_started(ziti_context ztx) { FREE(ztx->session_token); } -void ziti_set_unauthenticated(ziti_context ztx) { +void ziti_set_unauthenticated(ziti_context ztx, const ziti_error *err) { + if (err) { + ZITI_LOG(WARN, "auth error: %s", err->message); + } + ZTX_LOG(DEBUG, "setting auth_state[%d] to %d", ztx->auth_state, ZitiAuthStateUnauthenticated); ztx->auth_state = ZitiAuthStateUnauthenticated; FREE(ztx->session_token); @@ -230,12 +234,29 @@ void ziti_set_unauthenticated(ziti_context ztx) { model_map_clear(&ztx->sessions, (void (*)(void *)) free_ziti_session_ptr); ziti_ctrl_clear_api_session(ztx_get_controller(ztx)); + + if (err && !ztx->closing) { + ziti_send_event(ztx, &(ziti_event_t) { + .type = ZitiContextEvent, + .ctx = (struct ziti_context_event) { + .err = err->message, + .ctrl_status = (int) err->err, + }, + }); + } } -void ziti_set_impossible_to_authenticate(ziti_context ztx) { +void ziti_set_impossible_to_authenticate(ziti_context ztx, const ziti_error *err) { ZTX_LOG(DEBUG, "setting api_session_state[%d] to %d", ztx->auth_state, ZitiAuthImpossibleToAuthenticate); FREE(ztx->session_token); ziti_ctrl_clear_api_session(ztx_get_controller(ztx)); + ziti_send_event(ztx, &(ziti_event_t){ + .type = ZitiContextEvent, + .ctx = (struct ziti_context_event){ + .ctrl_status = ZITI_AUTHENTICATION_FAILED, + .err = err->message, + } + }); } void ziti_set_partially_authenticated(ziti_context ztx, const ziti_auth_query_mfa *mfa_q) { @@ -356,7 +377,7 @@ void ziti_set_fully_authenticated(ziti_context ztx, const char *session_token) { static void logout_cb(void *resp, const ziti_error *err, void *ctx) { ziti_context ztx = ctx; - ziti_set_unauthenticated(ztx); + ziti_set_unauthenticated(ztx, NULL); ziti_close_channels(ztx, ZITI_DISABLED); ziti_ctrl_close(&ztx->ctrl); @@ -367,8 +388,6 @@ static void logout_cb(void *resp, const ziti_error *err, void *ctx) { if (ztx->closing) { ztx->logout = true; shutdown_and_free(ztx); - } else { - update_ctrl_status(ztx, ZITI_DISABLED, ziti_errorstr(ZITI_DISABLED)); } } @@ -387,7 +406,6 @@ const char* ziti_get_api_session_token(ziti_context ztx) { static void ziti_stop_internal(ziti_context ztx, void *data) { if (ztx->enabled) { ZTX_LOG(INFO, "disabling Ziti Context"); - ztx->enabled = false; metrics_rate_close(&ztx->up_rate); metrics_rate_close(&ztx->down_rate); @@ -438,6 +456,8 @@ static void ziti_stop_internal(ziti_context ztx, void *data) { ziti_ctrl_cancel(ztx_get_controller(ztx)); // logout ziti_ctrl_logout(ztx_get_controller(ztx), logout_cb, ztx); + update_ctrl_status(ztx, ZITI_DISABLED, ziti_errorstr(ZITI_DISABLED)); + ztx->enabled = false; } } @@ -482,7 +502,7 @@ static void ziti_start_internal(ziti_context ztx, void *init_req) { uv_prepare_start(ztx->prepper, ztx_prepare); ztx->start = uv_now(ztx->loop); - ziti_set_unauthenticated(ztx); + ziti_set_unauthenticated(ztx, NULL); ziti_re_auth(ztx); } @@ -680,7 +700,7 @@ static void free_ztx(uv_handle_t *h) { ziti_posture_checks_free(ztx->posture_checks); model_map_clear(&ztx->services, (_free_f) free_ziti_service_ptr); model_map_clear(&ztx->sessions, (_free_f) free_ziti_session_ptr); - ziti_set_unauthenticated(ztx); + ziti_set_unauthenticated(ztx, NULL); free_ziti_identity_data(ztx->identity_data); FREE(ztx->identity_data); FREE(ztx->last_update); @@ -1521,9 +1541,9 @@ static void update_ctrl_status(ziti_context ztx, int errCode, const char *errMsg .ctrl_status = errCode, .err = errMsg, }}; - ztx->ctrl_status = errCode; ziti_send_event(ztx, &ev); } + ztx->ctrl_status = errCode; } void ziti_invalidate_session(ziti_context ztx, const char *service_id, ziti_session_type type) { @@ -1871,10 +1891,7 @@ void ztx_auth_state_cb(void *ctx, ziti_auth_state state, const void *data) { ziti_context ztx = ctx; switch (state) { case ZitiAuthStateUnauthenticated: - if (data) { - ZITI_LOG(WARN, "auth error: %s", (const char*)data); - } - ziti_set_unauthenticated(ztx); + ziti_set_unauthenticated(ztx, (const ziti_error *) data); break; case ZitiAuthStateAuthStarted: ziti_set_auth_started(ztx); @@ -1887,7 +1904,7 @@ void ztx_auth_state_cb(void *ctx, ziti_auth_state state, const void *data) { ziti_set_fully_authenticated(ztx, data); break; case ZitiAuthImpossibleToAuthenticate: - ziti_set_impossible_to_authenticate(ztx); + ziti_set_impossible_to_authenticate(ztx, (const ziti_error*)data); break; } ztx->auth_state = state; diff --git a/library/ziti_ctrl.c b/library/ziti_ctrl.c index d52f4b68..3455c555 100644 --- a/library/ziti_ctrl.c +++ b/library/ziti_ctrl.c @@ -167,7 +167,7 @@ static void ctrl_resp_cb(tlsuv_http_resp_t *r, void *data) { ziti_controller *ctrl = resp->ctrl; assert(ctrl->active_reqs > 0); - ctrl->active_reqs--; + ctrl->active_reqs--; resp->status = r->code; if (r->code < 0) {