From 91a482e8dccaa68e741e1621e7052ad3be760a0c Mon Sep 17 00:00:00 2001 From: Leonty Chudinov Date: Fri, 15 Oct 2021 10:18:17 +0500 Subject: [PATCH 01/25] Use public key for JWT signature verification Signed-off-by: Leonty Chudinov --- CHANGELOG.md | 1 + build/build_zss.sh | 3 +- c/jwk.c | 398 +++++++++++++++++++++++++++++++++++++++++++++ c/zss.c | 90 +++++++++- deps/zowe-common-c | 2 +- h/jwk.h | 62 +++++++ h/zssLogging.h | 27 +++ 7 files changed, 577 insertions(+), 6 deletions(-) create mode 100644 c/jwk.c create mode 100644 h/jwk.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 29f6e6aaa..a19650e9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to the ZSS package will be documented in this file. ## `1.26.0` - Enhancement: Add support for continuations in the ZIS PARMLIB member +- Enhancement: Get public key for JWT signature verification using APIML ## `1.25.0` diff --git a/build/build_zss.sh b/build/build_zss.sh index b9a1b6900..0c9041320 100755 --- a/build/build_zss.sh +++ b/build/build_zss.sh @@ -19,7 +19,7 @@ ZSS="../.." COMMON="../../deps/zowe-common-c" GSKDIR=/usr/lpp/gskssl GSKINC="${GSKDIR}/include" -GSKLIB="${GSKDIR}/lib/GSKSSL.x" +GSKLIB="${GSKDIR}/lib/GSKSSL.x ${GSKDIR}/lib/GSKCMS31.x" echo "********************************************************************************" @@ -147,6 +147,7 @@ if c89 \ ${ZSS}/c/rasService.c \ ${ZSS}/c/userInfoService.c \ ${ZSS}/c/passTicketService.c \ + ${ZSS}/c/jwk.c \ ${GSKLIB} ; then extattr +p ${ZSS}/bin/zssServer diff --git a/c/jwk.c b/c/jwk.c new file mode 100644 index 000000000..f26307f18 --- /dev/null +++ b/c/jwk.c @@ -0,0 +1,398 @@ + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#ifdef METTLE +#error Metal C not supported +#endif // METTLE + +#include +#include +#include +#include +#include +#include +#include +#include +#include "zowetypes.h" +#include "alloc.h" +#include "utils.h" +#include "xlate.h" +#include "bpxnet.h" +#include "collections.h" +#include "unixfile.h" +#include "socketmgmt.h" +#include "le.h" +#include "logging.h" +#include "scheduling.h" +#include "json.h" +#include "httpserver.h" +#include "charsets.h" +#include "httpclient.h" +#include "zssLogging.h" +#include "jwt.h" +#include "jwk.h" + +static Json *receiveResponse(ShortLivedHeap *slh, HttpClientContext *httpClientContext, HttpClientSession *session, int *statusOut); +static Json *doRequest(ShortLivedHeap *slh, HttpClientSettings *clientSettings, TlsEnvironment *tlsEnv, char *path, int *statusOut); +static void getPublicKey(Json *jwk, x509_public_key_info *publicKeyOut, int *statusOut); +static int getJwk(JwkContext *context); +static int checkJwtSignature(JwsAlgorithm algorithm, int sigLen, const uint8_t *signature, int msgLen, const uint8_t *message, void *userData); +static int decodeBase64Url(const char *data, char *resultBuf, int *statusOut); +static int jwkTaskMain(RLETask *task); + +void configureJwt(HttpServer *server, JwkSettings *settings) { + int rc = 0; + + if (!settings) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "JWT disabled or APIML is not configured\n"); + return; + } + + JwkContext *context = (JwkContext*)safeMalloc(sizeof(*context), "Jwk Context"); + if (!context) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to allocate JWK context\n"); + return; + } + + context->settings = settings; + if (httpServerInitJwtContextCustom(server, settings->fallback, checkJwtSignature, context, &rc) != 0) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to init JWT context for HTTP server, rc = %d\n", rc); + safeFree((char*)context, sizeof(*context)); + return; + }; + + RLETask *task = makeRLETask(server->base->rleAnchor, RLE_TASK_TCB_CAPABLE | RLE_TASK_DISPOSABLE, jwkTaskMain); + if (!task) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to create background task for JWK\n"); + safeFree((char*)context, sizeof(*context)); + return; + } + task->userPointer = context; + startRLETask(task, NULL); + + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_INFO, ZSS_LOG_JWK_URL_MSG, settings->host, settings->port, settings->path); +} + +static int jwkTaskMain(RLETask *task) { + JwkContext *context = (JwkContext*)task->userPointer; + JwkSettings *settings = context->settings; + const int maxAttempts = 1000; + const int retryIntervalSeconds = 30; + bool success = false; + + for (int i = 0; i < maxAttempts; i++) { + int status = getJwk(context); + if (status == JWK_STATUS_OK) { + success = true; + context->isPublicKeyInitialized = true; + break; + } else if (status == JWK_STATUS_UNRECOGNIZED_FMT_ERROR) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_WARNING, ZSS_LOG_JWK_UNRECOGNIZED_MSG); + break; + } else if (status == JWK_STATUS_HTTP_CONTEXT_ERROR) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to init http context\n"); + break; + } else { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_WARNING, "failed to get JWK - %s, retry in %d seconds\n", + jwkGetStrStatus(status), retryIntervalSeconds); + sleep(retryIntervalSeconds); + } + } + if (success) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_INFO, ZSS_LOG_JWK_READY_MSG, settings->fallback ? "with" : "without"); + } else { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_WARNING, ZSS_LOG_JWK_FAILED_MSG); + } + fflush(stdout); +} + +static int getJwk(JwkContext *context) { + JwkSettings *settings = context->settings; + int status = 0; + ShortLivedHeap *slh = makeShortLivedHeap(0x40000, 0x40); + + HttpClientSettings clientSettings = {0}; + clientSettings.host = settings->host; + clientSettings.port = settings->port; + clientSettings.recvTimeoutSeconds = (settings->timeoutSeconds > 0) ? settings->timeoutSeconds : 10; + + Json *jwkJson = doRequest(slh, &clientSettings, settings->tlsEnv, settings->path, &status); + if (status) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to obtain JWK, status = %d\n", status); + } else { + x509_public_key_info publicKey; + getPublicKey(jwkJson, &publicKey, &status); + if (status == 0) { + context->publicKey = publicKey; + } + } + SLHFree(slh); + return status; +} + +static Json *doRequest(ShortLivedHeap *slh, HttpClientSettings *clientSettings, TlsEnvironment *tlsEnv, char *path, int *statusOut) { + int status = 0; + HttpClientContext *httpClientContext = NULL; + HttpClientSession *session = NULL; + LoggingContext *loggingContext = makeLoggingContext(); + Json *jsonBody = NULL; + + do { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "JWK request to https://%s:%d%s\n", + clientSettings->host, clientSettings->port, path); + status = httpClientContextInitSecure(clientSettings, loggingContext, tlsEnv, &httpClientContext); + if (status) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "error in httpcb ctx init: %d\n", status); + *statusOut = JWK_STATUS_HTTP_CONTEXT_ERROR; + break; + } + status = httpClientSessionInit(httpClientContext, &session); + if (status) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "error initing session: %d\n", status); + *statusOut = JWK_STATUS_HTTP_REQUEST_ERROR; + break; + } + status = httpClientSessionStageRequest(httpClientContext, session, "GET", path, NULL, NULL, NULL, 0); + if (status) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "error staging request: %d\n", status); + *statusOut = JWK_STATUS_HTTP_REQUEST_ERROR; + break; + } + requestStringHeader(session->request, TRUE, "accept", "application/json"); + status = httpClientSessionSend(httpClientContext, session); + if (status) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "error sending request: %d\n", status); + *statusOut = JWK_STATUS_HTTP_REQUEST_ERROR; + break; + } + jsonBody = receiveResponse(slh, httpClientContext, session, &status); + if (status) { + *statusOut = status; + break; + } + int statusCode = session->response->statusCode; + if (statusCode != 200) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "HTTP status %d\n", statusCode); + *statusOut = JWK_STATUS_RESPONSE_ERROR; + break; + } + } while (0); + if (session) { + httpClientSessionDestroy(session); + } + if (httpClientContext) { + httpClientContextDestroy(httpClientContext); + } + return jsonBody; +} + +static Json *receiveResponse(ShortLivedHeap *slh, HttpClientContext *httpClientContext, HttpClientSession *session, int *statusOut) { + bool done = false; + Json *jsonBody = NULL; + while (!done) { + int status = httpClientSessionReceiveNative(httpClientContext, session, 1024); + if (status != 0) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "error receiving response: %d\n", status); + break; + } + if (session->response) { + done = true; + break; + } + } + if (done) { + int contentLength = session->response->contentLength; + char *body = session->response->body; + char responseEbcdic[contentLength + 1]; + memset(responseEbcdic, '\0', contentLength + 1); + memcpy(responseEbcdic, body, contentLength); + __atoe_l(responseEbcdic, contentLength); + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "JWK response: %s\n", responseEbcdic); + char errorBuf[1024]; + ShortLivedHeap *slh = session->slh; + jsonBody = jsonParseUnterminatedUtf8String(slh, CCSID_IBM1047, body, contentLength, errorBuf, sizeof(errorBuf)); + if (!jsonBody) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "error parsing JSON response: %s\n", errorBuf); + *statusOut = JWK_STATUS_JSON_RESPONSE_ERROR; + return NULL; + } else { + *statusOut = JWK_STATUS_OK; + } + } else { + *statusOut = JWK_STATUS_RESPONSE_ERROR; + } + return jsonBody; +} + +static void getPublicKey(Json *jwk, x509_public_key_info *publicKeyOut, int *statusOut) { + int status = 0; + JsonObject *jwkObject = jsonAsObject(jwk); + + if (!jwkObject) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "JWK is not object\n"); + *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; + return; + } + + JsonArray *keysArray = jsonObjectGetArray(jwkObject, "keys"); + if (!keysArray) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "JWK doesn't have 'keys' array\n"); + *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; + return; + } + + JsonObject *keyObject = jsonArrayGetObject(keysArray, 0); + if (!keyObject) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "JWK doesn't contain key\n"); + *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; + return; + } + + char *alg = jsonObjectGetString(keyObject, "kty"); + if (!alg) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "JWK key doesn't have 'kty' property\n"); + *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; + return; + } + + if (0 != strcasecmp(alg, "RSA")) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "JWK key uses unsupported algorithm - '%s'\n", alg); + *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; + return; + } + + char *modulusBase64Url = jsonObjectGetString(keyObject, "n"); + char *exponentBase64Url = jsonObjectGetString(keyObject, "e"); + if (!modulusBase64Url || !exponentBase64Url) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "modulus or exponent not found\n"); + *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; + return; + } + + char modulus[strlen(modulusBase64Url)+1]; + int modulusLen = decodeBase64Url(modulusBase64Url, modulus, &status); + if (status != 0) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to decode modulus '%s'\n", modulusBase64Url); + *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; + return; + } + + char exponent[strlen(exponentBase64Url)+1]; + int exponentLen = decodeBase64Url(exponentBase64Url, exponent, &status); + if (status != 0) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to decode exponent '%s'\n", exponentBase64Url); + *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; + return; + } + + gsk_buffer modulusBuf = { .data = (void*)modulus, .length = modulusLen }; + gsk_buffer exponentBuf = { .data = (void*)exponent, .length = exponentLen }; + status = gsk_construct_public_key_rsa(&modulusBuf, &exponentBuf, publicKeyOut); + if (status != 0) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to construct public key: %s\n", gsk_strerror(status)); + *statusOut = JWK_STATUS_PUBLIC_KEY_ERROR; + return; + } + + *statusOut = JWK_STATUS_OK; +} + +static int decodeBase64Url(const char *data, char *resultBuf, int *statusOut) { + size_t dataLen = strlen(data); + size_t base64Size = dataLen + 16; + char base64[base64Size]; + + strcpy(base64, data); + if (base64urlToBase64(base64, base64Size) < 0) { + *statusOut = JWK_STATUS_INVALID_BASE64_URL; + return 0; + } + + int decodedLen = decodeBase64(base64, resultBuf); + if (decodedLen <= 0) { + *statusOut = JWK_STATUS_INVALID_BASE64; + return 0; + } + return decodedLen; +} + +static int checkJwtSignature(JwsAlgorithm algorithm, + int sigLen, const uint8_t *signature, + int msgLen, const uint8_t *message, + void *userData) { + JwkContext *context = userData; + if (!context->isPublicKeyInitialized) { + return RC_JWT_NOT_CONFIGURED; + } + + if (algorithm == JWS_ALGORITHM_none) { + if (sigLen == 0) { + return RC_JWT_INSECURE; + } else { + return RC_JWT_INVALID_SIGLEN; + } + } + + x509_algorithm_type alg = x509_alg_unknown; + gsk_buffer msgBuffer = { .length = msgLen, .data = (void*)message }; + gsk_buffer sigBuffer = { .length = sigLen, .data = (void*)signature }; + + switch (algorithm) { + case JWS_ALGORITHM_RS256: + alg = x509_alg_sha256WithRsaEncryption; + break; + } + int gskStatus = gsk_verify_data_signature(alg, &context->publicKey, 0, &msgBuffer, &sigBuffer); + if (gskStatus != 0) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to verify signature with status %d - %s\n", + gskStatus, gsk_strerror(gskStatus)); + return RC_JWT_SIG_MISMATCH; + } + return RC_JWT_OK; +} + +static const char *MESSAGES[] = { + [JWK_STATUS_OK] = "OK", + [JWK_STATUS_HTTP_ERROR] = "HTTP error", + [JWK_STATUS_RESPONSE_ERROR] = "HTTP response error", + [JWK_STATUS_JSON_RESPONSE_ERROR] = "HTTP response error - invalid JSON", + [JWK_STATUS_UNRECOGNIZED_FMT_ERROR] = "JWK is in unrecognized format", + [JWK_STATUS_NOT_RSA_KEY] = "not RSA public key", + [JWK_STATUS_RSA_FACTORS_NOT_FOUND] = "RSA factors not found", + [JWK_STATUS_INVALID_BASE64_URL] = "failed to decode base64URL", + [JWK_STATUS_INVALID_BASE64] = "failed to decode base64", + [JWK_STATUS_PUBLIC_KEY_ERROR] = "failed to create public key", + [JWK_STATUS_HTTP_CONTEXT_ERROR] = "failed to init HTTP context", + [JWK_STATUS_HTTP_REQUEST_ERROR] = "failed to send HTTP request" +}; + +#define MESSAGE_COUNT sizeof(MESSAGES)/sizeof(MESSAGES[0]) + +const char *jwkGetStrStatus(int status) { + if (status >= MESSAGE_COUNT || status < 0) { + return "Unknown status code"; + } + const char *message = MESSAGES[status]; + if (!message) { + return "Unknown status code"; + } + return message; +} + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ diff --git a/c/zss.c b/c/zss.c index e2697bac3..950f08916 100644 --- a/c/zss.c +++ b/c/zss.c @@ -82,6 +82,7 @@ #include "storage.h" #include "storageApiml.h" #include "passTicketService.h" +#include "jwk.h" #define PRODUCT "ZLUX" #ifndef PRODUCT_MAJOR_VERSION @@ -122,7 +123,9 @@ static JsonObject *readServerSettings(ShortLivedHeap *slh, const char *filename) static hashtable *getServerTimeoutsHt(ShortLivedHeap *slh, Json *serverTimeouts, const char *key); static InternalAPIMap *makeInternalAPIMap(void); static bool readGatewaySettings(JsonObject *serverConfig, JsonObject *envConfig, char **outGatewayHost, int *outGatewayPort); +static bool isMediationLayerEnabled(JsonObject *serverConfig, JsonObject *envConfig); static bool isCachingServiceEnabled(JsonObject *serverConfig, JsonObject *envConfig); +static bool isJwtFallbackEnabled(JsonObject *serverConfig, JsonObject *envSettings); static int servePluginDefinitions(HttpService *service, HttpResponse *response){ zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2, "begin %s\n", __FUNCTION__); @@ -902,6 +905,44 @@ static ApimlStorageSettings *readApimlStorageSettings(ShortLivedHeap *slh, JsonO return settings; } +static JwkSettings *readJwkSettings(ShortLivedHeap *slh, JsonObject *serverConfig, JsonObject *envConfig, TlsEnvironment *tlsEnv) { + char *host = NULL; + int port = 0; + bool configured = false; + bool fallback = false; + + do { + if (!isMediationLayerEnabled(serverConfig, envConfig)) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG, "APIML disabled\n"); + break; + } + if (!readGatewaySettings(serverConfig, envConfig, &host, &port)) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG, "Gateway settings not found\n"); + break; + } + if (!tlsEnv) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG, "TLS settings not found\n"); + break; + } + fallback = isJwtFallbackEnabled(serverConfig, envConfig); + configured = true; + } while(0); + + if (!configured) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG, "JWK URL not configured"); + return NULL; + } + JwkSettings *settings = (JwkSettings*)SLHAlloc(slh, sizeof(*settings)); + memset(settings, 0, sizeof(*settings)); + settings->host = host; + settings->port = port; + settings->tlsEnv = tlsEnv; + settings->timeoutSeconds = 10; + settings->path = "/gateway/api/v1/auth/keys/public/current"; + settings->fallback = fallback; + return settings; +} + static const char defaultConfigPath[] = "../defaults/serverConfig/server.json"; static @@ -956,6 +997,7 @@ static void initLoggingComponents(void) { logConfigureComponent(NULL, LOG_COMP_ID_MVD_SERVER, "ZSS server", LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); logConfigureComponent(NULL, LOG_COMP_ID_CTDS, "CT/DS", LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); logConfigureComponent(NULL, LOG_COMP_ID_APIML_STORAGE, "APIML Storage", LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); + logConfigureComponent(NULL, LOG_COMP_ID_JWK, "JWK", LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_INFO, ZSS_LOG_ZSS_START_VER_MSG, productVersion); } @@ -1164,6 +1206,48 @@ static bool readGatewaySettings(JsonObject *serverConfig, #define NODE_MEDIATION_LAYER_CACHING_SERVICE_KEY(key) NODE_MEDIATION_LAYER_CACHING_SERVICE_PREFIX key #define ENABLED_KEY "enabled" +static bool isMediationLayerEnabled(JsonObject *serverConfig, JsonObject *envConfig) { + bool mediationLayerEnabledFound = jsonObjectHasKey(envConfig, ENV_NODE_MEDIATION_LAYER_KEY(ENABLED_KEY)); + bool mediationLayerEnabled = false; + if (mediationLayerEnabledFound) { + mediationLayerEnabled = jsonObjectGetBoolean(envConfig, ENV_NODE_MEDIATION_LAYER_KEY(ENABLED_KEY)); + return mediationLayerEnabled; + } + JsonObject *nodeSettings = jsonObjectGetObject(serverConfig, "node"); + JsonObject *mediationLayerSettings = NULL; + if (nodeSettings) { + mediationLayerSettings = jsonObjectGetObject(nodeSettings, "mediationLayer"); + } + if (!mediationLayerSettings) { + return false; + } + mediationLayerEnabled = jsonObjectGetBoolean(mediationLayerSettings, ENABLED_KEY); + return mediationLayerEnabled; +} + +#define AGENT_JWT_PREFIX "ZWED_agent_jwt_" +#define ENV_AGENT_JWT_KEY(key) AGENT_JWT_PREFIX key + +static bool isJwtFallbackEnabled(JsonObject *serverConfig, JsonObject *envSettings) { + Json *fallbackJson = jsonObjectGetPropertyValue(envSettings, ENV_AGENT_JWT_KEY("fallback")); + if (fallbackJson) { + return jsonIsBoolean(fallbackJson) ? jsonAsBoolean(fallbackJson) : false; + } + JsonObject *const agentSettings = jsonObjectGetObject(serverConfig, "agent"); + if (!agentSettings) { + return false; + } + JsonObject *jwtSettings = jsonObjectGetObject(agentSettings, "jwt"); + if (!jwtSettings) { + return false; + } + fallbackJson = jsonObjectGetPropertyValue(jwtSettings, "enabled"); + if (fallbackJson) { + return jsonIsBoolean(fallbackJson) ? jsonAsBoolean(fallbackJson) : false; + } + return false; +} + static bool isCachingServiceEnabled(JsonObject *serverConfig, JsonObject *envConfig) { bool mediationLayerEnabledFound = jsonObjectHasKey(envConfig, ENV_NODE_MEDIATION_LAYER_KEY(ENABLED_KEY)); bool mediationLayerEnabled = false; @@ -1620,15 +1704,13 @@ int main(int argc, char **argv){ server = makeHttpServer2(base, inetAddress, port, requiredTLSFlag, &returnCode, &reasonCode); } if (server){ - if (0 != initializeJwtKeystoreIfConfigured(mvdSettings, server, envSettings)) { - zssStatus = ZSS_STATUS_ERROR; - goto out_term_stcbase; - } ApimlStorageSettings *apimlStorageSettings = readApimlStorageSettings(slh, mvdSettings, envSettings, tlsEnv); + JwkSettings *jwkSettings = readJwkSettings(slh, mvdSettings, envSettings, tlsEnv); server->defaultProductURLPrefix = PRODUCT; initializePluginIDHashTable(server); loadWebServerConfig(server, mvdSettings, envSettings, htUsers, htGroups, defaultSeconds); readWebPluginDefinitions(server, slh, pluginsDir, mvdSettings, apimlStorageSettings); + configureJwt(server, jwkSettings); installCertificateService(server); installUnixFileContentsService(server); installUnixFileRenameService(server); diff --git a/deps/zowe-common-c b/deps/zowe-common-c index d24613130..2c2aa3a8f 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit d24613130719ab14d7b10172390867bc8c4f9647 +Subproject commit 2c2aa3a8f1a12ea261c4bcb24f39c383c760ae9a diff --git a/h/jwk.h b/h/jwk.h new file mode 100644 index 000000000..a84d1730e --- /dev/null +++ b/h/jwk.h @@ -0,0 +1,62 @@ +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#ifndef JWK_H +#define JWK_H + +#include +#include "tls.h" + +typedef struct JwkSettings_tag JwkSettings; +typedef struct JwkContext_tag JwkContext; + + +struct JwkSettings_tag { + TlsEnvironment *tlsEnv; + char *host; + int port; + int timeoutSeconds; + char *path; + bool fallback; +}; + +struct JwkContext_tag { + x509_public_key_info publicKey; + bool isPublicKeyInitialized; + JwkSettings *settings; +}; + +#define JWK_STATUS_OK 0 +#define JWK_STATUS_HTTP_ERROR 1 +#define JWK_STATUS_RESPONSE_ERROR 2 +#define JWK_STATUS_JSON_RESPONSE_ERROR 3 +#define JWK_STATUS_UNRECOGNIZED_FMT_ERROR 4 +#define JWK_STATUS_NOT_RSA_KEY 5 +#define JWK_STATUS_RSA_FACTORS_NOT_FOUND 6 +#define JWK_STATUS_INVALID_BASE64_URL 7 +#define JWK_STATUS_INVALID_BASE64 8 +#define JWK_STATUS_PUBLIC_KEY_ERROR 9 +#define JWK_STATUS_HTTP_CONTEXT_ERROR 10 +#define JWK_STATUS_HTTP_REQUEST_ERROR 11 + +void configureJwt(HttpServer *server, JwkSettings *jwkSettings); +const char *jwkGetStrStatus(int status); + +#endif // JWK_H + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ \ No newline at end of file diff --git a/h/zssLogging.h b/h/zssLogging.h index 57736edfe..d970ccb07 100644 --- a/h/zssLogging.h +++ b/h/zssLogging.h @@ -39,6 +39,7 @@ #define LOG_COMP_ID_UNIXFILE 0x008F000300040000 #define LOG_COMP_ID_DATASERVICE 0x008F000300050000 #define LOG_COMP_ID_APIML_STORAGE 0x008F000300060000 +#define LOG_COMP_ID_JWK 0x008F000300070000 bool isLogLevelValid(int level); @@ -520,6 +521,32 @@ bool isLogLevelValid(int level); #define ZSS_LOG_PASSTICKET_GEN_FAILED_MSG_TEXT "Failed to generate PassTicket - userId='%s', applId='%s', %s\n" #define ZSS_LOG_PASSTICKET_GEN_FAILED_MSG ZSS_LOG_PASSTICKET_GEN_FAILED_MSG_ID" "ZSS_LOG_PASSTICKET_GEN_FAILED_MSG_TEXT +/* JWK */ + +#ifndef ZSS_LOG_JWK_URL_MSG_ID +#define ZSS_LOG_JWK_URL_MSG_ID ZSS_LOG_MSG_PRFX"1600I" +#endif +#define ZSS_LOG_JWK_URL_MSG_TEXT "JWT will be configured using JWK URL https://%s:%d%s\n" +#define ZSS_LOG_JWK_URL_MSG ZSS_LOG_JWK_URL_MSG_ID" "ZSS_LOG_JWK_URL_MSG_TEXT + +#ifndef ZSS_LOG_JWK_READY_MSG_ID +#define ZSS_LOG_JWK_READY_MSG_ID ZSS_LOG_MSG_PRFX"1601I" +#endif +#define ZSS_LOG_JWK_READY_MSG_TEXT "Server is ready to accept JWT %s fallback to legacy tokens\n" +#define ZSS_LOG_JWK_READY_MSG ZSS_LOG_JWK_READY_MSG_ID" "ZSS_LOG_JWK_READY_MSG_TEXT + +#ifndef ZSS_LOG_JWK_UNRECOGNIZED_MSG_ID +#define ZSS_LOG_JWK_UNRECOGNIZED_MSG_ID ZSS_LOG_MSG_PRFX"1602W" +#endif +#define ZSS_LOG_JWK_UNRECOGNIZED_MSG_TEXT "JWK is in unrecognized format\n" +#define ZSS_LOG_JWK_UNRECOGNIZED_MSG ZSS_LOG_JWK_UNRECOGNIZED_MSG_ID" "ZSS_LOG_JWK_UNRECOGNIZED_MSG_TEXT + +#ifndef ZSS_LOG_JWK_FAILED_MSG_ID +#define ZSS_LOG_JWK_FAILED_MSG_ID ZSS_LOG_MSG_PRFX"1603W" +#endif +#define ZSS_LOG_JWK_FAILED_MSG_TEXT "Server will not accept JWT\n" +#define ZSS_LOG_JWK_FAILED_MSG ZSS_LOG_JWK_FAILED_MSG_ID" "ZSS_LOG_JWK_FAILED_MSG_TEXT + #endif /* MVD_H_ZSSLOGGING_H_ */ From eb0c1f0aa7a684b11ad172f717594968db1b92dd Mon Sep 17 00:00:00 2001 From: Leonty Chudinov Date: Fri, 29 Oct 2021 18:25:31 +0500 Subject: [PATCH 02/25] Assign ID to "Failed to get JWK" message Signed-off-by: Leonty Chudinov --- c/jwk.c | 2 +- h/zssLogging.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/c/jwk.c b/c/jwk.c index f26307f18..f73450ec8 100644 --- a/c/jwk.c +++ b/c/jwk.c @@ -101,7 +101,7 @@ static int jwkTaskMain(RLETask *task) { zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to init http context\n"); break; } else { - zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_WARNING, "failed to get JWK - %s, retry in %d seconds\n", + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_WARNING, ZSS_LOG_JWK_RETRY_MSG, jwkGetStrStatus(status), retryIntervalSeconds); sleep(retryIntervalSeconds); } diff --git a/h/zssLogging.h b/h/zssLogging.h index d970ccb07..4ea60c8dc 100644 --- a/h/zssLogging.h +++ b/h/zssLogging.h @@ -547,6 +547,12 @@ bool isLogLevelValid(int level); #define ZSS_LOG_JWK_FAILED_MSG_TEXT "Server will not accept JWT\n" #define ZSS_LOG_JWK_FAILED_MSG ZSS_LOG_JWK_FAILED_MSG_ID" "ZSS_LOG_JWK_FAILED_MSG_TEXT +#ifndef ZSS_LOG_JWK_RETRY_MSG_ID +#define ZSS_LOG_JWK_RETRY_MSG_ID ZSS_LOG_MSG_PRFX"1604W" +#endif +#define ZSS_LOG_JWK_RETRY_MSG_TEXT "Failed to get JWK - %s, retry in %d seconds\n" +#define ZSS_LOG_JWK_RETRY_MSG ZSS_LOG_JWK_RETRY_MSG_ID" "ZSS_LOG_JWK_RETRY_MSG_TEXT + #endif /* MVD_H_ZSSLOGGING_H_ */ From 86b4e56cddd05f4a462ab7b5bc09e7db71563ec8 Mon Sep 17 00:00:00 2001 From: Vit Tomica Date: Thu, 4 Nov 2021 08:46:14 -0400 Subject: [PATCH 03/25] Added SAF IDT support Signed-off-by: Vit Tomica --- build/build_zss.sh | 1 + c/safIdtService.c | 336 ++++++++++++++++++++++++++++++++++++++++++ c/zis/client.c | 47 ++++++ c/zis/services/auth.c | 44 +++++- c/zss.c | 1 + deps/zowe-common-c | 2 +- h/safIdtService.h | 29 ++++ h/zis/client.h | 5 + h/zis/services/auth.h | 6 +- 9 files changed, 462 insertions(+), 9 deletions(-) create mode 100755 c/safIdtService.c create mode 100755 h/safIdtService.h diff --git a/build/build_zss.sh b/build/build_zss.sh index b9a1b6900..44bac8d13 100755 --- a/build/build_zss.sh +++ b/build/build_zss.sh @@ -147,6 +147,7 @@ if c89 \ ${ZSS}/c/rasService.c \ ${ZSS}/c/userInfoService.c \ ${ZSS}/c/passTicketService.c \ + ${ZSS}/c/safIdtService.c \ ${GSKLIB} ; then extattr +p ${ZSS}/bin/zssServer diff --git a/c/safIdtService.c b/c/safIdtService.c new file mode 100755 index 000000000..5d211acdd --- /dev/null +++ b/c/safIdtService.c @@ -0,0 +1,336 @@ + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#include +#include +#include + +#include "safIdtService.h" +#include "httpserver.h" +#include "json.h" +#include "http.h" +#include "zssLogging.h" +#include "zis/client.h" +#include "utils.h" + +#define _STRINGIFY(s) #s +#define STRINGIFY(s) _STRINGIFY(s) + +static void respondWithInvalidMethod(HttpResponse *response) { + jsonPrinter *p = respondWithJsonPrinter(response); + + setResponseStatus(response, 405, "Method Not Allowed"); + setDefaultJSONRESTHeaders(response); + addStringHeader(response, "Allow", "POST"); + writeHeader(response); + + finishResponse(response); +} + +Json *parseContentBody(HttpRequest *request) { + + char *inPtr = request->contentBody; + char *nativeBody = copyStringToNative(request->slh, inPtr, strlen(inPtr)); + int inLen = nativeBody == NULL ? 0 : strlen(nativeBody); + char errBuf[1024]; + + if (nativeBody == NULL) { + return NULL; + } + return jsonParseUnterminatedString(request->slh, nativeBody, inLen, errBuf, sizeof(errBuf)); +} + +static int authenticate(HttpResponse *response, CrossMemoryServerName *privilegedServerName) { + + ZISAuthServiceStatus status = {0}; + char safIdt[ZIS_AUTH_SERVICE_PARMLIST_SAFIDT_LENGTH + 1] = {0}; + HttpRequest *request = response->request; + + Json *body = parseContentBody(request); + if (body == NULL) { + respondWithJsonStatus(response, "No body found", HTTP_STATUS_BAD_REQUEST, "Bad Request"); + return HTTP_SERVICE_FAILED; + } + + JsonObject *jsonObject = jsonAsObject(body); + char *username = jsonObjectGetString(jsonObject, "username"); + char *pass = jsonObjectGetString(jsonObject, "pass"); + + if (username == NULL || strlen(username) == 0) { + respondWithJsonStatus(response, "No username provided", HTTP_STATUS_BAD_REQUEST, "Bad Request"); + return HTTP_SERVICE_FAILED; + } + + if (pass == NULL || strlen(pass) == 0) { + respondWithJsonStatus(response, "No pass provided", HTTP_STATUS_BAD_REQUEST, "Bad Request"); + return HTTP_SERVICE_FAILED; + } + + strupcase(username); + + if (isLowerCasePasswordAllowed() == FALSE && isPassPhrase(pass) == FALSE) { + strupcase(pass); /* upfold password */ + } + + int zisRC = zisGenerateOrValidateSafIdt(privilegedServerName, username, + pass, + safIdt, + &status); + + if (zisRC != RC_ZIS_SRVC_OK) { + + if (status.baseStatus.serviceRC == RC_ZIS_AUTHSRV_SAF_ERROR) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_INFO, + "Authetication request for %s failed: safRC=%d, racfRC=%d, racfRSN=%d\n", + username, status.safStatus.safRC, status.safStatus.racfRC, status.safStatus.racfRSN); + respondWithJsonError(response, "Not authenticated", HTTP_STATUS_UNAUTHORIZED, "Unauthorized"); + return HTTP_SERVICE_FAILED; + } + else if (status.baseStatus.serviceRC == RC_ZIS_AUTHSRV_SAF_ABENDED) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_SEVERE, "Auth service abend: system CC=%X, reason=%X\n", + status.abendInfo.completionCode, status.abendInfo.reasonCode); + respondWithJsonError(response, "Check zssServer log for more details", HTTP_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error"); + return HTTP_SERVICE_FAILED; + } + else { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_SEVERE, + "zis auth service failed: zisRC=%d, serviceRC=%d, serviceRSN=%d, cmsRC=%d, cmsRSN=%d\n", + zisRC, + status.baseStatus.serviceRC, status.baseStatus.serviceRSN, + status.baseStatus.cmsRC, status.baseStatus.cmsRSN); + respondWithJsonError(response, "Check zssServer log for more details", HTTP_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error"); + return HTTP_SERVICE_FAILED; + } + } + + a2e(safIdt, sizeof(safIdt) - 1); + jsonPrinter *p = respondWithJsonPrinter(response); + + setResponseStatus(response, HTTP_STATUS_CREATED, "Created"); + setDefaultJSONRESTHeaders(response); + writeHeader(response); + + jsonStart(p); + { + jsonAddString(p, "jwt", safIdt); + } + jsonEnd(p); + + finishResponse(response); + + return HTTP_SERVICE_SUCCESS; +} + +void extractUsernameFromJwt(HttpResponse *response, char *jwt, char *username) { + char *startPayload, *endPayload; + int payLoadLength; + int allocateLength; + int decodedLength; + char base64Padding[3] = {0}; + char errBuf[1024]; + + // locate JWT payload + if ((startPayload = strchr(jwt, '.')) == NULL) { + return; + } + + startPayload++; // move at the beginning of the JWT payload + + if ((endPayload = strchr(startPayload, '.')) == NULL) { + return; + } + + payLoadLength = endPayload - startPayload; + allocateLength = payLoadLength + 1; // count on '\0' terminating character + + // count on '=' padding characters that are omitted in JWT + if (payLoadLength % 4 == 3) { + allocateLength = payLoadLength + 1; + base64Padding[0] = '='; + } + else if (payLoadLength % 4 == 2) { + allocateLength = payLoadLength + 2; + base64Padding[0] = '='; + base64Padding[1] = '='; + } + + // prepare for base64 decoding + char *encodedPayload = (char *) SLHAlloc(response->request->slh, allocateLength); + char *decodedPayload = (char *) SLHAlloc(response->request->slh, allocateLength); + memset(encodedPayload, 0, allocateLength); + memset(decodedPayload, 0, allocateLength); + + memcpy(encodedPayload, startPayload, payLoadLength); + strcat(encodedPayload, base64Padding); + + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2,"Encoded JWT payload: %s\n", encodedPayload); + + // decode JWT payload + if ((decodedLength = decodeBase64(encodedPayload, decodedPayload)) < 0) { + return; + } + + // convert to native encoding + a2e(decodedPayload, decodedLength); + + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2,"Decoded JWT payload: %s\n", decodedPayload); + + // Parse the username out from the JWT + Json *payload = jsonParseUnterminatedString(response->request->slh, decodedPayload, decodedLength, errBuf, sizeof(errBuf)); + if (payload == NULL) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_WARNING,"Error parsing JWT payload.\n"); + return; + } + + JsonObject *jsonObject = jsonAsObject(payload); + char *sub = jsonObjectGetString(jsonObject, "sub"); + + if (sub == NULL || strlen(sub) == 0) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_WARNING,"No 'sub' claim provided.\n"); + return; + } + + if (strlen(sub) <= 8) { + strcpy(username, sub); + } + + return; +} + +static int verify(HttpResponse *response, CrossMemoryServerName *privilegedServerName) { + + ZISAuthServiceStatus status = {0}; + char safIdt[ZIS_AUTH_SERVICE_PARMLIST_SAFIDT_LENGTH + 1] = {0}; + char username[9] = {0}; + HttpRequest *request = response->request; + + Json *body = parseContentBody(request); + if (body == NULL) { + respondWithJsonStatus(response, "No body found", HTTP_STATUS_BAD_REQUEST, "Bad Request"); + return HTTP_SERVICE_FAILED; + } + + JsonObject *jsonObject = jsonAsObject(body); + char *jwt = jsonObjectGetString(jsonObject, "jwt"); + + if (jwt == NULL || strlen(jwt) == 0) { + respondWithJsonStatus(response, "No jwt provided", HTTP_STATUS_BAD_REQUEST, "Bad Request"); + return HTTP_SERVICE_FAILED; + } + + if (strlen(jwt) > ZIS_AUTH_SERVICE_PARMLIST_SAFIDT_LENGTH) { + respondWithJsonStatus(response, "JWT size exceeds length of "STRINGIFY(ZIS_AUTH_SERVICE_PARMLIST_SAFIDT_LENGTH), + HTTP_STATUS_BAD_REQUEST, "Bad Request"); + return HTTP_SERVICE_FAILED; + } + + memcpy(safIdt, jwt, strlen(jwt)); + e2a(safIdt, sizeof(safIdt) - 1); + + // A username variable will be set to a jwt 'sub' claim value or remains an empty string. + // Extracting a username is done because ACF2 has a bug that requires the username to be specified + // in the VERIFY macro along with the JWT SAF IDT token. The extractUsernameFromJwt() function can be removed once + // the ACF2 bug is fixed. + extractUsernameFromJwt(response, jwt, username); + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2, "extracted username is: %s\n", username); + + int zisRC = zisGenerateOrValidateSafIdt(privilegedServerName, username, + "", + safIdt, + &status); + + a2e(safIdt, sizeof(safIdt) - 1); + if (zisRC != RC_ZIS_SRVC_OK) { + + if (status.baseStatus.serviceRC == RC_ZIS_AUTHSRV_SAF_ERROR) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_INFO, + "JWT verification failed: safRC=%d, racfRC=%d, racfRSN=%d\njwt=%s\n", + status.safStatus.safRC, status.safStatus.racfRC, status.safStatus.racfRSN, safIdt); + respondWithJsonError(response, "Not authenticated", HTTP_STATUS_UNAUTHORIZED, "Unauthorized"); + return HTTP_SERVICE_FAILED; + } + else if (status.baseStatus.serviceRC == RC_ZIS_AUTHSRV_SAF_ABENDED) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_SEVERE, "Auth service abend: system CC=%X, reason=%X\n", + status.abendInfo.completionCode, status.abendInfo.reasonCode); + respondWithJsonError(response, "Check zssServer log for more details", HTTP_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error"); + return HTTP_SERVICE_FAILED; + } + else { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_SEVERE, + "zis auth service failed: zisRC=%d, serviceRC=%d, serviceRSN=%d, cmsRC=%d, cmsRSN=%d\n", + zisRC, + status.baseStatus.serviceRC, status.baseStatus.serviceRSN, + status.baseStatus.cmsRC, status.baseStatus.cmsRSN); + respondWithJsonError(response, "Check zssServer log for more details", HTTP_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error"); + return HTTP_SERVICE_FAILED; + } + } + + jsonPrinter *p = respondWithJsonPrinter(response); + + setResponseStatus(response, HTTP_STATUS_OK, "OK"); + setDefaultJSONRESTHeaders(response); + writeHeader(response); + + finishResponse(response); + + return HTTP_SERVICE_SUCCESS; +} + +static int serveSafIdt(HttpService *service, HttpResponse *response) { + int rc = HTTP_SERVICE_SUCCESS; + HttpRequest *request = response->request; + CrossMemoryServerName *privilegedServerName = getConfiguredProperty(service->server, + HTTP_SERVER_PRIVILEGED_SERVER_PROPERTY); + + if (strcmp(request->method, methodPOST) == 0) { + + // Get the request function + char *requestType = stringListPrint(request->parsedFile, service->parsedMaskPartCount, 1, "/", 0); + + if (strcasecmp("authenticate", requestType) == 0) { + rc = authenticate(response, privilegedServerName); + } + else if (strcasecmp("verify", requestType) == 0) { + rc = verify(response, privilegedServerName); + } + else { + respondWithJsonError(response, "Endpoint not found.", 404, "Not Found"); + return HTTP_SERVICE_FAILED; + } + } + else { + respondWithInvalidMethod(response); + return HTTP_SERVICE_FAILED; + } + + return rc; +} + +void installSAFIdtTokenService(HttpServer *server) { + HttpService *httpService = makeGeneratedService("safIdtService", "saf/**"); + httpService->authType = SERVICE_AUTH_NONE; + httpService->serviceFunction = serveSafIdt; + httpService->runInSubtask = TRUE; + + registerHttpService(server, httpService); +} + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ \ No newline at end of file diff --git a/c/zis/client.c b/c/zis/client.c index 5e414e391..a7f2f1676 100644 --- a/c/zis/client.c +++ b/c/zis/client.c @@ -27,6 +27,9 @@ #include "zis/client.h" #include "zis/service.h" #include "zis/server.h" +#ifdef __ZOWE_OS_ZOS +#include "zos.h" +#endif CrossMemoryServerName zisGetDefaultServerName() { return CMS_DEFAULT_SERVER_NAME; @@ -134,6 +137,50 @@ static int authRequest(const CrossMemoryServerName *serverName, return rc; } +/* + * safIdt - a buffer for SAF IDT token. The buffer must be ZIS_AUTH_SERVICE_PARMLIST_SAFIDT_LENGTH + 1 bytes long. + */ +int zisGenerateOrValidateSafIdt(const CrossMemoryServerName *serverName, + const char *userName, const char *password, + const char *safIdt, + ZISAuthServiceStatus *status) { + AuthServiceParmList parmList = {0}; + + memcpy(&parmList.eyecatcher[0], ZIS_AUTH_SERVICE_PARMLIST_EYECATCHER, + sizeof(parmList.eyecatcher)); + parmList.fc = ZIS_AUTH_SERVICE_PARMLIST_FC_VERIFY_PASSWORD; + + if (strlen(userName) >= sizeof (parmList.userIDNullTerm)) { + status->baseStatus.serviceRC = RC_ZIS_AUTHSRV_INPUT_STRING_TOO_LONG; + return RC_ZIS_SRVC_SERVICE_FAILED; + } + strncpy(parmList.userIDNullTerm, userName, sizeof(parmList.userIDNullTerm)); + + if (strlen(password) >= sizeof (parmList.passwordNullTerm)) { + status->baseStatus.serviceRC = RC_ZIS_AUTHSRV_INPUT_STRING_TOO_LONG; + return RC_ZIS_SRVC_SERVICE_FAILED; + } + strncpy(parmList.passwordNullTerm, password, sizeof(parmList.passwordNullTerm)); + + parmList.options |= ZIS_AUTH_SERVICE_PARMLIST_OPTION_GENERATE_IDT; + parmList.safIdtLen = strlen(safIdt); + + if (strlen(safIdt) >= sizeof(parmList.safIdt)) { + status->baseStatus.serviceRC = RC_ZIS_AUTHSRV_INPUT_STRING_TOO_LONG; + return RC_ZIS_SRVC_SERVICE_FAILED; + } + memcpy((void *)parmList.safIdt, (void *)safIdt, strlen(safIdt)); + + int rc = authRequest(serverName, &parmList, status); + + if (parmList.safIdtLen > 0) { + memset((void *)safIdt, 0, ZIS_AUTH_SERVICE_PARMLIST_SAFIDT_LENGTH + 1); + memcpy((void *)safIdt, (void *)parmList.safIdt, parmList.safIdtLen); + } + + return rc; +} + int zisCheckUsernameAndPassword(const CrossMemoryServerName *serverName, const char *userName, const char *password, ZISAuthServiceStatus *status) { diff --git a/c/zis/services/auth.c b/c/zis/services/auth.c index 3a9996c59..f1067fde3 100644 --- a/c/zis/services/auth.c +++ b/c/zis/services/auth.c @@ -35,20 +35,46 @@ static int handleVerifyPassword(AuthServiceParmList *parmList, int safRC = 0, racfRC = 0, racfRsn = 0; int deleteSAFRC = 0, deleteRACFRC = 0, deleteRACFRsn = 0; int rc = RC_ZIS_AUTHSRV_OK; + Idta *idta = NULL; + int options = VERIFY_CREATE; + + if (parmList->options & ZIS_AUTH_SERVICE_PARMLIST_OPTION_GENERATE_IDT) { + idta = (Idta *) safeMalloc31(sizeof(Idta), "Idta structure"); + memset(idta, 0, sizeof(Idta)); + memcpy(idta->id, "IDTA", 4); + idta->version = IDTA_VERSION_0001; + idta->length = sizeof(Idta); + idta->idt_type = IDTA_JWT_IDT_Type; + idta->idt_buffer_ptr = parmList->safIdt; + idta->idt_buffer_len = sizeof(parmList->safIdt); + idta->idt_len = parmList->safIdtLen; + idta->idt_prop_in = IDTA_End_User_IDT; + options |= VERIFY_GENERATE_IDT; + } CMS_DEBUG(globalArea, "handleVerifyPassword(): username = %s, password = %s\n", parmList->userIDNullTerm, "******"); - safRC = safVerify(VERIFY_CREATE, parmList->userIDNullTerm, - parmList->passwordNullTerm, &acee, &racfRC, &racfRsn); + safRC = safVerify(options, parmList->userIDNullTerm, + parmList->passwordNullTerm, &acee, &racfRC, &racfRsn, idta); CMS_DEBUG(globalArea, "safVerify(VERIFY_CREATE) safStatus = %d, RACF RC = %d, " "RSN = %d, ACEE=0x%p\n", safRC, racfRC, racfRsn, acee); + if (idta != NULL) { + CMS_DEBUG(globalArea, "IDTA token: gen_rc = %d, prop_out = %X, prop_in = %X " + "token length = %d\n", idta->idt_gen_rc, idta->idt_prop_out, idta->idt_prop_in, + idta->idt_len); + } + if (safRC != 0) { rc = RC_ZIS_AUTHSRV_SAF_ERROR; goto acee_deleted; } + if (idta != NULL) { + parmList->safIdtLen = idta->idt_len; + } + deleteSAFRC = safVerify(VERIFY_DELETE, NULL, NULL, &acee, &deleteRACFRC, - &deleteRACFRsn); + &deleteRACFRsn, NULL); CMS_DEBUG(globalArea, "safVerify(VERIFY_DELETE) safStatus = %d, RACF RC = %d, " "RSN = %d, ACEE=0x%p\n", deleteSAFRC, deleteRACFRC, deleteRACFRsn, acee); @@ -59,6 +85,10 @@ static int handleVerifyPassword(AuthServiceParmList *parmList, FILL_SAF_STATUS(&parmList->safStatus, safRC, racfRC, racfRsn); CMS_DEBUG(globalArea, "handleVerifyPassword() done\n"); + if (idta != NULL) { + safeFree(idta, sizeof(Idta)); + idta = NULL; + } return rc; } @@ -120,7 +150,7 @@ static int handleEntityCheck(AuthServiceParmList *parmList, " access = %x\n", parmList->userIDNullTerm, parmList->entityNullTerm, class.valueNullTerm, parmList->access); safRC = safVerify(VERIFY_CREATE | VERIFY_WITHOUT_PASSWORD, - parmList->userIDNullTerm, NULL, &acee, &racfRC, &racfRsn); + parmList->userIDNullTerm, NULL, &acee, &racfRC, &racfRsn, NULL); CMS_DEBUG(globalArea, "safVerify(VERIFY_CREATE) safStatus = %d, RACF RC = %d, " "RSN = %d, ACEE=0x%p\n", safRC, racfRC, racfRsn, acee); if (safRC != 0) { @@ -151,7 +181,7 @@ static int handleEntityCheck(AuthServiceParmList *parmList, recoveryPop(); recovery_removed: - safRC = safVerify(VERIFY_DELETE, NULL, NULL, &acee, &racfRC, &racfRsn); + safRC = safVerify(VERIFY_DELETE, NULL, NULL, &acee, &racfRC, &racfRsn, NULL); CMS_DEBUG(globalArea, "safVerify(VERIFY_DELETE) safStatus = %d, RACF RC = %d, " "RSN = %d, ACEE=0x%p\n", safRC, racfRC, racfRsn, acee); if (safRC != 0) { @@ -183,7 +213,7 @@ static int getAccessType(const CrossMemoryServerGlobalArea *globalArea, safRC = safVerify(VERIFY_CREATE | VERIFY_WITHOUT_PASSWORD | VERIFY_WITHOUT_LOG, - (char *)idNullTerm, NULL, &acee, &racfRC, &racfRsn); + (char *)idNullTerm, NULL, &acee, &racfRC, &racfRsn, NULL); CMS_DEBUG2(globalArea, traceLevel, "safVerify(VERIFY_CREATE) safStatus = %d, RACF RC = %d, " @@ -228,7 +258,7 @@ static int getAccessType(const CrossMemoryServerGlobalArea *globalArea, } safRC = safVerify(VERIFY_DELETE | VERIFY_WITHOUT_LOG, NULL, NULL, &acee, - &racfRC, &racfRsn); + &racfRC, &racfRsn, NULL); CMS_DEBUG2(globalArea, traceLevel, "safVerify(VERIFY_DELETE) safStatus = %d, RACF RC = %d, " diff --git a/c/zss.c b/c/zss.c index 77ebb16ff..d3d724f52 100644 --- a/c/zss.c +++ b/c/zss.c @@ -1674,6 +1674,7 @@ int main(int argc, char **argv){ installRASService(server); installUserInfoService(server); installPassTicketService(server); + installSAFIdtTokenService(server); #endif installLoginService(server); installLogoutService(server); diff --git a/deps/zowe-common-c b/deps/zowe-common-c index d24613130..a5698c8be 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit d24613130719ab14d7b10172390867bc8c4f9647 +Subproject commit a5698c8be09ce73776daa28499a7aa0a89343f9f diff --git a/h/safIdtService.h b/h/safIdtService.h new file mode 100755 index 000000000..da74c517f --- /dev/null +++ b/h/safIdtService.h @@ -0,0 +1,29 @@ +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#ifndef __SAF_IDT_SERVICE_H__ +#define __SAF_IDT_SERVICE_H__ + +#include "httpserver.h" + +void installSAFIdtTokenService(HttpServer *server); + +#endif /* __SAF_IDT_SERVICE_H__ */ + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ \ No newline at end of file diff --git a/h/zis/client.h b/h/zis/client.h index 9b6398999..3e42a3e41 100644 --- a/h/zis/client.h +++ b/h/zis/client.h @@ -420,6 +420,11 @@ int zisCallNWMService(const CrossMemoryServerName *serverName, #define RC_ZIS_SRVC_NWM_BUFFER_NULL (ZIS_MAX_GEN_SRVC_RC + 1) +int zisGenerateOrValidateSafIdt(const CrossMemoryServerName *serverName, + const char *userName, const char *password, + const char *safIdt, + ZISAuthServiceStatus *status); + #endif /* ZIS_CLIENT_H_ */ diff --git a/h/zis/services/auth.h b/h/zis/services/auth.h index c87f2c871..5b29878b4 100644 --- a/h/zis/services/auth.h +++ b/h/zis/services/auth.h @@ -55,7 +55,8 @@ typedef struct AuthServiceParmList_tag { char passwordNullTerm[ZIS_AUTH_SERVICE_PASSWORD_MAX_LENGTH + 1]; /* up to 8 characters: */ char classNullTerm[ZIS_AUTH_SERVICE_CLASS_MAX_LENGTH + 1]; - char _padding0[1]; + char options; +#define ZIS_AUTH_SERVICE_PARMLIST_OPTION_GENERATE_IDT 0x80 int access; /* up to 255 characters: */ char entityNullTerm[ZIS_AUTH_SERVICE_ENTITY_MAX_LENGTH + 1]; @@ -64,6 +65,9 @@ typedef struct AuthServiceParmList_tag { AbendInfo abendInfo; }; int traceLevel; + int safIdtLen; + #define ZIS_AUTH_SERVICE_PARMLIST_SAFIDT_LENGTH (2 * IDTA_IDT_BUFFER_LEN_MIN) + char safIdt[ZIS_AUTH_SERVICE_PARMLIST_SAFIDT_LENGTH]; } AuthServiceParmList; ZOWE_PRAGMA_PACK_RESET From 8846b7419bd39994cc9996a7cc7871e5511c6d11 Mon Sep 17 00:00:00 2001 From: Leonty Chudinov Date: Sun, 14 Nov 2021 12:56:06 +0500 Subject: [PATCH 04/25] Update build_zss64.sh Signed-off-by: Leonty Chudinov --- build/build_zss64.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/build_zss64.sh b/build/build_zss64.sh index 79c9c483b..344ad7ea6 100755 --- a/build/build_zss64.sh +++ b/build/build_zss64.sh @@ -20,7 +20,7 @@ ZSS="../.." COMMON="../../deps/zowe-common-c" GSKDIR=/usr/lpp/gskssl GSKINC="${GSKDIR}/include" -GSKLIB="${GSKDIR}/lib/GSKSSL64.x" +GSKLIB="${GSKDIR}/lib/GSKSSL64.x ${GSKDIR}/lib/GSKCMS64.x" echo "********************************************************************************" echo "Building ZSS..." @@ -122,6 +122,7 @@ if ! c89 \ ${ZSS}/c/rasService.c \ ${ZSS}/c/userInfoService.c \ ${ZSS}/c/passTicketService.c \ + ${ZSS}/c/jwk.c \ ${GSKLIB} ; then extattr +p ${ZSS}/bin/zssServer64 From d11c3ccb8caa8316f7e7fbc7778878c17c832f62 Mon Sep 17 00:00:00 2001 From: Vit Tomica Date: Mon, 15 Nov 2021 10:22:00 -0500 Subject: [PATCH 05/25] Resolve review comments Signed-off-by: Vit Tomica --- c/zis/services/auth.c | 43 ++++++++++++++++++++++++------------------- deps/zowe-common-c | 2 +- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/c/zis/services/auth.c b/c/zis/services/auth.c index f1067fde3..4325a4f66 100644 --- a/c/zis/services/auth.c +++ b/c/zis/services/auth.c @@ -35,33 +35,38 @@ static int handleVerifyPassword(AuthServiceParmList *parmList, int safRC = 0, racfRC = 0, racfRsn = 0; int deleteSAFRC = 0, deleteRACFRC = 0, deleteRACFRsn = 0; int rc = RC_ZIS_AUTHSRV_OK; - Idta *idta = NULL; + IDTA *idta = NULL; int options = VERIFY_CREATE; if (parmList->options & ZIS_AUTH_SERVICE_PARMLIST_OPTION_GENERATE_IDT) { - idta = (Idta *) safeMalloc31(sizeof(Idta), "Idta structure"); - memset(idta, 0, sizeof(Idta)); + idta = (IDTA *) safeMalloc31(sizeof(IDTA), "Idta structure"); + memset(idta, 0, sizeof(IDTA)); memcpy(idta->id, "IDTA", 4); idta->version = IDTA_VERSION_0001; - idta->length = sizeof(Idta); - idta->idt_type = IDTA_JWT_IDT_Type; - idta->idt_buffer_ptr = parmList->safIdt; - idta->idt_buffer_len = sizeof(parmList->safIdt); - idta->idt_len = parmList->safIdtLen; - idta->idt_prop_in = IDTA_End_User_IDT; + idta->length = sizeof(IDTA); + idta->idtType = IDTA_JWT_IDT_Type; + idta->idtBufferPtr = parmList->safIdt; + idta->idtBufferLen = sizeof(parmList->safIdt); + idta->idtLen = parmList->safIdtLen; + idta->idtPropIn = IDTA_End_User_IDT; options |= VERIFY_GENERATE_IDT; } CMS_DEBUG(globalArea, "handleVerifyPassword(): username = %s, password = %s\n", parmList->userIDNullTerm, "******"); - safRC = safVerify(options, parmList->userIDNullTerm, + if (idta == NULL) { + safRC = safVerify(VERIFY_CREATE, parmList->userIDNullTerm, + parmList->passwordNullTerm, &acee, &racfRC, &racfRsn); + } else { + safRC = safVerify6(options, parmList->userIDNullTerm, parmList->passwordNullTerm, &acee, &racfRC, &racfRsn, idta); + } CMS_DEBUG(globalArea, "safVerify(VERIFY_CREATE) safStatus = %d, RACF RC = %d, " "RSN = %d, ACEE=0x%p\n", safRC, racfRC, racfRsn, acee); if (idta != NULL) { CMS_DEBUG(globalArea, "IDTA token: gen_rc = %d, prop_out = %X, prop_in = %X " - "token length = %d\n", idta->idt_gen_rc, idta->idt_prop_out, idta->idt_prop_in, - idta->idt_len); + "token length = %d\n", idta->idtGenRc, idta->idtPropOut, idta->idtPropIn, + idta->idtLen); } if (safRC != 0) { @@ -70,11 +75,11 @@ static int handleVerifyPassword(AuthServiceParmList *parmList, } if (idta != NULL) { - parmList->safIdtLen = idta->idt_len; + parmList->safIdtLen = idta->idtLen; } deleteSAFRC = safVerify(VERIFY_DELETE, NULL, NULL, &acee, &deleteRACFRC, - &deleteRACFRsn, NULL); + &deleteRACFRsn); CMS_DEBUG(globalArea, "safVerify(VERIFY_DELETE) safStatus = %d, RACF RC = %d, " "RSN = %d, ACEE=0x%p\n", deleteSAFRC, deleteRACFRC, deleteRACFRsn, acee); @@ -86,7 +91,7 @@ static int handleVerifyPassword(AuthServiceParmList *parmList, FILL_SAF_STATUS(&parmList->safStatus, safRC, racfRC, racfRsn); CMS_DEBUG(globalArea, "handleVerifyPassword() done\n"); if (idta != NULL) { - safeFree(idta, sizeof(Idta)); + safeFree(idta, sizeof(IDTA)); idta = NULL; } return rc; @@ -150,7 +155,7 @@ static int handleEntityCheck(AuthServiceParmList *parmList, " access = %x\n", parmList->userIDNullTerm, parmList->entityNullTerm, class.valueNullTerm, parmList->access); safRC = safVerify(VERIFY_CREATE | VERIFY_WITHOUT_PASSWORD, - parmList->userIDNullTerm, NULL, &acee, &racfRC, &racfRsn, NULL); + parmList->userIDNullTerm, NULL, &acee, &racfRC, &racfRsn); CMS_DEBUG(globalArea, "safVerify(VERIFY_CREATE) safStatus = %d, RACF RC = %d, " "RSN = %d, ACEE=0x%p\n", safRC, racfRC, racfRsn, acee); if (safRC != 0) { @@ -181,7 +186,7 @@ static int handleEntityCheck(AuthServiceParmList *parmList, recoveryPop(); recovery_removed: - safRC = safVerify(VERIFY_DELETE, NULL, NULL, &acee, &racfRC, &racfRsn, NULL); + safRC = safVerify(VERIFY_DELETE, NULL, NULL, &acee, &racfRC, &racfRsn); CMS_DEBUG(globalArea, "safVerify(VERIFY_DELETE) safStatus = %d, RACF RC = %d, " "RSN = %d, ACEE=0x%p\n", safRC, racfRC, racfRsn, acee); if (safRC != 0) { @@ -213,7 +218,7 @@ static int getAccessType(const CrossMemoryServerGlobalArea *globalArea, safRC = safVerify(VERIFY_CREATE | VERIFY_WITHOUT_PASSWORD | VERIFY_WITHOUT_LOG, - (char *)idNullTerm, NULL, &acee, &racfRC, &racfRsn, NULL); + (char *)idNullTerm, NULL, &acee, &racfRC, &racfRsn); CMS_DEBUG2(globalArea, traceLevel, "safVerify(VERIFY_CREATE) safStatus = %d, RACF RC = %d, " @@ -258,7 +263,7 @@ static int getAccessType(const CrossMemoryServerGlobalArea *globalArea, } safRC = safVerify(VERIFY_DELETE | VERIFY_WITHOUT_LOG, NULL, NULL, &acee, - &racfRC, &racfRsn, NULL); + &racfRC, &racfRsn); CMS_DEBUG2(globalArea, traceLevel, "safVerify(VERIFY_DELETE) safStatus = %d, RACF RC = %d, " diff --git a/deps/zowe-common-c b/deps/zowe-common-c index a5698c8be..7c685671a 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit a5698c8be09ce73776daa28499a7aa0a89343f9f +Subproject commit 7c685671ae9b32806248cc256d70b7853e3d5ecb From e219f66e5cf23e8487175785ad84c1983c429031 Mon Sep 17 00:00:00 2001 From: Vit Tomica Date: Tue, 16 Nov 2021 12:47:54 +0100 Subject: [PATCH 06/25] update the build_zss64.sh script Signed-off-by: Vit Tomica --- build/build_zss64.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build/build_zss64.sh b/build/build_zss64.sh index 79c9c483b..0930ac980 100755 --- a/build/build_zss64.sh +++ b/build/build_zss64.sh @@ -122,6 +122,7 @@ if ! c89 \ ${ZSS}/c/rasService.c \ ${ZSS}/c/userInfoService.c \ ${ZSS}/c/passTicketService.c \ + ${ZSS}/c/safIdtService.c \ ${GSKLIB} ; then extattr +p ${ZSS}/bin/zssServer64 From 08f858a1bcb0ad24c7b05604f6ad7a7c47050957 Mon Sep 17 00:00:00 2001 From: Vit Tomica Date: Tue, 30 Nov 2021 05:09:53 -0500 Subject: [PATCH 07/25] Update dependency Signed-off-by: Vit Tomica --- deps/zowe-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zowe-common-c b/deps/zowe-common-c index 3ac716c8f..0020e5484 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit 3ac716c8f4dd5d5ec6d5935224eb6ca4373c9dae +Subproject commit 0020e5484a53deee1b50928b2a2869700703082b From 86932d2c19986922fc8474c1548ed546e3f61a4b Mon Sep 17 00:00:00 2001 From: Vit Tomica Date: Thu, 2 Dec 2021 09:06:04 -0500 Subject: [PATCH 08/25] Update dependency Signed-off-by: Vit Tomica --- deps/zowe-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zowe-common-c b/deps/zowe-common-c index 0020e5484..0494e8817 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit 0020e5484a53deee1b50928b2a2869700703082b +Subproject commit 0494e88172a7c287fe848fe6bc0d630ea1f3f3d3 From 8e9845cc9e858c21e0ce4f1bdc876b059bf54a3c Mon Sep 17 00:00:00 2001 From: Leonty Chudinov Date: Thu, 2 Dec 2021 16:27:07 +0500 Subject: [PATCH 09/25] Update pointer to zowe-common-c Signed-off-by: Leonty Chudinov --- deps/zowe-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zowe-common-c b/deps/zowe-common-c index 1f8408015..0c205b486 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit 1f8408015b037c3c826eda1da63c6d757d56bad9 +Subproject commit 0c205b486d8201f75fa65370791c562eaff5b311 From b7a89401724eb222c414b5aaf3486edebfe504cd Mon Sep 17 00:00:00 2001 From: Vit Tomica Date: Tue, 7 Dec 2021 05:56:09 -0500 Subject: [PATCH 10/25] Update dependency Signed-off-by: Vit Tomica --- deps/zowe-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zowe-common-c b/deps/zowe-common-c index 0494e8817..4641a67ae 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit 0494e88172a7c287fe848fe6bc0d630ea1f3f3d3 +Subproject commit 4641a67aed9a48781b95f9fd1a0ccf9d12d6a023 From b94e6f23af11720ac53bdb7dae821376a18c43f4 Mon Sep 17 00:00:00 2001 From: Leonty Chudinov Date: Wed, 8 Dec 2021 10:00:14 +0500 Subject: [PATCH 11/25] Fix error handling and changelog Signed-off-by: Leonty Chudinov --- CHANGELOG.md | 4 ++++ c/jwk.c | 33 ++++++++++++++++----------------- h/jwk.h | 10 +++------- h/zssLogging.h | 16 ++++++++++++++-- 4 files changed, 37 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a19650e9f..e3994a83f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to the ZSS package will be documented in this file. ## Recent Changes +## `1.27.0` + +- Enhancement: Get public key for JWT signature verification using APIML + ## `1.26.0` - Enhancement: Add support for continuations in the ZIS PARMLIB member diff --git a/c/jwk.c b/c/jwk.c index f73450ec8..313d4cfc4 100644 --- a/c/jwk.c +++ b/c/jwk.c @@ -45,7 +45,7 @@ static Json *doRequest(ShortLivedHeap *slh, HttpClientSettings *clientSettings, static void getPublicKey(Json *jwk, x509_public_key_info *publicKeyOut, int *statusOut); static int getJwk(JwkContext *context); static int checkJwtSignature(JwsAlgorithm algorithm, int sigLen, const uint8_t *signature, int msgLen, const uint8_t *message, void *userData); -static int decodeBase64Url(const char *data, char *resultBuf, int *statusOut); +static bool decodeBase64Url(const char *data, char *resultBuf, int *lenOut); static int jwkTaskMain(RLETask *task); void configureJwt(HttpServer *server, JwkSettings *settings) { @@ -97,8 +97,11 @@ static int jwkTaskMain(RLETask *task) { } else if (status == JWK_STATUS_UNRECOGNIZED_FMT_ERROR) { zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_WARNING, ZSS_LOG_JWK_UNRECOGNIZED_MSG); break; + } else if (status == JWK_STATUS_PUBLIC_KEY_ERROR) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_WARNING, ZSS_LOG_JWK_PUBLIC_KEY_ERROR_MSG); + break; } else if (status == JWK_STATUS_HTTP_CONTEXT_ERROR) { - zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to init http context\n"); + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_WARNING, ZSS_LOG_JWK_HTTP_CTX_ERROR_MSG); break; } else { zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_WARNING, ZSS_LOG_JWK_RETRY_MSG, @@ -278,17 +281,17 @@ static void getPublicKey(Json *jwk, x509_public_key_info *publicKeyOut, int *sta } char modulus[strlen(modulusBase64Url)+1]; - int modulusLen = decodeBase64Url(modulusBase64Url, modulus, &status); - if (status != 0) { + int modulusLen = 0; + if (!decodeBase64Url(modulusBase64Url, modulus, &modulusLen)) { zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to decode modulus '%s'\n", modulusBase64Url); *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; return; } char exponent[strlen(exponentBase64Url)+1]; - int exponentLen = decodeBase64Url(exponentBase64Url, exponent, &status); - if (status != 0) { - zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to decode exponent '%s'\n", exponentBase64Url); + int exponentLen = 0; + if (!decodeBase64Url(exponentBase64Url, exponent, &exponentLen)) { + zowelog(NULL, LOG_COMP_ID_JWK, ZOWE_LOG_DEBUG, "failed to decode exponent '%s' - %s\n", exponentBase64Url, jwkGetStrStatus(status)); *statusOut = JWK_STATUS_UNRECOGNIZED_FMT_ERROR; return; } @@ -305,23 +308,23 @@ static void getPublicKey(Json *jwk, x509_public_key_info *publicKeyOut, int *sta *statusOut = JWK_STATUS_OK; } -static int decodeBase64Url(const char *data, char *resultBuf, int *statusOut) { +/* Returns true in case of success */ +static bool decodeBase64Url(const char *data, char *resultBuf, int *lenOut) { size_t dataLen = strlen(data); size_t base64Size = dataLen + 16; char base64[base64Size]; strcpy(base64, data); if (base64urlToBase64(base64, base64Size) < 0) { - *statusOut = JWK_STATUS_INVALID_BASE64_URL; - return 0; + return false; } int decodedLen = decodeBase64(base64, resultBuf); if (decodedLen <= 0) { - *statusOut = JWK_STATUS_INVALID_BASE64; - return 0; + return false; } - return decodedLen; + *lenOut = decodedLen; + return true; } static int checkJwtSignature(JwsAlgorithm algorithm, @@ -365,10 +368,6 @@ static const char *MESSAGES[] = { [JWK_STATUS_RESPONSE_ERROR] = "HTTP response error", [JWK_STATUS_JSON_RESPONSE_ERROR] = "HTTP response error - invalid JSON", [JWK_STATUS_UNRECOGNIZED_FMT_ERROR] = "JWK is in unrecognized format", - [JWK_STATUS_NOT_RSA_KEY] = "not RSA public key", - [JWK_STATUS_RSA_FACTORS_NOT_FOUND] = "RSA factors not found", - [JWK_STATUS_INVALID_BASE64_URL] = "failed to decode base64URL", - [JWK_STATUS_INVALID_BASE64] = "failed to decode base64", [JWK_STATUS_PUBLIC_KEY_ERROR] = "failed to create public key", [JWK_STATUS_HTTP_CONTEXT_ERROR] = "failed to init HTTP context", [JWK_STATUS_HTTP_REQUEST_ERROR] = "failed to send HTTP request" diff --git a/h/jwk.h b/h/jwk.h index a84d1730e..6a92ac214 100644 --- a/h/jwk.h +++ b/h/jwk.h @@ -38,13 +38,9 @@ struct JwkContext_tag { #define JWK_STATUS_RESPONSE_ERROR 2 #define JWK_STATUS_JSON_RESPONSE_ERROR 3 #define JWK_STATUS_UNRECOGNIZED_FMT_ERROR 4 -#define JWK_STATUS_NOT_RSA_KEY 5 -#define JWK_STATUS_RSA_FACTORS_NOT_FOUND 6 -#define JWK_STATUS_INVALID_BASE64_URL 7 -#define JWK_STATUS_INVALID_BASE64 8 -#define JWK_STATUS_PUBLIC_KEY_ERROR 9 -#define JWK_STATUS_HTTP_CONTEXT_ERROR 10 -#define JWK_STATUS_HTTP_REQUEST_ERROR 11 +#define JWK_STATUS_PUBLIC_KEY_ERROR 7 +#define JWK_STATUS_HTTP_CONTEXT_ERROR 8 +#define JWK_STATUS_HTTP_REQUEST_ERROR 9 void configureJwt(HttpServer *server, JwkSettings *jwkSettings); const char *jwkGetStrStatus(int status); diff --git a/h/zssLogging.h b/h/zssLogging.h index 4ea60c8dc..23860a0d1 100644 --- a/h/zssLogging.h +++ b/h/zssLogging.h @@ -541,14 +541,26 @@ bool isLogLevelValid(int level); #define ZSS_LOG_JWK_UNRECOGNIZED_MSG_TEXT "JWK is in unrecognized format\n" #define ZSS_LOG_JWK_UNRECOGNIZED_MSG ZSS_LOG_JWK_UNRECOGNIZED_MSG_ID" "ZSS_LOG_JWK_UNRECOGNIZED_MSG_TEXT +#ifndef ZSS_LOG_JWK_PUBLIC_KEY_ERROR_MSG_ID +#define ZSS_LOG_JWK_PUBLIC_KEY_ERROR_MSG_ID ZSS_LOG_MSG_PRFX"1603W" +#endif +#define ZSS_LOG_JWK_PUBLIC_KEY_ERROR_MSG_TEXT "Failed to construct public key using JWK\n" +#define ZSS_LOG_JWK_PUBLIC_KEY_ERROR_MSG ZSS_LOG_JWK_PUBLIC_KEY_ERROR_MSG_ID" "ZSS_LOG_JWK_PUBLIC_KEY_ERROR_MSG_TEXT + +#ifndef ZSS_LOG_JWK_HTTP_CTX_ERROR_MSG_ID +#define ZSS_LOG_JWK_HTTP_CTX_ERROR_MSG_ID ZSS_LOG_MSG_PRFX"1604W" +#endif +#define ZSS_LOG_JWK_HTTP_CTX_ERROR_MSG_TEXT "JWK: failed to init HTTP context, ensure that APIML and TLS settings are correct\n" +#define ZSS_LOG_JWK_HTTP_CTX_ERROR_MSG ZSS_LOG_JWK_HTTP_CTX_ERROR_MSG_ID" "ZSS_LOG_JWK_HTTP_CTX_ERROR_MSG_TEXT + #ifndef ZSS_LOG_JWK_FAILED_MSG_ID -#define ZSS_LOG_JWK_FAILED_MSG_ID ZSS_LOG_MSG_PRFX"1603W" +#define ZSS_LOG_JWK_FAILED_MSG_ID ZSS_LOG_MSG_PRFX"1605W" #endif #define ZSS_LOG_JWK_FAILED_MSG_TEXT "Server will not accept JWT\n" #define ZSS_LOG_JWK_FAILED_MSG ZSS_LOG_JWK_FAILED_MSG_ID" "ZSS_LOG_JWK_FAILED_MSG_TEXT #ifndef ZSS_LOG_JWK_RETRY_MSG_ID -#define ZSS_LOG_JWK_RETRY_MSG_ID ZSS_LOG_MSG_PRFX"1604W" +#define ZSS_LOG_JWK_RETRY_MSG_ID ZSS_LOG_MSG_PRFX"1606W" #endif #define ZSS_LOG_JWK_RETRY_MSG_TEXT "Failed to get JWK - %s, retry in %d seconds\n" #define ZSS_LOG_JWK_RETRY_MSG ZSS_LOG_JWK_RETRY_MSG_ID" "ZSS_LOG_JWK_RETRY_MSG_TEXT From 2bec1632b23609d170c7c9787ca49853f5ca28ce Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 10:44:18 -0500 Subject: [PATCH 12/25] test workflow Signed-off-by: James Struga --- .github/workflows/build_test.yml | 58 ++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/workflows/build_test.yml diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml new file mode 100644 index 000000000..fe57325fe --- /dev/null +++ b/.github/workflows/build_test.yml @@ -0,0 +1,58 @@ +name: Build and Test Workflow +on: + push: + branches: + - "*" + pull_request: + types: [opened, reopened, synchronize] + workflow_dispatch: + inputs: + PERFORM_RELEASE: + description: '[Release] perform release' + required: false + default: 'false' + + +jobs: + check-permission: + runs-on: ubuntu-latest + steps: + # this action will fail the whole workflow if permission check fails + - name: check permission + uses: zowe-actions/shared-actions/permission-check@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + build-test: + runs-on: ubuntu-latest + needs: validate-package-json + steps: + - name: '[Prep 1] Checkout' + uses: actions/checkout@v2 + + - name: '[Prep 2] Setup jFrog CLI' + uses: jfrog/setup-jfrog-cli@v2 + env: + JF_ARTIFACTORY_1: ${{ secrets.JF_ARTIFACTORY_TOKEN }} + + - name: '[Prep 3] Prepare workflow' + uses: zowe-actions/shared-actions/prepare-workflow@main + + - name: '[Packaging] Make pax' + uses: zowe-actions/shared-actions/make-pax@main + with: + pax-name: 'zss' + pax-options: '-x os390 -pp' + pax-ssh-username: ${{ secrets.SSH_MARIST_USERNAME }} + pax-ssh-password: ${{ secrets.SSH_MARIST_RACF_PASSWORD }} + extra-files: 'zss.tar' + + - name: '[Publish] Publish' + uses: zowe-actions/shared-actions/publish@main + if: success() + with: + artifacts: | + .pax/zss.pax + .pax/zss.tar + perform-release: ${{ github.event.inputs.PERFORM_RELEASE }} + \ No newline at end of file From 9463530d5a4d64326d26585b7b28e2f92032355a Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 10:46:22 -0500 Subject: [PATCH 13/25] add permission check Signed-off-by: James Struga --- .github/workflows/build_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index fe57325fe..7ea5bab79 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -25,7 +25,7 @@ jobs: build-test: runs-on: ubuntu-latest - needs: validate-package-json + needs: check-permission steps: - name: '[Prep 1] Checkout' uses: actions/checkout@v2 From 8e267bd418414fceabf23c796d7d1db4046cf0e8 Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 11:02:02 -0500 Subject: [PATCH 14/25] add submodule Signed-off-by: James Struga --- .github/workflows/build_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 7ea5bab79..93b7f90b2 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -29,6 +29,8 @@ jobs: steps: - name: '[Prep 1] Checkout' uses: actions/checkout@v2 + with: + submodules: true - name: '[Prep 2] Setup jFrog CLI' uses: jfrog/setup-jfrog-cli@v2 From 0aa1df49deea6865fd37ed69fc3a187af2ba2225 Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 11:41:26 -0500 Subject: [PATCH 15/25] add submodule Signed-off-by: James Struga --- .github/workflows/build_test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 93b7f90b2..ed81b43ba 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -47,7 +47,6 @@ jobs: pax-options: '-x os390 -pp' pax-ssh-username: ${{ secrets.SSH_MARIST_USERNAME }} pax-ssh-password: ${{ secrets.SSH_MARIST_RACF_PASSWORD }} - extra-files: 'zss.tar' - name: '[Publish] Publish' uses: zowe-actions/shared-actions/publish@main From 266fbfdf43a65fcf2358053641881b44ee048586 Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 12:01:43 -0500 Subject: [PATCH 16/25] renamed manifest Signed-off-by: James Struga --- manifest.yaml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 manifest.yaml diff --git a/manifest.yaml b/manifest.yaml new file mode 100644 index 000000000..8c212fab7 --- /dev/null +++ b/manifest.yaml @@ -0,0 +1,33 @@ +--- +name: zss +# Component identifier. This identifier matches artifact path in Zowe Artifactory https://zowe.jfrog.io/. +id: org.zowe.zss +# Without the v +version: "{{build.version}}" +# Component version is defined in gradle.properties for Gradle project +# Human readable component name +title: Zowe System Services (ZSS) +# Human readable component description +description: 'Zowe System Services is an HTTPS and Websocket server that makes it easy to have secure, powerful web APIs backed by low-level z/OS constructs. It contains services for essential z/OS abilities such as working with files, datasets, and ESMs, but is also extensible by REST and Websocket "Dataservices" which are optionally present in App Framework "Plugins".' +homepage: https://zowe.org +keywords: + - zss + - appfw +license: EPL-2.0 +repository: + type: git + url: https://github.com/zowe/zss.git +build: + branch: "{{build.branch}}" + number: "{{build.number}}" + commitHash: "{{build.commitHash}}" + timestamp: "{{build.timestamp}}" +commands: + install: bin/internal-install.sh + start: bin/start.sh + configure: bin/configure.sh + validate: bin/validate.sh +# we do not specify encoding here because its already tagged ascii +apimlServices: + static: + - file: apiml-static-reg.yaml.template From 644b9556bea432f9d5b81cba69a6ad8f30e8770b Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 12:13:17 -0500 Subject: [PATCH 17/25] fix Signed-off-by: James Struga --- .github/workflows/build_test.yml | 15 +++++++++++++++ manifest.yaml | 33 -------------------------------- 2 files changed, 15 insertions(+), 33 deletions(-) delete mode 100644 manifest.yaml diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index ed81b43ba..e2d383f82 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -37,6 +37,21 @@ jobs: env: JF_ARTIFACTORY_1: ${{ secrets.JF_ARTIFACTORY_TOKEN }} + - name: 'conver manifest' + run: | + commit_hash=$(git rev-parse --verify HEAD) + current_timestamp=$(date +%s%3N) + zss_version=$(cat version.txt) + sed -e "s|{{build\.branch}}|${BRANCH_NAME}|g" \ + -e "s|{{build\.number}}|${BUILD_NUMBER}|g" \ + -e "s|{{build\.commitHash}}|${commit_hash}|g" \ + -e "s|{{build\.timestamp}}|${current_timestamp}|g" \ + -e "s|{{build\.version}}|${zss_version}|g" \ + "manifest.template.yaml" > "${PAX_WORKSPACE_DIR}/ascii/manifest.yaml.tmp" + mv "${PAX_WORKSPACE_DIR}/ascii/manifest.yaml.tmp" "${PAX_WORKSPACE_DIR}/ascii/manifest.yaml" + echo "[${SCRIPT_NAME}] manifest:" + cat "${PAX_WORKSPACE_DIR}/ascii/manifest.yaml" + - name: '[Prep 3] Prepare workflow' uses: zowe-actions/shared-actions/prepare-workflow@main diff --git a/manifest.yaml b/manifest.yaml deleted file mode 100644 index 8c212fab7..000000000 --- a/manifest.yaml +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: zss -# Component identifier. This identifier matches artifact path in Zowe Artifactory https://zowe.jfrog.io/. -id: org.zowe.zss -# Without the v -version: "{{build.version}}" -# Component version is defined in gradle.properties for Gradle project -# Human readable component name -title: Zowe System Services (ZSS) -# Human readable component description -description: 'Zowe System Services is an HTTPS and Websocket server that makes it easy to have secure, powerful web APIs backed by low-level z/OS constructs. It contains services for essential z/OS abilities such as working with files, datasets, and ESMs, but is also extensible by REST and Websocket "Dataservices" which are optionally present in App Framework "Plugins".' -homepage: https://zowe.org -keywords: - - zss - - appfw -license: EPL-2.0 -repository: - type: git - url: https://github.com/zowe/zss.git -build: - branch: "{{build.branch}}" - number: "{{build.number}}" - commitHash: "{{build.commitHash}}" - timestamp: "{{build.timestamp}}" -commands: - install: bin/internal-install.sh - start: bin/start.sh - configure: bin/configure.sh - validate: bin/validate.sh -# we do not specify encoding here because its already tagged ascii -apimlServices: - static: - - file: apiml-static-reg.yaml.template From dbdbab398f16929c943e9c42ae0fed3bb70dbbd2 Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 12:23:43 -0500 Subject: [PATCH 18/25] tets Signed-off-by: James Struga --- .github/workflows/build_test.yml | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index e2d383f82..d813f0afa 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -39,18 +39,24 @@ jobs: - name: 'conver manifest' run: | - commit_hash=$(git rev-parse --verify HEAD) - current_timestamp=$(date +%s%3N) - zss_version=$(cat version.txt) - sed -e "s|{{build\.branch}}|${BRANCH_NAME}|g" \ - -e "s|{{build\.number}}|${BUILD_NUMBER}|g" \ - -e "s|{{build\.commitHash}}|${commit_hash}|g" \ - -e "s|{{build\.timestamp}}|${current_timestamp}|g" \ - -e "s|{{build\.version}}|${zss_version}|g" \ - "manifest.template.yaml" > "${PAX_WORKSPACE_DIR}/ascii/manifest.yaml.tmp" - mv "${PAX_WORKSPACE_DIR}/ascii/manifest.yaml.tmp" "${PAX_WORKSPACE_DIR}/ascii/manifest.yaml" - echo "[${SCRIPT_NAME}] manifest:" - cat "${PAX_WORKSPACE_DIR}/ascii/manifest.yaml" + COMMIT_HASH=$(git rev-parse --verify HEAD) + CURRENT_TIME=$(date +%s%3N) + ZSS_VERSION=$(cat version.txt) + if [ -z ${{ github.event.pull_request.number }} ] + then + CURRENT_BRANCH=${GITHUB_REF#refs/heads/} + else + CURRENT_BRANCH=PR-${{ github.event.pull_request.number }} + fi + sed -e "s#{BUILD_BRANCH}#${CURRENT_BRANCH}#g" \ + -e "s#{BUILD_NUMBER}#${{ github.run_number }}#g" \ + -e "s#{BUILD_COMMIT_HASH}#${COMMIT_HASH}#g" \ + -e "s#{BUILD_TIMESTAMP}#${CURRENT_TIME}#g" \ + -e "s|{{build\.version}}|${ZSS_VERSION}|g" \ + manifest.template.yaml > manifest.json + + echo "Current manifest.json is:" + cat manifest.json - name: '[Prep 3] Prepare workflow' uses: zowe-actions/shared-actions/prepare-workflow@main From b54a426522df4eeb48b38255dcdd168ac517c052 Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 12:32:21 -0500 Subject: [PATCH 19/25] test Signed-off-by: James Struga --- .github/workflows/build_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index d813f0afa..6f0a78144 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -50,9 +50,9 @@ jobs: fi sed -e "s#{BUILD_BRANCH}#${CURRENT_BRANCH}#g" \ -e "s#{BUILD_NUMBER}#${{ github.run_number }}#g" \ - -e "s#{BUILD_COMMIT_HASH}#${COMMIT_HASH}#g" \ + -e "s#{BUILD_COMMITHASH}#${COMMIT_HASH}#g" \ -e "s#{BUILD_TIMESTAMP}#${CURRENT_TIME}#g" \ - -e "s|{{build\.version}}|${ZSS_VERSION}|g" \ + -e "s#{VERSION}#${ZSS_VERSION}#g" \ manifest.template.yaml > manifest.json echo "Current manifest.json is:" From cb803fdef56daa7d27be8a86fc210e400d3872f9 Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 12:33:50 -0500 Subject: [PATCH 20/25] remove --- Signed-off-by: James Struga --- manifest.template.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/manifest.template.yaml b/manifest.template.yaml index 8c212fab7..c23cee949 100644 --- a/manifest.template.yaml +++ b/manifest.template.yaml @@ -1,4 +1,3 @@ ---- name: zss # Component identifier. This identifier matches artifact path in Zowe Artifactory https://zowe.jfrog.io/. id: org.zowe.zss From 84cd4e5207b35cf2ad9eb36233dcbed2b4a637c2 Mon Sep 17 00:00:00 2001 From: James Struga Date: Wed, 8 Dec 2021 12:41:08 -0500 Subject: [PATCH 21/25] test Signed-off-by: James Struga --- .github/workflows/build_test.yml | 16 ++++++++-------- manifest.template.yaml | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 6f0a78144..9402c0cc1 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -48,15 +48,15 @@ jobs: else CURRENT_BRANCH=PR-${{ github.event.pull_request.number }} fi - sed -e "s#{BUILD_BRANCH}#${CURRENT_BRANCH}#g" \ - -e "s#{BUILD_NUMBER}#${{ github.run_number }}#g" \ - -e "s#{BUILD_COMMITHASH}#${COMMIT_HASH}#g" \ - -e "s#{BUILD_TIMESTAMP}#${CURRENT_TIME}#g" \ - -e "s#{VERSION}#${ZSS_VERSION}#g" \ - manifest.template.yaml > manifest.json + sed -e "s|{{build\.branch}}|${CURRENT_BRANCH}|g" \ + -e "s|{{build\.number}}|${{ github.run_number }}|g" \ + -e "s|{{build\.commitHash}}|${COMMIT_HASH}|g" \ + -e "s|{{build\.timestamp}}|${CURRENT_TIME}|g" \ + -e "s|{{build\.version}}|${ZSS_VERSION}|g" \ + manifest.template.yaml > manifest.yaml - echo "Current manifest.json is:" - cat manifest.json + echo "Current manifest.yaml is:" + cat manifest.yaml - name: '[Prep 3] Prepare workflow' uses: zowe-actions/shared-actions/prepare-workflow@main diff --git a/manifest.template.yaml b/manifest.template.yaml index c23cee949..8c212fab7 100644 --- a/manifest.template.yaml +++ b/manifest.template.yaml @@ -1,3 +1,4 @@ +--- name: zss # Component identifier. This identifier matches artifact path in Zowe Artifactory https://zowe.jfrog.io/. id: org.zowe.zss From 9fc1be74e848f92e9be25681d4378c90bcdf28db Mon Sep 17 00:00:00 2001 From: 1000TurquoisePogs Date: Wed, 8 Dec 2021 14:41:18 -0500 Subject: [PATCH 22/25] Fix changelog Signed-off-by: 1000TurquoisePogs --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3994a83f..d41581d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,6 @@ All notable changes to the ZSS package will be documented in this file. ## `1.26.0` - Enhancement: Add support for continuations in the ZIS PARMLIB member -- Enhancement: Get public key for JWT signature verification using APIML ## `1.25.0` From 013d7f90836c668e7b49de1a1d52ff620fbe0ca9 Mon Sep 17 00:00:00 2001 From: Vit Tomica Date: Thu, 9 Dec 2021 09:58:43 +0100 Subject: [PATCH 23/25] Update dependency Signed-off-by: Vit Tomica --- deps/zowe-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zowe-common-c b/deps/zowe-common-c index 0494e8817..1c4031858 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit 0494e88172a7c287fe848fe6bc0d630ea1f3f3d3 +Subproject commit 1c40318583e1411a8f1e5b94e18c7c29d02fd019 From 5d6421393a1ae8b3012726f276663f38c663f4f5 Mon Sep 17 00:00:00 2001 From: James Struga Date: Mon, 20 Dec 2021 20:52:58 -0500 Subject: [PATCH 24/25] fixed changes Signed-off-by: James Struga --- .github/workflows/build_test.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 9402c0cc1..cb6fa6007 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -2,7 +2,7 @@ name: Build and Test Workflow on: push: branches: - - "*" + - staging, master, rc pull_request: types: [opened, reopened, synchronize] workflow_dispatch: @@ -37,7 +37,7 @@ jobs: env: JF_ARTIFACTORY_1: ${{ secrets.JF_ARTIFACTORY_TOKEN }} - - name: 'conver manifest' + - name: 'convert manifest' run: | COMMIT_HASH=$(git rev-parse --verify HEAD) CURRENT_TIME=$(date +%s%3N) @@ -75,6 +75,5 @@ jobs: with: artifacts: | .pax/zss.pax - .pax/zss.tar perform-release: ${{ github.event.inputs.PERFORM_RELEASE }} \ No newline at end of file From ed7b49649b813e46a1d0beb9d161b4507543a2a1 Mon Sep 17 00:00:00 2001 From: James Struga Date: Tue, 4 Jan 2022 11:08:41 -0500 Subject: [PATCH 25/25] upgrade zss version Signed-off-by: James Struga --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 5ff8c4f5d..5db08bf2d 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.26.0 +1.27.0